Skip to content

Commit

Permalink
feat(like): users can like articles
Browse files Browse the repository at this point in the history
- Write unit test
- add route to like articles
- add user can like articles controller

[Delivers #159206057]
  • Loading branch information
OKiMaureen committed Aug 16, 2018
1 parent 6b9dd41 commit 56bfd5c
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 36 deletions.
107 changes: 82 additions & 25 deletions controllers/ArticleController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import cloudinary from 'cloudinary';
import { Op } from 'sequelize';
import Utilities from '../helpers/utilities';
import { Article, User } from '../models';
import { Article, User, Like } from '../models';

import createArticleHelper from '../helpers/createArticleHelper';

/**
Expand All @@ -23,6 +25,7 @@ class ArticleController {

const { userId } = req;


const articleObject = {
title, description, body, tagList, imageUrl, userId
};
Expand Down Expand Up @@ -75,17 +78,17 @@ class ArticleController {
}

return res.status(200).json({ article });
})
.catch(() => res.status(501).send('oops seems there is an error finding the article'));
});
}

/**
* get all articles created
* @param {object} req - request object
* @param {object} res - response object
* @param {object} next: response object
* @returns {object} - the found article from database or empty if not found
*/
static listAllArticles(req, res) {
static listAllArticles(req, res, next) {
return Article
.findAll({
include: [{
Expand All @@ -105,7 +108,7 @@ class ArticleController {

return res.status(200).json({ articles, articlesCount: articles.length });
})
.catch(() => res.status(501).send('oops seems there is an error finding all articles'));
.catch(next);
}

/**
Expand All @@ -114,11 +117,12 @@ class ArticleController {
* to edit an article
* @param {object} req: request object
* @param {object} res: response object
* @param {object} next: response object
* @returns {object} api response: article object for
* successful requests, or error object for
* requests that fail
*/
static editArticle(req, res) {
static editArticle(req, res, next) {
const { title, description, body } = req.body.article;
const { count } = req;
const { slug } = req.params;
Expand All @@ -138,16 +142,7 @@ class ArticleController {
success: true,
article: result[1]
}))
.catch((err) => {
res.status(500).json({
errors: {
body: [
'sorry there was an error updating this article',
err
]
}
});
});
.catch(next);
}

/**
Expand All @@ -156,26 +151,88 @@ class ArticleController {
* to delete an article
* @param {object} req: request object
* @param {object} res: response object
* @param {object} next: response object
* @returns {object} api response: article object for
* successful requests, or error object for
* requests that fail
*/
static deleteArticle(req, res) {
static deleteArticle(req, res, next) {
const { slug } = req.params;
Article.destroy({
where: { slug }
})
.then(() => res.status(204).json())
.catch(() => {
res.status(500).json({
errors: {
body: [
'sorry there was an error deleting this article',
]
}
.catch(next);
}

/**
* @description - model query function
* @static
*
*
* @param {object} res
* @param {integer} id
*
* @returns {object} newOrFoundUser
*
* @memberof AuthController
*
*/
static countLikes(res, id) {
Like.findAndCountAll({
where: {
[Op.and]: [
{ articleId: id }
]
}
})
.then((articleLikes) => {
res.status(200).json({
success: 'true',
totalLikes: articleLikes.count,
});
});
}
}


/**
* @function likeArticle
* @summary: API controller to handle requests
* to like an article
* @param {object} req: request object
* @param {object} res: response object
* @param {object} next: response object
* @returns {object} api response: article object for
* successful requests, or error object for
* requests that fail
*/
static likeArticle(req, res, next) {
const { userId } = req;
const { id } = req.params;
Like.find({
where: {
userId, articleId: id
}
})
.then((like) => {
if (!like) {
Like.create({
articleId: id,
userId,
})
.then(() => {
ArticleController.countLikes(res, id);
});
} else {
Like.destroy({
where: { id: like.id }
})
.then(() => {
ArticleController.countLikes(res, id);
});
}
})
.catch(next);
}
}
export default ArticleController;
2 changes: 1 addition & 1 deletion helpers/createArticleHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const createArticleHelper = (res, articleObject, imageUrl = null) => {
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt'] }
}],
attributes: { exclude: ['id', 'userId'] }
attributes: { exclude: ['userId'] }
}))
.then(article => res.status(201).json({ article }))
.catch(err => res.status(400).send({
Expand Down
1 change: 1 addition & 0 deletions helpers/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export default class Utilities {
})
.catch(next);
}

/** @function increaseCount
* @summary: API controller to handle requests
* to delete an article
Expand Down
29 changes: 29 additions & 0 deletions middlewares/checkArticle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Article } from '../models';
/**
* validate Request id parameter
* @param {Object} req
* @param {Object} res
*
* @param {Function} next
*
* @return {Object} json
*/
const checkArticle = (req, res, next) => {
const { id } = req.params;
Article.find({
where: {
id
}
})
.then((article) => {
if (article) return next();
return res.status(404).json({
errors: {
body: [
'Ooops! the article cannot be found.'
]
}
});
});
};
export default checkArticle;
36 changes: 36 additions & 0 deletions middlewares/idIsInteger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Validate from 'validatorjs';

/**
* validate Request id parameter
* @param {Object} req
* @param {Object} res
*
* @param {Function} next
*
* @return {Object} json
*/
const idIsNumber = (req, res, next) => {
const {
id,
} = req.params;

const data = {
id,
};
const rules = {
id: ['required', 'integer'],
};
const validations = new Validate(data, rules, {
'integer.id': 'The parameter :attribute must be an integer.',
});

if (validations.passes()) {
return next();
}

return res.status(406).json({
success: 'false',
errors: validations.errors.all(),
});
};
export default idIsNumber;
39 changes: 39 additions & 0 deletions migrations/20180807152049-create-likes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
articleId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Articles',
key: 'id',
as: 'articleId',
},
},
userId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Users',
key: 'id',
as: 'userId',
},
},
}),
down: (queryInterface/* , Sequelize */) => {
queryInterface.dropTable('Likes');
}
};
5 changes: 4 additions & 1 deletion models/Article.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ module.exports = (sequelize, DataTypes) => {
onDelete: 'CASCADE',
}
);
Article.hasMany(models.Like, {
foreignKey: 'articleId',
as: 'likes',
});
};

return Article;
};
16 changes: 16 additions & 0 deletions models/Likes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

module.exports = (sequelize) => {
const Like = sequelize.define('Like', {}, {});

Like.associate = (models) => {
Like.belongsTo(models.Article, {
foreignKey: 'articleId',
as: 'likedArticle',
});
Like.belongsTo(models.User, {
foreignKey: 'userId',
as: 'user',
});
};
return Like;
};
4 changes: 4 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ module.exports = (sequelize, DataTypes) => {
foreignKey: 'userId',
as: 'articles',
});
User.hasMany(models.Like, {
foreignKey: 'userId',
as: 'likes',
});
};
return User;
};
Loading

0 comments on commit 56bfd5c

Please sign in to comment.