Make lock charges calculation functional for happy path
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-redux": "^7.0.3",
|
||||
|
||||
59
client/src/scenes/UploadDLockData/components/FileUpload.js
Normal file
59
client/src/scenes/UploadDLockData/components/FileUpload.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {Form} from "semantic-ui-react";
|
||||
|
||||
import { uploadDoorLockData } from "../../../store/actions";
|
||||
|
||||
class FileUpload extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
file: null,
|
||||
};
|
||||
|
||||
this.onFileChange = this.onFileChange.bind(this);
|
||||
this.onUploadClick = this.onUploadClick.bind(this);
|
||||
}
|
||||
|
||||
onFileChange(event) {
|
||||
const file = event.target.files[0];
|
||||
this.setState({file});
|
||||
};
|
||||
|
||||
onUploadClick() {
|
||||
const { uploadDoorLockData } = this.props;
|
||||
const { file } = this.state;
|
||||
|
||||
if (file) {
|
||||
uploadDoorLockData(file);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { pending } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Form.Input
|
||||
fluid
|
||||
required
|
||||
label="Select DLock file"
|
||||
type="file"
|
||||
accept=".csv"
|
||||
onChange={this.onFileChange}
|
||||
/>
|
||||
<Form.Button onClick={this.onUploadClick} disabled={pending} >Upload</Form.Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
pending: state.doorLockData.pending,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
uploadDoorLockData: (doorLockDataFile) => uploadDoorLockData(dispatch, doorLockDataFile)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(FileUpload);
|
||||
@@ -0,0 +1,86 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Loader, Message, Tab, Label, Menu } from 'semantic-ui-react';
|
||||
|
||||
class UploadResults extends Component {
|
||||
render(){
|
||||
const {pending, result, error} = this.props;
|
||||
|
||||
const parsedEntries = result && result.parsedData ? result.parsedData : [];
|
||||
const errorEntries = result && result.parserErrors ? result.parserErrors : [];
|
||||
const unknownMembers = result && result.unknownMembers ? result.unknownMembers : [];
|
||||
|
||||
const renderParsedEntriesTab = () => {
|
||||
return (
|
||||
<div>
|
||||
<br/>
|
||||
<Message positive>
|
||||
<p>{parsedEntries.length} entries successfully parsed</p>
|
||||
</Message>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderErrorTabResults = (results) => {
|
||||
return (
|
||||
<div>
|
||||
<br/>
|
||||
{
|
||||
results.map((entry, index) => {
|
||||
return (
|
||||
<div key={`error-${entry.error}-${index}`}>
|
||||
<br/>
|
||||
<Message negative>
|
||||
<Message.Header>{entry.error}</Message.Header>
|
||||
<p>{JSON.stringify(entry.details)}</p>
|
||||
<p>File : {entry.file}</p>
|
||||
</Message>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const parsedEntriesTabTitle = (<Menu.Item key="parsed-entries">Parsed Entries<Label>{parsedEntries.length}</Label></Menu.Item>);
|
||||
const errorEntriesTabTitle = (<Menu.Item key="error-entries">Error Entries<Label>{errorEntries.length}</Label></Menu.Item>);
|
||||
const unknownMembersTabTitle = (<Menu.Item key="unknown-members">Unknown Members<Label>{unknownMembers.length}</Label></Menu.Item>);
|
||||
|
||||
const panes = [
|
||||
{menuItem: parsedEntriesTabTitle, render: renderParsedEntriesTab},
|
||||
{menuItem: errorEntriesTabTitle, render: () => renderErrorTabResults(errorEntries)},
|
||||
{menuItem: unknownMembersTabTitle, render: () => renderErrorTabResults(unknownMembers)}
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
pending && <Loader active />
|
||||
}
|
||||
<br/>
|
||||
{
|
||||
!pending && !error && result &&
|
||||
<Tab panes={panes}/>
|
||||
}
|
||||
{
|
||||
!pending && error &&
|
||||
<Message>
|
||||
<Message.Header>Upload Failed</Message.Header>
|
||||
<p>{error.data}</p>
|
||||
</Message>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
pending: state.doorLockData.pending,
|
||||
result: state.doorLockData.result,
|
||||
error: state.doorLockData.error,
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(UploadResults);
|
||||
@@ -1,39 +1,22 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import React from 'react';
|
||||
import { Container, Form } from "semantic-ui-react";
|
||||
|
||||
import MainMenu from '../../components/MainMenu';
|
||||
import { uploadDoorLockData } from "../../store/actions";
|
||||
import FileUpload from './components/FileUpload';
|
||||
import UploadResults from './components/UploadResults';
|
||||
|
||||
class UploadDLockData extends Component {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Container>
|
||||
<MainMenu/>
|
||||
<h3>DLock Data</h3>
|
||||
<hr/>
|
||||
<Form>
|
||||
<Form.Input
|
||||
fluid
|
||||
required
|
||||
label="Select DLock file"
|
||||
type="file"
|
||||
/>
|
||||
<Form.Button onClick={this.props.uploadDoorLockData}>Upload</Form.Button>
|
||||
</Form>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
function UploadDLockData() {
|
||||
return (
|
||||
<Container>
|
||||
<MainMenu/>
|
||||
<h3>DLock Data</h3>
|
||||
<hr/>
|
||||
<Form>
|
||||
<FileUpload/>
|
||||
</Form>
|
||||
<UploadResults/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
uploadDoorLockData: () => uploadDoorLockData(dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UploadDLockData);
|
||||
export default UploadDLockData;
|
||||
|
||||
@@ -4,14 +4,21 @@ import {
|
||||
UPLOAD_DOOR_LOCK_DATA_FAILED
|
||||
} from "../constants";
|
||||
|
||||
export const uploadDoorLockData = (dispatch) => {
|
||||
import API from '../../utilities/api';
|
||||
|
||||
export const uploadDoorLockData = (dispatch, doorLockDataFile) => {
|
||||
const formData = new FormData();
|
||||
formData.append('doorLockDataFile', doorLockDataFile);
|
||||
const additionalConfig = {
|
||||
headers: {'content-type': 'multipart/form-data'}
|
||||
};
|
||||
|
||||
dispatch({type: UPLOAD_DOOR_LOCK_DATA_PENDING});
|
||||
fetch('/api/doorLockData')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
dispatch({type: UPLOAD_DOOR_LOCK_DATA_SUCCESS, payload: data})
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch({type: UPLOAD_DOOR_LOCK_DATA_FAILED, payload: err})
|
||||
API.post('doorLock/upload', formData, additionalConfig)
|
||||
.then(response => {
|
||||
dispatch({type: UPLOAD_DOOR_LOCK_DATA_SUCCESS, payload: response.data})
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({type: UPLOAD_DOOR_LOCK_DATA_FAILED, payload: error.response})
|
||||
});
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
|
||||
const initialState = {
|
||||
pending: false,
|
||||
result: {},
|
||||
error: '',
|
||||
result: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
export const doorLockData = (state, action) => {
|
||||
@@ -18,15 +18,18 @@ export const doorLockData = (state, action) => {
|
||||
case UPLOAD_DOOR_LOCK_DATA_PENDING:
|
||||
return Object.assign({}, state, {
|
||||
pending: true,
|
||||
error: null,
|
||||
});
|
||||
case UPLOAD_DOOR_LOCK_DATA_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
pending: false,
|
||||
result: action.payload,
|
||||
error: null,
|
||||
});
|
||||
case UPLOAD_DOOR_LOCK_DATA_FAILED:
|
||||
return Object.assign({}, state, {
|
||||
pending: false,
|
||||
result: {},
|
||||
error: action.payload,
|
||||
});
|
||||
default:
|
||||
|
||||
5
client/src/utilities/api.js
Normal file
5
client/src/utilities/api.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export default axios.create({
|
||||
baseURL: '/api/'
|
||||
});
|
||||
@@ -1597,6 +1597,13 @@ aws4@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
||||
|
||||
axios@^0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
|
||||
dependencies:
|
||||
follow-redirects "^1.3.0"
|
||||
is-buffer "^1.1.5"
|
||||
|
||||
axobject-query@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
|
||||
@@ -3547,7 +3554,7 @@ flush-write-stream@^1.0.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.3.6"
|
||||
|
||||
follow-redirects@^1.0.0:
|
||||
follow-redirects@^1.0.0, follow-redirects@^1.3.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
|
||||
dependencies:
|
||||
|
||||
30
constants/constants.js
Normal file
30
constants/constants.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const USER_ENTRY_EVENT = 'User Entry';
|
||||
const ENABLE_PASSAGE_MODE = 'Enable Passage Mode by Group 2';
|
||||
const DISABLE_PASSAGE_MODE = 'Disable Passage Mode by Group 2';
|
||||
|
||||
const USER_LOCKED_DOOR = 'locked';
|
||||
const USER_UNLOCKED_DOOR = 'unlocked';
|
||||
|
||||
const VALID_CSV_HEADERS = ['Date', 'Time', 'User No', 'Name', 'Event'];
|
||||
|
||||
const csvParserErrors = {
|
||||
INVALID_HEADERS: 'Invalid headers',
|
||||
INVALID_ENTRY_EXPECTED_USER: 'Invalid entry type. Expected user entry type',
|
||||
INVALID_ENTRY_EXPECTED_PASSAGE_MODE: 'Invalid entry type. Expected enable/disable passage mode following user entry',
|
||||
UNKNOWN_MEMBER: 'Member is not registered in OfficeRnD system',
|
||||
};
|
||||
|
||||
const officeRnDAPIErrors = {
|
||||
FAILED_TO_FETCH_MEMBERS: 'Failed to fetch members',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
VALID_CSV_HEADERS,
|
||||
USER_ENTRY_EVENT,
|
||||
ENABLE_PASSAGE_MODE,
|
||||
DISABLE_PASSAGE_MODE,
|
||||
USER_LOCKED_DOOR,
|
||||
USER_UNLOCKED_DOOR,
|
||||
csvParserErrors,
|
||||
officeRnDAPIErrors,
|
||||
};
|
||||
@@ -1,7 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const { parseDoorLockDataFile, writeDoorLockEvent } = require("../services/doorLock");
|
||||
const { fetchAllBookings, writeBookingReservation } = require('../services/officeRnD/bookings');
|
||||
const { officeRnDAPIErrors } = require('../constants/constants');
|
||||
|
||||
const IncomingForm = require('formidable').IncomingForm;
|
||||
|
||||
const uploadDoorLockData = (req, res) => {
|
||||
res.json({status: 'ok'});
|
||||
const form = new IncomingForm();
|
||||
const parsingResults = [];
|
||||
|
||||
form.on('file', (field, file) => {
|
||||
if (file && file.type === 'text/csv') {
|
||||
parsingResults.push(parseDoorLockDataFile(file));
|
||||
}
|
||||
});
|
||||
|
||||
form.on('end', () => {
|
||||
Promise.all(parsingResults)
|
||||
.then((parserResults) => {
|
||||
const parsedData = [];
|
||||
const parserErrors = [];
|
||||
const unknownMembers = [];
|
||||
|
||||
parserResults.forEach((parserResult) => {
|
||||
parsedData.push(...parserResult.parsedData);
|
||||
parserErrors.push(...parserResult.errors);
|
||||
unknownMembers.push(...parserResult.unknownMembers);
|
||||
});
|
||||
|
||||
res.json({
|
||||
parsedData,
|
||||
parserErrors,
|
||||
unknownMembers
|
||||
});
|
||||
|
||||
fetchAllBookings()
|
||||
.then((bookingEntries) => {
|
||||
bookingEntries.forEach((bookingEntry) => writeBookingReservation(bookingEntry));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('===> ERROR');
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
parsedData.forEach((entry) => {
|
||||
writeDoorLockEvent(entry);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
res.status(500).send(officeRnDAPIErrors.FAILED_TO_FETCH_MEMBERS);
|
||||
});
|
||||
});
|
||||
|
||||
form.parse(req);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
12
helpers/api.js
Normal file
12
helpers/api.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const axios = require('axios');
|
||||
|
||||
const officeRnDToken = process.env.OFFICE_RnD_TOKEN;
|
||||
|
||||
const API = axios.create({
|
||||
baseURL: 'https://app.officernd.com/api/v1/organizations/sima-space-test-environment',
|
||||
headers: {'Authorization': `Bearer ${officeRnDToken}`}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
API,
|
||||
};
|
||||
33
migrations/20190529103954-create-door-lock-events.js
Normal file
33
migrations/20190529103954-create-door-lock-events.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('doorLockEvents', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
memberName: Sequelize.TEXT,
|
||||
memberNumber: Sequelize.INTEGER,
|
||||
memberId: Sequelize.TEXT,
|
||||
event: {
|
||||
type: Sequelize.ENUM,
|
||||
values: ['locked', 'unlocked']
|
||||
},
|
||||
timestamp: Sequelize.DATE,
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('doorLockEvents');
|
||||
}
|
||||
};
|
||||
30
migrations/20190530140559-create-booking-reservations.js
Normal file
30
migrations/20190530140559-create-booking-reservations.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('bookingReservations', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
reservationId: Sequelize.TEXT,
|
||||
memberId: Sequelize.TEXT,
|
||||
resource: Sequelize.TEXT,
|
||||
start: Sequelize.DATE,
|
||||
end: Sequelize.DATE,
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('bookingReservations');
|
||||
}
|
||||
};
|
||||
40
migrations/20190531154129-door-lock-incidents.js
Normal file
40
migrations/20190531154129-door-lock-incidents.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('doorLockIncidents', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
reservationId: Sequelize.TEXT,
|
||||
memberId: Sequelize.TEXT,
|
||||
resource: Sequelize.TEXT,
|
||||
bookingStart: Sequelize.DATE,
|
||||
bookingEnd: Sequelize.DATE,
|
||||
doorLockEventTimestamp: Sequelize.DATE,
|
||||
doorLockEventType: {
|
||||
type: Sequelize.ENUM,
|
||||
values: ['locked', 'unlocked']
|
||||
},
|
||||
chargeType: {
|
||||
type: Sequelize.ENUM,
|
||||
values: ['unlocked', 'unscheduled']
|
||||
},
|
||||
chargeFee: Sequelize.FLOAT,
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('doorLockIncidents');
|
||||
}
|
||||
};
|
||||
15
models/bookingReservation.js
Normal file
15
models/bookingReservation.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
const bookingReservation = sequelize.define('bookingReservation', {
|
||||
reservationId: DataTypes.TEXT,
|
||||
memberId: DataTypes.TEXT,
|
||||
resource: DataTypes.TEXT,
|
||||
start: DataTypes.DATE,
|
||||
end: DataTypes.DATE,
|
||||
}, {});
|
||||
bookingReservation.associate = function(models) {
|
||||
// associations can be defined here
|
||||
};
|
||||
return bookingReservation;
|
||||
};
|
||||
20
models/doorLockEvent.js
Normal file
20
models/doorLockEvent.js
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const { USER_LOCKED_DOOR, USER_UNLOCKED_DOOR } = require('../constants/constants');
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
const doorLockEvent = sequelize.define('doorLockEvent', {
|
||||
memberName: DataTypes.TEXT,
|
||||
memberNumber: DataTypes.INTEGER,
|
||||
memberId: DataTypes.TEXT,
|
||||
event: {
|
||||
type: DataTypes.ENUM,
|
||||
values: [USER_LOCKED_DOOR, USER_UNLOCKED_DOOR]
|
||||
},
|
||||
timestamp: DataTypes.DATE,
|
||||
}, {});
|
||||
doorLockEvent.associate = function(models) {
|
||||
// associations can be defined here
|
||||
};
|
||||
return doorLockEvent;
|
||||
};
|
||||
177
package-lock.json
generated
177
package-lock.json
generated
@@ -119,6 +119,15 @@
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.3.0",
|
||||
"is-buffer": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
@@ -292,6 +301,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"requires": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
},
|
||||
"buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
@@ -542,8 +575,7 @@
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"create-error-class": {
|
||||
"version": "3.0.2",
|
||||
@@ -571,6 +603,56 @@
|
||||
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
|
||||
"dev": true
|
||||
},
|
||||
"csv-parser": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-2.3.0.tgz",
|
||||
"integrity": "sha512-yfYRZ9P9LNKuRK0lEFf40Be4HcFxe1XgxWL/QmlkakSE8SHcWbOGFZA/u7YpfGX/hVbRUdbnO5xO8XuYxrcBtA==",
|
||||
"requires": {
|
||||
"buffer-alloc": "^1.1.0",
|
||||
"buffer-from": "^1.0.0",
|
||||
"execa": "^1.0.0",
|
||||
"generate-function": "^1.0.1",
|
||||
"generate-object-property": "^1.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"ndjson": "^1.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
|
||||
"requires": {
|
||||
"nice-try": "^1.0.4",
|
||||
"path-key": "^2.0.1",
|
||||
"semver": "^5.5.0",
|
||||
"shebang-command": "^1.2.0",
|
||||
"which": "^1.2.9"
|
||||
}
|
||||
},
|
||||
"execa": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
|
||||
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
|
||||
"requires": {
|
||||
"cross-spawn": "^6.0.0",
|
||||
"get-stream": "^4.0.0",
|
||||
"is-stream": "^1.1.0",
|
||||
"npm-run-path": "^2.0.0",
|
||||
"p-finally": "^1.0.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"strip-eof": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
|
||||
@@ -1045,12 +1127,35 @@
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
|
||||
"integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
|
||||
"requires": {
|
||||
"debug": "^3.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
|
||||
"dev": true
|
||||
},
|
||||
"formidable": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
|
||||
"integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg=="
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
@@ -1633,6 +1738,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"generate-function": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-1.1.0.tgz",
|
||||
"integrity": "sha1-VMIbCAGSsW2Yd3ecW7gWZudyNl8="
|
||||
},
|
||||
"generate-object-property": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
|
||||
"integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
|
||||
"requires": {
|
||||
"is-property": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||
@@ -1864,8 +1982,7 @@
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "1.2.1",
|
||||
@@ -2006,6 +2123,11 @@
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
|
||||
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
|
||||
},
|
||||
"is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
|
||||
},
|
||||
"is-redirect": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
|
||||
@@ -2032,8 +2154,7 @@
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
@@ -2069,6 +2190,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
@@ -2264,8 +2390,7 @@
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.1",
|
||||
@@ -2347,6 +2472,17 @@
|
||||
"to-regex": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"ndjson": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ndjson/-/ndjson-1.5.0.tgz",
|
||||
"integrity": "sha1-rmA7NrE0vOw0e0UkIrC/mNWDLsg=",
|
||||
"requires": {
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"minimist": "^1.2.0",
|
||||
"split2": "^2.1.0",
|
||||
"through2": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
@@ -2750,8 +2886,7 @@
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
|
||||
},
|
||||
"proto-list": {
|
||||
"version": "1.2.4",
|
||||
@@ -2824,7 +2959,6 @@
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -3287,6 +3421,14 @@
|
||||
"extend-shallow": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"split2": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
|
||||
"integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
|
||||
"requires": {
|
||||
"through2": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"static-extend": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||
@@ -3326,7 +3468,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -3373,6 +3514,15 @@
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||
"requires": {
|
||||
"readable-stream": "~2.3.6",
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"timed-out": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
|
||||
@@ -3641,8 +3791,7 @@
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"docker-build": "docker build -t simaspace .",
|
||||
"docker-start": "docker run -e POSTGRES_USER=docker -e POSTGRES_PASSWORD=docker -e POSTGRES_DB=CrmIntegration --name pg_simaspace -d -p 5432:5432 simaspace",
|
||||
"docker-stop": "docker stop pg_simaspace",
|
||||
"setup": "npm run install-server && npm run install-client && npm run docker-build && npm run docker-start && sleep 2 && npm run migrate",
|
||||
"setup": "npm run install-server && npm run install-client && npm run docker-build && npm run docker-start && sleep 5 && npm run migrate",
|
||||
"migrate": "npx sequelize db:migrate",
|
||||
"start-server": "nodemon server.js",
|
||||
"start-client": "cd client && yarn start",
|
||||
@@ -21,9 +21,13 @@
|
||||
"node": "11.12.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"csv-parser": "^2.3.0",
|
||||
"dotenv": "^8.0.0",
|
||||
"express": "^4.17.0",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
"formidable": "^1.2.1",
|
||||
"moment": "^2.24.0",
|
||||
"pg": "^7.11.0",
|
||||
"sequelize": "^5.8.6",
|
||||
"sequelize-cli": "^5.4.0"
|
||||
|
||||
@@ -7,6 +7,6 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', apiStatusCheck);
|
||||
router.get('/doorLockData', uploadDoorLockData);
|
||||
router.post('/doorLock/upload', uploadDoorLockData);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
150
services/doorLock.js
Normal file
150
services/doorLock.js
Normal file
@@ -0,0 +1,150 @@
|
||||
'use strict';
|
||||
|
||||
const db = require('../models/index');
|
||||
const fs = require('fs');
|
||||
const csv = require('csv-parser');
|
||||
const moment = require('moment');
|
||||
|
||||
const {
|
||||
USER_ENTRY_EVENT,
|
||||
ENABLE_PASSAGE_MODE,
|
||||
DISABLE_PASSAGE_MODE,
|
||||
USER_UNLOCKED_DOOR,
|
||||
USER_LOCKED_DOOR,
|
||||
VALID_CSV_HEADERS,
|
||||
csvParserErrors,
|
||||
} = require('../constants/constants');
|
||||
|
||||
const { fetchAllMembers, findMember } = require('../services/officeRnD/members');
|
||||
|
||||
|
||||
const parseDoorLockDataFile = (file) => {
|
||||
return new Promise ((resolve, reject) => {
|
||||
const results = [];
|
||||
const errors = [];
|
||||
const unknownMembers = [];
|
||||
let isValidFile = true;
|
||||
|
||||
fetchAllMembers()
|
||||
.then(() => {
|
||||
fs.createReadStream(file.path)
|
||||
.pipe(csv({
|
||||
mapHeaders: ({ header, index }) => header.trim().toLowerCase(),
|
||||
mapValues: ({ header, index, value }) => value.trim()
|
||||
}))
|
||||
.on('headers', (headers) => {
|
||||
|
||||
const sortedValidHeadersArray = VALID_CSV_HEADERS.concat().sort();
|
||||
const sortedParsedHeadersArray = headers.map(header => header.trim()).sort();
|
||||
|
||||
let validHeaders = true;
|
||||
if (sortedParsedHeadersArray.length !== sortedValidHeadersArray.length) {
|
||||
validHeaders = false;
|
||||
}else {
|
||||
for (let i = 0; i < sortedValidHeadersArray.length; i++){
|
||||
validHeaders = validHeaders
|
||||
&& (sortedValidHeadersArray[i] === sortedParsedHeadersArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!validHeaders){
|
||||
isValidFile = false;
|
||||
errors.push({
|
||||
error: csvParserErrors.INVALID_HEADERS,
|
||||
details: `Expected headers : ${JSON.stringify(VALID_CSV_HEADERS)}`,
|
||||
file: file.name,
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('data', (data) => {
|
||||
if (!isValidFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const eventType = data.event.trim();
|
||||
if ([USER_ENTRY_EVENT, ENABLE_PASSAGE_MODE, DISABLE_PASSAGE_MODE].includes(eventType)){
|
||||
results.push(data);
|
||||
}
|
||||
})
|
||||
.on('end', () => {
|
||||
const parsedData = [];
|
||||
let i = 0;
|
||||
while (i < results.length){
|
||||
//Verify pair
|
||||
//First entry type should be user entry and second should be enable / disable passage
|
||||
const firstEntry = results[i];
|
||||
const secondEntry = results[i+1];
|
||||
|
||||
if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){
|
||||
const memberObject = findMember(firstEntry.name);
|
||||
|
||||
if (!memberObject){
|
||||
//Check if member is already labeled as unknown
|
||||
const unknownMember = unknownMembers.find((member) => member.details === firstEntry.name);
|
||||
if (!unknownMember){
|
||||
unknownMembers.push({
|
||||
error: csvParserErrors.UNKNOWN_MEMBER,
|
||||
details: firstEntry.name,
|
||||
file: file.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (secondEntry && (secondEntry.event === ENABLE_PASSAGE_MODE || secondEntry.event === DISABLE_PASSAGE_MODE)){
|
||||
const event = (secondEntry.event === ENABLE_PASSAGE_MODE) ? USER_UNLOCKED_DOOR : USER_LOCKED_DOOR;
|
||||
const dateTimeString = `${firstEntry.date} ${firstEntry.time}`;
|
||||
const timestamp = moment.utc(dateTimeString, 'MM/DD/YY HH:mm:ss A').toISOString();
|
||||
|
||||
//Verify that member is registered in OfficeRnD system
|
||||
if (memberObject){
|
||||
const entryData = {
|
||||
memberName: firstEntry.name,
|
||||
memberNumber: firstEntry['user no'],
|
||||
memberId: memberObject.memberId,
|
||||
timestamp,
|
||||
event,
|
||||
};
|
||||
|
||||
parsedData.push(entryData);
|
||||
}
|
||||
i+=2;
|
||||
} else {
|
||||
errors.push({
|
||||
error: csvParserErrors.INVALID_ENTRY_EXPECTED_PASSAGE_MODE,
|
||||
details: firstEntry,
|
||||
file: file.name,
|
||||
});
|
||||
i+=1;
|
||||
}
|
||||
} else {
|
||||
errors.push({
|
||||
error: csvParserErrors.INVALID_ENTRY_EXPECTED_USER,
|
||||
details: firstEntry,
|
||||
file: file.name,
|
||||
});
|
||||
i+=1;
|
||||
}
|
||||
}
|
||||
resolve({
|
||||
parsedData,
|
||||
unknownMembers,
|
||||
errors
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const writeDoorLockEvent = (entry) => {
|
||||
db.doorLockEvent.findOrCreate({where: {...entry}, defaults: {...entry}})
|
||||
.then()
|
||||
.catch();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
parseDoorLockDataFile,
|
||||
writeDoorLockEvent,
|
||||
};
|
||||
40
services/officeRnD/bookings.js
Normal file
40
services/officeRnD/bookings.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
const db = require('../../models/index');
|
||||
|
||||
const { API } = require('../../helpers/api');
|
||||
|
||||
const fetchAllBookings = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
API.get('/bookings')
|
||||
.then((result) => {
|
||||
const cleanedBookingReservations = [];
|
||||
const bookingData = result && result.data ? result.data : [];
|
||||
|
||||
bookingData.forEach((fullBookingEntry) => {
|
||||
cleanedBookingReservations.push({
|
||||
reservationId: fullBookingEntry['_id'],
|
||||
memberId: fullBookingEntry.member,
|
||||
resource: fullBookingEntry.resourceId,
|
||||
start: fullBookingEntry.start.dateTime,
|
||||
end: fullBookingEntry.end.dateTime,
|
||||
});
|
||||
});
|
||||
resolve(cleanedBookingReservations);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const writeBookingReservation = (bookingReservation) => {
|
||||
db.bookingReservation.findOrCreate({where: {...bookingReservation}, defaults: {...bookingReservation}})
|
||||
.then()
|
||||
.catch();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
fetchAllBookings,
|
||||
writeBookingReservation,
|
||||
};
|
||||
33
services/officeRnD/members.js
Normal file
33
services/officeRnD/members.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const { API } = require('../../helpers/api');
|
||||
|
||||
const membersList = [];
|
||||
|
||||
const fetchAllMembers = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
API.get('/members')
|
||||
.then((result) => {
|
||||
const members = result.data || [];
|
||||
members.forEach((member) => {
|
||||
membersList.push({
|
||||
name: member.name,
|
||||
memberId: member['_id'],
|
||||
});
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const findMember = (memberName) => {
|
||||
return membersList.find((member) => member.name === memberName);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
fetchAllMembers,
|
||||
findMember,
|
||||
};
|
||||
Reference in New Issue
Block a user