Skip to content

Commit

Permalink
feature(notitfication):user comment notification
Browse files Browse the repository at this point in the history
    Implementation of user comment notification.
    Users should receive comment notification when comments are created
    on thier trip request.

    Finished[167728039]
  • Loading branch information
Wokoro committed Sep 14, 2019
1 parent 788f084 commit 8d2ab58
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 88 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
],
"reporter": [
"lcov",
"text"
"text",
"text-summary"
],
"cache": false,
"require": [
Expand Down
17 changes: 15 additions & 2 deletions src/controllers/CommentController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import UserRepository from '../repositories/UserRepository';
import CommentRepository from '../repositories/CommentRepository';
import TripRequestRepository from '../repositories/TripRequestRepository';
import NotificationRepository from '../repositories/NotificationRepository';

import { sendSuccessResponse, sendErrorResponse } from '../utils/sendResponse';

Expand Down Expand Up @@ -35,17 +36,29 @@ class CommentController {
const tripRequestDetails = await TripRequestRepository.findById({ uuid: tripRequestUuid });
if (!tripRequestDetails) return sendErrorResponse(res, 404, 'This trip request does not exist');
const { user_uuid: tripRequestOwner } = tripRequestDetails;


// ensuring ownership
const isAllowed = await checkrequestOwner(tripRequestOwner, userUuid, userRole);
if (!isAllowed && userRole === 'Manager') return sendErrorResponse(res, 403, 'You are not the manager of the user that created this trip request');
if (!isAllowed && userRole !== 'Manager') return sendErrorResponse(res, 403, 'You can\'t comment on a trip request you did not create');


// creating comment
const commentDetails = {
user_uuid: userUuid,
trip_request_uuid: tripRequestUuid,
message
};

// create notification
const notificationDetails = {
user_uuid: userUuid,
message,
status: 'unread',
notification_type: 'comment'
};

const createdComment = await CommentRepository.create(commentDetails);
await NotificationRepository.create(notificationDetails);
return sendSuccessResponse(res, 201, createdComment);
} catch (err) {
return next(err);
Expand Down
99 changes: 99 additions & 0 deletions src/controllers/CommentNotificationController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* eslint-disable */
import Model from '../models';
import { setHeaders, addHook } from '../utils/NotificationHelpers';
import tripRepository from '../repositories/TripRequestRepository';
import NotificationRepository from '../repositories/NotificationRepository';
import dotenv from 'dotenv';

dotenv.config();

const { Comment } = Model;

/**
* @description Comment controller
*/
class CommentNotifications {
/**
* @param {Object} req - HTTP request object
*
* @param {Object} res - HTTP response object
*
* @param {Function} next - Function to trigger next middleware
*
* @return {Object} Return success message and account creation status
*/

constructor() {
this.model = Comment;
}

/**
* @description Add notifications for comment model
*
* @param {Object} req - HTTP request object
*
* @param {Object} res - HTTP response object
*
* @param {Function} next - Function to execute next function
*
* @returns {Void} Nothing is been returned. Just sets hooks to a given model
*/
async create (req, res, next) {

const appBaseURL = process.env['APP_BASE_PATH'];
let tripsObj = [];
const currentUserUUID = req.userData.uuid;

// Set HTTP headers for SSE
setHeaders(req, res);

try {
// Get all unread notifications
const notifs = await NotificationRepository.getAll({
user_uuid: currentUserUUID,
status: 'unread',
notification_type: 'comment'
});

// Construct comment summary and link to comment.
notifs.reduce((trips, currentTrip)=>{
return tripsObj.push({
uuid: currentTrip.uuid,
message: currentTrip.message,
link: `${appBaseURL}/trips/${currentTrip.uuid}`
})
}, tripsObj);
// Send unread comments to user
res.write('event: comment\n');
res.write(`data: ${JSON.stringify(tripsObj)}\n\n`);

// Method to execute for every comment creation
const helper = async (comment) => {
const {dataValues: {trip_request_uuid}} = comment;

const {
dataValues: {
user_uuid: requester_id,
uuid: trip_uuid
}
} = await tripRepository.findById({uuid: trip_request_uuid});

if(requester_id == currentUserUUID){
res.write(`event: comment\n`);
res.write(`data: ${JSON.stringify({
message: comment.message,
link: `${appBaseURL}/trips/${trip_uuid}`
})}\n\n`);
}
}

// Add realtime notification. Triggered after new comment is created.
addHook(Comment, 'afterCreate', 'notification', helper);

} catch (error) {
next(error);
}
}
}

export default new CommentNotifications();
75 changes: 0 additions & 75 deletions src/controllers/ExampleUserNotificationController.js

This file was deleted.

10 changes: 10 additions & 0 deletions src/database/seeders/20190825080303-create-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ export default {
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
},
{
uuid: uuid(),
name: 'Wokoro Samuel Douye',
email: 'wokorosamuel@yahoo.com',
role: 'Requester',
is_verified: true,
password: hashPassword('Samsizzy777'),
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
},
{
uuid: uuid(),
name: 'Makaraba Blessing',
Expand Down
33 changes: 33 additions & 0 deletions src/database/seeders/20190913015500-notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import uuid from 'uuid';

'use strict';

module.exports = {
up: (queryInterface, Sequelize) => {
const notificationData = [
{
uuid: uuid(),
user_uuid: 'abef6009-48be-4b38-80d0-b38c1bc39922',
message: 'Can i have my trip request approved?',
status: 'unread',
notification_type: 'comment',
created_at: new Date(),
updated_at: new Date()
},
{
uuid: uuid(),
user_uuid: 'abef6009-48be-4b38-80d0-b38c1bc39922',
message: 'Please i need my trip request approved',
status: 'unread',
notification_type: 'comment',
created_at: new Date(),
updated_at: new Date()
}
];
return queryInterface.bulkInsert('Notifications', notificationData, {});
},

down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('Notifications', null, {});
}
};
13 changes: 3 additions & 10 deletions src/routes/notifications.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { Router } from 'express';

import userNotificationController from '../controllers/ExampleUserNotificationController';
import authenticateUser from '../middlewares/authenticateUser'
import commentNotificationController from '../controllers/CommentNotificationController';

const notificationRoutes = Router();

// Live notification endpoint for user creation.
notificationRoutes.get('/user', userNotificationController.create);

// Example Live notification endpoint for request.

// notificationRoutes.get(
// '/request',
// authenticateUser,
// notificationController.requestNotifications
// );
notificationRoutes.get('/comment', authenticateUser, commentNotificationController.create);

export default notificationRoutes;
56 changes: 56 additions & 0 deletions tests/comment.notification.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { describe, it } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chai, { expect } from 'chai';
import chaiHttp from 'chai-http';
import CommentNotificationController from '../src/controllers/CommentNotificationController';
import {req, res, next} from './mock.data';
import NotificationRepository from '../src/repositories/NotificationRepository';
import tripRepository from '../src/repositories/TripRequestRepository';

chai.use(chaiHttp);
chai.use(sinonChai);

describe('Comment notification', () => {

before(()=>{
sinon.spy(res, 'writeHead');
sinon.spy(res, 'write');
});

after(()=>{ sinon.restore(); });

it('it should set headers should be set', ()=>{
CommentNotificationController.create(req, res, next);
expect(res.writeHead).to.be.calledWith(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
});
expect(res.write).to.be.calledWith('\n');
});

it('it should return unread comments', ()=>{

sinon.stub(NotificationRepository, 'getAll').returns([
{
uuid: 'abef6009-48be-4b38-80d0-b38c1bc39922',
user_uuid: 'abef6009-48be-4b38-80d0-b38c1bc39922',
message: 'Please i need my trip request approved',
status: 'unread',
notification_type: 'comment',
created_at: new Date(),
updated_at: new Date()
}
]);
CommentNotificationController.create(req, res, next);
expect(res.write.called).to.be.true;
NotificationRepository.getAll.restore();
});

it('it should call next function for server error', ()=>{
sinon.stub(NotificationRepository, 'getAll').throws();
expect(CommentNotificationController.create).throws
NotificationRepository.getAll.restore();
});
});
3 changes: 3 additions & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import './office.test';
// Password reset
import './passwordReset.test';

// Comment notification
import './comment.notification.test';

// UTILITIES

// Getting user info test
Expand Down
1 change: 1 addition & 0 deletions tests/request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,5 +398,6 @@ describe('Test Create Trip Request', () => {
expect(next.called).to.true;
sinon.restore();
});

});
});

0 comments on commit 8d2ab58

Please sign in to comment.