Skip to content

Commit

Permalink
Merge 6244f0a into afc4e2c
Browse files Browse the repository at this point in the history
  • Loading branch information
Oluwafayokemi committed Aug 8, 2018
2 parents afc4e2c + 6244f0a commit 4f8d002
Show file tree
Hide file tree
Showing 29 changed files with 1,183 additions and 28 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"retainLines": true,
"env": {
"test": {
"plugins": [
"istanbul"
]
}
}
}
Binary file added .vs/slnx.sqlite
Binary file not shown.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
"dev": "nodemon server/index.js --exec babel-node --presets babel-preset-env",
"migrate": "sequelize db:migrate",
"migrate:undo": "sequelize db:migrate:undo:all",
"migrate:test": "sequelize db:migrate:undo:all --env=test && sequelize db:migrate --env=test",
"migrate:dev": "sequelize db:migrate:undo && sequelize db:migrate"
"migrate:test": "sequelize db:migrate:undo:all --env=test && sequelize db:migrate --env=test && sequelize db:seed:all --env=test",
"migrate:dev": "sequelize db:migrate:undo && sequelize db:migrate",
"seed:all": "sequelize db:seed:all",
"unseed:all": "sequelize db:seed:undo:all"
},
"author": "Andela Simulations Programme",
"license": "MIT",
Expand Down
186 changes: 175 additions & 11 deletions server/controllers/ArticleController.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,180 @@
import models from '../models';
import randomString from '../helpers/randomString';
import dashReplace from '../helpers/replaceDash';

const {
Article
} = models;
/**
* This class contains all the methods responsible for creating and querying
* articles on the app
* It is made up static methods which can be called from anywhere in the app.
*/
* This class contains all the methods responsible for creating and querying
* articles on the app
* It is made up static methods which can be called from anywhere in the app.
*/
export default class ArticleController {
/**
* Create an article and returns it.
* @param {object} req the request object
* @param {object} res the response object
* @returns {object} the article that was created.
*/
static createArticle() {
// to be implemented
* Create an article and return the Data.
* @param {object} req the request object
* @param {object} res the response object
* @returns {object} the article that was created.
*/
static createArticle(req, res) {
const {
id,
username
} = req.user;
const slug = `${dashReplace(req.body.title)}-${randomString(10)}`;
const {
title,
body,
imageUrl,
categoryId
} = req.body;

Article.create({
slug: slug.toLowerCase(),
title,
body,
imageUrl,
categoryId,
userId: id,
createdAt: Date.now(),
})
.then(newArticle => res.status(200).json({
status: 200,
success: true,
message: 'Article has been created',
article: {
slug: newArticle.slug,
authorId: newArticle.userId,
categoryId: newArticle.categoryId,
title: newArticle.title,
body: newArticle.body,
imageUrl: newArticle.imageUrl,
Author: username,
createdAt: new Date(newArticle.createdAt).toLocaleString('en-GB', { hour12: true }),
},
}))
.catch(() => {
res.status(400).json({
status: 400,
success: false,
error: 'Request was not successfully created',
});
});
}
/**
* Update an article and return the Data.
* @param {object} req the request object
* @param {object} res the response object
* @returns {object} the article that was updated.
*/

static updateArticle(req, res) {
const {
slug
} = req.params;
const {
title,
body,
imageUrl,
categoryId,
createdAt
} = req.body;
Article.findOne({
where: {
slug
},
attributes: ['title', 'slug', 'body', 'imageUrl', 'categoryId', 'createdAt'],
})
.then((foundArticle) => {
if (!foundArticle) {
return res.status(404).json({
status: 404,
success: false,
message: 'The specified artcile does not exist',
});
}

Article.update({
slug: foundArticle.slug,
title: title || foundArticle.title,
body: body || foundArticle.body,
imageUrl: imageUrl || foundArticle.imageUrl,
categoryId: categoryId || foundArticle.categoryId,
createdAt,
updatedAt: Date.now()
}, {
returning: true,
where: {
slug
}
})
.then(([, [updatedArticle]]) => res.status(200).json({
status: 200,
success: true,
message: `Article with ${slug} has been updated`,
newArticle: {
slug: updatedArticle.slug,
title: updatedArticle.title,
body: updatedArticle.body,
imageUrl: updatedArticle.imageUrl,
categoryId: updatedArticle.categoryId,
createdAt: new Date(updatedArticle.createdAt).toLocaleString('en-GB', { hour12: true }),
updatedAt: new Date(updatedArticle.updatedAt).toLocaleString('en-GB', { hour12: true })
}
}))
.catch(() => {
res.status(400).json({
status: 400,
success: false,
error: 'Request not successfully updated',
});
});
});
}

/**
* Delet an article and return the Data.
* @param {object} req the request object
* @param {object} res the response object
* @returns {object} the article that was deleted.
*/

static removeArticle(req, res) {
const {
slug
} = req.params;
Article.findOne({
where: {
slug
},
attributes: ['title', 'slug', 'body', 'imageUrl', 'categoryId', 'createdAt'],
})
.then((foundArticle) => {
if (!foundArticle) {
return res.status(404).json({
status: 404,
success: false,
message: 'The specified artcile does not exist',
});
}
Article.destroy({
where: {
slug
},
})
.then(() => {
res.status(200).json({
status: 200,
success: true,
message: `Article with ${slug} has been successfully deleted`,
});
});
})
.catch(() => res.status(400).json({
status: 400,
success: false,
Error: 'Article can not be deleted'
}));
}
}
11 changes: 11 additions & 0 deletions server/helpers/randomString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable no-plusplus */
const randomString = (length) => {
let result = '';
const alphaNumeric = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i = length; i > 0; i--) {
result += alphaNumeric[Math.floor(Math.random() * alphaNumeric.length)];
}
return result;
};

export default randomString;
3 changes: 3 additions & 0 deletions server/helpers/replaceDash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const dashReplace = string => string.replace(/[\W_]+/g, '-');

export default dashReplace;
90 changes: 90 additions & 0 deletions server/middlewares/validations/ArticleValidation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* eslint-disable class-methods-use-this */
import Validator from 'validatorjs';

/**
* @export
* @class Validation
*/
export default class ArticleValidation {
/**
* Validate create Article Data
*
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
* @param {function} next
* @returns {object} Class instance
* @memberof Validation
*/
static validateCreateArticle(req, res, next) {
const articleRules = {
categoryId: 'required|integer|max:5',
title: 'required|string',
body: 'required|string',
};

const validate = new Validator(req.body, articleRules);
if (validate.passes()) return next();

const error = {};
const categoryId = validate.errors.first('categoryId');
const title = validate.errors.first('title');
const body = validate.errors.first('body');

if (categoryId) {
error.categoryId = categoryId;
}
if (title) {
error.title = title;
}
if (body) {
error.body = body;
}

return res.status(400).json({
message: 'A Required Field is Missing',
status: 400,
error,
});
}

/**
* Validate create Article Data
*
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
* @param {function} next
* @returns {object} Class instance
* @memberof Validation
*/
static validateUpdateArticle(req, res, next) {
const articleRules = {
categoryId: 'integer|max:5',
title: 'string',
body: 'string',
};

const validate = new Validator(req.body, articleRules);
if (validate.passes()) return next();

const error = {};
const categoryId = validate.errors.first('categoryId');
const title = validate.errors.first('title');
const body = validate.errors.first('body');

if (categoryId) {
error.categoryId = categoryId;
}
if (title) {
error.title = title;
}
if (body) {
error.body = body;
}

return res.status(400).json({
message: 'A Required Field is Missing',
status: 400,
error,
});
}
}
2 changes: 1 addition & 1 deletion server/migrations/20180716204138-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ module.exports = {
type: Sequelize.DATE,
},
}),
down: (queryInterface/* , Sequelize */) => { queryInterface.dropTable('Users'); },
down: (queryInterface) => { queryInterface.dropTable('Users'); },
};
24 changes: 24 additions & 0 deletions server/migrations/20180804173244-create-category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Categories', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: true,
type: Sequelize.DATE
},
updatedAt: {
allowNull: true,
type: Sequelize.DATE
}
}),
down: queryInterface => queryInterface.dropTable('Categories')
};
Loading

0 comments on commit 4f8d002

Please sign in to comment.