-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
642 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
{ | ||
"presets": ["env"], | ||
"sourceMaps": true, | ||
"retainLines": true, | ||
"env": { | ||
"test": { | ||
"plugins": [ | ||
"istanbul" | ||
] | ||
} | ||
} | ||
} | ||
"presets": ["env"], | ||
"sourceMaps": true, | ||
"retainLines": true, | ||
"env": { | ||
"test": { | ||
"plugins": [ | ||
"istanbul" | ||
] | ||
} | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
import models from '../models'; | ||
|
||
const { Comment, User, Article } = models; | ||
|
||
/** | ||
* This class contains all the methods responsible for creating and querying | ||
* comments on the app | ||
* It is made up static methods which can be called from anywhere in the app. | ||
*/ | ||
export default class CommentController { | ||
/** | ||
* Create a comment and return the data. | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {object} the comment that was created. | ||
*/ | ||
static async createComment(req, res) { | ||
const { id, username } = req.user; | ||
const { body } = req.body; | ||
const parentId = req.query.id === undefined ? null : req.query.id; | ||
const { slug } = req.params; | ||
const article = await CommentController.getArticleFromSlug(slug); | ||
if (article !== null) { | ||
return Comment.create({ | ||
articleId: article.id, | ||
userId: id, | ||
parentId, | ||
body, | ||
}).then(newComment => CommentController.commentResponse(res, newComment, slug, username)); | ||
} | ||
res.status(400).json({ | ||
status: 'fail', | ||
message: 'Unable to create comment because article does not exist.', | ||
}); | ||
} | ||
|
||
/** | ||
* Get all comments with one level reply nesting. | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {object} an object containing an array of all comments. | ||
*/ | ||
static getComments(req, res) { | ||
Comment.findAll({ | ||
include: [ | ||
{ model: User, as: 'commenter' }, | ||
] | ||
}).then((comments) => { | ||
const destructuredComments = comments.map(comment => Object.assign( | ||
{}, | ||
{ | ||
id: comment.id, | ||
parentId: comment.parentId, | ||
createdAt: new Date(comment.createdAt).toLocaleString('en-GB', { hour12: true }), | ||
updatedAt: new Date(comment.updatedAt).toLocaleString('en-GB', { hour12: true }), | ||
body: comment.body, | ||
author: { | ||
username: comment.commenter.username, | ||
bio: comment.commenter.bio, | ||
image: comment.commenter.image, | ||
}, | ||
} | ||
)); | ||
const result = CommentController.nestComments(destructuredComments); | ||
res.status(200).json({ | ||
status: 'success', | ||
comments: result, | ||
}); | ||
}).catch(() => { | ||
res.status(400).json({ | ||
status: 'fail', | ||
message: 'Unable to get comments.', | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* Get a comment by its id | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {object} an object containing an array of all comments. | ||
*/ | ||
static getComment(req, res, next) { | ||
Comment.findById((req.params.id), { | ||
include: [ | ||
{ model: User, as: 'commenter' }, | ||
] | ||
}).then((comment) => { | ||
if (!comment) { | ||
res.status(404).json({ | ||
status: 'fail', | ||
message: 'Unable to get the comment with supplied id.', | ||
}); | ||
} | ||
res.status(200).json({ | ||
status: 'success', | ||
comment: { | ||
id: comment.id, | ||
parentId: comment.parentId, | ||
createdAt: new Date(comment.createdAt).toLocaleString('en-GB', { hour12: true }), | ||
updatedAt: new Date(comment.updatedAt).toLocaleString('en-GB', { hour12: true }), | ||
body: comment.body, | ||
author: { | ||
username: comment.commenter.username, | ||
bio: comment.commenter.bio, | ||
image: comment.commenter.image, | ||
}, | ||
} | ||
}); | ||
}).catch(err => next(err)); | ||
} | ||
|
||
/** | ||
* Update a comment | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {object} the updated comment. | ||
*/ | ||
static updateComment(req, res, next) { | ||
Comment.findById(parseInt(req.params.id, 10)) | ||
.then((comment) => { | ||
if (!comment) { | ||
return res.status(404).json({ | ||
status: 'fail', | ||
message: 'No comment found, please check the id supplied.', | ||
}); | ||
} | ||
Comment.update({ | ||
body: req.body.body, | ||
}, { | ||
returning: true, | ||
where: { id: comment.id }, | ||
}) | ||
.then(([, [updatedComment]]) => { | ||
res.status(200).json({ | ||
status: 'success', | ||
comment: { | ||
id: updatedComment.id, | ||
parentId: updatedComment.parentId, | ||
createdAt: new Date(updatedComment.createdAt).toLocaleString('en-GB', { hour12: true }), | ||
updatedAt: new Date(updatedComment.updatedAt).toLocaleString('en-GB', { hour12: true }), | ||
body: updatedComment.body, | ||
author: updatedComment.author, | ||
} | ||
}); | ||
}).catch(() => res.status(400).json({ | ||
status: 'fail', | ||
message: 'Unable to update comment', | ||
})); | ||
}).catch(err => next(err)); | ||
} | ||
|
||
/** | ||
* Delete a comment | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {null} | ||
*/ | ||
static deleteComment(req, res) { | ||
Comment.findById(parseInt(req.params.id, 10)) | ||
.then((comment) => { | ||
if (!comment) { | ||
return res.status(404).json({ | ||
status: 'fail', | ||
message: 'No comment with the supplied id found.', | ||
}); | ||
} | ||
Comment.destroy({ where: { id: comment.id } }) | ||
.then(() => res.status(200).json({ | ||
status: 'success', | ||
message: 'Comment deleted.' | ||
})); | ||
}).catch(() => res.status(400).json({ | ||
status: 'fail', | ||
message: 'Invalid comment id supplied.', | ||
})); | ||
} | ||
|
||
/** | ||
* One-level nest an array of comments and replies | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {array} an array of nested comments. | ||
*/ | ||
static nestComments(comments) { | ||
const mainComments = comments.filter(comment => comment.parentId === null); | ||
const replies = comments.filter(comment => comment.parentId !== null); | ||
const result = []; | ||
mainComments.forEach((comment) => { | ||
comment.replies = replies.filter(reply => reply.parentId === comment.id); | ||
result.push(comment); | ||
}); | ||
return result; | ||
} | ||
|
||
/** | ||
* Return the article that has the supplied slug | ||
* @param {object} req the request object | ||
* @param {object} res the response object | ||
* @returns {promise} the article object found | ||
*/ | ||
static getArticleFromSlug(slug) { | ||
return new Promise((resolve, reject) => { | ||
Article.findOne({ | ||
where: { slug }, | ||
attributes: [ | ||
'id', 'title', 'userId', 'slug', 'body', | ||
'imageUrl', 'categoryId', 'createdAt', 'updatedAt' | ||
], | ||
}) | ||
.then(article => resolve(article)) | ||
.catch(err => reject(err)); | ||
}); | ||
} | ||
|
||
/** | ||
* Formats and sends the response to the user when a comment is created. | ||
* @param {object} res the response object | ||
* @param {object} newComment the created comment | ||
* @param {string} slug the slug of the article to which the comment belongs | ||
* @param {string} username the author of the comment | ||
* @returns {object} the comment that was created. | ||
*/ | ||
static commentResponse(res, newComment, slug, username) { | ||
return res.status(201).json({ | ||
status: 'success', | ||
message: 'Comment has been created', | ||
comment: { | ||
id: newComment.id, | ||
parentId: newComment.parentId, | ||
createdAt: new Date(newComment.createdAt).toLocaleString('en-GB', { hour12: true }), | ||
updatedAt: new Date(newComment.updatedAt).toLocaleString('en-GB', { hour12: true }), | ||
body: newComment.body, | ||
article: slug, | ||
author: username, | ||
}, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import Validator from 'validatorjs'; | ||
|
||
/** | ||
* @export | ||
* @class Validation | ||
*/ | ||
export default class CommentValidation { | ||
/** | ||
* Validate data for comment creation | ||
* | ||
* @param {object} req - HTTP Request | ||
* @param {object} res - HTTP Response | ||
* @param {function} next | ||
* @returns {object} Class instance | ||
* @memberof Validation | ||
*/ | ||
static validateComment(req, res, next) { | ||
const commentProperties = { | ||
body: 'required|string', | ||
}; | ||
|
||
const validator = new Validator(req.body, commentProperties); | ||
validator.passes(() => next()); | ||
validator.fails(() => { | ||
const errors = validator.errors.all(); | ||
return res.status(400).json({ | ||
status: 'error', | ||
errors, | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
|
||
export default (sequelize, DataTypes) => { | ||
const Category = sequelize.define('Category', { | ||
title: { | ||
|
Oops, something went wrong.