Skip to content

Commit

Permalink
fix: type of validation function (#4283)
Browse files Browse the repository at this point in the history
* fix: types of validation functions in request

* docs: add errors description to validation funcs

* Update types/request.d.ts

* Apply suggestions from code review

Fix lint

* Apply suggestions from code review

* deps: upgrade @fastify/ajv-compile to v3.3.1

* test: add tests

* fix: comments

Co-authored-by: Uzlopak <aras.abbasi@googlemail.com>
  • Loading branch information
budarin and Uzlopak committed Sep 19, 2022
1 parent 9d9d62c commit b5e4157
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 9 deletions.
15 changes: 12 additions & 3 deletions docs/Reference/Request.md
Expand Up @@ -98,6 +98,9 @@ it will return a `validation` function that can be used to
validate diverse inputs. It returns `undefined` if no
serialization function was found using either of the provided inputs.

This function has property errors. Errors encountered during the last validation
are assigned to errors

```js
const validate = request
.getValidationFunction({
Expand All @@ -108,13 +111,15 @@ const validate = request
}
}
})
validate({ foo: 'bar' }) // true
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null

// or

const validate = request
.getValidationFunction('body')
validate({ foo: 0.5 }) // false
console.log(validate({ foo: 0.5 })) // false
console.log(validate.errors) // validation errors
```

See [.compilaValidationSchema(schema, [httpStatus])](#compilevalidationschema)
Expand All @@ -133,6 +138,8 @@ The optional parameter `httpPart`, if provided, is forwarded directly
the `ValidationCompiler`, so it can be used to compile the validation
function if a custom `ValidationCompiler` is provided for the route.

This function has property errors. Errors encountered during the last validation
are assigned to errors

```js
const validate = request
Expand All @@ -145,6 +152,7 @@ const validate = request
}
})
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null

// or

Expand All @@ -158,6 +166,7 @@ const validate = request
}
}, 200)
console.log(validate({ hello: 'world' })) // false
console.log(validate.errors) // validation errors
```

Note that you should be careful when using this function, as it will cache
Expand Down Expand Up @@ -247,4 +256,4 @@ request
```

See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
for more information on how to compile validation schemas.
for more information on how to compile validation schemas.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -171,7 +171,7 @@
"yup": "^0.32.11"
},
"dependencies": {
"@fastify/ajv-compiler": "^3.2.0",
"@fastify/ajv-compiler": "^3.3.1",
"@fastify/error": "^3.0.0",
"@fastify/fast-json-stringify-compiler": "^4.1.0",
"abstract-logging": "^2.0.1",
Expand Down
92 changes: 90 additions & 2 deletions test/internals/request-validate.test.js
Expand Up @@ -36,7 +36,7 @@ const requestSchema = {
}

test('#compileValidationSchema', subtest => {
subtest.plan(5)
subtest.plan(7)

subtest.test('Should return a function - Route without schema', async t => {
const fastify = Fastify()
Expand All @@ -59,6 +59,49 @@ test('#compileValidationSchema', subtest => {
})
})

subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
const fastify = Fastify()

t.plan(3)

fastify.get('/', (req, reply) => {
const validate = req.compileValidationSchema(defaultSchema)

t.ok(validate({ hello: 'world' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.equal(validate.errors, null)

reply.send({ hello: 'world' })
})

await fastify.inject({
path: '/',
method: 'GET'
})
})

subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
const fastify = Fastify()

t.plan(4)

fastify.get('/', (req, reply) => {
const validate = req.compileValidationSchema(defaultSchema)

t.notOk(validate({ world: 'foo' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.ok(Array.isArray(validate.errors))
t.ok(validate.errors.length > 0)

reply.send({ hello: 'world' })
})

await fastify.inject({
path: '/',
method: 'GET'
})
})

subtest.test(
'Should reuse the validate fn across multiple invocations - Route without schema',
async t => {
Expand Down Expand Up @@ -206,7 +249,7 @@ test('#compileValidationSchema', subtest => {
})

test('#getValidationFunction', subtest => {
subtest.plan(4)
subtest.plan(6)

subtest.test('Should return a validation function', async t => {
const fastify = Fastify()
Expand All @@ -228,6 +271,51 @@ test('#getValidationFunction', subtest => {
})
})

subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
const fastify = Fastify()

t.plan(3)

fastify.get('/', (req, reply) => {
req.compileValidationSchema(defaultSchema)
const validate = req.getValidationFunction(defaultSchema)

t.ok(validate({ hello: 'world' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.equal(validate.errors, null)

reply.send({ hello: 'world' })
})

await fastify.inject({
path: '/',
method: 'GET'
})
})

subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
const fastify = Fastify()

t.plan(4)

fastify.get('/', (req, reply) => {
req.compileValidationSchema(defaultSchema)
const validate = req.getValidationFunction(defaultSchema)

t.notOk(validate({ world: 'foo' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.ok(Array.isArray(validate.errors))
t.ok(validate.errors.length > 0)

reply.send({ hello: 'world' })
})

await fastify.inject({
path: '/',
method: 'GET'
})
})

subtest.test('Should return undefined if no schema compiled', async t => {
const fastify = Fastify()

Expand Down
12 changes: 9 additions & 3 deletions types/request.d.ts
@@ -1,3 +1,4 @@
import { ErrorObject } from '@fastify/ajv-compiler'
import { FastifyBaseLogger } from './logger'
import { ContextConfigDefault, RawServerBase, RawServerDefault, RawRequestDefaultExpression, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './utils'
import { RouteGenericInterface } from './route'
Expand All @@ -14,6 +15,11 @@ export interface RequestGenericInterface {
Headers?: RequestHeadersDefault;
}

export interface ValidationFunction {
(input: any): boolean
errors?: null | ErrorObject[];
}

/**
* FastifyRequest is an instance of the standard http or http2 request objects.
* It defaults to http.IncomingMessage, and it also extends the relative request object.
Expand Down Expand Up @@ -61,9 +67,9 @@ export interface FastifyRequest<RouteGeneric extends RouteGenericInterface = Rou
readonly is404: boolean;
readonly socket: RawRequest['socket'];

getValidationFunction(httpPart: HTTPRequestPart): (input: any) => boolean
getValidationFunction(schema: {[key: string]: any}): (input: any) => boolean
compileValidationSchema(schema: {[key: string]: any}, httpPart?: HTTPRequestPart): (input: any) => boolean
getValidationFunction(httpPart: HTTPRequestPart): ValidationFunction
getValidationFunction(schema: {[key: string]: any}): ValidationFunction
compileValidationSchema(schema: {[key: string]: any}, httpPart?: HTTPRequestPart): ValidationFunction
validateInput(input: any, schema: {[key: string]: any}, httpPart?: HTTPRequestPart): boolean
validateInput(input: any, httpPart?: HTTPRequestPart): boolean

Expand Down

0 comments on commit b5e4157

Please sign in to comment.