Skip to content

Commit

Permalink
feat(image-upload): enable image upload on articles
Browse files Browse the repository at this point in the history
- add image upload support when creating and updating articles
- add multiple image upload support

Finishes [#167194031]
  • Loading branch information
Deschant Kounou committed Jul 15, 2019
1 parent ffa16a7 commit 4dbb521
Show file tree
Hide file tree
Showing 15 changed files with 137 additions and 41 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"license": "MIT",
"dependencies": {
"@hapi/joi": "^15.0.3",
"async": "^2.6.3",
"bcrypt": "^3.0.6",
"body-parser": "^1.19.0",
"cloudinary": "^1.14.0",
Expand Down
14 changes: 12 additions & 2 deletions src/api/controllers/articlesController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import readTime from '../../helpers/ReadTime.helper';
import eventEmitter from '../../helpers/notifications/EventEmitter';
import findUser from '../../helpers/FindUser';
import AuthorNotifier from '../../helpers/NotifyAuthorOnArticleBlock';
import workers from '../../workers';

const {
notifyAuthorblock, notifyAuthorUnblock
} = AuthorNotifier;
const { uploadImageWorker } = workers;

const {
User,
Expand Down Expand Up @@ -51,6 +53,7 @@ class articlesController {
}

const dataValues = await articles.createNewArticle(req);

const {
slug,
title,
Expand Down Expand Up @@ -78,6 +81,7 @@ class articlesController {
readtime,
views
};

res.status(201).send({
article: result
});
Expand Down Expand Up @@ -154,7 +158,7 @@ class articlesController {
const newReadTime = readTime(updateSlug.body);

// @Updating the article's data in Database
await Article.update(
const updatedArticle = await Article.update(
{
slug: newSlug,
title: updateSlug.title,
Expand All @@ -163,9 +167,15 @@ class articlesController {
tagList: updateSlug.tagList,
readtime: newReadTime
},
{ where: { slug } }
{ where: { slug }, returning: true }
);


// Uplooad article image
if (req.files) {
uploadImageWorker(req.files, updatedArticle[1][0].dataValues.id, 'article', null);
}

// @returning the response
res.status(200).send({
message: 'Article updated successfully',
Expand Down
10 changes: 5 additions & 5 deletions src/api/controllers/profiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ export default class ProfilesController {
*/
static async updateProfile(req, res) {
let { body } = req;
const { user, file, params } = req;
const { user, files, params } = req;

if (!file && Object.keys(body) < 1) return res.status(400).send({ message: 'Cannot update empty object' });
if (!files && Object.keys(body) < 1) return res.status(400).send({ message: 'Cannot update empty object' });

if (file && Object.keys(body) < 1) {
uploadImageWorker(file, params.id, null);
if (files && Object.keys(body) < 1) {
uploadImageWorker(files, params.id, 'user', null);
return res.status(200).json({ status: 200, message: 'Your image will be updated shortly' });
}

Expand All @@ -63,7 +63,7 @@ export default class ProfilesController {
.json({ message: `Could not find user with id: ${user.id}` });
}

uploadImageWorker(file, params.id, null);
uploadImageWorker(files, params.id, 'user', null);

return res.status(200).json({ user: { updatedUser } });
} catch (error) {
Expand Down
5 changes: 3 additions & 2 deletions src/api/routes/articlesRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import stats from '../controllers/stats';
import highlight from '../controllers/highlightController';
import highlightExist from '../../middleware/highlightExist';
import textExist from '../../middleware/textExist';
import upload from '../../handlers/multer';

const articlesRouter = Router();
const {
Expand Down Expand Up @@ -54,12 +55,12 @@ const { checkComment, checkParameter, articleExists } = comment;
const { liked, disliked } = checkLikesandDislikes;

articlesRouter
.post('/', verifyToken, validateBody('createArticle'), createArticle)
.post('/', verifyToken, upload.fields([{ name: 'gallery', maxCount: 10 }]), validateBody('createArticle'), createArticle)
.get('/', paginate, searchForArticle, getAllArticle);

articlesRouter
.get('/:slug', slugExist, isThisArticleBlocked, getOneArticle)
.put('/:slug', verifyToken, check.articleOwner, validateBody('updateArticle'), updateArticle)
.put('/:slug', verifyToken, check.articleOwner, upload.fields([{ name: 'gallery', maxCount: 10 }]), validateBody('updateArticle'), updateArticle)
.delete('/:slug', verifyToken, check.articleOwner, deleteArticle);

articlesRouter
Expand Down
4 changes: 1 addition & 3 deletions src/api/routes/userRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ userRouter.delete('/optinemail', verifyToken, optOutEmail);
userRouter.delete('/optinapp', verifyToken, optOutApp);
userRouter
.route('/:id')
.put(verifyToken, checkOwnership, validateBody('updateUser'), upload.single('image'), updateProfile)
.put(verifyToken, checkOwnership, upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'cover', maxCount: 1 }]), validateBody('updateUser'), updateProfile)
.delete(verifyToken, checkIsAdmin, deleteProfile);

userRouter.put('/', verifyToken, validateBody('updateUser'), upload.single('image'), updateProfile);

export default userRouter;
14 changes: 11 additions & 3 deletions src/helpers/articlesHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import slug from 'slug';
import uniqid from 'uniqid';
import models from '../sequelize/models';
import readTime from './ReadTime.helper';
import workers from '../workers';

const { Article, User } = models;
const { uploadImageWorker } = workers;

/**
* @description Helpers for articles
Expand Down Expand Up @@ -38,10 +40,16 @@ class ArticlesHelper {
description,
body,
tagList: tagList.split(','),
authorId: parseInt(id, 10),
authorId: id,
readtime,
views: 0,
});

// Uplooad article image
if (req.file) {
uploadImageWorker(req.file, dataValues.id, 'article', null);
}

const userInfo = await this.getUserInfo(id);
const { username, bio, image } = userInfo;
const author = { username, bio, image };
Expand All @@ -61,7 +69,7 @@ class ArticlesHelper {
include: [{
as: 'author',
model: User,
attributes: ['username', 'bio', 'image']
attributes: ['username', 'bio', 'avatar']
}],
attributes: ['id', 'slug', 'title', 'description', 'readtime', 'body', 'tagList', 'updatedAt', 'createdAt'],
limit: 10
Expand All @@ -76,7 +84,7 @@ class ArticlesHelper {
include: [{
as: 'author',
model: User,
attributes: ['username', 'bio', 'image']
attributes: ['username', 'bio', 'avatar']
}],
attributes: ['slug', 'title', 'description', 'readtime', 'body', 'tagList', 'views', 'updatedAt', 'createdAt']
});
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/validationSchemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default {
.label('title is required and should be a string'),
body: Joi.string()
.required()
.label('body is required and shoud be a string'),
.label('body is required and should be a string'),
description: Joi.string()
.required()
.label('description is required and should be a string'),
Expand Down
24 changes: 24 additions & 0 deletions src/sequelize/migrations/20190715070539-upload-multiple-images.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export default {
up: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('Users', 'image');
await queryInterface.removeColumn('Articles', 'image');
await queryInterface.addColumn('Users', 'avatar', {
type: Sequelize.STRING,
});
await queryInterface.addColumn('Users', 'cover', {
type: Sequelize.STRING,
});
return queryInterface.addColumn('Articles', 'gallery', {
type: Sequelize.ARRAY(Sequelize.STRING),
defaultValue: []
});
},

down: async (queryInterface) => {
await queryInterface.removeColumn('Users', 'avatar');
await queryInterface.removeColumn('Users', 'cover');
await queryInterface.removeColumn('Articles', 'gallery');
await queryInterface.addColumn('Users', 'image');
await queryInterface.addColumn('Articles', 'image');
}
};
4 changes: 2 additions & 2 deletions src/sequelize/models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true
},
image: {
type: DataTypes.TEXT,
gallery: {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true
},
authorId: {
Expand Down
3 changes: 2 additions & 1 deletion src/sequelize/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ module.exports = (sequelize, DataTypes) => {
email: DataTypes.STRING,
password: DataTypes.STRING,
bio: DataTypes.TEXT,
image: DataTypes.STRING,
avatar: DataTypes.STRING,
cover: DataTypes.STRING,
dateOfBirth: DataTypes.DATE,
gender: DataTypes.STRING,
provider: DataTypes.STRING,
Expand Down
33 changes: 22 additions & 11 deletions src/sequelize/seeders/20190618062925-test-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ module.exports = {
email: 'nimilleer@gmail.com',
password: 'Mireille1!',
bio: '',
image: '',
avatar: '',
cover: '',
dateOfBirth: '12/12/2000',
gender: '',
provider: '',
Expand All @@ -24,7 +25,8 @@ module.exports = {
email: 'hhhhhhhhhhhhhh3h3hh3@gmail.com',
password: 'eric123',
bio: 'nan',
image: 'nan',
avatar: '',
cover: '',
dateOfBirth: new Date(),
gender: 'male',
provider: 'nan',
Expand All @@ -41,7 +43,8 @@ module.exports = {
email: 'nimilleer@gmail.com',
password: 'Mireille1!',
bio: '',
image: '',
avatar: '',
cover: '',
dateOfBirth: '12/12/2000',
gender: '',
provider: '',
Expand All @@ -58,7 +61,8 @@ module.exports = {
email: 'u.audace@gmail.com',
password: 'Uhiriwe1!',
bio: '',
image: '',
avatar: '',
cover: '',
dateOfBirth: '12/12/1999',
gender: '',
provider: '',
Expand All @@ -75,7 +79,8 @@ module.exports = {
email: 'gprestein055@gmail.com',
password: '$2b$08$aKninSz.39G5SxBE5QOro.xBJXIEtnXkrOBriWfVCpqND8AzeJMaC',
bio: 'nan',
image: 'nan',
avatar: '',
cover: '',
dateOfBirth: new Date(),
gender: 'male',
provider: 'nan',
Expand All @@ -92,7 +97,8 @@ module.exports = {
email: 'gprestein555@gmail.com',
password: '$2b$08$aKninSz.39G5SxBE5QOro.xBJXIEtnXkrOBriWfVCpqND8AzeJMaC',
bio: 'nan',
image: 'nan',
avatar: '',
cover: '',
dateOfBirth: new Date(),
gender: 'male',
provider: 'nan',
Expand All @@ -109,7 +115,8 @@ module.exports = {
email: 'mahorodiane@gmail.com',
password: 'cooler12345',
bio: 'nan',
image: 'nan',
avatar: '',
cover: '',
dateOfBirth: new Date(),
gender: 'male',
provider: 'nan',
Expand All @@ -126,7 +133,8 @@ module.exports = {
email: 'diegohirwa@gmail.com',
password: 'coolest12345',
bio: 'nan',
image: 'nan',
avatar: '',
cover: '',
dateOfBirth: new Date(),
gender: 'male',
provider: 'nan',
Expand All @@ -144,7 +152,8 @@ module.exports = {
email: 'espoirmugenzie@gmail.com',
password: 'ericprestein',
bio: 'nan',
image: 'nan',
avatar: '',
cover: '',
dateOfBirth: new Date(),
gender: 'male',
provider: 'nan',
Expand All @@ -161,7 +170,8 @@ module.exports = {
email: 'superuser@gmail.com',
password: superUserPsw,
bio: '',
image: '',
avatar: '',
cover: '',
dateOfBirth: '12/12/2000',
gender: '',
provider: '',
Expand All @@ -178,7 +188,8 @@ module.exports = {
email: 'rukundoemma@gmail.com',
password: 'EmyRukundo00',
bio: '',
image: '',
avatar: '',
cover: '',
dateOfBirth: '12/12/1900',
gender: '',
provider: '',
Expand Down
3 changes: 0 additions & 3 deletions src/sequelize/seeders/20190620204605-articles-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ module.exports = {
blocked: false,
body:
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
image: '',
createdAt: new Date(),
updatedAt: new Date(),
readtime: '2 min',
Expand All @@ -22,7 +21,6 @@ module.exports = {
blocked: false,
body:
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
image: '',
createdAt: new Date(),
updatedAt: new Date(),
readtime: '1min',
Expand All @@ -35,7 +33,6 @@ module.exports = {
readtime: '2 min',
body:
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
image: '',
createdAt: new Date(),
updatedAt: new Date(),
authorId: 4,
Expand Down
Loading

0 comments on commit 4dbb521

Please sign in to comment.