Skip to content

Commit

Permalink
Merge 87fbd29 into 5d4bbaf
Browse files Browse the repository at this point in the history
  • Loading branch information
OKiMaureen committed Aug 27, 2018
2 parents 5d4bbaf + 87fbd29 commit ef195ea
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 24 deletions.
15 changes: 15 additions & 0 deletions config/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import dotenv from 'dotenv';

dotenv.config();

const config = {
secret: process.env.SECRET,
jwtSecret: process.env.JWT_TOKEN_SECRET,
cloudinary: {
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
}
};

export default config;
76 changes: 71 additions & 5 deletions 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 @@ -90,7 +92,7 @@ class ArticleController {
let offset = null;

if (page || limit) {
// calculate offset
// calculate offset
offset = limit * (page - 1);
}

Expand All @@ -105,9 +107,9 @@ class ArticleController {
})
.then((articles) => {
if (articles.length === 0) {
/** check if there was no article created
* for the page query
*/
/** check if there was no article created
* for the page query
*/
const message = page ? 'articles limit exceeded'
: 'Sorry, no articles created';
return res.status(200).json({
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: 0 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import methodOverride from 'method-override';
import morgan from 'morgan';
import debugLog from 'debug';
import expressValidator from 'express-validator';

import { } from 'dotenv/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;
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;
};
20 changes: 8 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@
"methods": "^1.1.2",
"mocha": "^5.2.0",
"morgan": "^1.9.0",
"node-time-uuid": "^0.1.5",
"nodemailer": "^4.6.7",
"nodemailer-stub-transport": "^1.1.0",
"node-time-uuid": "^0.1.5",
"nyc": "^12.0.2",
"passport": "^0.4.0",
"passport-facebook": "^2.1.1",
Expand All @@ -57,9 +57,10 @@
"request": "^2.87.0",
"sequelize": "^4.38.0",
"slug": "^0.9.1",
"slugify": "^1.3.0",
"underscore": "^1.9.1",
"winston": "^3.0.0",
"slugify": "^1.3.0"
"validatorjs": "^3.14.2",
"winston": "^3.0.0"
},
"devDependencies": {
"@types/dotenv": "^4.0.3",
Expand Down
4 changes: 4 additions & 0 deletions routes/api/articleRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import ArticleControllers from '../../controllers/ArticleController';
import validateArticle from '../../middlewares/validateArticle';
import verifyToken from '../../middlewares/verifyToken';
import ParamsValidator from '../../middlewares/ParamsValidator';
import idIsInteger from '../../middlewares/idIsInteger';
import checkArticle from '../../middlewares/checkArticle';
import { checkCount, articleExists } from '../../middlewares/checkUser';


Expand All @@ -20,4 +22,6 @@ router.get('/articles/:slug', ArticleControllers.getArticle);

router.get('/articles', ParamsValidator.validatePageQuery, ArticleControllers.listAllArticles);

router.put('/articles/:id/like', verifyToken, idIsInteger, checkArticle, ArticleControllers.likeArticle);

export default router;
Loading

0 comments on commit ef195ea

Please sign in to comment.