-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(notification): add user notification
- Add event listener and emitter on a new article created - Add option for followers to receive notification when a new articles created - Add functionalities of choosing preferred notificastions - Add unit tests - Add swagger documentation [Delivers #165413114]
- Loading branch information
1 parent
3436d23
commit 4aaed8f
Showing
19 changed files
with
745 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import NotificationHelper from '../helpers/notification'; | ||
|
||
/** | ||
* @author Samuel Niyitanga | ||
* @exports ArticleController | ||
* @class ArticleController | ||
* @description Handles all related notifications functioanlities | ||
* */ | ||
class NotificationController { | ||
/** | ||
* @param {object} req - Request object | ||
* @param {object} res - Response object | ||
* @returns {object} response | ||
* @static | ||
*/ | ||
static async setNotification(req, res) { | ||
await NotificationHelper.setOption(req); | ||
return res.status(200).send({ Message: 'Notification option setted.' }); | ||
} | ||
|
||
/** | ||
* @param {object} req - Request object | ||
* @param {object} res - Response object | ||
* @returns {object} response | ||
* @static | ||
*/ | ||
static async unsetNotification(req, res) { | ||
await NotificationHelper.UnsetOption(req); | ||
|
||
return res.status(200).send({ Message: 'Notification successfully unsetted.' }); | ||
} | ||
|
||
/** | ||
* @param {object} req - Request object | ||
* @param {object} res - Response object | ||
* @returns {object} response | ||
* @static | ||
*/ | ||
static async getNotificationByUser(req, res) { | ||
const notification = await NotificationHelper.getByUser(req); | ||
return res.status(200).send({ notification }); | ||
} | ||
} | ||
export default NotificationController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { EventEmitter } from 'events'; | ||
|
||
const emitter = new EventEmitter(); | ||
|
||
export default emitter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
import db from '../models'; | ||
import passportHelper from './passport'; | ||
import mailSender from './utils/mail-sender'; | ||
|
||
const { | ||
User, Follows, Notification | ||
} = db; | ||
|
||
/** | ||
* @author Samuel Niyitanga | ||
* @exports NotificationHelper | ||
* @class NotificationHelper | ||
* @description Notification Helper | ||
* */ | ||
class NotificationHelper { | ||
/** | ||
* Set notification option | ||
* @param {object} req - an object | ||
* @return {object} Returns response | ||
* @static | ||
*/ | ||
static async setOption(req) { | ||
const { option } = req.params; | ||
const user = await User.findOne({ where: { id: req.user.id } }); | ||
const notifsArray = user.dataValues.notification; | ||
notifsArray.push(option); | ||
const optionSet = await user.update({ notification: notifsArray }); | ||
return optionSet; | ||
} | ||
|
||
/** | ||
* Unset notification option | ||
* @param {object} req - an object | ||
* @return {object} Returns response | ||
* @static | ||
*/ | ||
static async UnsetOption(req) { | ||
const { option } = req.params; | ||
const user = await User.findOne({ where: { id: req.user.id } }); | ||
const notifsArray = user.dataValues.notification; | ||
const optionIndex = notifsArray.indexOf(option); | ||
notifsArray.splice(optionIndex, 1); | ||
const optionUnsetted = await user.update({ notification: notifsArray }); | ||
return optionUnsetted; | ||
} | ||
|
||
/** | ||
* Check if option exists | ||
* @param {object} req - an object | ||
* @param {object} res - an object | ||
* @param {object} next - an object | ||
* @return {boolean} Returns if true if it is valid else return false | ||
* @static | ||
*/ | ||
static async isOptionAvailable(req, res, next) { | ||
const user = await User.findOne({ where: { id: req.user.id } }); | ||
const index = user.dataValues.notification | ||
.findIndex(notification => notification === req.params.option); | ||
if (index !== -1) { | ||
return res.status(422).send({ status: 422, Error: 'You already have this Option set' }); | ||
} | ||
next(); | ||
} | ||
|
||
/** | ||
* Check if option exists | ||
* @param {object} req - an object | ||
* @param {object} res - an object | ||
* @param {object} next - an object | ||
* @return {boolean} Returns if true if it is valid else return false | ||
* @static | ||
*/ | ||
static async isOptionIn(req, res, next) { | ||
const user = await User.findOne({ where: { id: req.user.id } }); | ||
const index = user.dataValues.notification | ||
.findIndex(notification => notification === req.params.option); | ||
if (index === -1) { | ||
return res.status(422).send({ status: 422, Error: 'You don\'t have this Option set' }); | ||
} | ||
next(); | ||
} | ||
|
||
/** | ||
* Check if it is valid params | ||
* @param {object} req - an object | ||
* @param {object} res - an object | ||
* @param {object} next - an object | ||
* @return {boolean} Returns if true if it is valid else return false | ||
* @static | ||
*/ | ||
static isValidOption(req, res, next) { | ||
const validValues = ['email', 'in-app', 'follower', 'articleFavorite']; | ||
if (validValues.indexOf(req.params.option) === -1) { | ||
return res.status(422).send({ status: 422, Error: 'Only option must be email,in-app,follower or articleFavorite' }); | ||
} | ||
next(); | ||
} | ||
|
||
/** | ||
* @param {integer} authorId | ||
* @returns {object} return list of followers | ||
*/ | ||
static async getFollowersList(authorId) { | ||
const lists = await Follows.findAll({ | ||
where: { follower: authorId }, | ||
attributes: ['following'] | ||
}); | ||
return lists; | ||
} | ||
|
||
/** | ||
* @param {object} userIds | ||
* @param {string} userType | ||
* @returns {Array} return array of object | ||
*/ | ||
static async serializeUsers(userIds, userType) { | ||
const type = userType === 'follower' ? 'follower' : 'following'; | ||
const userList = []; | ||
await Promise.all(userIds.map(async (currentId) => { | ||
const userInfo = await passportHelper.findRecord(User, currentId[type]); | ||
userList.push({ | ||
id: currentId[type], | ||
username: userInfo.dataValues.username, | ||
bio: userInfo.dataValues.bio, | ||
image: userInfo.dataValues.image, | ||
email: userInfo.dataValues.email, | ||
notification: userInfo.dataValues.notification | ||
}); | ||
})); | ||
return userList; | ||
} | ||
|
||
/** | ||
* @param {object} followers | ||
* @returns {object} return emails | ||
*/ | ||
static getfollowersWithEmailOption(followers) { | ||
const followersWithEmails = []; | ||
followers.forEach((follower) => { | ||
if (follower.notification.indexOf('email') !== -1) { | ||
followersWithEmails.push(follower); | ||
} | ||
}); | ||
return followersWithEmails; | ||
} | ||
|
||
/** | ||
* @param {object} followers | ||
* @param {integer} authorId | ||
* @param {string} articleSlug | ||
* @returns {object} return followers | ||
*/ | ||
static getfollowersWithAppOption(followers) { | ||
const followersWithApp = []; | ||
followers.forEach((follower) => { | ||
if (follower.notification.indexOf('in-app') !== -1) { | ||
followersWithApp.push(follower); | ||
} | ||
}); | ||
return followersWithApp; | ||
} | ||
|
||
/** | ||
* Send notification email when users they follow publish new articles | ||
* @param {string } email - A user aemail | ||
* @param {string} author | ||
* @param {string} title | ||
* @returns {object} return email to send | ||
*/ | ||
static sendEmailToFollowers(email, author, title) { | ||
return mailSender.send({ | ||
email, | ||
subject: `${author} created a new article`, | ||
html: `<html>You can check <strong>${title}</strong> created on Author Haven</html>` | ||
}); | ||
} | ||
|
||
/** | ||
* Send notification email when articles they favorited have new interaction | ||
* @param {string } email - A user aemail | ||
* @param {string} user - People who interacted with an article | ||
* @param {string} title | ||
* @returns {object} return email to send | ||
*/ | ||
static sendEmailOnInteraction(email, user, title) { | ||
return mailSender.send({ | ||
email, | ||
subject: `Reaction on Article by ${user} on Author Haven`, | ||
html: `<html>On Author Haven<strong>${title}</strong> had a new interaction </html>` | ||
}); | ||
} | ||
|
||
/** | ||
* @param {object} followers followers object | ||
* @param {string} title | ||
* @param {string} username | ||
* @returns {array} return array of notification object | ||
*/ | ||
static inAppNotifications(followers, title, username) { | ||
const notifications = []; | ||
followers.forEach((follower) => { | ||
if (follower.notification.indexOf('follower') !== -1) { | ||
const singleNotification = { | ||
userId: follower.id, | ||
notificationMessage: `New article called ${title} was created by ${username} `, | ||
}; | ||
notifications.push(singleNotification); | ||
return; | ||
} | ||
if (follower.notification.indexOf('articleFavorite') !== -1) { | ||
const singleNotification = { | ||
userId: follower.id, | ||
notificationMessage: `Reaction on Article called ${title} by ${username} `, | ||
}; | ||
notifications.push(singleNotification); | ||
} | ||
}); | ||
return notifications; | ||
} | ||
|
||
/** | ||
* Return one article | ||
* @param {object} req - an object | ||
*@return {object} Return article | ||
*/ | ||
static async getByUser(req) { | ||
const article = await Notification.findAll({ | ||
where: { userId: req.user.id }, | ||
include: [{ | ||
model: User, | ||
as: 'user', | ||
attributes: ['username', 'bio', 'image'] | ||
}] | ||
}); | ||
return article; | ||
} | ||
} | ||
export default NotificationHelper; |
Oops, something went wrong.