Skip to content

Commit

Permalink
Merge 856abe2 into a1f7459
Browse files Browse the repository at this point in the history
  • Loading branch information
kleva-j committed Mar 18, 2019
2 parents a1f7459 + 856abe2 commit 62c0753
Show file tree
Hide file tree
Showing 17 changed files with 796 additions and 422 deletions.
72 changes: 36 additions & 36 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
{
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"root": true,
"extends": "airbnb-base",
"env": {
"node": true,
"es6": true,
"mocha": true
},
"rules": {
"one-var": 0,
"one-var-declaration-per-line": 0,
"new-cap": 0,
"consistent-return": 0,
"no-param-reassign": 0,
"comma-dangle": 0,
"curly": ["error", "multi-line"],
"import/no-unresolved": [2, { "commonjs": true }],
"no-shadow": ["error", { "allow": ["req", "res", "err"] }],
"valid-jsdoc": ["error", {
"requireReturn": true,
"requireReturnType": true,
"requireParamDescription": false,
"requireReturnDescription": true
}],
"require-jsdoc": ["error", {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true
}
}]
}
}
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"root": true,
"extends": "airbnb-base",
"env": {
"node": true,
"es6": true,
"mocha": true
},
"rules": {
"one-var": 0,
"one-var-declaration-per-line": 0,
"new-cap": 0,
"consistent-return": 0,
"no-param-reassign": 0,
"comma-dangle": 0,
"curly": ["error", "multi-line"],
"import/no-unresolved": [2, { "commonjs": true }],
"no-shadow": ["error", { "allow": ["req", "res", "err"] }],
"valid-jsdoc": ["error", {
"requireReturn": true,
"requireReturnType": true,
"requireParamDescription": false,
"requireReturnDescription": true
}],
"require-jsdoc": ["error", {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true
}
}]
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Authors Haven - A Social platform for the creative at heart.

[![Build Status](https://travis-ci.com/andela/vidar-ah-backend.svg?branch=develop)](https://travis-ci.com/andela/vidar-ah-backend) [![Coverage Status](https://coveralls.io/repos/github/andela/vidar-ah-backend/badge.svg?branch=ft-create-user-article-%23164139686)](https://coveralls.io/github/andela/vidar-ah-backend?branch=ft-create-user-article-%23164139686) [![](https://img.shields.io/badge/Protected_by-Hound-a873d1.svg)](https://houndci.com)
[![Build Status](https://travis-ci.com/andela/vidar-ah-backend.svg?branch=develop)](https://travis-ci.com/andela/vidar-ah-backend) [![Coverage Status](https://coveralls.io/repos/github/andela/vidar-ah-backend/badge.svg?branch=develop)](https://coveralls.io/github/andela/vidar-ah-backend?branch=develop) [![](https://img.shields.io/badge/Protected_by-Hound-a873d1.svg)](https://houndci.com)
=======

## Vision
Expand Down
40 changes: 38 additions & 2 deletions controllers/articles.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Article } from '../models';
import { Article, Ratings } from '../models';

/**
* @class ArticleController
Expand Down Expand Up @@ -31,7 +31,43 @@ export default class ArticleController {
article: result,
});
} catch (error) {
return res.status(500).json({ success: false, error: [error.message] });
return res.status(500).json({ success: false, errors: [error.message] });
}
}

/**
* @description - Rate an article
* @static
* @param {Object} req - the request object
* @param {Object} res - the response object
* @memberof ArticleController
* @returns {Object} class instance
*/
static async rateArticle(req, res) {
const { id } = req.user;
const rating = Number(req.body.rating);
const { articleId } = req.params;
try {
const previousRating = await Ratings.findOne({ where: { userId: id, articleId } });
if (previousRating) {
const updatedRating = await previousRating.update({ rating });
return res.status(201).json({
success: true,
message: `Article rating has been updated as ${rating}`,
rating: updatedRating
});
}
return res.status(200).json({
success: true,
message: `Article has been rated as ${rating}`,
articleRating: (await Ratings.create({
userId: id,
articleId,
rating
}))
});
} catch (error) {
return res.status(400).json({ success: false, errors: ['Error rating this article'] });
}
}
}
128 changes: 48 additions & 80 deletions doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,92 +231,56 @@
}
}
},
"/requestpasswordreset": {
"/articles": {
"post": {
"tags": [
"Reset password"
"Articles"
],
"summary": "Send reset link to user email",
"description": "Send reset link to user email",
"summary": "Create an article",
"description": "",
"operationId": "addArticle",
"consumes": [
"application/x-www-form-urlencoded",
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"in": "body",
"name": "Request body",
"description": "Reset user password",
"name": "authorization",
"in": "header",
"description": "A token to verify the user",
"required": true,
"schema": {
"$ref": "#/definitions/requestPasswordReset"
}
}
],
"responses": {
"201": {
"description": "Save password reset key in database and send link to email"
},
"404": {
"description": "User not registered or verified."
"type": "string"
},
"422": {
"description": "Email is invalid"
}
}
}
},
"/resetpassword/{key}": {
"post": {
"tags": [
"Reset password"
],
"summary": "Verify user reset password key and change password",
"description": "Verify user reset password key and change password",
"consumes": [
"application/x-www-form-urlencoded",
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"in": "body",
"name": "key",
"description": "key sent with the link to the user email",
"name": "body",
"description": "Create user article",
"required": true,
"type": "integer",
"format": "int64",
"schema": {
"$ref": "#/definitions/resetPassword"
"$ref": "#/definitions/articles"
}
}
],
"responses": {
"200": {
"description": "You can now reset your password."
},
"410": {
"description": "Your link has expired. Please try to reset password again."
"201": {
"description": "New article created successfully"
},
"404": {
"description": "Password reset token not found."
"422": {
"description": "Invalid title/description/body supplied"
}
}
}
},
"/articles": {
"/articles/rate/:articleId": {
"post": {
"tags": [
"Articles"
],
"summary": "Create an article",
"description": "",
"operationId": "addArticle",
"summary": "Rate an article",
"description": "Give ratings on an article",
"operationId": "rateArticle",
"consumes": [
"application/json"
],
Expand All @@ -333,20 +297,26 @@
},
{
"in": "body",
"name": "body",
"description": "Update user profile",
"name": "rating",
"description": "Rate an article",
"required": true,
"schema": {
"$ref": "#/definitions/articles"
"$ref": "#/definitions/rating"
}
}
],
"responses": {
"201": {
"description": "New article created successfully"
"description": "Article has been rated as (rating)"
},
"422": {
"description": "Invalid title/description/body supplied"
"description": "Please provide a valid rating for this article"
},
"404": {
"description": "This article does not exist"
},
"403": {
"description": "Permission denied, user cannot rate their own article"
}
}
}
Expand Down Expand Up @@ -408,22 +378,6 @@
}
}
},
"requestPasswordReset": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
}
},
"resetPassword": {
"type": "object",
"properties": {
"password": {
"type": "string"
}
}
},
"articles": {
"type": "object",
"properties": {
Expand All @@ -438,9 +392,23 @@
}
}
},
"externalDocs": {
"description": "Find out more about Swagger",
"url": "http://swagger.io"
"rating": {
"type": "object",
"properties": {
"rating": {
"type": "integer"
},
"articleId": {
"type": "string"
},
"userId": {
"type": "number"
}
}
}
},
"externalDocs": {
"description": "Find out more about Swagger",
"url": "http://swagger.io"
}
}
27 changes: 27 additions & 0 deletions middleware/checkIfArticleExist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

import { Article } from '../models';

export default async (req, res, next) => {
const { id } = req.user;
const { articleId } = req.params;
try {
const articleExist = await Article.findOne({ where: { id: articleId } });
if (articleExist) {
if (articleExist.dataValues.userId === id) {
return res.status(403).json({
success: false,
errors: ['Permission denied, user cannot rate their own article']
});
} return next();
}
return res.status(404).json({
success: false,
errors: ['This article does not exist']
});
} catch (error) {
return res.status(500).json({
success: false,
errors: ['Error rating article']
});
}
};
16 changes: 16 additions & 0 deletions middleware/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,19 @@ export const validatePassword = [
.custom(value => !/\s/.test(value))
.withMessage('No spaces are allowed in the password.')
];

export const validateArticleRating = [
check('rating')
.exists()
.withMessage('Please provide a rating for this article.')
.custom(value => `${Number(value)}` !== 'NaN')
.withMessage('Rating should be a number.')
.custom(value => [1, 2, 3, 4, 5].indexOf(Number(value)) !== -1)
.withMessage('Ratings should have values ranging from 1 to 5.')
];

export const validateArticleId = [
check('articleId')
.isUUID()
.withMessage('Please provide a valid id for the article')
];
Loading

0 comments on commit 62c0753

Please sign in to comment.