Skip to content

Commit

Permalink
feat: add json-schema validation option for POST endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
anoack93 committed May 27, 2021
1 parent f001f40 commit a3246d6
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 27 deletions.
1 change: 1 addition & 0 deletions examples/mock-api/v1/user.post.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"requestOptions": {},
"responseOptions": {
"delay_ms": 2000,
"statusCode": 201
Expand Down
34 changes: 34 additions & 0 deletions examples/mock-api/v2/user.post.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"requestOptions": {
"validation": {
"jsonSchema": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
},
"required": [
"id",
"firstName"
],
"additionalProperties": false
}
}
},
"responseOptions": {
"delay_ms": 2000,
"statusCode": 201
},
"response": {
"id": 20320,
"firstName": "Lord",
"lastName": "Bar"
}
}
27 changes: 8 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"url": "https://github.com/diva-e/servemocks/issues"
},
"dependencies": {
"ajv": "^6.12.6",
"chalk": "^4.1.0",
"commander": "^7.2.0",
"cors": "^2.8.5",
Expand Down
19 changes: 19 additions & 0 deletions src/serve-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const fs = require('fs')
const glob = require('glob')
const chalk = require('chalk')
const path = require('path')
const Ajv = require('ajv')
const ajv = new Ajv()

const mockFileTypes = require('./mock-file-types')

Expand Down Expand Up @@ -104,11 +106,28 @@ function serveMocks (mockDirectory, port, hostname) {
app.post(apiPath, function (req, res) {
const endpointParams = JSON.parse(fs.readFileSync(fileName, 'utf8'))
const responseOptions = endpointParams.responseOptions ? endpointParams.responseOptions : {}
const requestOptions = endpointParams.requestOptions ? endpointParams.requestOptions : {}
const requestValidation = requestOptions.validation ? requestOptions.validation : {}
const responseDelay = responseOptions.delay_ms ? responseOptions.delay_ms : 2000
const statusCode = responseOptions.statusCode ? responseOptions.statusCode : 200
const response = endpointParams.response ? endpointParams.response : { success: true }
console.log(`receiving POST request on ${apiPath} with body:`, req.body)

setTimeout(() => {
// validate request body agains json schema if provided in requestOptions
if (requestValidation.jsonSchema) {
const isValid = ajv.compile(requestValidation.jsonSchema)
if (!isValid(req.body)) {
const errors = isValid.errors
console.info('validation of request body failed; errors:', errors)
res.status(422).send({
message: 'request body is not compliant to the expected schema',
errors
})
return
}
}

res.status(statusCode).send(response)
}, responseDelay)
})
Expand Down
48 changes: 40 additions & 8 deletions src/serve-mocks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,63 @@ describe('serve-mocks', () => {

const response = await request.get('/v1/user/1')
const expectedUser = {
'id': 1,
'firstName': 'Max',
'lastName': 'Mustermann'
id: 1,
firstName: 'Max',
lastName: 'Mustermann'
}

expect(response.status).toBe(200)
expect(response.headers['content-type']).toBe('application/json')
expect(response.body).toEqual(expectedUser)
})

it('should respond with specified data when posting user', async () => {
it('should respond with specified data when posting valid user', async () => {
expect.assertions(3) // number of expect calls in this test

const response = await request.post('/v1/user')
const response = await request.post('/v1/user').send({ foo: 'bar' })

const expectedResponse = {
id: 20320,
firstName: 'Lord',
lastName: 'Bar'
}

expect(response.status).toBe(201)
expect(response.headers['content-type']).toBe('application/json; charset=utf-8')
expect(response.body).toEqual(expectedResponse)
})

it('should respond with 201 when posting valid user', async () => {
expect.assertions(3) // number of expect calls in this test

const response = await request.post('/v2/user').send({
id: 42,
firstName: 'Valid first name',
lastName: 'Valid last name'
})

const expectedResponse = {
'id': 20320,
'firstName': 'Lord',
'lastName': 'Bar'
id: 20320,
firstName: 'Lord',
lastName: 'Bar'
}

expect(response.status).toBe(201)
expect(response.headers['content-type']).toBe('application/json; charset=utf-8')
expect(response.body).toEqual(expectedResponse)
})

it('should respond with 422 unprocessable entity when posting invalid user', async () => {
expect.assertions(1) // number of expect calls in this test

const response = await request.post('/v2/user').send({
id: 2332,
firstName: 3232 // numeric value is incorrect
})

expect(response.status).toBe(422)
})

it('should delay response to post requests for 2 seconds', async () => {
expect.assertions(3) // number of expect calls in this test

Expand Down

0 comments on commit a3246d6

Please sign in to comment.