Skip to content

Commit

Permalink
Merge 07effe6 into 7103c35
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentayorinde committed Jul 16, 2019
2 parents 7103c35 + 07effe6 commit 08c41e1
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 1 deletion.
31 changes: 31 additions & 0 deletions controllers/comments/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import db from '../../db/models';

export default {
addComment: async (req, res) => {
const { params: { slug }, body: { content }, user } = req;
try {
const foundArticle = await db.Article.findOne({
where: { slug }
});
if (!foundArticle) {
return res.status(404).send({
error: 'Article does not exist'
});
}
const comment = await db.Comment.create({
articleId: foundArticle.id,
userId: user.id,
content
});
return res.status(201).json({
message: 'Comment added successfully',
comment
});
} catch (e) {
/* istanbul ignore next */
return res.status(500).json({
message: 'something went wrong'
});
}
}
};
42 changes: 42 additions & 0 deletions db/migrations/20190715134956-create-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Comments', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
required: true,
references: {
model: 'Users',
key: 'id',
as: 'user'
}
},
articleId: {
type: Sequelize.INTEGER,
required: true,
references: {
model: 'Articles',
key: 'id',
as: 'article'
}
},
content: {
allowNull: false,
type: Sequelize.TEXT
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: queryInterface => queryInterface.dropTable('Comments')
};
21 changes: 21 additions & 0 deletions db/models/Comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@


module.exports = (sequelize, DataTypes) => {
const Comment = sequelize.define('Comment', {
userId: DataTypes.INTEGER,
articleId: DataTypes.INTEGER,
content: DataTypes.TEXT
}, {});
Comment.associate = (models) => {
Comment.belongsTo(models.User, {
foreignKey: 'userId',
as: 'user'
});
Comment.belongsTo(models.Article, {
foreignKey: 'articleId',
as: 'article',
cascade: true
});
};
return Comment;
};
6 changes: 6 additions & 0 deletions db/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ module.exports = (sequelize, DataTypes) => {
as: 'followers',
cascade: true,
});

User.hasMany(models.Comment, {
foreignKey: 'userId',
as: 'comment',
cascade: true,
});
};

User.prototype.passwordsMatch = function match(password) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"express": "^4.17.1",
"express-fileupload": "^1.1.5",
"express-session": "^1.16.2",
"faker": "^4.1.0",
"husky": "^2.5.0",
"indicative": "^5.0.8",
"jsonwebtoken": "^8.5.1",
Expand All @@ -85,4 +86,4 @@
"swagger-ui-express": "^4.0.6",
"yamljs": "^0.3.0"
}
}
}
9 changes: 9 additions & 0 deletions routes/v1/articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express from 'express';
import Validation from '../../validators/articles';
import Article from '../../controllers/articles';
import Middleware from '../../middlewares';
import Comment from '../../controllers/comments';

const router = express.Router();

Expand Down Expand Up @@ -54,4 +55,12 @@ router.get(
Article.getArticleRatings,
);

router.post(
'/:slug/comments',
Middleware.authenticate,
Middleware.isblackListedToken,
Validation.articleSlug,
Validation.addComment,
Comment.addComment
);
export default router;
43 changes: 43 additions & 0 deletions swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,39 @@ paths:
description: Pass in the appropriate status (true or false)
404:
description: This article does not exist
/articles/{slug}/comments:
post:
tags:
- "Articles"
summary: Comment on an article
description: authenticated user should be able to comment on an article
operationId: addComment
produces:
- application/json
parameters:
- name: slug
in: path
description: Slug of article to comment on
required: true
type: string
- name: x-access-token
in: header
description: Authorization token
required: true
type: string
- in: body
name: content
description: user comment to be added
required: true
schema:
$ref: '#/definitions/addComment'
responses:
201:
description: Comment added successfully
400:
description: Input your content
404:
description: Article does not exist
/profiles/{username}:
get:
tags:
Expand Down Expand Up @@ -656,3 +689,13 @@ definitions:
example: admin
xml:
name: changeRole
addComment:
type: object
required:
- content
properties:
content:
type: string
example: sample user comment
xml:
name: addComment
58 changes: 58 additions & 0 deletions tests/routes/articles.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,62 @@ describe('ARTICLES TEST', () => {
expect(res.body.message).to.equal('Article does not exist');
});
});
describe('Comment on Articles', () => {
let userResponse;
let articleData;
let userToken;
beforeEach(async () => {
const user = await createUser(register);
userResponse = user.response();
const { token } = userResponse;
userToken = token;
articleData = await createArticle({ ...article, authorId: userResponse.id });
});
it('should add a comment if user is authenticated', async () => {
const res = await chai
.request(app)
.post(`/api/v1/articles/${articleData.slug}/comments`)
.set('x-access-token', userToken)
.send({ content: 'This is my first comment' });
expect(res.statusCode).to.equal(201);
expect(res.body).to.be.an('object');
expect(res.body.message).to.be.equal('Comment added successfully');
expect(res.body.comment).to.be.an('object');
expect(res.body.comment.content).to.be.a('string');
});

it('should not add a comment if user is not authenticated', async () => {
const token = await utils.getToken(45345, 'wrong@gmail.com');
const res = await chai
.request(app)
.post(`/api/v1/articles/${articleData.slug}/comments`)
.set('x-access-token', token)
.send({ content: 'This is my first comment' });
expect(res.statusCode).to.equal(401);
expect(res.body).to.be.an('object');
expect(res.body.message).to.be.equal('Unauthorized');
});

it('should not add a comment if article does not exist', async () => {
const res = await chai
.request(app)
.post('/api/v1/articles/wrong-article/comments')
.set('x-access-token', userToken)
.send({ content: 'This is my first comment' });
expect(res.statusCode).to.equal(404);
expect(res.body).to.be.an('object');
expect(res.body.error).to.be.equal('Article does not exist');
});

it('should not add a comment if comment is empty', async () => {
const res = await chai
.request(app)
.post(`/api/v1/articles/${articleData.slug}/comments`)
.set('x-access-token', userToken)
.send({ content: '' });
expect(res.statusCode).to.equal(400);
expect(res.body).to.be.an('object');
expect(res.body.message[0].message).to.be.equal('Input your content');
});
});
});
17 changes: 17 additions & 0 deletions validators/articles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,22 @@ export default {
message: e,
});
}
},

addComment: async (req, res, next) => {
const rules = {
content: 'required|string'
};

const data = req.body;

try {
await validatorInstance.validateAll(data, rules, messages);
next();
} catch (e) {
return res.status(400).json({
message: e,
});
}
}
};

0 comments on commit 08c41e1

Please sign in to comment.