-
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.
Issue-30: added json-schema implementation to validate response confi…
…gs (#45) * Issue-30: added json-schema implementation to validate response configs * Issue-30: improve logging and coverage * Dependency updates - Bumped supertest to version 6 - Bumped yargs to version 16 - Upgraded patch/minor versions * Issue-30: increased project version for next release
- Loading branch information
Showing
6 changed files
with
589 additions
and
252 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
var Ajv = require('ajv'); | ||
|
||
const SUPPORTED_METHODS = ['get', 'post', 'put', 'delete', 'patch']; | ||
|
||
const responseSchema = { | ||
'$id': 'Response.json', | ||
'resposeDef': { | ||
'type': 'object', | ||
'properties': { | ||
'status': { | ||
'type': 'integer' | ||
}, | ||
'headers': { | ||
'type': 'object', | ||
'propertyNames': { | ||
'pattern': '^[\\w-]+$' | ||
}, | ||
'patternProperties': { | ||
'[\\w-]+': { 'type': 'string' } | ||
} | ||
}, | ||
'data': { | ||
'type': 'object' | ||
} | ||
}, | ||
'additionalProperties': false | ||
} | ||
}; | ||
|
||
const schema = { | ||
'$id': 'ResponseDefs.json', | ||
'oneOf': [ | ||
{ | ||
'$ref': 'Response.json#/resposeDef' | ||
}, | ||
{ | ||
'type': 'array', | ||
'items': { | ||
'$ref': 'Response.json#/resposeDef' | ||
} | ||
} | ||
] | ||
}; | ||
|
||
module.exports = (responseConfig, resourcePath, method) => { | ||
if (!SUPPORTED_METHODS.includes(method)) { | ||
throw new Error(`Method ${method} is not supported.`); | ||
} | ||
var ajv = new Ajv({ schemas: [schema, responseSchema] }); | ||
var validate = ajv.getSchema('ResponseDefs.json'); | ||
const valid = validate(responseConfig); | ||
if (!valid) { | ||
// usually only the first error is interesting, further ones are just the result of parser panicking and | ||
// failing the oneOf check | ||
const prop = validate.errors[0].propertyName; | ||
const path = validate.errors[0].dataPath; | ||
const msg = validate.errors[0].message; | ||
throw new Error(`Validation error: ${prop ? `property [${prop}] at path ` : ''}'${path}' ${msg}.`); | ||
} | ||
}; |
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
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,82 @@ | ||
const validateResponseConfig = require('../lib/configValidator'); | ||
|
||
describe('response config validation', () => { | ||
describe('validate HTTP methods', () => { | ||
const validResponseObject = { | ||
status: 201, | ||
headers: { 'some-header': 'foo' }, | ||
data: { field1: 1, field2: 'value', field3: false } | ||
}; | ||
|
||
it.each(['get', 'post', 'put', 'delete'])('valid method [%s]', (supportedMethod) => { | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', supportedMethod)).not.toThrow(); | ||
}); | ||
|
||
it.each(['connect', 'foo'])('invalid method [%s] results in error thrown', (supportedMethod) => { | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', supportedMethod)).toThrow(Error); | ||
}); | ||
}); | ||
|
||
describe('validate response config', () => { | ||
it('successfully validates empty response config', () => { | ||
expect(() => validateResponseConfig({}, '/foo', 'get')).not.toThrow(); | ||
}); | ||
|
||
it('successfully validates response config with only status', () => { | ||
const validResponseObject = { | ||
status: 201 | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')).not.toThrow(); | ||
}); | ||
|
||
it('successfully validates response config with only headers', () => { | ||
const validResponseObject = { | ||
headers: { 'some-header': 'foo' } | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')).not.toThrow(); | ||
}); | ||
|
||
it('successfully validates response config with only response body', () => { | ||
const validResponseObject = { | ||
data: { field1: 1, field2: 'value', field3: false } | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')).not.toThrow(); | ||
}); | ||
|
||
it('successfully validates complex response body config', () => { | ||
const validResponseObject = { | ||
status: 201, | ||
headers: { 'some-header': 'foo', 'some-other-header': 'bar' }, | ||
data: { field1: 1, field2: 'value', field3: false } | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')).not.toThrow(); | ||
}); | ||
|
||
it('throws error for invalid status config', () => { | ||
const validResponseObject = { | ||
status: '400?', | ||
data: { '3field1': 1, field2: 'value', field3: false } | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')) | ||
.toThrow('Validation error: \'.status\' should be integer'); | ||
}); | ||
|
||
it('throws error for invalid headers config', () => { | ||
const validResponseObject = { | ||
status: 201, | ||
headers: { 'some-header/3': 1, 'some-other-header': 'bar' }, | ||
data: { '3field1': 1, field2: 'value', field3: false } | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')) | ||
.toThrow('Validation error: property [some-header/3] at path \'.headers\' should match pattern "^[\\w-]+$"'); | ||
}); | ||
|
||
it('throws error for invalid response body config', () => { | ||
const validResponseObject = { | ||
data: false | ||
}; | ||
expect(() => validateResponseConfig(validResponseObject, '/foo', 'get')) | ||
.toThrow('Validation error: \'.data\' should be object'); | ||
}); | ||
}); | ||
}); |
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.