Skip to content

Commit

Permalink
Merge 8991a31 into fe16abe
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismeeky committed Sep 8, 2019
2 parents fe16abe + 8991a31 commit 7c9831f
Show file tree
Hide file tree
Showing 18 changed files with 584 additions and 7 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"request": "^2.87.0",
"sequelize": "^5.14.0",
"sequelize-cli": "^5.5.0",
"socket.io": "^2.2.0",
"swagger-ui-express": "^4.0.7",
"underscore": "^1.9.1"
},
Expand Down
110 changes: 110 additions & 0 deletions src/controllers/NotificationController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* eslint-disable import/no-cycle */
import models from '../models';
import { HelperMethods } from '../utils';
/**
* Class representing the Notification controller
* @class Notification
* @description notification controller
*/
class Notification {
/**
* Test real-tim in-app notification
* Route: POST: api/v1/
* @param {object} req - HTTP Request object
* @param {object} res - HTTP Response object
* @return {res} res - HTTP Response object
* @memberof Notification
*/
static async notify(req, res) {
const { id } = req.params;
const { Message } = models;
let messageCount;
try {
const messages = await Message.findAll({
where: {
lineManager: id,
isRead: false,
}
});
messageCount = messages.length;
} catch (error) {
return HelperMethods.clientError(res);
}
const notify = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Barefoot Nomad</title>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<h1>Barefoot Nomad</h1>
<h2 id="message"></h2>
<script>
const socket = io();
let countMessage = ${messageCount};
const messageDisplay = messageCount => {
const message = document.getElementById('message');
message.textContent = 'Messages:' + messageCount;
};
messageDisplay(${messageCount});
const showDesktopNotification = (message, body, icon, sound, timeout) => {
if (!timeout) {
timeout = 4000;
}
const Notification = window.Notification || window.mozNotification
|| window.webkitNotification;
Notification.requestPermission(permission => {});
const requestNotificationPermissions = () => {
if (Notification.permission !== 'denied') {
Notification.requestPermission(permission => {});
}
}
requestNotificationPermissions();
const instance = new Notification(message, {
body,
icon,
sound
});
if (sound) {
instance.sound;
}
setTimeout(instance.close.bind(instance), timeout);
return false;
};
const sendNodeNotification = (title, message, icon) => {
socket.emit('new_notification', {
message,
title,
icon
});
};
const setNotification = data => {
showDesktopNotification('Barefoot Nomad', data, '/index.png');
sendNodeNotification(
'Barefoot Nomad',
'Browser Notification..!',
'/index.png'
);
};
socket.on('${id}', data => {
countMessage += 1;
messageDisplay(countMessage);
setNotification(data);
});
socket.on('show_notification', data => {
showDesktopNotification(data.title, data.message, data.icon);
});
</script>
</body>
</html>`;
res.send(notify);
}
}
export default Notification;
18 changes: 17 additions & 1 deletion src/controllers/RequestController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable import/no-cycle */
import models from '../models';
import { HelperMethods } from '../utils';
import { HelperMethods, Notification } from '../utils';

const { Request, User, Sequelize } = models;
const { Op } = Sequelize;
Expand All @@ -24,6 +25,11 @@ class RequestController {
const { body } = req;
const { dataValues } = await Request.create({ ...body, userId: id, });
if (dataValues.id) {
Notification.sendNewRequestNotifications(res, {
id,
type: 'single trip',
dataValues,
});
HelperMethods.requestSuccessful(res, {
success: true,
message: 'Trip booked successfully',
Expand Down Expand Up @@ -53,6 +59,11 @@ class RequestController {
}
const { dataValues } = await Request.create({ ...req.body, userId: id });
if (dataValues.id) {
Notification.sendNewRequestNotifications(res, {
id,
type: 'return trip',
dataValues,
});
return HelperMethods.requestSuccessful(res, {
success: true,
message: 'Trip booked successfully',
Expand Down Expand Up @@ -253,6 +264,11 @@ class RequestController {
multiflightDate: [...flightDate],
});
if (dataValues.id) {
Notification.sendNewRequestNotifications(res, {
id,
type: 'multi-city trip',
dataValues,
});
return HelperMethods.requestSuccessful(res, {
success: true,
message: 'Trip booked successfully',
Expand Down
4 changes: 3 additions & 1 deletion src/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* eslint-disable import/no-cycle */
import UserController from './UserController';
import NotificationController from './NotificationController';
import RequestController from './RequestController';

export { UserController, RequestController };
export { UserController, RequestController, NotificationController };
8 changes: 7 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable import/no-cycle */
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import swaggerUI from 'swagger-ui-express';
import dotenv from 'dotenv';
import morgan from 'morgan';
import passport from 'passport';
import socketIO from 'socket.io';
import doc from '../doc.json';
import { HelperMethods } from './utils';
import routes from './routes';
Expand Down Expand Up @@ -52,8 +54,12 @@ app.all('*', (req, res) => res.send({
message: 'route not found'
}));

app.listen(port, () => {
const server = app.listen(port, () => {
console.info(`Server is up and listening on port ${port}`);
});

export const io = socketIO(server);
io.on('connection', socket => {
console.info(`${socket.id} has connected`);
});
export default app;
4 changes: 4 additions & 0 deletions src/migrations/01-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ module.exports = {
type: Sequelize.BOOLEAN,
defaultValue: false
},
isSubscribed: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
username: {
type: Sequelize.STRING,
allowNull: false,
Expand Down
45 changes: 45 additions & 0 deletions src/migrations/04-create-messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Messages', {
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
},
userId: {
type: Sequelize.UUID,
references: {
model: 'Users',
key: 'id',
}
},
lineManager: {
type: Sequelize.UUID,
references: {
model: 'Users',
key: 'id',
}
},
type: {
type: Sequelize.ENUM,
values: ['approval', 'rejection', 'edition', 'creation'],
},
message: {
type: Sequelize.STRING,
},
isRead: {
type: Sequelize.BOOLEAN,
defaultValue: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
}),

down: queryInterface => queryInterface.dropTable('Messages')
};

35 changes: 35 additions & 0 deletions src/models/Messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export default (sequelize, DataTypes) => {
const Message = sequelize.define('Message', {
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
},
lineManager: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
},
type: {
type: DataTypes.ENUM(['approval', 'rejection', 'edition', 'creation'])
},
message: {
type: DataTypes.STRING,
},
isRead: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
},
{
classMethods: {
associate: models => {
Message.belongsTo(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE',
});
}
}
});
return Message;
};
4 changes: 4 additions & 0 deletions src/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ export default (sequelize, DataTypes) => {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
isSubscribed: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
Expand Down
3 changes: 3 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint-disable import/no-cycle */
import authRoute from './authRoute';
import requestRoute from './requestRoute';
import userRoute from './userRoute';
import socialAuthRoute from './socialAuthRoute';
import notificationRoute from './notificationRoute';
/**
* Handles request
* @param {object} app - An instance of the express module
Expand All @@ -19,6 +21,7 @@ const routes = app => {
requestRoute(app);
userRoute(app);
socialAuthRoute(app);
notificationRoute(app);
};

export default routes;
9 changes: 9 additions & 0 deletions src/routes/notificationRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { NotificationController } from '../controllers';

const notificationRoute = app => {
app.get(
'/api/v1/managerNotification/:id',
NotificationController.notify
);
};
export default notificationRoute;
5 changes: 5 additions & 0 deletions src/seeders/01-demo-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
email: 'demo1@demo.com',
username: 'user1',
isVerified: true,
isSubscribed: true,
role: 'Super Administrator',
password: await init(),
createdAt: new Date(),
Expand All @@ -28,6 +29,7 @@ module.exports = {
email: 'demo2@demo.com',
username: 'user2',
isVerified: true,
isSubscribed: true,
password: await init(),
role: 'Manager',
createdAt: new Date(),
Expand All @@ -40,6 +42,7 @@ module.exports = {
email: 'demo3@demo.com',
username: 'user3',
isVerified: true,
isSubscribed: true,
password: await init(),
role: 'Manager',
createdAt: new Date(),
Expand All @@ -53,6 +56,7 @@ module.exports = {
username: 'user4',
password: await init(),
isVerified: true,
isSubscribed: true,
role: 'Requester',
lineManager: '3821b930-ce48-4ac8-9ddf-ee3bf7980d08',
createdAt: new Date(),
Expand All @@ -66,6 +70,7 @@ module.exports = {
username: 'user5',
password: await init(),
isVerified: true,
isSubscribed: true,
role: 'Requester',
lineManager: '3821b930-ce48-4ac8-9ddf-ee3bf7980d08',
createdAt: new Date(),
Expand Down
8 changes: 8 additions & 0 deletions src/test/integrationTests/requestController.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ describe('Integration tests for the request controller', () => {
nonLineManagerToken = nonLineManager.body.data.userDetails.token;
});

describe('Test notification controller', () => {
it('should serve a html file', async () => {
const response = await chai.request(app)
.get('/api/v1/managerNotification/3821b930-ce48-4ac8-9ddf-ee3bf7980d08');
expect(response.headers['content-type']).to.equal('text/html; charset=utf-8');
});
});

describe('Authentication tests', () => {
it('should return an error if the authentication token is missing', async () => {
const response = await chai
Expand Down
6 changes: 6 additions & 0 deletions src/test/unitTests/sendEmail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ describe('Utility to send emails', () => {
expect(response).to.equal(true);
stubEmailSender.restore();
});
it('should send verification email after new trip request', async () => {
const stubEmailSender = sinon.stub(SendEmail, 'emailSender').returns(true);
const response = await SendEmail.sendRequestNotification('jideajayi11@gmail.com');
expect(response).to.equal(true);
stubEmailSender.restore();
});
it('should send email when passed the email details', async () => {
const details = {
email: 'jideajayi11@gmail.com',
Expand Down
Loading

0 comments on commit 7c9831f

Please sign in to comment.