Skip to content

Commit

Permalink
feat(recipe): Pagination Support for Recipes (#22)
Browse files Browse the repository at this point in the history
* feat(pagination): add pagination functionality

* ft(recipes-pagination): document with swagger

* feat(recipes-pagination): fix logic

* feat(recipes-pagination): use offset instead of page

* feat(recipes-pagination): fix test

* feat(recipes-pagination): test for offsets

[Delivers #164139782]
  • Loading branch information
LordUche authored and andela-pessien committed Mar 22, 2019
1 parent b8e6f86 commit f779d53
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 92 deletions.
3 changes: 2 additions & 1 deletion .sequelizerc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
require('@babel/register');
const path = require('path');

module.exports = {
"config": path.resolve('./server/config', 'db-config.js'),
"models-path": path.resolve('./server/models'),
"seeders-path": path.resolve('./server/seeders'),
"migrations-path": path.resolve('./server/migrations')
};
};
146 changes: 69 additions & 77 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@
"node_modules/*"
]
}
}
}
11 changes: 9 additions & 2 deletions server/config/db-config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import env from './env-config';

const { DATABASE_URL } = env;

module.exports = {
development: {
env_variable: 'DATABASE_URL',
dialect: 'postgres',
operatorsAliases: false
operatorsAliases: false,
url: DATABASE_URL
},
test: {
env_variable: 'DATABASE_URL',
dialect: 'postgres',
operatorsAliases: false
operatorsAliases: false,
url: DATABASE_URL
},
production: {
env_variable: 'DATABASE_URL',
dialect: 'postgres',
operatorsAliases: false,
url: DATABASE_URL,
logging: false
}
};
27 changes: 20 additions & 7 deletions server/controllers/recipe-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import {
validationErrorResponse,
rowArrayToObjectList
} from '../utils/helpers';
import { recipeSchema, recipeUpdateSchema } from '../utils/validators';
import {
recipeSchema,
paginationSchema,
recipeUpdateSchema
} from '../utils/validators';

const { Recipe, User } = db;

Expand Down Expand Up @@ -148,20 +152,29 @@ class RecipeController {
* @param {Object} res Express response object
* @returns {undefined}
*/
static getRecipes(req, res) {
Recipe.findAll(defaultRecipeDbFilter)
static async getRecipes(req, res) {
let { limit, offset } = req.query;
try {
await validate(req.query, paginationSchema);
} catch (error) {
return validationErrorResponse(res, error.details);
}
limit = offset && !limit ? 20 : limit;
offset = offset || 0;

Recipe.findAll({ ...defaultRecipeDbFilter, offset, limit })
.then(recipeRows =>
recipeRows.length
? successResponse(res, {
recipes: rowArrayToObjectList(recipeRows)
})
: successResponse(res, {
recipes: [],
message: 'no recipes have been created'
message: 'no recipes found'
})
)
.catch(() => {
errorResponse(res, 'Oops, an error occured. Please try again', 500);
errorResponse(res, 'Oops, an error occurred. Please try again', 500);
});
}

Expand All @@ -181,10 +194,10 @@ class RecipeController {
.then(recipe =>
recipe
? successResponse(res, { recipe })
: errorResponse(res, 'recipe Not found', 404)
: errorResponse(res, 'Recipe not found', 404)
)
.catch(() => {
errorResponse(res, 'Oops, an error occured. Please try again', 500);
errorResponse(res);
});
}
}
Expand Down
9 changes: 9 additions & 0 deletions server/utils/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,12 @@ export const changePasswordSchema = Joi.object().keys({
.min(8)
.required()
});

export const paginationSchema = Joi.object().keys({
offset: Joi.number()
.integer()
.greater(-1),
limit: Joi.number()
.integer()
.positive()
});
27 changes: 26 additions & 1 deletion swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:3000/api"
},
{
"url": "https://vale-ah-backend-staging.herokuapp.com/api"
}
Expand Down Expand Up @@ -201,7 +204,26 @@
"tags": [
"Recipes"
],
"parameters": [
{
"name": "offset",
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "number"
}
}
],
"responses": {
"400": {
"description": "Validation error"
},
"500": {
"description": "Database connection error"
},
Expand Down Expand Up @@ -239,6 +261,9 @@
"description": "Successfully created new recipe",
"content": {}
},
"500": {
"description": "Database connection error"
},
"400": {
"description": "Validation Error",
"content": {}
Expand Down Expand Up @@ -502,4 +527,4 @@
}
}
}
}
}
138 changes: 135 additions & 3 deletions test/recipes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,7 @@ describe('Recipes', () => {
expect(recipes)
.to.be.an('array')
.that.has.lengthOf(0);
expect(message)
.to.be.a('string')
.that.eqls('no recipes have been created');
expect(message).to.be.a('string');
done(err);
});
});
Expand Down Expand Up @@ -333,4 +331,138 @@ describe('Recipes', () => {
});
});
});

describe('Pagination', () => {
const url = '/api/recipes';
let items;
before(async () => {
const array = Array(50)
.fill()
.map(() =>
chai
.request(server)
.post('/api/recipes')
.set({ authorization: runtimeFixture.token })
.send(recipe)
);
await Promise.all(array);

const {
body: { recipes }
} = await chai
.request(server)
.get('/api/recipes')
.set({ authorization: runtimeFixture.token });

items = recipes;
});

it('should get all recipes when no offset or limit is set', done => {
chai
.request(server)
.get(url)
.end((err, res) => {
const { recipes } = res.body;
expect(res).to.have.status(200);
expect(recipes)
.to.be.an('Array')
.and.to.have.lengthOf.at.least(50);
done(err);
});
});

it('should get the first 30 recipes when limit is 30 but offset is not set', done => {
chai
.request(server)
.get(`${url}?limit=30`)
.end((err, res) => {
const { recipes } = res.body;
expect(res).to.have.status(200);
expect(recipes)
.to.be.an('Array')
.and.has.lengthOf(30);
expect(res.body.recipes[0]).to.deep.equal(items[0]);
done(err);
});
});

it('should get 20 recipes when offset is 0 limit is not set', done => {
chai
.request(server)
.get(`${url}?offset=0`)
.end((err, res) => {
const { recipes } = res.body;
expect(res).to.have.status(200);
expect(recipes).to.be.an('Array');
expect(recipes).to.have.lengthOf(20);
expect(res.body.recipes[0]).to.deep.equal(items[0]);
done(err);
});
});

it('should get 20 recipes when offset is 10 limit is not set', done => {
chai
.request(server)
.get(`${url}?offset=10`)
.end((err, res) => {
const { recipes } = res.body;
expect(res).to.have.status(200);
expect(recipes).to.be.an('Array');
expect(recipes).to.have.lengthOf(20);
expect(res.body.recipes[0]).to.not.deep.equal(items[0]);
expect(res.body.recipes[0]).to.deep.equal(items[10]);
done(err);
});
});

it('should get 25 recipes when offset is 20 and limit is 25', done => {
chai
.request(server)
.get(`${url}?offset=20&limit=25`)
.end((err, res) => {
const { recipes } = res.body;
expect(res).to.have.status(200);
expect(recipes).to.be.an('Array');
expect(recipes.length).to.equal(25);
expect(res.body.recipes[0]).to.not.deep.equal(items[0]);
expect(res.body.recipes[0]).to.deep.equal(items[20]);
done(err);
});
});

it('should not get recipes if offset or limit is a negative number', done => {
chai
.request(server)
.get(`${url}?offset=-1&limit=-1`)
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.errors).to.haveOwnProperty('offset');
expect(res.body.errors).to.haveOwnProperty('limit');
done(err);
});
});

it('should not get recipes if limit is zero', done => {
chai
.request(server)
.get(`${url}?offset=0&limit=0`)
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.errors).to.haveOwnProperty('limit');
done(err);
});
});

it('should not get recipes if offset or limit is not an integer', done => {
chai
.request(server)
.get(`${url}?offset=text&limit=text`)
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.errors).to.haveOwnProperty('offset');
expect(res.body.errors).to.haveOwnProperty('limit');
done(err);
});
});
});
});

0 comments on commit f779d53

Please sign in to comment.