fix item categories API.

This commit is contained in:
Ahmed Bouhuolia
2020-03-31 14:28:13 +02:00
parent d9e10fd6b4
commit da05239e84
4 changed files with 181 additions and 92 deletions

View File

@@ -2,7 +2,7 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('items_categories', (table) => { return knex.schema.createTable('items_categories', (table) => {
table.increments(); table.increments();
table.string('label'); table.string('name');
table.integer('parent_category_id').unsigned(); table.integer('parent_category_id').unsigned();
table.text('description'); table.text('description');
table.integer('user_id').unsigned(); table.integer('user_id').unsigned();

View File

@@ -11,34 +11,34 @@ export default {
*/ */
router() { router() {
const router = express.Router(); const router = express.Router();
const permit = Authorization('items_categories'); // const permit = Authorization('items_categories');
router.use(JWTAuth); router.use(JWTAuth);
router.post('/:id', router.post('/:id',
permit('create', 'edit'), // permit('create', 'edit'),
this.editCategory.validation, this.editCategory.validation,
asyncMiddleware(this.editCategory.handler)); asyncMiddleware(this.editCategory.handler));
router.post('/', router.post('/',
permit('create'), // permit('create'),
this.newCategory.validation, this.newCategory.validation,
asyncMiddleware(this.newCategory.handler)); asyncMiddleware(this.newCategory.handler));
router.delete('/:id', router.delete('/:id',
permit('create', 'edit', 'delete'), // permit('create', 'edit', 'delete'),
this.deleteItem.validation, this.deleteItem.validation,
asyncMiddleware(this.deleteItem.handler)); asyncMiddleware(this.deleteItem.handler));
router.get('/:id', router.get('/:id',
permit('view'), // permit('view'),
this.getCategory.validation, this.getCategory.validation,
asyncMiddleware(this.getCategory.handler)); asyncMiddleware(this.getCategory.handler));
router.get('/', router.get('/',
permit('view'), // permit('view'),
this.getList.validation, this.getList.validation,
asyncMiddleware(this.getList.validation)); asyncMiddleware(this.getList.handler));
return router; return router;
}, },
@@ -48,7 +48,7 @@ export default {
*/ */
newCategory: { newCategory: {
validation: [ validation: [
check('name').exists({ checkFalsy: true }).trim().escape(), check('name').exists().trim().escape(),
check('parent_category_id').optional().isNumeric().toInt(), check('parent_category_id').optional().isNumeric().toInt(),
check('description').optional().trim().escape(), check('description').optional().trim().escape(),
], ],
@@ -61,10 +61,12 @@ export default {
}); });
} }
const { name, parent_category_id: parentCategoryId, description } = req.body; const { user } = req;
const form = { ...req.body };
if (parentCategoryId) { if (form.parent_category_id) {
const foundParentCategory = await ItemCategory.where('id', parentCategoryId).fetch(); const foundParentCategory = await ItemCategory.query()
.where('id', form.parent_category_id).first();
if (!foundParentCategory) { if (!foundParentCategory) {
return res.boom.notFound('The parent category ID is not found.', { return res.boom.notFound('The parent category ID is not found.', {
@@ -72,14 +74,11 @@ export default {
}); });
} }
} }
const category = await ItemCategory.forge({ const category = await ItemCategory.query().insert({
label: name, ...form,
parent_category_id: parentCategoryId, user_id: user.id,
description,
}); });
return res.status(200).send({ category });
await category.save();
return res.status(200).send({ id: category.get('id') });
}, },
}, },
@@ -89,7 +88,7 @@ export default {
editCategory: { editCategory: {
validation: [ validation: [
param('id').toInt(), param('id').toInt(),
check('name').exists({ checkFalsy: true }).trim().escape(), check('name').exists().trim().escape(),
check('parent_category_id').optional().isNumeric().toInt(), check('parent_category_id').optional().isNumeric().toInt(),
check('description').optional().trim().escape(), check('description').optional().trim().escape(),
], ],
@@ -102,14 +101,19 @@ export default {
code: 'validation_error', ...validationErrors, code: 'validation_error', ...validationErrors,
}); });
} }
const { name, parent_category_id: parentCategoryId, description } = req.body;
const itemCategory = await ItemCategory.where('id', id).fetch(); const form = { ...req.body };
const itemCategory = await ItemCategory.query().where('id', id).first()
if (!itemCategory) { if (!itemCategory) {
return res.boom.notFound(); return res.boom.notFound({
errors: [{ type: 'ITEM_CATEGORY.NOT.FOUND', code: 100 }],
});
} }
if (parentCategoryId && parentCategoryId !== itemCategory.attributes.parent_category_id) { if (form.parent_category_id
const foundParentCategory = await ItemCategory.where('id', parentCategoryId).fetch(); && form.parent_category_id !== itemCategory.parent_category_id) {
const foundParentCategory = await ItemCategory.query()
.where('id', form.parent_category_id).first();
if (!foundParentCategory) { if (!foundParentCategory) {
return res.boom.notFound('The parent category ID is not found.', { return res.boom.notFound('The parent category ID is not found.', {
@@ -117,13 +121,9 @@ export default {
}); });
} }
} }
await itemCategory.save({ const updateItemCategory = await ItemCategory.query().where('id', id).update({ ...form });
label: name,
description,
parent_category_id: parentCategoryId,
});
return res.status(200).send({ id: itemCategory.id }); return res.status(200).send({ id: updateItemCategory });
}, },
}, },
@@ -132,16 +132,18 @@ export default {
*/ */
deleteItem: { deleteItem: {
validation: [ validation: [
param('id').toInt(), param('id').exists().toInt(),
], ],
async handler(req, res) { async handler(req, res) {
const { id } = req.params; const { id } = req.params;
const itemCategory = await ItemCategory.where('id', id).fetch(); const itemCategory = await ItemCategory.query().where('id', id).first();
if (!itemCategory) { if (!itemCategory) {
return res.boom.notFound(); return res.boom.notFound();
} }
await itemCategory.destroy();
await ItemCategory.query().where('id', itemCategory.id).delete();
return res.status(200).send(); return res.status(200).send();
}, },
}, },
@@ -152,12 +154,9 @@ export default {
getList: { getList: {
validation: [], validation: [],
async handler(req, res) { async handler(req, res) {
const items = await ItemCategory.fetch(); const categories = await ItemCategory.query();
if (!items) { return res.status(200).send({ categories });
return res.boom.notFound();
}
return res.status(200).send({ items: items.toJSON() });
}, },
}, },

View File

@@ -14,15 +14,17 @@ export default class ItemCategory extends BaseModel {
* Relationship mapping. * Relationship mapping.
*/ */
static get relationMappings() { static get relationMappings() {
const Item = require('@/models/Item');
return { return {
/** /**
* Item category may has many items. * Item category may has many items.
*/ */
items: { items: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
modelBase: path.join(__dirname, 'Item'), modelClass: Item.default,
join: { join: {
from: 'items_categories.item_id', from: 'items_categories.itemId',
to: 'items.id', to: 'items.id',
}, },
}, },

View File

@@ -1,24 +1,47 @@
import { request, expect, create } from '~/testInit'; import {
request,
expect,
create,
login,
} from '~/testInit';
import knex from '@/database/knex'; import knex from '@/database/knex';
describe('routes: /item_categories/', () => { let loginRes;
describe('POST `/items_categories``', async () => {
it('Should not create a item category if the user was not authorized.', () => {
describe.only('routes: /item_categories/', () => {
beforeEach(async () => {
loginRes = await login();
});
afterEach(() => {
loginRes = null;
});
describe('POST `/items_categories``', async () => {
it('Should not create a item category if the user was not authorized.', async () => {
const res = await request().post('/api/item_categories').send();
expect(res.status).equals(401);
expect(res.body.message).equals('unauthorized');
}); });
it('Should `name` be required.', async () => { it('Should `name` be required.', async () => {
const res = await request().post('/api/item_categories').send(); const res = await request()
.post('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(422); expect(res.status).equals(422);
expect(res.body.code).equals('validation_error'); expect(res.body.code).equals('validation_error');
}); });
it('Should `parent_category_id` be exist in the storage.', async () => { it('Should `parent_category_id` be exist in the storage.', async () => {
const res = await request().posjt('/api/item_categories').send({ const res = await request()
name: 'Clothes', .post('/api/item_categories')
parent_category_id: 10, .set('x-access-token', loginRes.body.token)
}); .send({
name: 'Clothes',
parent_category_id: 10,
});
expect(res.status).equals(404); expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({ expect(res.body.errors).include.something.that.deep.equals({
@@ -27,55 +50,76 @@ describe('routes: /item_categories/', () => {
}); });
it('Should response success with correct form data.', async () => { it('Should response success with correct form data.', async () => {
const res = await request().post('/api/item_categories').send({ const res = await request()
name: 'Clothes', .post('/api/item_categories')
description: 'Here is description', .set('x-access-token', loginRes.body.token)
}); .send({
name: 'Clothes',
description: 'Here is description',
});
// eslint-disable-next-line no-unused-expressions
expect(res.body.id).to.exist;
expect(res.status).equals(200); expect(res.status).equals(200);
expect(res.body.category).to.be.a('object');
expect(res.body.category.id).to.be.a('number');
expect(res.body.category.name).to.be.a('string');
expect(res.body.category.description).to.be.a('string');
}); });
it('Should item category data be saved to the storage.', async () => { it('Should item category data be saved to the storage.', async () => {
const category = await create('item_category'); const category = await create('item_category');
const res = await request().post('/api/item_categories').send({ const res = await request()
name: 'Clothes', .post('/api/item_categories')
description: 'Here is description', .set('x-access-token', loginRes.body.token)
parent_category_id: category.id, .send({
}); name: 'Clothes',
description: 'Here is description',
parent_category_id: category.id,
});
// eslint-disable-next-line no-unused-expressions expect(res.status).equals(200);
expect(res.body.id).to.exist;
const storedCategory = await knex('items_categories').where('id', res.body.id).first(); const storedCategory = await knex('items_categories')
.where('id', res.body.category.id).first();
expect(storedCategory.label).equals('Clothes'); expect(storedCategory.name).equals('Clothes');
expect(storedCategory.description).equals('Here is description'); expect(storedCategory.description).equals('Here is description');
expect(storedCategory.parent_category_id).equals(category.id); expect(storedCategory.parentCategoryId).equals(category.id);
expect(storedCategory.userId).to.be.a('number');
}); });
}); });
describe('POST `/items_category/{id}`', () => { describe('POST `/items_category/{id}`', () => {
it('Should not update a item category if the user was not authorized.', () => { it('Should not update a item category if the user was not authorized.', async () => {
const category = await create('item_category');
const res = await request()
.post(`/api/item_categories/${category.id}`)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('unauthorized');
}); });
it('Should `name` be required.', async () => { it('Should `name` be required.', async () => {
const category = await create('item_category'); const category = await create('item_category');
const res = await request().post(`/api/item_categories/${category.id}`).send({ const res = await request()
name: '', .post(`/api/item_categories/${category.id}`)
}); .set('x-access-token', loginRes.body.token)
.send({
name: '',
});
expect(res.status).equals(422); expect(res.status).equals(422);
expect(res.body.code).equals('validation_error'); expect(res.body.code).equals('validation_error');
}); });
it('Should `parent_category_id` be exist in the storage.', async () => { it('Should `parent_category_id` be exist in the storage.', async () => {
const category = await create('item_category'); const category = await create('item_category');
const res = await request().post(`/api/item_categories/${category.id}`).send({ const res = await request()
name: 'Name', .post(`/api/item_categories/${category.id}`)
parent_category_id: 10, .set('x-access-token', loginRes.body.token)
}); .send({
name: 'Name',
parent_category_id: 10,
});
expect(res.status).equals(404); expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({ expect(res.body.errors).include.something.that.deep.equals({
@@ -87,11 +131,14 @@ describe('routes: /item_categories/', () => {
const category = await create('item_category'); const category = await create('item_category');
const anotherCategory = await create('item_category'); const anotherCategory = await create('item_category');
const res = await request().post(`/api/item_categories/${category.id}`).send({ const res = await request()
name: 'Name', .post(`/api/item_categories/${category.id}`)
parent_category_id: anotherCategory.id, .set('x-access-token', loginRes.body.token)
description: 'updated description', .send({
}); name: 'Name',
parent_category_id: anotherCategory.id,
description: 'updated description',
});
expect(res.status).equals(200); expect(res.status).equals(200);
}); });
@@ -100,42 +147,61 @@ describe('routes: /item_categories/', () => {
const category = await create('item_category'); const category = await create('item_category');
const anotherCategory = await create('item_category'); const anotherCategory = await create('item_category');
const res = await request().post(`/api/item_categories/${category.id}`).send({ const res = await request()
name: 'Name', .post(`/api/item_categories/${category.id}`)
parent_category_id: anotherCategory.id, .set('x-access-token', loginRes.body.token)
description: 'updated description', .send({
}); name: 'Name',
parent_category_id: anotherCategory.id,
description: 'updated description',
});
const storedCategory = await knex('items_categories').where('id', res.body.id).first(); const storedCategory = await knex('items_categories')
.where('id', res.body.id).first();
expect(storedCategory.label).equals('Name'); expect(storedCategory.name).equals('Name');
expect(storedCategory.description).equals('updated description'); expect(storedCategory.description).equals('updated description');
expect(storedCategory.parent_category_id).equals(anotherCategory.id); expect(storedCategory.parentCategoryId).equals(anotherCategory.id);
}); });
}); });
describe('DELETE: `/items_categories`', async () => { describe('DELETE: `/items_categories`', async () => {
it('Should not delete the give item category if the user was not authorized.', () => { it('Should not delete the give item category if the user was not authorized.', async () => {
const category = await create('item_category');
const res = await request()
.delete(`/api/item_categories/${category.id}`)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('unauthorized');
}); });
it('Should not delete if the item category was not found.', async () => { it('Should not delete if the item category was not found.', async () => {
const res = await request().delete('/api/item_categories/10'); const res = await request()
.delete('/api/item_categories/10')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(404); expect(res.status).equals(404);
}); });
it('Should response success after delete the given item category.', async () => { it('Should response success after delete the given item category.', async () => {
const category = await create('item_category'); const category = await create('item_category');
const res = await request()
const res = await request().delete(`/api/item_categories/${category.id}`); .delete(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(200); expect(res.status).equals(200);
}); });
it('Should delete the give item category from the storage.', async () => { it('Should delete the give item category from the storage.', async () => {
const category = await create('item_category'); const category = await create('item_category');
await request().delete(`/api/item_categories/${category.id}`); const res = await request()
.delete(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.send();
const categories = await knex('items_categories').where('id', category.id); const categories = await knex('items_categories').where('id', category.id);
@@ -143,6 +209,28 @@ describe('routes: /item_categories/', () => {
}); });
}); });
describe('GET: `/item_categories`', () => {
it('Should retrieve list of item categories.', async () => {
const category1 = await create('item_category');
const category2 = await create('item_category', { parent_category_id: category1.id });
const res = await request()
.get('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.body.categories).to.be.a('array');
expect(res.body.categories.length).equals(2);
expect(res.body.categories[0].id).to.be.a('number');
expect(res.body.categories[0].name).to.be.a('string');
expect(res.body.categories[0].parent_category_id).to.be.a('null');
expect(res.body.categories[0].description).to.be.a('string');
expect(res.body.categories[1].parent_category_id).to.be.a('number');
});
});
describe('GET `/items_category/{id}', () => { describe('GET `/items_category/{id}', () => {
it('Should response not found with incorrect item category ID.', () => { it('Should response not found with incorrect item category ID.', () => {