Skip to content
This repository has been archived by the owner on May 9, 2021. It is now read-only.

Commit

Permalink
Merge 64db570 into 34db164
Browse files Browse the repository at this point in the history
  • Loading branch information
sulenchy committed Nov 14, 2018
2 parents 34db164 + 64db570 commit c2431c1
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 37 deletions.
99 changes: 98 additions & 1 deletion server/controllers/CommentController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import Sequelize from 'sequelize';
import models from '../models';

const { User, Comment } = models;
const { User, Comment, CommentLike } = models;

const { Op } = Sequelize;

/**
* @class CommentController
* @description Contains Comment related Operations
Expand Down Expand Up @@ -53,5 +57,98 @@ class CommentController {
}
}));
}

/**
* controller to like or dislike an commentId
* @param {object} req
* @param {object} res
* @param {object} next
* @returns {void}
*/
static async likeOrDislikeComment(req, res, next) {
const { commentId, action } = req.params;
const userId = req.userData.id;

/**
* This function likes or dislikes an comment
* @param {boolean} status - true for like, false for dislike
* @returns {void}
*/
const likeDisliker = async (status) => {
let commentLike;
let created;
try {
// find the comment like or create it
[commentLike, created] = await CommentLike.findOrCreate({
where: { [Op.and]: [{ userId }, { commentId }] },
defaults: { status, commentId, userId, }
});
} catch (err) {
return next(err);
}

// suffix for message
const suffix = status ? 'like' : 'dislike';
// get the article like id
const commentLikeId = commentLike.dataValues.id;

if (created) {
// if like/dislike was added
return res.status(201).json({
status: 'success',
message: `comment successfully ${suffix}d`,
});
}

// if like/dislike was not added
if (status !== commentLike.dataValues.status) {
// opposite action was triggered, update the like
const [rowCount] = await CommentLike.update({
status: !commentLike.dataValues.status,
}, {
where: {
id: commentLikeId,
}
});
if (rowCount > 0) {
return res.status(200).json({
status: 'success',
message: `you changed your mind, comment successfully ${suffix}d`,
});
}
}
// same action was triggered, undo the like or dislike
const undoRows = await CommentLike.destroy({
where: {
id: commentLikeId,
}
});
if (undoRows > 0) {
return res.status(200).json({
status: 'success',
message: `comment ${suffix}, undo successful`,
});
}
};

// error for unknown action in switch statement
const unknownActionError = new Error(
'unknown action, you may either like or dislike only'
);
unknownActionError.status = 422;

// switch statement for like or dislike
switch (action) {
case 'like':
likeDisliker(true);
break;
case 'dislike':
likeDisliker(false);
break;
default:
return next(unknownActionError);
}
}
}

export default CommentController;
31 changes: 0 additions & 31 deletions server/middlewares/validateArticleId.js

This file was deleted.

49 changes: 49 additions & 0 deletions server/middlewares/validateResourceId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import models from '../models';

const { Article, User, Comment } = models;

const validateResourceId = (req, res, next) => {
const {
articleId,
userId,
commentId,
} = req.params;

const validate = async (type, id, model) => {
if (Number.isNaN(Number(id))) {
// articleId passed in is not a number
const nanError = new Error(`invalid id, ${type} id must be a number`);
nanError.status = 400;
return next(nanError);
}

let resource;
try {
resource = await model.findByPk(id);
} catch (err) {
next(err);
}

if (!resource) {
// checks if resource exists
const notFoundError = new Error(`Sorry, that ${type} was not found`);
notFoundError.status = 404;
return next(notFoundError);
}
};

if (articleId) {
// if article Id param exists
validate('article', articleId, Article);
}
if (userId) {
// if user Id param exists
validate('user', userId, User);
}
if (commentId) {
// if comment Id param exists
validate('comment', commentId, Comment);
}
next();
};
export default validateResourceId;
4 changes: 2 additions & 2 deletions server/routes/api/articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import express from 'express';
import { verifyToken } from '../../middlewares/tokenUtils';
import ArticleController from '../../controllers/ArticleController';
import ArticleValidation from '../../middlewares/ArticleValidation';
import validateArticleId from '../../middlewares/validateArticleId';
import validateResourceId from '../../middlewares/validateResourceId';

const articles = express.Router();

Expand All @@ -28,7 +28,7 @@ articles.get('/articles', validateQuery, fetchAllArticles);
// routes to like or dislike articles
articles.post(
'/articles/:articleId/reaction/:action',
verifyToken, validateArticleId, likeOrDislikeArticle
verifyToken, validateResourceId, likeOrDislikeArticle
);

export default articles;
14 changes: 11 additions & 3 deletions server/routes/api/comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@ import { verifyToken } from '../../middlewares/tokenUtils';
import CommentController from '../../controllers/CommentController';
import ArticleCommentValidation from
'../../middlewares/ArticleValidation';
import validateArticleId from '../../middlewares/validateArticleId';
import validateResourceId from '../../middlewares/validateResourceId';

const comments = express.Router();

const { validateArticleCommentInput } = ArticleCommentValidation;
const {
addCommentOnArticle
addCommentOnArticle,
likeOrDislikeComment
} = CommentController;

comments.post(
'/articles/:articleId/comments',
verifyToken,
validateArticleCommentInput,
validateArticleId,
validateResourceId,
addCommentOnArticle
);

comments.post(
'/articles/:articleId/comments/:commentId/reaction/:action',
verifyToken,
validateResourceId,
likeOrDislikeComment
);

export default comments;
114 changes: 114 additions & 0 deletions test/server/controllers/CommentController.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ chai.should();

chai.use(chaiHttp);
const signupUrl = '/api/v1/users/signup';
const articleBaseUrl = '/api/v1/articles';
let articleId;
let commentId;

const userData = {};

Expand Down Expand Up @@ -63,3 +66,114 @@ describe('Testing comment on articles', () => {
});
});
});

describe('Testing like or disliked comment', () => {
articleId = 10;
commentId = 1;
it(
'shouldt POST api/v1/articles/:articleId/comments/:commentId/reaction/like',
(done) => {
chai.request(app)
.post(
`${articleBaseUrl}/${articleId}/comments/${commentId}/reaction/like`
)
.end((err, res) => {
res.should.have.status(401);
res.body.should.be.a('object');
res.body.status.should.be.eql('unauthorized');
res.body.message.should.be.eql('please provide a token');
done();
});
}
);
it(
'POST /api/v1/articles/:articleId/comments/:commentId/reaction/like',
(done) => {
chai.request(app)
.post(
`${articleBaseUrl}/${articleId}/comments/${commentId}/reaction/like`
)
.set('authorization', userData.token)
.end((err, res) => {
res.should.have.status(201);
res.body.should.be.a('object');
res.body.status.should.be.eql('success');
res.body.message.should.be.eql('comment successfully liked');
done();
});
}
);
it(
'POST /api/v1/articles/:articleId/comments/:commentId/reaction/like',
(done) => {
chai.request(app)
.post(
`${articleBaseUrl}/${articleId}/comments/${commentId}/reaction/like`
)
.set('authorization', userData.token)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.status.should.be.eql('success');
res.body.message.should.be.eql('comment like, undo successful');
done();
});
}
);
it(
'POST /api/v1/articles/:articleId/comments/:commentId/reaction/dislike',
(done) => {
chai.request(app)
.post(
`${articleBaseUrl}/${articleId}/comments/
${commentId}/reaction/dislike`
)
.set('authorization', userData.token)
.end((err, res) => {
res.should.have.status(201);
res.body.should.be.a('object');
res.body.status.should.be.eql('success');
res.body.message.should.be.eql('comment successfully disliked');
done();
});
}
);
it(
'POST /api/v1/articles/:articleId/comments/:commentId/reaction/like',
(done) => {
chai.request(app)
.post(
`${articleBaseUrl}/${articleId}/comments/
${commentId}/reaction/dislike`
)
.set('authorization', userData.token)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.status.should.be.eql('success');
res.body.message.should.be.eql('comment dislike, undo successful');
done();
});
}
);
it(
'POST /api/v1/articles/:articleId/comments/:commentId/reaction/dislike',
(done) => {
chai.request(app)
.post(
`${articleBaseUrl}/${articleId}/comments/
${commentId}/reaction/dislikes`
)
.set('authorization', userData.token)
.end((err, res) => {
res.should.have.status(422);
res.body.should.be.a('object');
res.body.status.should.be.eql('failure');
res.body.errors.message.should.be.eql(
'unknown action, you may either like or dislike only'
);
done();
});
}
);
});

0 comments on commit c2431c1

Please sign in to comment.