Skip to content

Commit

Permalink
Merge d000ce5 into c8d00ca
Browse files Browse the repository at this point in the history
  • Loading branch information
meetKazuki committed Sep 11, 2019
2 parents c8d00ca + d000ce5 commit 780465b
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 2 deletions.
49 changes: 49 additions & 0 deletions src/controllers/Comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import models from '../database/models';
import Response from '../helpers/Response';

const { Comment, Trip } = models;

/**
* @class
* @description controller class for comments
* @exports CommentController
*/
class CommentController {
/**
* Post a Comment
* Route: POST: /trips/comments
* @param {object} req - HTTP Request object
* @param {object} res - HTTP Response object
* @return {res} res - HTTP Response object
* @memberof CommentController
*/
static async createComment(req, res) {
const { payload: { id: userId } } = req.payload;
const { tripId } = req.params;
const { body } = req.body;
try {
const getTrip = await Trip.findOne({
where: { id: tripId },
returning: true
});
if (!getTrip) {
const response = new Response(false, 404, 'Trip does not exist');
return res.status(response.status).json(response);
}

const newComment = await Comment.create({ userId, tripId, body });
const response = new Response(
true,
201,
'Comment posted successfully',
newComment
);
return res.status(response.code).json(response);
} catch (error) {
const response = new Response(false, 500, error.message);
return res.status(response.code).json(response);
}
}
}

export default CommentController;
1 change: 0 additions & 1 deletion src/database/migrations/20190828140409-create-trip.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

module.exports = {
up: (queryInterface, Sequelize) => queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";').then(() => queryInterface.createTable('Trips', {
id: {
Expand Down
47 changes: 47 additions & 0 deletions src/database/migrations/20190909153139-create-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";').then(() => queryInterface.createTable('Comments', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.literal('uuid_generate_v4()'),
},
tripId: {
type: Sequelize.UUID,
allowNull: false,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
references: {
model: 'Trips',
key: 'id',
as: 'tripId',
}
},
userId: {
type: Sequelize.UUID,
allowNull: false,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
references: {
model: 'Users',
key: 'id',
as: 'userId',
},
},
body: {
type: Sequelize.TEXT,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now')
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now')
}
})),
down: queryInterface => queryInterface.dropTable('Comments')
};
23 changes: 23 additions & 0 deletions src/database/models/comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = (sequelize, DataTypes) => {
const Comment = sequelize.define('Comment', {
tripId: DataTypes.UUID,
userId: DataTypes.UUID,
body: DataTypes.TEXT
}, {});
Comment.associate = (models) => {
// associations can be defined here
Comment.belongsTo(models.Trip, {
foreignKey: 'tripId',
as: 'trip',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
Comment.belongsTo(models.User, {
foreignKey: 'userId',
as: 'user',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
};
return Comment;
};
8 changes: 7 additions & 1 deletion src/database/models/trip.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = (sequelize, DataTypes) => {
foreignKey: 'tripId',
as: 'stop',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
onUpdate: 'CASCADE',
});
Trip.belongsTo(models.Branch, {
foreignKey: 'startBranchId',
Expand All @@ -29,6 +29,12 @@ module.exports = (sequelize, DataTypes) => {
onUpdate: 'CASCADE',
sourceKey: 'startBranchId'
});
Trip.hasMany(models.Comment, {
foreignKey: 'tripId',
as: 'comments',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
};
return Trip;
};
6 changes: 6 additions & 0 deletions src/database/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ module.exports = (sequelize, DataTypes) => {
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
User.hasMany(models.Comment, {
foreignKey: 'userId',
as: 'comments',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
User.belongsTo(models.Company, {
foreignKey: 'companyId',
as: 'company',
Expand Down
18 changes: 18 additions & 0 deletions src/database/seeders/20190909184348-create-comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
up: queryInterface => queryInterface.bulkInsert('Comments', [
{
id: 'ffe25dbe-29ea-4755-8461-ed116f6739df',
tripId: 'ffe25dbe-29ea-4759-8461-ed116f6739df',
userId: 'ffe25dbe-29ea-4759-8461-ed116f6739dd',
body: 'Robert Mugabe is no more'
},
{
id: 'ffe25dbe-29ea-4751-8461-ed116f6739df',
tripId: 'ffe25dbe-29ea-4759-8461-ed116f6739df',
userId: 'ffe25dbe-29ea-4759-8461-ed116f6739dd',
body: 'There is free food at the cafeteria'
},
]),

down: queryInterface => queryInterface.bulkDelete('Comments', null, {})
};
18 changes: 18 additions & 0 deletions src/routes/comment.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Router } from 'express';
import CommentController from '../controllers/Comment';
import permit from '../middlewares/permission';
import Token from '../helpers/Token';
import validator from '../middlewares/validator';
import { createCommentSchema } from '../validation/commentSchema';

const commentRoute = Router();

commentRoute.post(
'/:tripId/comment',
Token.verifyToken,
permit('staff', 'manager'),
validator(createCommentSchema),
CommentController.createComment,
);

export default commentRoute;
2 changes: 2 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import adminRoute from './admin.routes';
import feedbackRoute from './feedback.routes';
import searchRoute from './search.routes';
import accomodationRoutes from './accomodation.routes';
import commentRoute from './comment.routes';

const router = Router();

Expand All @@ -16,5 +17,6 @@ router.use('/user', adminRoute);
router.use('/accomodation', feedbackRoute);
router.use('/search', searchRoute);
router.use('/admin/accomodation', accomodationRoutes);
router.use('/trips', commentRoute);

export default router;
30 changes: 30 additions & 0 deletions src/validation/commentSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { check } from 'express-validator';
import models from '../database/models';

const { Trip } = models;

const createCommentSchema = [
check('body')
.trim()
.exists()
.withMessage('Comment is required')
.isLength({ min: 2 })
.withMessage('Comment should not be less than 2 characters'),

check('userId')
.isUUID(4)
.optional(),

check('tripId')
.isUUID(4)
.withMessage('Trip ID is not valid. Should be of type UUID')
.custom(async (value) => {
const getTrip = await Trip.findOne({ where: { id: value } });
if (!getTrip) {
throw new Error('Trip ID does not exist');
}
return true;
})
];

export { createCommentSchema };
70 changes: 70 additions & 0 deletions test/comment.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import sinon from 'sinon';
import app from '../src/index';
import models from '../src/database/models';
import mockAuth from './mockData/mockAuth';
import mockComments from './mockData/mockComment';

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

const { Comment } = models;
const { user10 } = mockAuth;
const { myComments } = mockComments;
const baseURL = '/api/v1/trips';
const invalidID = 'ffe25dbe-29ea-4759-8461-ed116f6739da';
let userToken;

describe('Comments API', () => {
before('Login to get token', (done) => {
chai
.request(app)
.post('/api/v1/auth/login')
.send(user10)
.end((err, res) => {
const { token } = res.body.data.user;
userToken = token;
done(err);
});
});
describe('POST /trips/:tripId/comment', () => {
it('should successfully post a comment', (done) => {
chai
.request(app)
.post(`${baseURL}/${myComments[0].tripId}/comment`)
.send(myComments[0])
.set('authorization', userToken)
.end((err, res) => {
expect(res).to.have.status(201);
done(err);
});
});

it('should throw an error if trip ID is invalid', (done) => {
chai
.request(app)
.post(`${baseURL}/${invalidID}/comment`)
.send(myComments[0])
.set('authorization', userToken)
.end((err, res) => {
expect(res).to.have.status(400);
done(err);
});
});

it('should return a 500 error when an error occurs on the server', (done) => {
const stub = sinon.stub(Comment, 'create')
.rejects(new Error('Server error, Please try again later'));
chai.request(app)
.post(`${baseURL}/${myComments[0].tripId}/comment`)
.send(myComments[0])
.set('authorization', userToken)
.end((err, res) => {
expect(res.status).to.equal(500);
stub.restore();
done(err);
});
});
});
});
8 changes: 8 additions & 0 deletions test/mockData/mockComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const myComments = [
{
tripId: 'ffe25dbe-29ea-4759-8461-ed116f6739df',
body: 'Legend has it that to understand recursion, you must first understand recursion'
}
];

export default { myComments };

0 comments on commit 780465b

Please sign in to comment.