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 27, 2018
1 parent 376cc51 commit 5d054a0
Show file tree
Hide file tree
Showing 17 changed files with 11,238 additions and 73 deletions.
14 changes: 0 additions & 14 deletions .vscode/launch.json

This file was deleted.

68 changes: 67 additions & 1 deletion controllers/ArticleController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Op } from 'sequelize';
import cloudinary from '../config/cloudinary';
import Utilities from '../helpers/utilities';
import { Article, User } from '../models';
import { Article, User, Like } from '../models';
import createArticleHelper from '../helpers/createArticleHelper';

/**
Expand Down Expand Up @@ -59,6 +60,7 @@ class ArticleController {
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt'] }
}],
attributes: { exclude: ['userId'] }
})
.then((article) => {
// if the article does not exist
Expand Down Expand Up @@ -174,6 +176,70 @@ class ArticleController {
.then(() => res.status(200).json({ message: 'Article successfully deleted' }))
.catch(next);
}

/**
* @function CountLikes
* @summary: controller to handle counting likes
* @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;
1 change: 1 addition & 0 deletions helpers/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export default class Utilities {
})
.catch(next);
}

/**
* @function increaseCount
* @summary: A funtion to increase count
Expand Down
7 changes: 0 additions & 7 deletions helpers/vsDebug.js

This file was deleted.

3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import methodOverride from 'method-override';
import morgan from 'morgan';
import debugLog from 'debug';
import expressValidator from 'express-validator';

import { } from 'dotenv/config';
import cloudinary from 'cloudinary';
import config from './config';
import passportConfig from './config/passport';
import routes from './routes';

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;
19 changes: 0 additions & 19 deletions middlewares/validateArticle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,17 @@
* or continue to the next middleware using next()
*/
const validateArticle = (req, res, next) => {
<<<<<<< HEAD
if (!req.body.article) {
return res.status(400).json({
errors: {
body: [
'Please check that article field is present'
=======
let { title, description, body } = req.body.article;
try {
title = title.trim();
description = description.trim();
body = body.trim();
} catch (error) {
return res.status(400).json({
errors: {
body: [
'Please check that your title, description or body field is not empty'
>>>>>>> ft(create-article): create user article
]
}
});
}
<<<<<<< HEAD
const { title, description, body } = req.body.article;

=======
>>>>>>> ft(create-article): create user article
if (!title || !description || !body) {
return res.status(400).json({
errors: {
Expand All @@ -43,12 +27,9 @@ const validateArticle = (req, res, next) => {
}
});
}
<<<<<<< HEAD
req.body.article.title = title.trim();
req.body.article.description = description.trim();
req.body.article.body = body.trim();
=======
>>>>>>> ft(create-article): create user article

next();
};
Expand Down
8 changes: 0 additions & 8 deletions migrations/20180730142739-create-article.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,14 @@ module.exports = {
type: Sequelize.STRING
},
body: {
<<<<<<< HEAD
type: Sequelize.TEXT
=======
type: Sequelize.STRING
>>>>>>> ft(create-article): create user article
},
updatedCount: {
type: Sequelize.INTEGER,
defaultValue: '0'
},
tagList: {
<<<<<<< HEAD
type: Sequelize.ARRAY(Sequelize.STRING)
=======
type: Sequelize.ARRAY(Sequelize.TEXT)
>>>>>>> ft(create-article): create user article
},
favorited: {
type: Sequelize.BOOLEAN
Expand Down
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 @@ -47,6 +47,10 @@ module.exports = (sequelize, DataTypes) => {
as: 'amFollowing',
foreignKey: 'userId'
});
User.hasMany(models.Like, {
foreignKey: 'userId',
as: 'likes',
});
};
return User;
};
Loading

0 comments on commit 5d054a0

Please sign in to comment.