Skip to content
This repository has been archived by the owner on Jul 20, 2020. It is now read-only.

Commit

Permalink
Merge pull request #46 from andela/ft-notifications-travel-request-17…
Browse files Browse the repository at this point in the history
…0947555
  • Loading branch information
jabichris committed Mar 17, 2020
2 parents 79c2e6e + 4d49948 commit 5f22cef
Show file tree
Hide file tree
Showing 25 changed files with 247 additions and 44 deletions.
4 changes: 4 additions & 0 deletions nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"watch": ["src/"],
"ignore": ["src/services/localesServices/locales"]
}
30 changes: 16 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"dev": "nodemon --exec babel-node src/index.js",
"build": "rm -rf ./dist && babel -d ./dist ./src",
"test": "npm run db:reset --env test && npx sequelize db:seed:undo:all --env test && npm run seed --env test && npm run test:unit",
"test:unit": "NODE_ENV=test nyc --require @babel/register mocha --recursive './src/**/*.test.js' --timeout 20000 --exit",
"test:unit": "NODE_ENV=test nyc --require @babel/register mocha --recursive './src/**/*.test.js' --timeout 2000 --exit",
"db:migrate": "npx sequelize db:migrate --env test",
"db:reset": "npx sequelize db:migrate:undo:all --env test && npm run db:migrate --env test",
"create": "npx sequelize db:migrate",
Expand Down
1 change: 0 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
background-color: #eee;
font-size: 25px;
text-align: center;

}
.notifs {
color: rgb(13, 13, 46);
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/authController.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default class AuthController {
lastName: lastName.toLowerCase(),
email,
password: hashedPassword,
role: 'requester'
role: 'requester',
});
const token = provideToken(user.id, user.isVerified, email, user.role);
const link = `http://${process.env.BASE_URL}/api/v1/auth/verification?token=${token}&email=${email}`;
Expand Down
32 changes: 32 additions & 0 deletions src/controllers/notificationController.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,36 @@ export default class notificationController {
return Response.errorResponse(res, 500, res.__('server error'));
}
}

/**
* @param {object} req
* @param {object} res
* @param {object} next
* @return {object} notification
*/
static async optOutEmailNotifications(req, res) {
try {
const { user } = req;
await db.User.update({ emailNotifications: false }, { where: { id: user.id } });
return Response.success(res, 200, res.__('you have opted out of email notifications'));
} catch (error) {
return Response.errorResponse(res, 500, res.__('server error'));
}
}

/**
* @param {object} req
* @param {object} res
* @param {object} next
* @return {object} notification
*/
static async optInEmailNotifications(req, res) {
try {
const { user } = req;
await db.User.update({ emailNotifications: true }, { where: { id: user.id } });
return Response.success(res, 200, res.__('you have opted in for email notifications'));
} catch (error) {
return Response.errorResponse(res, 500, res.__('server error'));
}
}
}
55 changes: 55 additions & 0 deletions src/controllers/tripsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default class requestController {
static async createRequest(req, res) {
try {
const { user } = req;

const {
location, destination, reason, departureDate, gender, role, passportName
} = req.body;
Expand Down Expand Up @@ -51,6 +52,24 @@ export default class requestController {
role
}],
});

const managerInfo = await db.User.findOne({ where: { id: user.managerId } });

const notification = await notifService.createNotif(
newRequest.managerId,
managerInfo.email,
`A trip request to ${newRequest.destination} on ${newRequest.departureDate} has been requested by ${user.firstName}, ${user.lastName}, it is waiting your approval.`,
'#'
);
const content = {
intro: `${req.__('A trip request to')} ${newRequest.destination} ${req.__('on')} ${newRequest.departureDate} ${req.__('has been requested by')} ${newRequest.profileData[0].passportName}`,
instruction: req.__('To view this open request, click below'),
text: req.__('View request'),
signature: req.__('signature')
};

await SendNotification.sendNotif(notification, req, content);

return Response.success(res, 201, res.__('Request created successfully'), newRequest);
} catch (error) {
return Response.errorResponse(res, 500, error.message);
Expand Down Expand Up @@ -101,6 +120,24 @@ export default class requestController {
role: role.toLowerCase().trim()
}],
});

const managerInfo = await db.User.findOne({ where: { id: user.managerId } });

const notification = await notifService.createNotif(
newRequest.managerId,
managerInfo.email,
`A trip request to ${newRequest.destination} on ${newRequest.departureDate} has been requested by ${user.firstName}, ${user.lastName}, it is waiting your approval.`,
'#'
);
const content = {
intro: `${req.__('A trip request to')} ${newRequest.destination} ${req.__('on')} ${newRequest.departureDate} ${req.__('has been requested by')} ${newRequest.profileData[0].passportName}`,
instruction: req.__('To view this open request, click below'),
text: req.__('View request'),
signature: req.__('signature')
};

await SendNotification.sendNotif(notification, req, content);

return Response.success(res, 201, res.__('Request created successfully'), newRequest);
} catch (err) {
return Response.errorResponse(res, 500, err.message);
Expand Down Expand Up @@ -234,6 +271,24 @@ export default class requestController {
role
}],
});

const managerInfo = await db.User.findOne({ where: { id: user.managerId } });

const notification = await notifService.createNotif(
newMulticityRequest.managerId,
managerInfo.email,
`A trip request to ${newMulticityRequest.destination} on ${newMulticityRequest.departureDate} has been requested by ${user.firstName}, ${user.lastName}, it is waiting your approval.`,
'#'
);
const content = {
intro: `${req.__('A trip request to')} ${newMulticityRequest.destination} ${req.__('on')} ${newMulticityRequest.departureDate} ${req.__('has been requested by')} ${newMulticityRequest.profileData[0].passportName}`,
instruction: req.__('To view this open request, click below'),
text: req.__('View request'),
signature: req.__('signature')
};

await SendNotification.sendNotif(notification, req, content);

return Response.success(res, 201, res.__('Multi city request created successfully'), newMulticityRequest);
} catch (error) {
return Response.errorResponse(res, 500, error.message);
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ app.use((req, res, next) => {
req.io = io;
next();
});

app.use('/', welcome);
app.use('/api-doc', swagger);
app.use('/api/v1/auth', authRouter);
Expand Down
1 change: 1 addition & 0 deletions src/middlewares/protectRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default class protectRoutes {
static async verifyUser(req, res, next) {
try {
const { token } = req.headers;

if (!token) {
return Response.errorResponse(res, 401, res.__('No token provided'));
}
Expand Down
5 changes: 5 additions & 0 deletions src/migrations/20200209182554-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ module.exports = {
type: Sequelize.BOOLEAN,
defaultValue: false
},
emailNotifications: {
allowNull: false,
type: Sequelize.BOOLEAN,
defaultValue: true
},
language: {
type: Sequelize.STRING
},
Expand Down
1 change: 1 addition & 0 deletions src/models/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = (sequelize, DataTypes) => {
Notifications.associate = (models) => {
Notifications.belongsTo(models.User, {
foreignKey: 'receiverId',
as: 'user',
onDelete: 'CASCADE'
});
};
Expand Down
3 changes: 2 additions & 1 deletion src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module.exports = (sequelize, DataTypes) => {
residence: DataTypes.STRING,
birthdate: DataTypes.STRING,
image: DataTypes.STRING,
isVerified: DataTypes.BOOLEAN
isVerified: DataTypes.BOOLEAN,
emailNotifications: DataTypes.BOOLEAN
}, {});
User.associate = (models) => {
User.hasMany(models.Bookings, {
Expand Down
2 changes: 2 additions & 0 deletions src/routes/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ import protectRoute from '../middlewares/protectRoute';
const router = express.Router();

router.patch('/all-read', protectRoute.verifyUser, protectRoute.checkUnreadNotifications, validationResult, notificationController.markAllNotificationsAsRead);
router.patch('/email-opt-out', protectRoute.verifyUser, validationResult, notificationController.optOutEmailNotifications);
router.patch('/email-opt-in', protectRoute.verifyUser, validationResult, notificationController.optInEmailNotifications);

export default router;
1 change: 0 additions & 1 deletion src/routes/tripsRoutes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import express from 'express';
import tripsController from '../controllers/tripsController';
import {
Expand Down
12 changes: 9 additions & 3 deletions src/services/localesServices/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@
"User does not exist or they are not a manager or they are both managers": "User does not exist or they are not a manager or they are both managers",
"Sorry, you are not authorized to access this page.": "Sorry, you are not authorized to access this page.",
"server error": "server error",
"Your trip to gisenyi on 2020-12-01 you requested has been rejected": "Your trip to gisenyi on 2020-12-01 you requested has been rejected",
"To view this rejected request you made click below": "To view this rejected request you made click below",
"the trip to": "the trip to",
"on": "on",
Expand All @@ -130,7 +129,6 @@
"To view this As a your manager I can add any comment on your request. I can also cancel or approve your request but let me comment first request you made click below": "To view this As a your manager I can add any comment on your request. I can also cancel or approve your request but let me comment first request you made click below",
"View comment": "View comment",
"To view this thanks for your comment manager. This is my request too. And please Approve my request after this comment request you made click below": "To view this thanks for your comment manager. This is my request too. And please Approve my request after this comment request you made click below",
"To view this yeahahaha 252525626262 request you made click below": "To view this yeahahaha 252525626262 request you made click below",
"As a your manager I can add any comment on your request. I can also cancel or approve your request but let me comment first": "As a your manager I can add any comment on your request. I can also cancel or approve your request but let me comment first",
"thanks for your comment manager. This is my request too. And please Approve my request after this comment": "thanks for your comment manager. This is my request too. And please Approve my request after this comment",
"your manager posted a comment": "your manager posted a comment",
Expand All @@ -140,5 +138,13 @@
"To view this %s click below": "To view this %s click below",
"Your Manager commented: %s": "Your Manager commented: %s",
"Your requsester commented: %s": "Your requsester commented: %s",
"Your requester commented: %s": "Your requester commented: %s"
"Your requester commented: %s": "Your requester commented: %s",
"To view this open request you made click below": "To view this open request you made click below",
"that you requested has been sent made by": "that you requested has been sent made by",
"To view this open request, click below": "To view this open request, click below",
"you have opted out of email notifications": "you have opted out of email notifications",
"you have opted in for email notifications": "you have opted in for email notifications",
"that you requested has been made by": "that you requested has been made by",
"A trip request to": "A trip request to",
"has been requested by": "has been requested by"
}
7 changes: 6 additions & 1 deletion src/services/localesServices/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,10 @@
"your manager posted a comment: %s": "votre manager a posté un commentaire: %s",
"Your Manager commented: %s": "Votre demandeur a commenté: %s",
"View comment": "Afficher le commentaire",
"your manager posted a comment": "votre manager a posté un commentaire"
"your manager posted a comment": "votre manager a posté un commentaire",
"that you requested has been sent made by": "que vous avez demandé a été envoyé par",
"you have opted out of email notifications":"vous avez désactivé les notifications par e-mail",
"you have opted in for email notifications": "vous avez activé les notifications par e-mail",
"A trip request to": "Une demande de voyage à",
"has been requested by": "a été demandé par"
}
43 changes: 43 additions & 0 deletions src/swagger/notifications.swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,49 @@
* description: Server Error.
* */

/**
* @swagger
* /api/v1/notifications/email-opt-out:
* patch:
* security:
* - bearerAuth: []
* tags:
* - Notifications
* name: optOutEmailNotifications
* summary: enables a user to mark opt out for email notifications
* produces:
* - application/json
* parameters:
* - name: token
* in: header
* responses:
* '200':
* description: you have opted out of email notifications.
* '500':
* description: Server Error.
* */

/**
* @swagger
* /api/v1/notifications/email-opt-in:
* patch:
* security:
* - bearerAuth: []
* tags:
* - Notifications
* name: optInEmailNotifications
* summary: enables a user to mark opt in for email notifications
* produces:
* - application/json
* parameters:
* - name: token
* in: header
* responses:
* '200':
* description: you have opted in for email notifications.
* '500':
* description: Server Error.
* */

/**
* @swagger
Expand Down
2 changes: 1 addition & 1 deletion src/tests/comments.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ describe('COMMENTS TESTS', () => {
expect(res.body.data.requestId).to.equal('51e74db7-5510-4f50-9f15-e23710331ld5');
expect(res.body.data.commmentOwner).to.equal('0119b84a-99a4-41c0-8a0e-6e0b6c385165');
expect(res.body.data.comment).to.equal('As a your manager I can add any comment on your request. I can also cancel or approve your request but let me comment first');
done();
});
clientSocket.on('notification', (msg) => {
expect(JSON.parse(msg)).to.be.an('object');
expect(JSON.parse(msg).receiverId).to.equal('79660e6f-4b7d-4g21-81re-74f54e9e1c8a');
expect(JSON.parse(msg).status).to.equal('unread');
expect(JSON.parse(msg).content).to.equal('your manager posted a comment');
done();
});
});
it('should not allow a manager to comment on a request which does not belong to him', (done) => {
Expand Down
Loading

0 comments on commit 5f22cef

Please sign in to comment.