Skip to content

Commit

Permalink
fix: Emit a warning when validating undefined schemas (#4647)
Browse files Browse the repository at this point in the history
  • Loading branch information
franher committed Apr 7, 2023
1 parent 30eba8b commit d0a1e8c
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 39 deletions.
10 changes: 6 additions & 4 deletions lib/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const {
FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX
} = require('./errors')

const warning = require('./warnings')

function compileSchemasForSerialization (context, compile) {
if (!context.schema || !context.schema.response) {
return
Expand Down Expand Up @@ -81,25 +83,25 @@ function compileSchemasForValidation (context, compile, isCustom) {
}
context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
} else if (Object.hasOwnProperty.call(schema, 'headers')) {
throw new Error('headers schema is undefined')
warning.emit('FSTWRN001', 'headers', method, url)
}

if (schema.body) {
context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
} else if (Object.hasOwnProperty.call(schema, 'body')) {
throw new Error('body schema is undefined')
warning.emit('FSTWRN001', 'body', method, url)
}

if (schema.querystring) {
context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
} else if (Object.hasOwnProperty.call(schema, 'querystring')) {
throw new Error('querystring schema is undefined')
warning.emit('FSTWRN001', 'querystring', method, url)
}

if (schema.params) {
context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
} else if (Object.hasOwnProperty.call(schema, 'params')) {
throw new Error('params schema is undefined')
warning.emit('FSTWRN001', 'params', method, url)
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/warnings.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ warning.create('FastifyDeprecation', 'FSTDEP013', 'Direct return of "trailers" f

warning.create('FastifyDeprecation', 'FSTDEP014', 'You are trying to set/access the default route. This property is deprecated. Please, use setNotFoundHandler if you want to custom a 404 handler or the wildcard (*) to match all routes.')

warning.create('FastifyWarning', 'FSTWRN001', 'The %s schema for %s: %s is missing. This may indicate the schema is not well specified.', { unlimited: true })

module.exports = warning
163 changes: 128 additions & 35 deletions test/schema-feature.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fp = require('fastify-plugin')
const deepClone = require('rfdc')({ circles: true, proto: false })
const Ajv = require('ajv')
const { kSchemaController } = require('../lib/symbols.js')
const warning = require('../lib/warnings')

const echoParams = (req, reply) => { reply.send(req.params) }
const echoBody = (req, reply) => { reply.send(req.body) }
Expand Down Expand Up @@ -253,88 +254,180 @@ test('Should not change the input schemas', t => {
})
})

test('Should throw if the schema body is undefined', t => {
t.plan(2)
test('Should emit warning if the schema headers is undefined', t => {
t.plan(4)
const fastify = Fastify()

fastify.get('/:id', {
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'FastifyWarning')
t.equal(warning.code, 'FSTWRN001')
}

t.teardown(() => {
process.removeListener('warning', onWarning)
warning.emitted.set('FSTWRN001', false)
})

fastify.post('/:id', {
handler: echoParams,
schema: {
body: undefined
headers: undefined
}
})

fastify.ready(err => {
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error body schema is undefined')
fastify.inject({
method: 'POST',
url: '/123'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
})
})

test('Should throw if the schema headers is undefined', t => {
t.plan(2)
test('Should emit warning if the schema body is undefined', t => {
t.plan(4)
const fastify = Fastify()

fastify.get('/:id', {
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'FastifyWarning')
t.equal(warning.code, 'FSTWRN001')
}

t.teardown(() => {
process.removeListener('warning', onWarning)
warning.emitted.set('FSTWRN001', false)
})

fastify.post('/:id', {
handler: echoParams,
schema: {
headers: undefined
body: undefined
}
})

fastify.ready(err => {
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error headers schema is undefined')
fastify.inject({
method: 'POST',
url: '/123'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
})
})

test('Should throw if the schema params is undefined', t => {
t.plan(2)
test('Should emit warning if the schema query is undefined', t => {
t.plan(4)
const fastify = Fastify()

fastify.get('/:id', {
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'FastifyWarning')
t.equal(warning.code, 'FSTWRN001')
}

t.teardown(() => {
process.removeListener('warning', onWarning)
warning.emitted.set('FSTWRN001', false)
})

fastify.post('/:id', {
handler: echoParams,
schema: {
params: undefined
querystring: undefined
}
})

fastify.ready(err => {
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error params schema is undefined')
fastify.inject({
method: 'POST',
url: '/123'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
})
})

test('Should throw if the schema query is undefined', t => {
t.plan(2)
test('Should emit warning if the schema params is undefined', t => {
t.plan(4)
const fastify = Fastify()

fastify.get('/:id', {
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'FastifyWarning')
t.equal(warning.code, 'FSTWRN001')
}

t.teardown(() => {
process.removeListener('warning', onWarning)
warning.emitted.set('FSTWRN001', false)
})

fastify.post('/:id', {
handler: echoParams,
schema: {
querystring: undefined
params: undefined
}
})

fastify.ready(err => {
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error querystring schema is undefined')
fastify.inject({
method: 'POST',
url: '/123'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
})
})

test('Should throw if the schema query is undefined', t => {
t.plan(2)
test('Should emit a warning for every route with undefined schema', t => {
t.plan(16)
const fastify = Fastify()

fastify.get('/:id', {
let runs = 0
const expectedWarningEmitted = [0, 1, 2, 3]
// It emits 4 warnings:
// - 2 - GET and HEAD for /undefinedParams/:id
// - 2 - GET and HEAD for /undefinedBody/:id
// => 3 x 4 assertions = 12 assertions
function onWarning (warning) {
t.equal(warning.name, 'FastifyWarning')
t.equal(warning.code, 'FSTWRN001')
t.equal(runs++, expectedWarningEmitted.shift())
}

process.on('warning', onWarning)
t.teardown(() => {
process.removeListener('warning', onWarning)
warning.emitted.set('FSTWRN001', false)
})

fastify.get('/undefinedParams/:id', {
handler: echoParams,
schema: {
querystring: undefined
params: undefined
}
})

fastify.ready(err => {
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error querystring schema is undefined')
fastify.get('/undefinedBody/:id', {
handler: echoParams,
schema: {
body: undefined
}
})

fastify.inject({
method: 'GET',
url: '/undefinedParams/123'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
})

fastify.inject({
method: 'GET',
url: '/undefinedBody/123'
}, (error, res) => {
t.error(error)
t.equal(res.statusCode, 200)
})
})

Expand Down

0 comments on commit d0a1e8c

Please sign in to comment.