Skip to content

Commit

Permalink
bug(article-reaction: Fixes user reaction to articles
Browse files Browse the repository at this point in the history
create articleReaction model
update like functionality in the articleReaction service
Write tests
[Finishes #167392772]
  • Loading branch information
devPinheiro committed Jul 22, 2019
1 parent 21e40b6 commit 70ee9ca
Show file tree
Hide file tree
Showing 11 changed files with 49 additions and 157 deletions.
5 changes: 0 additions & 5 deletions src/db/migrations/20190708085450-create-article.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@ module.exports = {
allowNull: false,
defaultValue: 0
},
likesCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0
},
isPublished: {
type: Sequelize.BOOLEAN,
allowNull: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Reactions', {
return queryInterface.createTable('ArticleReactions', {
id: {
allowNull: false,
autoIncrement: true,
Expand All @@ -26,12 +26,6 @@ module.exports = {
args: true
}
},
commentId: {
type: Sequelize.INTEGER,
allowNull: {
args: true
}
},
isLiked: {
type: Sequelize.BOOLEAN,
allowNull: {
Expand All @@ -50,6 +44,6 @@ module.exports = {
});
},
down: queryInterface => {
return queryInterface.dropTable('Reactions');
return queryInterface.dropTable('ArticleReactions');
}
};
4 changes: 0 additions & 4 deletions src/db/models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ export default (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
default: 0
},
likesCount: {
type: DataTypes.INTEGER,
default: 0
},
isPublished: {
type: DataTypes.BOOLEAN,
default: false
Expand Down
16 changes: 5 additions & 11 deletions src/db/models/reaction.js → src/db/models/articleReaction.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = (sequelize, DataTypes) => {
const Reaction = sequelize.define('Reaction', {
const ArticleReaction = sequelize.define('ArticleReaction', {
userId: {
type: DataTypes.INTEGER,
allowNull: {
Expand All @@ -12,12 +12,6 @@ module.exports = (sequelize, DataTypes) => {
args: true
}
},
commentId: {
type: DataTypes.INTEGER,
allowNull: {
args: true
}
},
isLiked: {
type: DataTypes.BOOLEAN,
allowNull: {
Expand All @@ -26,16 +20,16 @@ module.exports = (sequelize, DataTypes) => {
defaultValue: true
}
});
Reaction.associate = models => {
ArticleReaction.associate = models => {
// associations can be defined here
Reaction.belongsTo(models.User, {
ArticleReaction.belongsTo(models.User, {
foreignKey: 'userId',
as: 'liker'
});
Reaction.belongsTo(models.Article, {
ArticleReaction.belongsTo(models.Article, {
foreignKey: 'articleId',
as: 'articleLiked'
});
};
return Reaction;
return ArticleReaction;
};
3 changes: 3 additions & 0 deletions src/routes/v1/article.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import upload from '../../middlewares/imageUpload.middleware';
import commentController from '../../controllers/comment.controller';
import commentsCheck from '../../middlewares/commentsCheck.middleware';
import reportController from '../../controllers/report.controller';
import reactions from '../../controllers/reaction.controller';

const { validator, checkValidationResult } = articleValidator;
const { articlesLike } = reactions;

const {
validator: paginationValidator,
Expand Down Expand Up @@ -125,5 +127,6 @@ router.patch(
editCommentCheck,
editComment
);
router.get('/:articleId/reactions', verifyToken, articlesLike);

export default router;
2 changes: 0 additions & 2 deletions src/routes/v1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import auth from './auth.route';
import users from './user.route';
import article from './article.route';
import profile from './profile.route';
import reaction from './reaction.route';

export default app => {
app.use(`${process.env.API_VERSION}/users`, auth);
app.use(`${process.env.API_VERSION}/users/`, users);
app.use(`${process.env.API_VERSION}/articles/`, article);
app.use(`${process.env.API_VERSION}/profiles`, profile);
app.use(`${process.env.API_VERSION}/reactions/`, reaction);
};
20 changes: 0 additions & 20 deletions src/routes/v1/reaction.route.js

This file was deleted.

25 changes: 23 additions & 2 deletions src/services/article.service.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import fs from 'fs';
import moment from 'moment';
import { upload } from '../helpers/image.helper';
import model from '../db/models';
import Helper from './helper';
import { paginationQueryMetadata, pageMetadata } from '../helpers/pagination';

const { Comment, Article, User, Follow, Rating } = model;
const { Comment, Article, User, Follow, Rating, ArticleReaction } = model;
/** Istanbul ignore next */
/**
* @method createArticleService
Expand Down Expand Up @@ -65,6 +64,7 @@ export const createArticleService = async data => {

export const getArticleService = async data => {
const articleSlug = data.params.slug;

const article = await Article.findOne({
where: { slug: articleSlug },
include: [
Expand All @@ -75,6 +75,27 @@ export const getArticleService = async data => {
}
]
});
if (article) {
// search for likes in reactions table
const articleLikes = await ArticleReaction.findAndCountAll({
where: { articleId: article.id, isLiked: true },
attributes: [],
include: [
{
model: User,
as: 'liker',
attributes: ['firstName', 'lastName', 'image']
}
]
});
let likesCount;
if (articleLikes) {
likesCount = articleLikes.count;
}
article.setDataValue('likesCount', likesCount);
article.setDataValue('likers', articleLikes.rows);
}

return article;
};

Expand Down
45 changes: 8 additions & 37 deletions src/services/reaction.service.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable import/prefer-default-export */
import models from '../db/models';

const { Article, Reaction } = models;
const { ArticleReaction } = models;
/**
* @method articleReaction
* - user can like or dislike article
Expand All @@ -13,54 +13,25 @@ const { Article, Reaction } = models;
* */

export const reactionService = async data => {
const { reaction } = data.body;
const { postId } = data.params;
const { articleId } = data.params;
const userId = data.user.id;
// checks if article has any reaction
const counter = 1;
const isLiked = reaction === 'like';

try {
const findReaction = await Reaction.findOne({
const findReaction = await ArticleReaction.findOne({
where: {
userId,
articleId: postId
articleId
}
});
const findArticle = await Article.findByPk(postId);
if (findReaction == null) {
await Reaction.create({
await ArticleReaction.create({
userId,
articleId: postId
});
await findArticle.increment('likesCount', {
by: counter,
where: { id: postId }
articleId
});
return { message: 'You have liked this article successfully' };
}
if (findReaction.isLiked === true && reaction === 'dislike') {
await findArticle.decrement('likesCount', {
by: counter,
where: { id: postId }
});
await findReaction.update({ isLiked });
return { message: 'You have disliked this article successfully' };
}
if (findReaction.isLiked === false && reaction === 'like') {
await findArticle.increment('likesCount', {
by: counter,
where: { id: postId }
});
await findReaction.update({ isLiked });
return { message: 'You have liked this article successfully' };
}
if (findReaction.isLiked === true && reaction === 'like') {
return { message: 'You can not like an article more than once' };
}
if (findReaction.isLiked === false && reaction === 'dislike') {
return { message: 'You can not dislike an article more than once' };
}
await findReaction.destroy({ where: { userId } });
return { message: 'You have disliked this article successfully' };
} catch (error) {
throw Error(error);
}
Expand Down
3 changes: 3 additions & 0 deletions src/tests/controller/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import AuthenticationMiddleWare from '../../middlewares/profileUpdateCheck.middl
import app from '../../index';
import { getPasswordResetToken } from '../../helpers/jwt.helper';
import * as imageHelper from '../../helpers/image.helper';
import model from '../../db/models';

const { User } = model;

dotenv.config();

Expand Down
73 changes: 5 additions & 68 deletions src/tests/controller/reaction.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { getArticleData, Response, getUser } from '../utils/db.utils';
import reactionsController from '../../controllers/reaction.controller';
import { getArticleData, getUser } from '../utils/db.utils';
import app from '../../index';

const { articlesLike } = reactionsController;
chai.use(chaiHttp);
chai.use(sinonChai);

Expand Down Expand Up @@ -68,8 +65,7 @@ describe('Articles Reactions API endpoints', () => {
it('Should allow a user like an article successfully ', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${firstArticle.id}`)
.send({ reaction: 'like' })
.get(`${process.env.API_VERSION}/articles/${firstArticle.id}/reactions`)
.set({ Authorization: `Bearer ${userToken}` })
.end((error, response) => {
expect(response.status).to.equal(200);
Expand All @@ -87,8 +83,7 @@ describe('Articles Reactions API endpoints', () => {
it('Should allow a user like another article successfully ', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${secondArticle.id}`)
.send({ reaction: 'like' })
.get(`${process.env.API_VERSION}/articles/${secondArticle.id}/reactions`)
.set({ Authorization: `Bearer ${userToken}` })
.end((error, response) => {
expect(response.status).to.equal(200);
Expand All @@ -106,8 +101,7 @@ describe('Articles Reactions API endpoints', () => {
it('Should allow another user like an article successfully ', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${firstArticle.id}`)
.send({ reaction: 'like' })
.get(`${process.env.API_VERSION}/articles/${firstArticle.id}/reactions`)
.set({ Authorization: `Bearer ${secondUserToken}` })
.end((error, response) => {
expect(response.status).to.equal(200);
Expand All @@ -122,30 +116,10 @@ describe('Articles Reactions API endpoints', () => {
});
});

it('Should not allow a user like an article more than once', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${firstArticle.id}`)
.send({ reaction: 'like' })
.set({ Authorization: `Bearer ${userToken}` })
.end((error, response) => {
expect(response.status).to.equal(200);
expect(response).to.be.an('Object');
expect(response.body).to.have.property('status');
expect(response.body).to.have.property('data');
expect(response.body.status).to.equal('success');
expect(response.body.data.message).to.equal(
'You can not like an article more than once'
);
done();
});
});

it('Should allow user dislike an article successfully ', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${firstArticle.id}`)
.send({ reaction: 'dislike' })
.get(`${process.env.API_VERSION}/articles/${firstArticle.id}/reactions`)
.set({ Authorization: `Bearer ${userToken}` })
.end((error, response) => {
expect(response.status).to.equal(200);
Expand All @@ -159,41 +133,4 @@ describe('Articles Reactions API endpoints', () => {
done();
});
});

it('Should not allow a user dislike an article more than once', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${firstArticle.id}`)
.send({ reaction: 'dislike' })
.set({ Authorization: `Bearer ${userToken}` })
.end((error, response) => {
expect(response.status).to.equal(200);
expect(response).to.be.an('Object');
expect(response.body).to.have.property('status');
expect(response.body).to.have.property('data');
expect(response.body.status).to.equal('success');
expect(response.body.data.message).to.equal(
'You can not dislike an article more than once'
);
done();
});
});

it('Should return an error if the body of the request is empty', done => {
chai
.request(app)
.post(`${process.env.API_VERSION}/reactions/article/${firstArticle.id}`)
.send({})
.set({ Authorization: `Bearer ${userToken}` })
.end((error, response) => {
expect(response.status).to.equal(400);
expect(response).to.be.an('Object');
expect(response.body).to.have.property('status');
expect(response.body).to.have.property('data');
expect(response.body.status).to.equal('fail');
expect(response.body.data[0].msg).to.equal('Invalid value');
expect(response.body.data[1].msg).to.equal('Invalid value');
done();
});
});
});

0 comments on commit 70ee9ca

Please sign in to comment.