Skip to content

Commit

Permalink
Merge pull request #26 from andela/ft-user-like-and-dislike-165413112
Browse files Browse the repository at this point in the history
#165413111 user is able to like and dislike an article
  • Loading branch information
Denis Niwemugisha committed May 16, 2019
2 parents 9a2e15a + 99d3506 commit 3436d23
Show file tree
Hide file tree
Showing 10 changed files with 690 additions and 3 deletions.
36 changes: 36 additions & 0 deletions controllers/article.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,42 @@ class ArticleController {

return res.status(200).send(response);
}

/**
* @param {object} req - Request object
* @param {object} res - Response object
* @returns {object} response
* @static
*/
static async reactOnArticle(req, res) {
const result = await ArticleHelper.createReaction(req);
const reactionCreated = result.toJSON();
return res.status(201).send({ reaction: reactionCreated });
}

/**
* @param {object} req - Request object
* @param {object} res - Response object
* @returns {object} response
* @static
*/
static async getLikes(req, res) {
const { numberOfLikes } = req.body;
const result = await ArticleHelper.getLikes(req);
return res.status(200).send({ likes: result, count: numberOfLikes });
}

/**
* @param {object} req - Request object
* @param {object} res - Response object
* @returns {object} response
* @static
*/
static async getDislikes(req, res) {
const { numberOfDislikes } = req.body;
const result = await ArticleHelper.getDislikes(req);
return res.status(200).send({ dislikes: result, count: numberOfDislikes });
}
}

export default ArticleController;
133 changes: 132 additions & 1 deletion helpers/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Joi from 'joi';
import PassportHelper from './passport';
import db from '../models';

const { Article, User } = db;
const { Article, User, Like } = db;

/**
* @exports ArticleHelper
Expand Down Expand Up @@ -209,5 +209,136 @@ class ArticleHelper {
});
return article;
}

/**
* Check article owner
* @param {object} req - an object
* @param {object} res - an object
* @param {object} next - an object
* @return {object} Returns response
* @static
*/
static async isExisting(req, res, next) {
const { slug } = req.params;
const { id } = req.user;
const { option } = req.params;
const result = await Article.findOne({ where: { slug } });
if (!result) {
return res.status(404).send({ message: `article with slug ${slug} do not exist` });
}
const Result = await Like.findOne({ where: { titleSlug: slug, userId: id } });
if (!Result) {
next();
return true;
}
if (option === 'dislike' && Result.status === 'like') {
await Like.update({ status: 'dislike' }, { where: { titleSlug: slug, userId: id } });
return res.status(200).send({ message: 'like is replaced by dislike' });
}
if (option === 'like' && Result.status === 'dislike') {
await Like.update({ status: 'like' }, { where: { titleSlug: slug, userId: id } });
return res.status(200).send({ message: 'dislike is replaced by like' });
}
if (Result.status === 'neutral') {
await Like.update({ status: option }, { where: { titleSlug: slug, userId: id } });
return res.status(200).send({ message: 'reaction updated' });
}
await Like.update({ status: 'neutral' }, { where: { titleSlug: slug, userId: id } });
return res.status(200).send({ message: 'your reaction is now neutral' });
}

/**
* function for number of likes on an article
* @function likesNumber
* @param {object} req
* @param {object} res
* @param {object} next
* @returns { number } number of comments
*/
static async likesNumber(req, res, next) {
let number = 0;
const { slug } = req.params;
const result = await Like.findAll({
where: { titleSlug: slug, status: 'like' },
include: [{ model: User, as: 'author', attributes: ['username', 'bio', 'image'] }],
attributes: ['id', 'titleSlug', 'status']
});
result.forEach(() => { number += 1; });
req.body.numberOfLikes = number;
next();
return true;
}

/**
* function for number of likes on an article
* @function dislikesNumber
* @param {object} req
* @param {object} res
* @param {object} next
* @returns { number } number of comments
*/
static async dislikesNumber(req, res, next) {
let number = 0;
const { slug } = req.params;
const result = await Like.findAll({
where: { titleSlug: slug, status: 'dislike' },
include: [{ model: User, as: 'author', attributes: ['username', 'bio', 'image'] }],
attributes: ['id', 'titleSlug', 'status']
});
result.forEach(() => { number += 1; });
req.body.numberOfDislikes = number;
next();
return true;
}

/**
* Create a new article
* @param {object} req - an object
* @return {Object} Returns if true if it is valid else return false
* @static
*/
static async createReaction(req) {
const { id } = req.user;
const { slug } = req.params;
const { option } = req.params;
const reactionCreated = await Like.create({
userId: id,
titleSlug: slug,
status: option
});
return reactionCreated;
}

/**
* Create a new article
* @param {object} req - an object
* @return {Object} Returns an object
* @static
*/
static async getLikes(req) {
const { slug } = req.params;
const likesFetched = await Like.findAll({
where: { titleSlug: slug, status: 'like' },
include: [{ model: User, as: 'author', attributes: ['username', 'bio', 'image'] }],
attributes: ['id', 'titleSlug', 'status']
});
return likesFetched;
}

/**
* Create a new article
* @param {object} req - an object
* @return {Object} Returns if true if it is valid else return false
* @static
*/
static async getDislikes(req) {
const { slug } = req.params;
const likesFetched = await Like.findAll({
where: { titleSlug: slug, status: 'dislike' },
include: [{ model: User, as: 'author', attributes: ['username', 'bio', 'image'] }],
attributes: ['id', 'titleSlug', 'status']
});
return likesFetched;
}
}
export default ArticleHelper;
42 changes: 42 additions & 0 deletions migrations/20190512132342-create-like.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const likeMigrations = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
references: {
model: 'Users',
key: 'id'
}
},
titleSlug: {
type: Sequelize.STRING,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
references: {
model: 'Articles',
key: 'slug'
}
},
status: {
type: Sequelize.ENUM,
values: ['like', 'dislike', 'neutral']
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: queryInterface => queryInterface.dropTable('Likes')
};
export default likeMigrations;
4 changes: 4 additions & 0 deletions models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const articles = (sequelize, DataTypes) => {
Article.associate = (models) => {
Article.belongsTo(models.User, { as: 'author' });
Article.hasMany(models.Rating, { foreignKey: 'articleId', allowNull: false });
Article.hasMany(models.Like, {
foreignKey: 'titleSlug',
sourceKey: 'slug'
});
Article.hasMany(models.Comment, {
foreignKey: 'titleSlug',
sourceKey: 'slug'
Expand Down
25 changes: 25 additions & 0 deletions models/like.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const likes = (sequelize, DataTypes) => {
const Like = sequelize.define('Like', {
userId: DataTypes.INTEGER,
titleSlug: DataTypes.STRING,
status: DataTypes.ENUM({
values: ['like', 'dislike', 'neutral']
})
}, {});
Like.associate = (models) => {
Like.belongsTo(models.User, {
foreignKey: 'userId',
as: 'author',
onDelete: 'CASCADE',
onupdate: 'CASCADE'
});
Like.belongsTo(models.Article, {
foreignKey: 'titleSlug',
targetKey: 'slug',
onDelete: 'CASCADE',
onupdate: 'CASCADE'
});
};
return Like;
};
export default likes;
1 change: 1 addition & 0 deletions models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const users = (sequelize, DataTypes) => {
User.hasMany(models.Rating, { foreignKey: 'reviewerId', allowNull: false });
User.hasMany(models.Article, { as: 'author', foreignKey: 'authorId' });
User.hasMany(models.Comment, { foreignKey: 'userId' });
User.hasMany(models.Like, { foreignKey: 'userId' });
};
return User;
};
Expand Down
3 changes: 3 additions & 0 deletions routes/api/article/articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ router.delete('/:slug', Auth, ArticleHelper.isOwner, articleController.deleteArt
router.get('/:slug', articleController.getArticle);
router.get('/', articleController.getAllArticles);
router.post('/rate/:id', Auth, ArticleMiddleware.checkRatedArticle, Ratingcontroller.rateArticle);
router.post('/:slug/reaction/:option', Auth, ArticleHelper.isExisting, articleController.reactOnArticle);
router.get('/:slug/likes', Auth, ArticleHelper.likesNumber, articleController.getLikes);
router.get('/:slug/dislikes', Auth, ArticleHelper.dislikesNumber, articleController.getDislikes);

export default router;
Loading

0 comments on commit 3436d23

Please sign in to comment.