Skip to content

Commit

Permalink
ft(paginate-article): Add pagination support for articles
Browse files Browse the repository at this point in the history
- Write unit tests
- Allow pagination for articles using query
- Validate the query parameter

[Delivers #159206061]
  • Loading branch information
Olumide Ogundele committed Aug 23, 2018
1 parent cd96a98 commit 578fd0f
Show file tree
Hide file tree
Showing 9 changed files with 11,112 additions and 152 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ dist/

#txt
txt.txt

#VS Code
.vscode
15 changes: 0 additions & 15 deletions config/index.js

This file was deleted.

156 changes: 23 additions & 133 deletions controllers/ArticleController.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
<<<<<<< HEAD
import cloudinary from '../config/cloudinary';
=======
import cloudinary from 'cloudinary';
>>>>>>> ft(create-article): create user article
import Utilities from '../helpers/utilities';
import { Article, User } from '../models';
import createArticleHelper from '../helpers/createArticleHelper';

/**
<<<<<<< HEAD
* Article class for users
* @param {method} createArticle - Create article
* @param {method} getArticle - Get a single article
Expand All @@ -24,76 +19,37 @@ class ArticleController {
static createArticle(req, res) {
const {
title, description, body, tagList, imageData,
=======
* Article class for users
* @param {method} createArticle - Create article
* @param {method} getArticle - Get a single article
* @param {method} editArticle update a single article
*/
class ArticleController {
/**
* Create an article for a user
* @param {object} req - The request object
* @param {object} res - The response object sent to user
* @return {object} A object containing created articles.
*/
static createArticle(req, res) {
const {
title, description, body, tagList, imageUrl,
>>>>>>> ft(create-article): create user article
} = req.body.article;

const { userId } = req;

const articleObject = {
<<<<<<< HEAD
title, description, body, tagList, imageData, userId
=======
title, description, body, tagList, imageUrl, userId
>>>>>>> ft(create-article): create user article
};
/**
* check if image was provided in the request
* upload the image to cloudinary, save the article
* with the cloudinary URL in database but if an error
* was encountered from cloudinary go ahead and create the article
<<<<<<< HEAD
*/
if (imageData) {
return cloudinary.v2.uploader.upload(imageData, { tags: 'basic_sample' })
=======
*/
if (imageUrl) {
return cloudinary.v2.uploader.upload(imageUrl, { tags: 'basic_sample' })
>>>>>>> ft(create-article): create user article
.then(image => createArticleHelper(res, articleObject, image.url))
.catch(() => createArticleHelper(res, articleObject));
}

<<<<<<< HEAD
// if there no image was provided go ahead to create the article
=======
/**
* if there no image was provided go ahead to create the article
*/
>>>>>>> ft(create-article): create user article
return createArticleHelper(res, articleObject);
}

/**
* get an article using slug as query parameter
* @param {object} req - request object
* @param {object} res - response object
<<<<<<< HEAD
* @param {function} next - next function
* @returns {object} - the found article from database or error if not found
*/
static getArticle(req, res, next) {
=======
* @returns {object} - the found article from database or error if not found
*/
static getArticle(req, res) {
>>>>>>> ft(create-article): create user article
const { slug } = req.params;

return Article
Expand All @@ -103,17 +59,9 @@ class ArticleController {
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt'] }
}],
<<<<<<< HEAD
attributes: { exclude: ['userId'] }
})
.then((article) => {
// if the article does not exist
=======
attributes: { exclude: ['id', 'userId'] }
})
.then((article) => {
/** if the article does not exist */
>>>>>>> ft(create-article): create user article
if (!article) {
return res.status(404).json({
errors: {
Expand All @@ -126,57 +74,53 @@ class ArticleController {

return res.status(200).json({ article });
})
<<<<<<< HEAD
.catch(next);
=======
.catch(() => res.status(501).send('oops seems there is an error find the article'));
>>>>>>> ft(create-article): create user article
}

/**
* get all articles created
* get all articles created and use the query
* if provided to implement pagination
* @param {object} req - request object
* @param {object} res - response object
<<<<<<< HEAD
* @param {function} next - next function
* @returns {object} - the found article from database or empty if not found
*/
static listAllArticles(req, res, next) {
=======
* @returns {object} - the found article from database or empty if not found
*/
static listAllArticles(req, res) {
>>>>>>> ft(create-article): create user article
const { page, limit } = req;
let offset = null;

if (page || limit) {
// calculate offset
offset = limit * (page - 1);
}

return Article
.findAll({
include: [{
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt'] }
}],
<<<<<<< HEAD
attributes: { exclude: ['userId'] }
offset,
limit,
})
.then((articles) => {
// check if there was no article created
if (articles.length === 0) {
/** check if there was no article created
* for the page query
*/
const message = page ? 'articles limit exceeded'
: 'Sorry, no articles created';
return res.status(200).json({
message: 'No articles created',
=======
attributes: { exclude: ['id', 'userId'] }
})
.then((articles) => {
/** check if there was no article created */
if (articles.length === 0) {
return res.status(200).json({
message: 'Your request was successful but no articles created',
>>>>>>> ft(create-article): create user article
message,
articles,
articlesCount: articles.length
});
}

return res.status(200).json({ articles, articlesCount: articles.length });
return res.status(200).json({
articles,
articlesCount: articles.length
});
})
<<<<<<< HEAD
.catch(next);
}

Expand All @@ -186,26 +130,11 @@ class ArticleController {
* @param {object} req: request object
* @param {object} res: response object
* @param {function} next - next function
=======
.catch(() => res.status(501).send('oops seems there is an error find all articles'));
}
/**
* @function editArticle
* @summary: API controller to handle requests
* to edit an article
* @param {object} req: request object
* @param {object} res: response object
>>>>>>> ft(create-article): create user article
* @returns {object} api response: article object for
* successful requests, or error object for
* requests that fail
*/
<<<<<<< HEAD
static editArticle(req, res, next) {
=======
static editArticle(req, res) {
>>>>>>> ft(create-article): create user article
const { title, description, body } = req.body.article;
const { count } = req;
const { slug } = req.params;
Expand All @@ -225,7 +154,6 @@ class ArticleController {
success: true,
article: result[1]
}))
<<<<<<< HEAD
.catch(next);
}

Expand All @@ -239,50 +167,12 @@ class ArticleController {
* successful requests, or error object for requests that fail
*/
static deleteArticle(req, res, next) {
=======
.catch((err) => {
res.status(500).json({
errors: {
body: [
'sorry there was an error updating this request',
err
]
}
});
});
}

/**
* @function deleteArticle
* @summary: API controller to handle requests
* to delete an article
* @param {object} req: request object
* @param {object} res: response object
* @returns {object} api response: article object for
* successful requests, or error object for
* requests that fail
*/
static deleteArticle(req, res) {
>>>>>>> ft(create-article): create user article
const { slug } = req.params;
Article.destroy({
where: { slug }
})
<<<<<<< HEAD
.then(() => res.status(200).json({ message: 'Article successfully deleted' }))
.catch(next);
=======
.then(() => res.status(204).json())
.catch(() => {
res.status(500).json({
errors: {
body: [
'sorry there was an error deleting this request',
]
}
});
});
>>>>>>> ft(create-article): create user article
}
}

Expand Down
1 change: 0 additions & 1 deletion helpers/createArticleHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const createArticleHelper = (res, articleObject, imageUrl = null) => {
model: User,
attributes: { exclude: ['id', 'email', 'hashedPassword', 'createdAt', 'updatedAt'] }
}],
attributes: { exclude: ['userId'] }
}))
.then(article => res.status(201).json({ article }));
};
Expand Down
Empty file removed helpers/isStringValidator.js
Empty file.
21 changes: 21 additions & 0 deletions middlewares/ParamsValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,25 @@ export default class ParamsValidator {
}
next();
}

/**
* Checks the page params provided for integer 1 and above
* and assign a default value when less than 1
* @param {string} req - The request to be validated
* @param {string} res - The response from validation
* @param {string} next - The next function
* @returns {undefined}
*/
static validatePageQuery(req, res, next) {
let { page, limit } = req.query;

if (page || limit) {
page = Number(page);
limit = Number(limit);

req.page = (page < 1 || !Number.isInteger(page)) ? 1 : page;
req.limit = (limit < 1 || !Number.isInteger(limit)) ? 10 : limit;
}
next();
}
}
Loading

0 comments on commit 578fd0f

Please sign in to comment.