Skip to content

Commit

Permalink
Merge branch 'develop' into feature/SIAP-121-seperate-login
Browse files Browse the repository at this point in the history
  • Loading branch information
horstenwillem committed Apr 24, 2018
2 parents 6f8e62b + c4aea98 commit 3c84b47
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 26 deletions.
2 changes: 1 addition & 1 deletion db/migrations/20180326113917_codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports.up = async (knex) => {

// Nullable
table.text('description').nullable();
table.boolean('deprecated').nullable();
table.boolean('isActive').defaultTo(true);

// Tracking
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now());
Expand Down
31 changes: 31 additions & 0 deletions docs/v1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,37 @@ paths:
tags:
- "metaOptions"
summary: "Returns all codes for a specific code type"
description: |-
permission | scope | effect
- | - | -
`USER` | `gobal` | **Required** for this endpoint
security:
- Bearer: []
parameters:
- $ref: "#/parameters/SortOrder"
- $ref: "#/parameters/SortField"
- $ref: "#/parameters/Offset"
- $ref: "#/parameters/Limit"
- $ref: "#/parameters/Search"
- in: path
type: "string"
name: codeType
description: The name of the code type
required: true
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/CodesListResponse"
400:
description: "401 Unauthorized"
schema:
$ref: "#/definitions/ErrorResponse"
/meta/codes/{codeType}/all:
get:
tags:
- "metaOptions"
summary: "Returns all codes (including inactive codes) for a specific code type"
description: |-
permission | scope | effect
- | - | -
Expand Down
10 changes: 8 additions & 2 deletions src/controllers/meta.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import { Request, Response } from 'express';
import { responder } from '../lib/responder';
import { codeSerializer } from '../serializers/meta.serializer';
import * as metaService from '../services/meta.service';
import { AuthRequest } from '../models/request.model';
import { hasRole } from '../lib/utils';
import { roles } from '../config/roles.config';



/**
* Return all codes for a specific code type
*/
export async function findAllCodes(req: Request, res: Response): Promise<void> {
export async function findAllCodes(req: AuthRequest, res: Response): Promise<void> {
const codeType = req.params.codeType;
const { data, totalCount } = await metaService.findAllCodes(codeType, req.query);
// if user is not an ADMIN, hide inactive codes
const filters = Object.assign({}, req.query, { hideInactive: !hasRole(req.session.user, roles.ADMIN) });

const { data, totalCount } = await metaService.findAllCodes(codeType, filters);
responder.success(res, {
totalCount,
status: httpStatus.OK,
Expand Down
7 changes: 4 additions & 3 deletions src/models/code.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,27 @@ export interface Code {
code: string;
name: string;
description?: string;
deprecated?: boolean;
isActive?: boolean;
}

export interface CodeCreate {
code: string;
name: string;
description?: string;
isActive?: boolean;
}


export interface CodeUpdate {
code: string;
name: string;
description: string;
deprecated: boolean;
isActive: boolean;
}

export interface PartialCodeUpdate {
code?: string;
name?: string;
description?: string;
deprecated?: boolean;
isActive?: boolean;
}
1 change: 1 addition & 0 deletions src/models/filters.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface FilterOptions {
search?: string;
sortField?: string;
sortOrder?: 'asc' | 'desc';
hideInactive?: boolean;
}

export interface Filters extends PaginationOptions, FilterOptions { }
5 changes: 4 additions & 1 deletion src/repositories/meta.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CodeType, CodeTypeCreate } from '../models/code-type.model';
import { applyPagination, applySearch, applySorting } from '../lib/filter';
import { Filters } from '../models/filters.model';

const defaultCodeReturnValues = ['id', 'code', 'name', 'description', 'codeTypeId', 'deprecated', 'createdAt', 'updatedAt'];
const defaultCodeReturnValues = ['id', 'code', 'name', 'description', 'codeTypeId', 'isActive', 'createdAt', 'updatedAt'];
const defaultCodeTypeReturnValues = ['id', 'code', 'name', 'description', 'createdAt', 'updatedAt'];

/**
Expand Down Expand Up @@ -87,6 +87,9 @@ export async function findAllCodes(codeTypeId: string, options: Filters): Promis
.from(tableNames.CODES)
.where('codeTypeId', codeTypeId);

if (allOptions.hideInactive) {
query.where('isActive', true);
}
applyPagination(query, allOptions);
applySearch(query, allOptions, ['id', 'code', 'name']);
applySorting(query, allOptions, ['code', 'name']);
Expand Down
4 changes: 4 additions & 0 deletions src/routes/v1/meta.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import * as controller from '../../controllers/meta.controller';
const defaultOptions = { allowUnknownQuery: false };
export const routes: Router = Router({ mergeParams: true })
.get('/codes/:codeType', (req, res, next) =>
hasPermission(req, res, next, roles.USER),
validateSchema(metaSchema.findAllCodes, defaultOptions),
handleAsyncFn(controller.findAllCodes))
.get('/codes/:codeType/all', (req, res, next) =>
hasPermission(req, res, next, roles.ADMIN),
validateSchema(metaSchema.findAllCodes, defaultOptions),
handleAsyncFn(controller.findAllCodes))
Expand Down
1 change: 1 addition & 0 deletions src/schemes/meta.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const metaSchema = {
sortOrder: Joi.string().valid('asc', 'desc'),
sortField: Joi.string(),
search: Joi.string(),
isActive: Joi.boolean(),
},
},
createCode: {
Expand Down
2 changes: 1 addition & 1 deletion src/serializers/meta.serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const codeSerializer = new Serializer('codes', {
'code',
'name',
'description',
'deprecated',
'isActive',
'createdAt',
'updatedAt',
],
Expand Down
2 changes: 1 addition & 1 deletion src/services/meta.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function createCode(codeType: string, values: CodeCreate): Promise<
*/
export async function deprecateCode(codeId: string) {
try {
const result = await metaRepository.updateCode(codeId, { deprecated: true });
const result = await metaRepository.updateCode(codeId, { isActive: true });
if (!result) throw new NotFoundError();
return result;
} catch (error) {
Expand Down
4 changes: 1 addition & 3 deletions tests/_helpers/payload-schemes/meta.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,16 @@ export const codeTypesSchema = Joi.object().keys({
data: Joi.array().items(codeTypeSchema),
});


export const codeSchema = Joi.object({
id: Joi.string().guid().required(),
code: Joi.string().required(),
name: Joi.string().required(),
description: Joi.string().allow(null),
deprecated: Joi.boolean().allow(null),
isActive: Joi.boolean().allow(null),
createdAt: Joi.date().iso().raw(),
updatedAt: Joi.date().iso().raw(),
});


export const codesSchema = Joi.object().keys({
meta: Joi.object().keys({
type: Joi.string().required().only('codes'),
Expand Down
65 changes: 51 additions & 14 deletions tests/integration/meta.route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('/meta', () => {

beforeAll(async () => {
codeType = await createCodeType({ code: 'LAN', name: 'Language' });
const code1 = await createCode(codeType.id, { name: 'English', code: 'EN' });
const code1 = await createCode(codeType.id, { name: 'English', code: 'EN', isActive: false });
const code2 = await createCode(codeType.id, { name: 'Nederlands', code: 'NL' });
const code3 = await createCode(codeType.id, { name: 'French', code: 'FR' });
const code4 = await createCode(codeType.id, { name: 'Weutelen', code: 'WEUTELS' });
Expand All @@ -51,16 +51,16 @@ describe('/meta', () => {
await clearCodeTypesData();
});

it('Should return all language codes with default pagination', async () => {
it('Should return language codes where isActive=true with default pagination', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/${codeType.code.toLowerCase()}`)
.set('Authorization', `Bearer ${adminToken}`);
.set('Authorization', `Bearer ${userToken}`);

expect(status).toEqual(httpStatus.OK);
expect(body.meta).toMatchObject({
type: 'codes',
count: 4,
totalCount: 4,
count: 3,
totalCount: 3,
});

Joi.validate(body, codesSchema, (err, value) => {
Expand All @@ -72,7 +72,7 @@ describe('/meta', () => {
it('Should return all country codes with provided pagination', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/${countryCodeType.code.toLowerCase()}`)
.set('Authorization', `Bearer ${adminToken}`)
.set('Authorization', `Bearer ${userToken}`)
.query('limit=1')
.query('offset=2');

Expand All @@ -92,19 +92,20 @@ describe('/meta', () => {
it('Should return codes in ascending order for value', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/${codeType.code.toLowerCase()}`)
.set('Authorization', `Bearer ${adminToken}`)
.set('Authorization', `Bearer ${userToken}`)
.query('sortField=value')
.query('sortOrder=asc');

expect(status).toEqual(httpStatus.OK);
expect(body.data).toHaveLength(4);
expect(body.data).toHaveLength(3);
expect(body.meta).toMatchObject({
type: 'codes',
count: 4,
totalCount: 4,
count: 3,
totalCount: 3,
});

const sorted = sortBy(languageCodes, 'value');
const activeLanguageCodes = languageCodes.filter(c => c.isActive);
const sorted = sortBy(activeLanguageCodes, 'value');
body.data.forEach((code, index) => {
expect(code.code).toEqual(sorted[index].code);
});
Expand All @@ -113,7 +114,7 @@ describe('/meta', () => {
it('Should return all codes matching `English` in value', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/${codeType.code.toLowerCase()}`)
.set('Authorization', `Bearer ${adminToken}`)
.set('Authorization', `Bearer ${userToken}`)
.query('search=English');

expect(status).toEqual(httpStatus.OK);
Expand All @@ -131,14 +132,50 @@ describe('/meta', () => {
it('Should throw an error when code type is not found', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/unknownType`)
.set('Authorization', `Bearer ${adminToken}`);
.set('Authorization', `Bearer ${userToken}`);

expect(status).toEqual(httpStatus.NOT_FOUND);
});
});

describe('GET /codes/:codeType/all', () => {
const prefix = `/api/${process.env.API_VERSION}`;
let codeType;
let languageCodes;

beforeAll(async () => {
codeType = await createCodeType({ code: 'LAN', name: 'Language' });
const code1 = await createCode(codeType.id, { name: 'English', code: 'EN', isActive: true });
const code2 = await createCode(codeType.id, { name: 'Nederlands', code: 'NL' });
const code3 = await createCode(codeType.id, { name: 'French', code: 'FR' });
const code4 = await createCode(codeType.id, { name: 'Weutelen', code: 'WEUTELS' });
languageCodes = [code1, code2, code3, code4];
});

afterAll(async () => {
await clearCodeTypesData();
});

it('Should return all language codes with default pagination', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/${codeType.code.toLowerCase()}/all`)
.set('Authorization', `Bearer ${adminToken}`);

expect(status).toEqual(httpStatus.OK);
expect(body.meta).toMatchObject({
type: 'codes',
count: 4,
totalCount: 4,
});

Joi.validate(body, codesSchema, (err, value) => {
if (err) throw err;
if (!value) throw new Error('no value to check schema');
});
});
it('Should throw an error without admin permission', async () => {
const { body, status } = await request(app)
.get(`${prefix}/meta/codes/${codeType.code.toLowerCase()}`)
.get(`${prefix}/meta/codes/${codeType.code.toLowerCase()}/all`)
.set('Authorization', `Bearer ${userToken}`);

expect(status).toEqual(httpStatus.UNAUTHORIZED);
Expand Down

0 comments on commit 3c84b47

Please sign in to comment.