Skip to content

Commit

Permalink
Merge branch 'development' of https://github.com/andela/coding-geeks-…
Browse files Browse the repository at this point in the history
…ah-backend into ft-user-update-and-delete-articles-167966138
  • Loading branch information
Musinda Kadhuwa committed Aug 23, 2019
2 parents 0d7d7d9 + 32f67b1 commit 035cf4a
Show file tree
Hide file tree
Showing 27 changed files with 1,423 additions and 9,356 deletions.
9,211 changes: 0 additions & 9,211 deletions package-lock.json

This file was deleted.

8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
"coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"build": "rm -rf build && babel -d ./build ./src -s",
"start": "npm run build && node ./build/index.js",
"pretest": "cross-env NODE_ENV=test npm run dropdb && NODE_ENV=test npm run createdb && npm run down && NODE_ENV=test npm run migrate && NODE_ENV=test npm run seed",
"test": "cross-env NODE_ENV=test nyc --reporter=text mocha --require @babel/register --require @babel/polyfill ./src/tests/*.test.js --timeout 30000 --exit",
"pretest": "cross-env NODE_ENV=test npm run down && NODE_ENV=test npm run migrate && NODE_ENV=test npm run seed",
"test": "cross-env NODE_ENV=test nyc --reporter=text mocha --require @babel/register --require @babel/polyfill ./src/tests/*.js --timeout 50000 --exit",
"down": "cross-env NODE_ENV=test node_modules/.bin/sequelize db:migrate:undo",
"migrate": "cross-env NODE_ENV=test node_modules/.bin/sequelize db:migrate",
"seed": "node_modules/.bin/sequelize db:seed:all",
"dropdb": "node_modules/.bin/sequelize db:drop",
"createdb": "node_modules/.bin/sequelize db:create authors-haven-test"
"seed": "node_modules/.bin/sequelize db:seed:all"
},
"engines": {
"node": "10.16.0"
Expand Down
9 changes: 6 additions & 3 deletions src/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ module.exports = {
database: process.env.DB_NAME,
host: process.env.DB_HOST,
dialect: 'postgres',
logging: false
logging: false,
seederStorage: 'sequelize'
},
test: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME_TEST,
host: process.env.DB_HOST,
dialect: 'postgres',
logging: false
logging: false,
seederStorage: 'sequelize'
},
production: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
use_env_variable: 'DATABASE_URL',
url: process.env.DATABASE_URL,
dialect: 'postgres',
logging: false
logging: false,
seederStorage: 'sequelize'
}
};
32 changes: 32 additions & 0 deletions src/controllers/articleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ArticleController {
if (article) {
return res.status(201).json({
article: {
id: article.id,
slug,
title: article.title,
description: article.title,
Expand Down Expand Up @@ -415,6 +416,37 @@ class ArticleController {
throw (err);
}
}

/**
* @description get an article
* @param {object} req
* @param {object} res
* @return {object} return object with a disliked article
*/
static async getSingleArticle(req, res) {
try {
const article = await Article.findOne({
where: { slug: req.params.slug },
include: [
{
model: User,
as: 'author',
attributes: ['userName', 'bio', 'image']
}
]
});
if (!article) {
return res.status(404).json({
message: 'Sorry! The requested article was not found.'
});
}
return res.status(200).json({
article
});
} catch (err) {
throw (err);
}
}
}


Expand Down
21 changes: 17 additions & 4 deletions src/controllers/profileController.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,24 @@ class Profile {
* @returns {object} all available profiles
*/
static async fetchProfiles(req, res) {
const currentUser = req.userData.role;
let users;
try {
const users = await User.findAll({
where: { role: 'user' },
attributes: ['userName', 'bio', 'image']
});
if (currentUser === 'user') {
users = await User.findAll({
where: { role: 'user' },
attributes: ['userName', 'bio', 'image']
});
} else if (currentUser === 'admin') {
users = await User.findAll({
where: { role: ['user', 'admin'] },
attributes: ['userName', 'bio', 'image', 'role']
});
} else {
users = await User.findAll({
attributes: ['userName', 'bio', 'image', 'role']
});
}
res.status(200).json({
message: `${users.length} Users found`,
data: users
Expand Down
44 changes: 44 additions & 0 deletions src/controllers/ratingController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import model from '../sequelize/models';

const { User, Rating } = model;

/**
* @description Rate the article
*/
class ArticleRate {
/**
* @description rate article
* @param {object} req
* @param {object} res
* @returns {object} returns object containing article rates
*/
static async rateArticle(req, res) {
const { rate } = req.body;
const rateAuthor = await User.findOne({ where: { id: req.userData.id } });
const ratings = await Rating.findOne(
{ where: { reviewerId: rateAuthor.id, articleId: req.params.id } }
);

if (ratings) {
const [, updatedRating] = await Rating.update(
{ rate }, { where: { id: ratings.id }, returning: true }
);
return res.status(200).send({
message: 'Successfully updated the rate of this article',
data: { rating: updatedRating }
});
}
const rating = await Rating.create({
rate,
articleId: req.params.id,
reviewerId: rateAuthor.id
});

return res.status(201).send({
message: 'Successfully rated this article',
data: { rating }
});
}
}

export default ArticleRate;
103 changes: 102 additions & 1 deletion src/controllers/userController.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ class Authentication {
*/
static async signup(req, res) {
try {
let message;
if (req.userData) {
if (req.userData.role === 'super-admin') {
message = 'User created. User should check their email for a verification link';
}
} else {
message = 'User created. Please, Check your email for a verification link';
}
const { email, userName, password } = req.body;
const user = await User.findOne({ where: { email } });
const name = await User.findOne({ where: { userName } });
Expand All @@ -37,14 +45,29 @@ class Authentication {

req.body.password = hashedPassword(password);

if (req.userData) {
req.body.role = (req.userData.role === 'super-admin') ? req.body.role : 'user';
}

const createdUser = await User.create(req.body);
const userToken = genToken(createdUser);
const action = 'verify-email';

await User.update(
{
role: req.body.role,
},
{
where: { userName: req.body.userName },
returning: true,
plain: true
}
);

await sendEmail(action, createdUser.email, userToken);

return res.status(201).json({
message: 'User created. Please, Check your email for a verification link.',
message,
data: {
id: createdUser.id,
firstName: createdUser.firstName,
Expand Down Expand Up @@ -139,6 +162,84 @@ class Authentication {
})(req, res);
}

/**
* @description admins delete a user
* @param {object} req
* @param {object} res
* @returns {obkject} success message
*/
static async deleteUser(req, res) {
try {
const name = req.params.username;
const userRole = req.userData.role;
const user = await User.findOne({ where: { userName: name } });
if (!user) {
return res.status(404).json({
error: `User with username ${name} not found`
});
}
if (userRole === 'user' || (user.role === 'super-admin'
|| (userRole === 'admin' && user.role === 'admin'))) {
return res.status(403).json({
error: 'You do not have permission to perform this action'
});
}

await User.destroy({
where: {
userName: name
}
});
return res.status(200).json({
message: `User ${name} successfully deleted`
});
} catch (err) {
throw (err);
}
}

/**
* @description admin change user role
* @param {object} req
* @param {object} res
*@returns {object} message
*/
static async updateRole(req, res) {
try {
const name = req.params.username;
const user = await User.findOne({ where: { userName: name } });
if (!user) {
return res.status(404).json({
error: `User with username ${name} not found`
});
}
if (user.role === 'super-admin') {
return res.status(403).json({
error: 'You do not have permission to perform this action'
});
}
const update = await User.update(
{
role: req.body.role
},
{ where: { userName: name }, returning: true, plain: true }
);
const updatedUser = {
id: update[1].id,
Username: update[1].userName,
email: update[1].email,
bio: update[1].bio,
role: update[1].role
};
return res.status(200).json({
message: 'User role successfully updated',
data: updatedUser
});
} catch (err) {
throw (err);
}
}

/**
* @param {object} req
* @param {object} res
Expand Down
49 changes: 49 additions & 0 deletions src/helpers/articles/rateArticleHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import db from '../../sequelize/models';

const { Article } = db;

/**
* @exports ArticRatelehelper
* @class Articlehelper
* @description Helps to generate token and passwor hashing
* */
class ArticleRatelehelper {
/**
* Check the environment
* @function validateArticleRated
* @param {integer} id - Check the rate
* @param {integer} user - Check the user
* @return {string} Validate the rate
*/
static async validateArticleRated(id, user) {
const findArticle = await Article.findOne({ where: { id } });
if (findArticle) {
const articleOwner = await Article.findAll({
where: { id, authorId: user }
});
if (articleOwner.length) {
return 'Sorry! You cannot rate your article';
}
}
if (findArticle == null) {
return 'This Article does not exist';
}
return true;
}

/**
* Check the environment
* @function validateRating
* @param {integer} rate - Check the rate
* @return {string} Validate the rate
*/
static validateRating(rate) {
const regRaterange = /^[1-5]{1}?$/;
if (!regRaterange.test(rate)) {
return 'Rate must be between 1 and 5';
}
return true;
}
}

export default ArticleRatelehelper;
13 changes: 5 additions & 8 deletions src/helpers/mailer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,25 @@ const sendEmail = async (action, to, token) => {
subject: verifySubject,
html: template(verifyContent, verifyLink)
};
if (NODE_ENV === 'test') return true;
mailer.send(message);
} else if (action === 'reset-password') {
return NODE_ENV === 'test' ? true : mailer.send(message);
} if (action === 'reset-password') {
const message = {
to,
from: EMAIL_SENDER,
subject: resetSubject,
text: 'Authors Haven',
html: template(resetContent, resetLink)
};
if (NODE_ENV === 'test') return true;
mailer.send(message);
} else if (action === 'notification') {
return NODE_ENV === 'test' ? true : mailer.send(message);
} if (action === 'notification') {
const message = {
to,
from: EMAIL_SENDER,
subject: notifySubject,
text: 'Authors Haven',
html: template(notifyContent, notifyLink)
};
if (NODE_ENV === 'test') return true;
mailer.send(message);
return NODE_ENV === 'test' ? true : mailer.send(message);
}
};

Expand Down
Loading

0 comments on commit 035cf4a

Please sign in to comment.