Skip to content

Commit

Permalink
feat(comment): user can comment on article
Browse files Browse the repository at this point in the history
- create comment model and migration

- fix routes, middleware and controllers to allow user comment

[Delivers #159206056]
  • Loading branch information
ascii-dev authored and veeqtor committed Sep 25, 2018
1 parent 2f3a7f8 commit 3072fc3
Show file tree
Hide file tree
Showing 25 changed files with 689 additions and 35 deletions.
4 changes: 4 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ NO_REPLY_MAIL=
CLOUD_NAME=
API_KEY=
API_SECRET=

SECRET_KEY=
STRIPE_SECRET_KEY=
STRIPE_PUBLIC_KEY=


GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
Expand Down
12 changes: 6 additions & 6 deletions controllers/ArticleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ class ArticleController {
* get an article using slug as query parameter
* @param {object} req - request object
* @param {object} res - response object
* @param {function} next - next function
* @param {function} next - to handle errors
* @returns {object} - the found article from database or error if not found
*/
*/
static getArticle(req, res, next) {
const { slug } = req.params;

Expand Down Expand Up @@ -82,9 +82,9 @@ class ArticleController {
* if provided to implement pagination
* @param {object} req - request object
* @param {object} res - response object
* @param {function} next - next function
* @param {function} next - to handle errors
* @returns {object} - the found article from database or empty if not found
*/
*/
static listAllArticles(req, res, next) {
const { page, limit } = req;
let offset = null;
Expand Down Expand Up @@ -129,7 +129,7 @@ class ArticleController {
* @summary: API controller to handle requests to edit an article
* @param {object} req: request object
* @param {object} res: response object
* @param {function} next - next function
* @param {function} next - to handle errors
* @returns {object} api response: article object for
* successful requests, or error object for
* requests that fail
Expand Down Expand Up @@ -240,4 +240,4 @@ class ArticleController {
}
}

export default ArticleController;
export default ArticleController;
110 changes: 110 additions & 0 deletions controllers/CommentsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import db from '../models';
import utils from '../helpers/utilities';

const { Comment, User, Reply } = db;

/** * Class representing comments */
export default class CommentsController {
/**
* @function createComment
* @summary Allows a user create comment
* @param {object} req - request object
* @param {object} res - response object
* @param {function} next - to handle errors
* @returns {object} status code 201 with comment created if successful
*/
static createComment(req, res, next) {
const { body } = req.body.comment;
Comment.create({
userId: req.userId,
articleId: req.articleObject.id,
body
})
.then(comment => res.status(201).json({
success: true,
comment,
user: utils.userToJson(req.userObject),
article: req.articleObject,
}))
.catch(next);
}

/**
* @function createReply
* @summary Allows a user create comment
* @param {object} req - request object
* @param {object} res - response object
* @param {function} next - to handle errors
* @returns {object} status code 201 with comment created if successful
*/
static createReply(req, res, next) {
const { body } = req.body.comment;
Reply.create({
userId: req.userId,
commentId: req.params.id,
body,
})
.then(reply => res.status(201).json({
success: true,
reply,
user: utils.userToJson(req.userObject),
comment: req.commentObject,
}))
.catch(next);
}

/**
* @function getComments
* @summary Get a single comment, the user and article related to it
* @param {object} req - request object
* @param {object} res - response object
* @param {function} next - to handle errors
* @returns {object} status code 200 with comment if successful
*/
static getComments(req, res, next) {
Comment.findAll({
include: [{
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt'] },
}, {
model: Reply,
include: [{
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt', 'lastname', 'firstname', 'bio'] },
}]
}],
where: {
articleId: req.articleObject.id,
}
})
.then(comments => res.status(200).json({
success: true,
article: req.articleObject,
comments,
}))
.catch(next);
}

/**
* @function deleteComment
* @summary Delete a single comment
* @param {object} req - request object
* @param {object} res - response object
* @param {function} next - to handle errors
* @returns {object} status code 200 if successful
*/
static deleteComment(req, res, next) {
Comment.destroy({
where: {
id: req.params.id,
}
})
.then(() => res.status(200).json({
success: true,
message: {
body: ['Comment deleted successfully']
}
}))
.catch(next);
}
}
2 changes: 1 addition & 1 deletion controllers/UsersController.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default class UsersController {
static getProfile(req, res, next) {
User.findOne({ where: { username: req.params.username } })
.then((user) => {
if (!user || user.rowCount < 1) {
if (!user) {
return res.status(404).json({
success: false,
errors: {
Expand Down
10 changes: 0 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ import morgan from 'morgan';
import debugLog from 'debug';
import expressValidator from 'express-validator';
import { } from 'dotenv/config';
import cloudinary from 'cloudinary';
import passportConfig from './config/passport';
import routes from './routes';
import config from './config/config';


const isProduction = process.env.NODE_ENV === 'production';

Expand All @@ -30,13 +27,6 @@ passport.deserializeUser(((user, done) => {
const app = express();
passportConfig(app);

/** configure cloudinary to be able to upload image */
cloudinary.config({
cloud_name: config.cloudinary.cloud_name,
api_key: config.cloudinary.api_key,
api_secret: config.cloudinary.api_secret,
});

app.use(cors());

// Normal express config defaults;
Expand Down
25 changes: 23 additions & 2 deletions middlewares/ParamsValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,34 @@ export default class ParamsValidator {
* @param {string} req - The request to be validated
* @param {string} res - The response from validation
* @param {string} next - The next function
* @returns {boolean} if the username is alphanumeric or not
* @returns {boolean} if the user id is alphaintegernumeric or not
*/
static validateId(req, res, next) {
const isId = /^[0-9]*$/;
const test = isId.test(req.userId);
if (!test) {
return res.status(400).json({
return res.status(403).json({
success: false,
errors: {
body: ['Invalid id'],
}
});
}
next();
}

/**
* Converts payload to JWT
* @param {string} req - The request to be validated
* @param {string} res - The response from validation
* @param {string} next - The next function
* @returns {boolean} if the params id is integer or not
*/
static validateParamId(req, res, next) {
const isId = /^[0-9]*$/;
const test = isId.test(req.params.id);
if (!test) {
return res.status(403).json({
success: false,
errors: {
body: ['Invalid id'],
Expand Down
6 changes: 1 addition & 5 deletions middlewares/checkUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ export const articleExists = (req, res, next) => {
req.count = foundArticle.updatedCount;
next();
})
.catch((err) => {
res.status(500).json({
error: err
});
});
.catch(next);
};

export const checkCount = (req, res, next) => {
Expand Down
21 changes: 21 additions & 0 deletions middlewares/getArticle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import db from '../models';

const { Article } = db;
const getArticle = (req, res, next) => {
Article.findOne({ where: { slug: req.params.slug } })
.then((article) => {
if (!article) {
return res.status(404).json({
success: false,
errors: {
body: ['The article does not exist']
},
});
}
req.articleObject = article;
next();
})
.catch(next);
};

export default getArticle;
21 changes: 21 additions & 0 deletions middlewares/getComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import db from '../models';

const { Comment } = db;
const getComment = (req, res, next) => {
Comment.findById(req.params.id)
.then((comment) => {
if (!comment) {
return res.status(404).json({
success: false,
errors: {
body: ['The comment does not exist']
},
});
}
req.commentObject = comment;
next();
})
.catch(next);
};

export default getComment;
2 changes: 1 addition & 1 deletion middlewares/getUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const getUser = (req, res, next) => {
}
db.User.find({ where: whereField })
.then((user) => {
if (!user || user.rowCount === 0) {
if (!user) {
return res.status(404).json({
success: false,
errors: {
Expand Down
26 changes: 26 additions & 0 deletions middlewares/validateComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const validateComment = (req, res, next) => {
const { body, commentId } = req.body.comment;
const errors = [];
if (!body) {
errors.push('Comment can not be empty');
}
if (typeof body !== 'string') {
errors.push('Comment must be a string');
}
if (commentId) {
if (typeof commentId !== 'number') {
errors.push('parentId must be a number');
}
}
if (errors.length !== 0) {
return res.status(403).json({
success: false,
errors: {
body: [...errors]
}
});
}
next();
};

export default validateComment;
2 changes: 1 addition & 1 deletion middlewares/verifyToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const verifyToken = (req, res, next) => {
}
});
}
req.user = decoded.id;
req.userId = decoded.id;
next();
});
};
Expand Down
File renamed without changes.
45 changes: 45 additions & 0 deletions migrations/20180802214839-create-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Comments', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
allowNull: false,
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Users',
key: 'id',
as: 'userId',
},
},
articleId: {
allowNull: false,
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Articles',
key: 'id',
as: 'articleId',
},
},
body: {
allowNull: false,
type: Sequelize.TEXT
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: (queryInterface/* , Sequelize */) => {
queryInterface.dropTable('Comments');
}
};
Loading

0 comments on commit 3072fc3

Please sign in to comment.