Skip to content

Commit

Permalink
Merge 99ccf70 into c415cf3
Browse files Browse the repository at this point in the history
  • Loading branch information
UhiriweAudace committed Jun 14, 2019
2 parents c415cf3 + 99ccf70 commit 749cee9
Show file tree
Hide file tree
Showing 18 changed files with 956 additions and 294 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
"regenerator-runtime": "^0.13.2",
"request": "^2.87.0",
"sequelize": "^5.8.7",
"slug": "^1.1.0",
"swagger-ui-express": "^4.0.6",
"underscore": "^1.9.1"
"underscore": "^1.9.1",
"uniqid": "^5.0.3"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
Expand Down
103 changes: 103 additions & 0 deletions src/api/controllers/articlesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* eslint-disable prefer-const */
/* eslint-disable require-jsdoc */
/* eslint-disable no-console */
import articles from '../../helpers/articlesHelper';
import models from '../../sequelize/models';

const { article } = models;

/**
* @Author - Audace Uhiriwe
*/
class articlesController {
/**
* creating a new article
* @param {object} req - Request.
* @param {object} res - Response.
* @returns {object} - Contains an article information.
*/
static async createArticle(req, res) {
const dataValues = await articles.createNewArticle(req);
const {
slug,
title,
description,
body,
tagList,
author,
updatedAt,
createdAt
} = dataValues;

const result = {
slug,
title,
description,
body,
tagList,
updatedAt,
createdAt,
author
};
res.status(201).send({
article: result
});
}

static async getAllArticle(req, res) {
const allArticle = await articles.getAllArticle();
res.status(200).send({
articles: allArticle
});
}

static async getOneArticle(req, res) {
const { slug } = req.params;
const oneArticle = await articles.getOneSlug(slug);
res.status(200).send({
article: oneArticle
});
}

static async updateArticle(req, res) {
const { slug } = req.params;
const { title, body, description } = req.body;

const updateSlug = {
title: title || req.foundArticle.title,
body: body || req.foundArticle.body,
description: description || req.foundArticle.description
};

// @updating the slug
const updatedSlug = await articles.createSlug(updateSlug.title);

// @Updating the article's data in Database
await article.update(
{
slug: updatedSlug,
title: updateSlug.title,
body: updateSlug.body,
description: updateSlug.description
},
{ where: { slug } }
);

// @returning the response
res.status(200).send({
message: 'Article updated successfully',
article: updateSlug
});
}

static async deleteArticle(req, res) {
const { slug } = req.params;

// @delete an article in Database
await article.destroy({ where: { slug } });

// @returning the response
res.status(200).send({ message: 'Article deleted successfully!' });
}
}
export default articlesController;
22 changes: 22 additions & 0 deletions src/api/routes/articlesRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Router } from 'express';
import articlesController from '../controllers/articlesController';
import isAuth from '../../middleware/auth';
import check from '../../middleware/checkOwner';
import validate from '../../middleware/validations';

const articlesRouter = Router();
const {
createArticle, getAllArticle, getOneArticle, updateArticle, deleteArticle
} = articlesController;

articlesRouter.post('/', isAuth.verifyToken, validate.createValidations, createArticle);

articlesRouter.get('/', getAllArticle);

articlesRouter.get('/:slug', getOneArticle);

articlesRouter.put('/:slug', isAuth.verifyToken, check.articleOwner, validate.updateValidations, updateArticle);

articlesRouter.delete('/:slug', isAuth.verifyToken, check.articleOwner, deleteArticle);

export default articlesRouter;
2 changes: 2 additions & 0 deletions src/api/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import express from 'express';
import authRouter from './authRouter';
import userRouter from './userRouter';
import profilesRouter from './profilesRouter';
import articlesRouter from './articlesRouter';

const api = express();

// Routers go here
api.use('/auth', authRouter);
api.use('/user', userRouter);
api.use('/profiles', profilesRouter);
api.use('/articles', articlesRouter);

export default api;
78 changes: 78 additions & 0 deletions src/helpers/articlesHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* eslint-disable require-jsdoc */
/* eslint-disable valid-jsdoc */
import slug from 'slug';
import uniqid from 'uniqid';
import models from '../sequelize/models';

const { article, User } = models;

/**
* @description Helpers for articles
*/
class ArticlesHelper {
/**
* @param {title}
* @returns {newSlug} return a new slug
*/
static createSlug(title) {
const newSlug = `${slug(title, { lower: true })}-${uniqid.process()}`;
return newSlug;
}

/**
* @param {object} req - Request.
* @param {object} res - Response.
* @returns {object} - Contains an article information.
*/
static async createNewArticle(req) {
const {
title, body, description, tagList
} = req.body;
const { id } = req.user;
const newSlug = this.createSlug(title);
const { dataValues } = await article.create({
slug: newSlug,
title,
description,
body,
tagList: tagList.split(','),
authorid: parseInt(id, 10)
});
const userInfo = await this.getUserInfo(id);
const { username, bio, image } = userInfo;
const author = { username, bio, image };
dataValues.author = author;
return dataValues;
}

static async getUserInfo(id) {
const { dataValues } = await User.findOne({ where: { id } });
return dataValues;
}

static async getAllArticle() {
const result = await article.findAll({
include: [{
as: 'author',
model: User,
attributes: ['username', 'bio', 'image']
}],
attributes: ['slug', 'title', 'description', 'body', 'tagList', 'updatedAt', 'createdAt']
});
return result;
}

static async getOneSlug(newSlug) {
const result = await article.findOne({
where: { slug: newSlug },
include: [{
as: 'author',
model: User,
attributes: ['username', 'bio', 'image']
}],
attributes: ['slug', 'title', 'description', 'body', 'tagList', 'updatedAt', 'createdAt']
});
return result;
}
}
export default ArticlesHelper;
27 changes: 27 additions & 0 deletions src/helpers/validationSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable require-jsdoc */
import Joi from 'joi';
/**
* @description - validating the request
*/
class validations {
static createValidations(data) {
const schema = {
title: Joi.string().required(),
body: Joi.string().required(),
description: Joi.string().required(),
tagList: Joi.string().required()
};
return Joi.validate(data, schema);
}

static updateValidations(updatedData) {
const schema = {
title: Joi.string(),
body: Joi.string(),
description: Joi.string(),
tagList: Joi.string()
};
return Joi.validate(updatedData, schema);
}
}
export default validations;
31 changes: 31 additions & 0 deletions src/middleware/checkOwner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* eslint-disable valid-jsdoc */
import models from '../sequelize/models';

const { article } = models;

/**
* @description Helpers for articles
*/
class checkOwner {
/**
* @author Audace Uhiriwe
* @param {req} - request
* @param {res} - response
*/
static async articleOwner(req, res, next) {
const { id } = req.user;
const { slug } = req.params;

// @check if the article's slug exist
const result = await article.findOne({ where: { slug } });
if (result === null) return res.status(404).send({ error: 'Slug Not found!' });

// @check if the user who logged in - is the owner of that slug
const response = await article.findOne({ where: { slug, authorid: id } });
if (!response) return res.status(403).send({ error: 'Sorry!, you are not the owner of this slug' });

req.foundArticle = response.dataValues;
return next();
}
}
export default checkOwner;
24 changes: 24 additions & 0 deletions src/middleware/validations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-disable no-useless-escape */
/* eslint-disable require-jsdoc */
import validate from '../helpers/validationSchema';
/**
* @description - Middleware for validating the request body
*/
class validations {
static createValidations(req, res, next) {
const { error } = validate.createValidations(req.body);

// @check if there is an error in the request sent
if (error) return res.status(400).send({ error: error.details[0].message.replace(/[$\/\\#,+()$~%.'":*<>{}]/g, '') });
return next();
}

static updateValidations(req, res, next) {
const { error } = validate.updateValidations(req.body);

// @check if there is an error in the request sent
if (error) return res.status(400).send({ error: error.details[0].message.replace(/[$\/\\#,+()$~%.'":*<>{}]/g, '') });
return next();
}
}
export default validations;
55 changes: 0 additions & 55 deletions src/sequelize/migrations/20190612225745-create-user.js

This file was deleted.

Loading

0 comments on commit 749cee9

Please sign in to comment.