Skip to content

Commit

Permalink
Merge pull request #52 from andela/ft-update-article-endpoint-166720110
Browse files Browse the repository at this point in the history
[166720110] User should be able to update an article
  • Loading branch information
MemunaHaruna committed Jun 28, 2019
2 parents b1f6b18 + 4fd5255 commit c532aa0
Show file tree
Hide file tree
Showing 23 changed files with 581 additions and 43 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ src/api/migrations

# Seeders
src/api/seeders

# models
src/api/models
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ package-lock.json
.vscode

# Eslint cache
.eslintcache
.eslintcache
17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,32 @@
"babel-plugin-istanbul": "^5.1.2",
"bcrypt": "^3.0.6",
"body-parser": "^1.18.3",
"cloudinary": "^1.14.0",
"cors": "^2.8.5",
"dotenv": "^8.0.0",
"expect": "^24.8.0",
"express": "^4.16.4",
"express-session": "^1.16.2",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.11",
"morgan": "^1.9.1",
"multer": "^1.4.1",
"passport": "^0.4.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth": "^2.0.0",
"passport-pinterest": "^1.0.0",
"passport-twitter": "^1.0.4",
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.11",
"morgan": "^1.9.1",
"pg": "^7.11.0",
"pino": "^5.12.6",
"redis": "^2.8.0",
"sequelize": "^5.8.7",
"sequelize-cli": "^5.5.0",
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"slug": "^1.1.0",
"swagger-node-express": "^2.1.3",
"swagger-ui-express": "^4.0.6"
"swagger-ui-express": "^4.0.6",
"uniqid": "^5.0.3"
},
"nyc": {
"per-file": true,
Expand All @@ -79,6 +83,7 @@
"statements": 80,
"exclude": [
"src/configs/environments.js",
"src/api/models",
"src/configs/redisConfigs.js",
"src/configs/passport.js",
"src/index.js"
Expand Down
68 changes: 68 additions & 0 deletions src/api/controllers/articleController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import cloudinary from 'cloudinary';
import model from '../models';
import slugHelper from '../../helpers/slug.maker';
import statuses from '../../helpers/constants/status.codes';

const { Article } = model;

/**
* this is the article controller
*
* @export
* @class ArticleController
*/
class ArticleController {
/**
* allow an author to update his/her article
*
* @author Frank Mutabazi
* @static
* @param {object} req the request
* @param {object} res the response to be sent
* @memberof ArticleController
* @returns {Object} res
*/
static async updateArticle(req, res) {
const { slug } = req.params;
const {
title, description, body, tagList, category
} = req.body;

let { coverImage } = req.Existing;

const slugCreation = slugHelper(title || req.Existing.title);

if (req.file) {
const image = await cloudinary.v2.uploader.upload(req.file.path);
coverImage = image.secure_url;
}
const updateContent = {
slug: slugCreation,
title: title || req.Existing.title,
description: description || req.Existing.description,
body: body || req.Existing.body,
tagList: tagList || req.Existing.tagList,
category: category || req.Existing.category,
};
await Article.update({
slug: updateContent.slug,
title: updateContent.title,
description: updateContent.description,
body: updateContent.body,
coverImage,
tagList: updateContent.tagList,
category: updateContent.category,
},
{
where: {
slug
}
});
return res.status(statuses.OK).json({
status: statuses.OK,
message: 'Your Article is up-to-date now, Thanks'
});
}
}

export default ArticleController;
59 changes: 59 additions & 0 deletions src/api/migrations/20190624184203-create-article.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export default {
up: (queryInterface, Sequelize) => queryInterface.createTable('Articles', {
id: {
allowNull: false,
unique: true,
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
},
title: {
type: Sequelize.STRING,
allowNull: false
},
slug: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
category: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.STRING,
allowNull: false
},
body: {
type: Sequelize.TEXT,
allowNull: false
},
coverImage: {
type: Sequelize.TEXT,
allowNull: true
},
tagList: {
type: Sequelize.ARRAY(Sequelize.TEXT),
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
allowNull: false
}
}),
down: (queryInterface, Sequelize) => queryInterface.dropTable('Articles')
};
2 changes: 1 addition & 1 deletion src/api/migrations/20190625141012-create-token-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ export default {
}
}),
down: (queryInterface, Sequelize) => queryInterface.dropTable('Tokens')
};
};
59 changes: 59 additions & 0 deletions src/api/models/article.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const Article = (sequelize, DataTypes) => {
const article = sequelize.define('Articles',
{
id: {
allowNull: false,
unique: true,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4
},
title: {
type: DataTypes.STRING,
allowNull: false
},
description: {
type: DataTypes.STRING,
allowNull: false
},
slug: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
category: {
type: DataTypes.STRING,
allowNull: false,
},
body: {
type: DataTypes.TEXT,
allowNull: false
},
coverImage: {
type: DataTypes.TEXT,
allowNull: true
},
tagList: {
type: DataTypes.ARRAY(DataTypes.TEXT),
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
allowNull: false
}
},
{});

Article.associate = (models) => {
Article.belongsTo(models.User, { foreignKey: 'userId' });
};
return article;
};

export default Article;
1 change: 1 addition & 0 deletions src/api/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const sequelize = new Sequelize(env.dbUrl, {

const models = {
User: sequelize.import('./user'),
Article: sequelize.import('./article'),
};

export { sequelize };
Expand Down
19 changes: 19 additions & 0 deletions src/api/routes/articleRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Router } from 'express';
import articleController from '../controllers/articleController';
import checkArticleOwner from '../../middlewares/checkArticleOwnership';
import checkValidToken from '../../middlewares/checkValidToken';
import bodyVerifier from '../../middlewares/validations/body.verifier';
import checkArticle from '../../middlewares/getOneArticle';
import uploadImage from '../../middlewares/upload';

const articleRouter = new Router();

articleRouter.patch('/:slug',
uploadImage.single('coverImage'),
checkValidToken,
bodyVerifier,
checkArticle.getArticle,
checkArticleOwner.checkOwner,
articleController.updateArticle);

export default articleRouter;
2 changes: 2 additions & 0 deletions src/api/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import express from 'express';
import authRouter from './authRouter';
import resetRouter from './password.reset';
import articleRouter from './articleRouter';

const router = express();

router.use('/users', authRouter);
router.use('/password-reset', resetRouter);
router.use('/articles', articleRouter);

export default router;
14 changes: 2 additions & 12 deletions src/api/seeders/20190621184055-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ const salt = genSaltSync(parseFloat(env.hashRounds));
const hashedPassword = hashSync('password23423', salt);

module.exports = {
up: (queryInterface, Sequelize) =>
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
*/
queryInterface.bulkInsert('Users',
up: (queryInterface, Sequelize) => queryInterface.bulkInsert('Users',
[
{
username: 'Burindy',
Expand All @@ -22,10 +17,5 @@ module.exports = {
],
{}),

down: (queryInterface, Sequelize) =>
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
*/
queryInterface.bulkDelete('Users', null, {})
down: (queryInterface, Sequelize) => queryInterface.bulkDelete('Users', null, {})
};
34 changes: 34 additions & 0 deletions src/api/seeders/20190624184857-Article.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default {
up: (queryInterface, Sequelize) => queryInterface.bulkInsert('Articles',
[
{
id: 'e6db9e0b-ebdf-468a-9e66-db314b7586c0',
title: 'How to create sequalize seeds',
slug: 'How-to-create-sequalize-seeds',
description: 'How to set dummy data automatically',
body: 'Suppose we want to insert some data.',
coverImage: 'default.jpeg',
category: 'Tech',
userId: 1,
tagList: ['postgres', 'express', 'sequelize'],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: '263920cd-c7ad-45b0-a5d9-d12f7fe237c3',
title: 'What is a Version 1 UUID',
slug: 'What-is-a-Version-1-UUID',
description: 'Velit non sit culpa pariatur proident',
body:'A Version 1 UUID is a universall',
coverImage: 'default.jpeg',
category: 'Tech',
userId: 1,
tagList: ['UUID', 'express', 'sequelize'],
createdAt: new Date(),
updatedAt: new Date()
},
],
{}),

down: (queryInterface, Sequelize) => queryInterface.bulkDelete('Articles', null, {})
};
Loading

0 comments on commit c532aa0

Please sign in to comment.