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

Commit

Permalink
ft(mark all notifications as read):
Browse files Browse the repository at this point in the history
add the function in middleware to check for unread notfications
created notification controller
created route for
created seeders to test the functionalit
added tests for the functions
added swagger documentation

[ Finishes #170947565 ]
  • Loading branch information
Ntare22 committed Mar 5, 2020
1 parent 0718732 commit 75a6b4c
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 43 deletions.
8 changes: 5 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io('http://localhost:8000');
import dotenv from 'dotenv';
dotenv.config();
const baseURL = process.env.BASE_URL
const socket = io(baseURL);
socket.on('notification', (data) => {
console.log(data, 'wow!!!!!');
socket.emit('messageFromClient', 'This is from the client')
});
</script>
</body>
</html>
</html>
18 changes: 17 additions & 1 deletion src/controllers/notificationController.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ export default class notificationController {
}
});
const allNotifs = notifications.reverse();
return Response(res, 200, 'success', allNotifs);
return Response.success(res, 200, 'success', allNotifs);
}

/**
* @param {object} req
* @param {object} res
* @param {object} next
* @return {object} notification
*/
static async markAllNotificationsAsRead(req, res) {
try {
const { user } = req;
await db.Notifications.update({ status: 'read' }, { where: { recieverId: user.id } });
return Response.success(res, 200, res.__('all unread notifications marked as read'));
} catch (error) {
return Response.errorResponse(res, 500, res.__('server error'));
}
}
}
6 changes: 0 additions & 6 deletions src/controllers/tripsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,6 @@ export default class requestController {
return Response.errorResponse(res, 400, res.__('the request is already re-confirmed'));
}
const updatedRequest = await request.update({ confirm: true });
await db.Notifications.create({
id: uuid(),
email: request.email,
requestId,
content: 'Your request has been approved'
});
return Response.success(res, 200, res.__('request re-confirmed'), updatedRequest);
} catch (err) {
return Response.errorResponse(res, 500, res.__('server error'));
Expand Down
4 changes: 1 addition & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ i18n.configure({
});

const app = express();
// const http = require('http').createServer(app);

app.use(i18n.init);
app.use(bodyParser.json());
Expand All @@ -51,13 +50,12 @@ app.use('/api/v1/auth', authRouter);
app.use('/api/v1/users', userRouter);
app.use('/api/v1/trips', tripsRouter);
app.use('/api/v1/facilities', facilitiesRouter);
app.use('api/v1/notifications', notificationsRouter);
app.use('/api/v1/notifications', notificationsRouter);

app.use((req, res) => res.status(404).send({ status: 404, error: res.__('Route %s not found', req.url) }));

app.use((err, req, res) => res.status(500).send({ status: 500, error: res.__('server error') }));

app.listen(port, () => process.stdout.write(`Server is running on http://localhost:${port}/api`));
const server = app.listen(port, () => process.stdout.write(`Server is running on http://localhost:${port}/api`));

const io = socketio(server);
Expand Down
18 changes: 18 additions & 0 deletions src/middlewares/protectRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,22 @@ export default class protectRoutes {
}
next();
}

/**
* @param {object} req
* @param {object} res
* @param {object} next
* @returns {object} object
*/
static async checkUnreadNotifications(req, res, next) {
const { user } = req;
const unreadUsersNotifications = await db.Notifications.findAll({
where: { recieverId: user.id, status: 'unread' }
});

if (unreadUsersNotifications.length === 0) {
return Response.errorResponse(res, 404, res.__('no unread notifications'));
}
next();
}
}
16 changes: 2 additions & 14 deletions src/migrations/20200302132135-create-notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@ module.exports = {
return queryInterface.createTable('Notifications', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
status: {
type: Sequelize.STRING,
allowNull: false
},
userId: {
primaryKey: true,
type: Sequelize.STRING
},
Expand All @@ -21,16 +12,13 @@ module.exports = {
defaultValue: 'unread',
allowNull: false
},
email: {
recieverEmail: {
type: Sequelize.STRING,
allowNull: false
},
requestId: {
recieverId: {
type: Sequelize.STRING,
},
timestamp: {
type: Sequelize.DATE
},
content: {
type: Sequelize.STRING,
allowNull: false
Expand Down
16 changes: 3 additions & 13 deletions src/models/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,10 @@ module.exports = (sequelize, DataTypes) => {
const Notifications = sequelize.define('Notifications', {
status: DataTypes.ENUM('read, unread'),
content: DataTypes.STRING,
email: DataTypes.STRING,
requestId: DataTypes.STRING
recieverEmail: DataTypes.STRING,
recieverId: DataTypes.STRING
}, {});
Notifications.associate = (models) => {
Notifications.belongsTo(models.User, {
foreignKey: 'email',
as: 'user',
onDelete: 'CASCADE'
});
Notifications.belongsTo(models.Request, {
foreignKey: 'requestId',
as: 'request',
onDelete: 'CASCADE'
});
Notifications.associate = () => {
};
return Notifications;
};
1 change: 1 addition & 0 deletions src/routes/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import protectRoute from '../middlewares/protectRoute';
const router = express.Router();

router.get('/', protectRoute.verifyUser, validationResult, notificationController.getAllNotifications); // include validations too
router.patch('/all-as-read', protectRoute.verifyUser, protectRoute.checkUnreadNotifications, validationResult, notificationController.markAllNotificationsAsRead);

export default router;
34 changes: 34 additions & 0 deletions src/seeders/20200304092534-notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = {
up: (queryInterface) => queryInterface.bulkInsert(
'Notifications', [
{
id: '51j74d57-1910-4f50-9h15-b23740331od5',
status: 'unread',
recieverEmail: 'jdev@andela.com',
recieverId: '79660e6f-4b7d-4g21-81re-74f54jk91c8a',
content: 'Your trip to Gisenyi on 2020-02-01 has been approved',
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: '01q74d57-1995-h45h-592f-b23740331od5',
status: 'read',
recieverEmail: 'jdev@andela.com',
recieverId: '79660e6f-4b7d-4g21-81re-74f54jk91c8a',
content: 'You have been assign Jamie Jules as your manager',
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: '91q74d57-1695-h40h-590f-b25b4063tod5',
status: 'unread',
recieverEmail: 'jeanne@andela.com',
recieverId: '79660e6f-4b7d-4g21-81re-74f54e9e1c8a',
content: 'You have been assign Jamie Jules as your manager',
createdAt: new Date(),
updatedAt: new Date(),
},],
{},
),
down: (queryInterface) => queryInterface.bulkDelete('Notifications', null, {}),
};
6 changes: 4 additions & 2 deletions src/services/localesServices/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,7 @@
"please check your input and enter a valid ID": "please check your input and enter a valid ID",
"request ID can't be empty": "request ID can't be empty",
"destination should only contain letter": "destination should only contain letter",
"Gender must either be Male or Female": "Gender must either be Male or Female"
}
"Gender must either be Male or Female": "Gender must either be Male or Female",
"all unread notifications marked as read": "all unread notifications marked as read",
"no unread notifications": "no unread notifications"
}
4 changes: 3 additions & 1 deletion src/services/localesServices/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,7 @@
"reason must be atleast one character": "la raison doit être au moins un caractère",
"destination should only contain letter": "la destination ne doit contenir qu'une lettre",
"please check your input and enter a valid ID": "veuillez vérifier votre saisie et saisir un ID valide",
"request ID can't be empty": "l'ID de la demande ne peut pas être vide"
"request ID can't be empty": "l'ID de la demande ne peut pas être vide",
"all unread notifications marked as read": "toutes les notifications non lues marquées comme lues",
"no unread notifications": "pas de notifications non lues"
}
23 changes: 23 additions & 0 deletions src/swagger/notifications.swagger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @swagger
* /api/v1/notifications/all-as-read:
* patch:
* security:
* - bearerAuth: []
* tags:
* - Notifications
* name: markAllNotificationsAsRead
* summary: enables a user to mark all as read
* produces:
* - application/json
* parameters:
* - name: token
* in: header
* responses:
* '200':
* description: all unread notifications marked as read.
* '404':
* description: no unread notifications.
* '500':
* description: Server Error.
* */
46 changes: 46 additions & 0 deletions src/tests/notifications.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import app from '../index';

const { expect } = chai;
chai.use(chaiHttp);
let token;

describe('MARK ALL AS READ TESTS', () => {
before((done) => {
chai
.request(app)
.post('/api/v1/auth/login')
.send({
email: 'jdev@andela.com',
password: 'Bien@BAR789'
})
.end((err, res) => {
token = res.body.data;
done();
});
});
it('should return all notfications marked as read', (done) => {
chai
.request(app)
.patch('/api/v1/notifications/all-as-read')
.set('token', token)
.end((err, res) => {
expect(res.status).to.equal(200);
expect(res.body.message).to.equal('all unread notifications marked as read');
done();
});
});
it('should return no unread notifications', (done) => {
chai
.request(app)
.patch('/api/v1/notifications/all-as-read')
.set('token', token)
.end((err, res) => {
// console.log(res.body);
expect(res.status).to.equal(404);
expect(res.body.error).to.equal('no unread notifications');
done();
});
});
});
16 changes: 16 additions & 0 deletions src/tests/unitTests/notifications.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import db from '../../models';


const { expect } = chai;
chai.use(chaiHttp);

describe('NOTIFICATION UNIT TESTS', () => {
it('should return unread notifications of user', async () => {
const unreadNotifications = await db.Notifications.findAll({
where: { recieverId: '79660e6f-4b7d-4g21-81re-74f54jk91c8a', status: 'unread' }
});
expect(unreadNotifications).to.be.an('array');
});
});

0 comments on commit 75a6b4c

Please sign in to comment.