-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from andela/feature/162727469/create-articles
#162727469 Create articles
- Loading branch information
Showing
11 changed files
with
678 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
import models from '../db/models'; | ||
import { Response, Slugify } from '../helpers/index'; | ||
|
||
const { Art, Media, Category } = models; | ||
|
||
let response; | ||
|
||
/** Arts Controller Class */ | ||
class ArtsController { | ||
/** | ||
* @desc POST /api/v1/articles | ||
* @param {object} req | ||
* @param {object} res | ||
* @memberof ArtsController | ||
* This will receive a media object containing a list of media files | ||
* @returns {Object} ARTicle details | ||
*/ | ||
static async create(req, res) { | ||
try { | ||
const defaultStatus = 0; | ||
const validationErrors = []; | ||
|
||
const { id: artistId } = req.verifyUser; | ||
|
||
const { | ||
title, description, categoryId, media, | ||
} = req.body; | ||
|
||
const mediaFilesArray = JSON.parse(media); | ||
|
||
req.check('title', 'Title is required').notEmpty(); | ||
req.check('description', 'Description should be longer').notEmpty() | ||
.isLength({ min: 15 }); | ||
|
||
const errors = req.validationErrors(); | ||
|
||
if (errors) { | ||
errors.map(err => validationErrors.push(err.msg)); | ||
response = new Response( | ||
'Not Ok', | ||
400, | ||
'Validation Errors Occurred', | ||
{ validationErrors } | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
|
||
const slugifiedTitle = Slugify.slugify(title); | ||
|
||
const checkCategory = await Category.findOne({ where: { id: 1 } }); | ||
if (!checkCategory) { | ||
await Category.create({ categoryName: 'Architecture' }); | ||
} | ||
|
||
const createArticle = await Art | ||
.create({ | ||
artistId, | ||
slug: slugifiedTitle, | ||
title, | ||
description, | ||
categoryId, | ||
featuredImg: mediaFilesArray[0].url | ||
|| process.env.DEFAULT_ARTICLE_IMAGE, | ||
status: defaultStatus | ||
}); | ||
|
||
const { | ||
id: artId, | ||
title: artTitle, | ||
description: artDescription, | ||
featuredImg: artFeaturedImg, | ||
categoryId: artCategoryId | ||
} = createArticle.dataValues; | ||
|
||
if (mediaFilesArray.length > 0) { | ||
let cnt = 0; | ||
await mediaFilesArray.some((mediaFile) => { | ||
cnt += 1; | ||
Media.create({ | ||
artId, | ||
contentUrl: mediaFile.url, | ||
mediaType: mediaFile.extension | ||
}); | ||
return cnt === 7; | ||
}); | ||
} | ||
|
||
response = new Response( | ||
'Ok', | ||
201, | ||
'Article created successfully', | ||
{ | ||
artId, | ||
artTitle, | ||
slugifiedTitle, | ||
artDescription, | ||
artFeaturedImg, | ||
artCategoryId | ||
} | ||
); | ||
|
||
return res.status(response.code).json(response); | ||
} catch (err) { | ||
response = new Response( | ||
'Not Ok', | ||
500, | ||
`${err}`, | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
} | ||
|
||
/** | ||
* @desc PUT /api/v1/articles | ||
* @param {object} req | ||
* @param {object} res | ||
* @memberof ArtsController | ||
* @returns {Object} ARTicle details | ||
*/ | ||
static async update(req, res) { | ||
try { | ||
const validationErrors = []; | ||
|
||
const { id: artistId } = req.verifyUser; | ||
const { slug } = req.params; | ||
|
||
const artToUpdate = await Art.findOne({ | ||
where: { slug } | ||
}); | ||
|
||
if (!artToUpdate) { | ||
response = new Response( | ||
'Not Found', | ||
404, | ||
'Sorry. Article Not Found' | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
|
||
if (artistId !== artToUpdate.artistId) { | ||
response = new Response( | ||
'Not Ok', | ||
403, | ||
'Unauthorized to Edit Article', | ||
{} | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
|
||
const { | ||
title, description, categoryId, media, | ||
} = req.body; | ||
|
||
const mediaFilesArray = JSON.parse(media); | ||
|
||
req.check('title', 'Title is required').notEmpty(); | ||
req.check('description', 'Description should be longer').notEmpty() | ||
.isLength({ min: 15 }); | ||
|
||
const errors = req.validationErrors(); | ||
if (errors) { | ||
errors.map(err => validationErrors.push(err.msg)); | ||
response = new Response( | ||
'Not Ok', | ||
400, | ||
'Validation Errors Occurred', | ||
{ validationErrors } | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
|
||
const slugifiedTitle = Slugify.slugify(title); | ||
|
||
const updatedArticle = { | ||
id: artToUpdate.id, | ||
title: title || artToUpdate.title, | ||
slug: slugifiedTitle, | ||
description: description || artToUpdate.description, | ||
categoryId: categoryId || artToUpdate.categoryId, | ||
featuredImg: mediaFilesArray[0].url || artToUpdate.featuredImg, | ||
createdAt: artToUpdate.createdAt | ||
}; | ||
|
||
const mediaToDelete = await Media.destroy({ | ||
where: { artId: artToUpdate.id } | ||
}); | ||
|
||
if (mediaToDelete) { | ||
mediaFilesArray.splice(6); | ||
await mediaFilesArray.forEach((mediaFile) => { | ||
Media.create({ | ||
artId: artToUpdate.id, | ||
contentUrl: mediaFile.url, | ||
mediaType: mediaFile.extension | ||
}); | ||
}); | ||
} | ||
|
||
const updateArticleSuccess = await artToUpdate.update(updatedArticle); | ||
|
||
response = new Response( | ||
'Ok', | ||
200, | ||
'Article updated successfully', | ||
updateArticleSuccess | ||
); | ||
return res.status(response.code).json(response); | ||
} catch (err) { | ||
response = new Response( | ||
'Not ok', | ||
500, | ||
`${err}`, | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
} | ||
|
||
/** | ||
* @desc DELETE /api/v1/articles | ||
* @param {object} req | ||
* @param {object} res | ||
* @memberof ArtsController | ||
* @returns {Object} ARTicle details | ||
*/ | ||
static async delete(req, res) { | ||
try { | ||
const { slug } = req.params; | ||
|
||
const { id: artistId } = req.verifyUser; | ||
|
||
const artToDelete = await Art.findOne({ | ||
where: { slug } | ||
}); | ||
|
||
if (!artToDelete) { | ||
response = new Response( | ||
'Not Found', | ||
404, | ||
'Sorry. Article Not Found' | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
|
||
if (artistId !== artToDelete.artistId) { | ||
response = new Response( | ||
'Not Ok', | ||
403, | ||
'Unauthorized to Delete Article', | ||
{} | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
|
||
const artDeleted = await Art.destroy({ | ||
where: { slug } | ||
}); | ||
|
||
response = new Response( | ||
'Ok', | ||
200, | ||
'Article deleted successfully', | ||
{ artToDelete: artDeleted } | ||
); | ||
return res.status(response.code).json(response); | ||
} catch (err) { | ||
response = new Response( | ||
'Not ok', | ||
500, | ||
`${err}`, | ||
); | ||
return res.status(response.code).json(response); | ||
} | ||
} | ||
} | ||
|
||
export default ArtsController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import slug from 'slug'; | ||
|
||
/** Slugify Helper Class */ | ||
export default class Slugify { | ||
/** | ||
* Represents a slugify function. | ||
* @param {string} string - The string to be slugified . | ||
* @returns {string} slug | ||
*/ | ||
static slugify(string) { | ||
const uuid = Math.random().toString(36).substring(7); | ||
string += `-${uuid}`; | ||
return slug(string, { lower: true }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
import EmailNotificationAPI from './EmailNotificationAPI'; | ||
import TokenAuthenticate from './TokenAuthenticate'; | ||
import Response from './response'; | ||
import Slugify from './Slugify'; | ||
|
||
export default { | ||
export { | ||
EmailNotificationAPI, | ||
Slugify, | ||
TokenAuthenticate, | ||
Response | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import express from 'express'; | ||
import ArtController from '../controllers/ArtsController'; | ||
import { TokenAuthenticate } from '../helpers/index'; | ||
|
||
const artsRoute = express.Router(); | ||
|
||
artsRoute.post('/', TokenAuthenticate.tokenVerify, ArtController.create); | ||
|
||
artsRoute.put('/:slug', TokenAuthenticate.tokenVerify, ArtController.update); | ||
|
||
artsRoute.delete( | ||
'/:slug', | ||
TokenAuthenticate.tokenVerify, | ||
ArtController.delete | ||
); | ||
|
||
export default artsRoute; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.