0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 11:51:01 +01:00
hono/deno_dist/middleware/validator/middleware.ts
Derrick Farris 9a0389e723
feat(validator): add support for JSON array path validation (#563)
* refactor(JSONPath): improve typing of JSONPath

* chore(vscode-settings): add deno.enable=false

* fix(validator): add null to type Type def

* feat(JSON-Path): add support for array JSONPath-Plus syntax

* fix(validator): update isRequired to pass valid bool types

* test: update tests for isRequired validator

* feat(validator): add support for JSON array path validation

* chore(deno): denoify array support changes

* fix(validator): type check all vals in array

* chore(deno): denoify changes

* test(validator): add tests for array type checking

* fix(validator): change JSONPrimative to JSONPrimitive

* refactor(json): More compatible with https://jsonpath.com/.

* implementation of `asArray`

* fix(validator): update JSONPath implementation and add isArray check in validation

* fix(validator): fix typing errors on SchemaToProp

* Revert "fix(validator): fix typing errors on SchemaToProp"

This reverts commit b8ddef85d0.

* fix(validator): fix SchemaToProp error for VTypeArrays

* chore(deno): denoify

Co-authored-by: Taku Amano <taku@taaas.jp>
Co-authored-by: Yusuke Wada <yusuke@kamawada.com>
2022-09-30 21:56:39 +09:00

125 lines
2.9 KiB
TypeScript

import type { Context } from '../../context.ts'
import type { Environment, Next, ValidatedData } from '../../hono.ts'
import { VBase, Validator } from './validator.ts'
import type {
VString,
VNumber,
VBoolean,
VObject,
VNumberArray,
VStringArray,
VBooleanArray,
ValidateResult,
} from './validator.ts'
type Schema = {
[key: string]:
| VString
| VNumber
| VBoolean
| VObject
| VStringArray
| VNumberArray
| VBooleanArray
| Schema
}
type SchemaToProp<T> = {
[K in keyof T]: T[K] extends VNumberArray
? number[]
: T[K] extends VBooleanArray
? boolean[]
: T[K] extends VStringArray
? string[]
: T[K] extends VNumber
? number
: T[K] extends VBoolean
? boolean
: T[K] extends VString
? string
: T[K] extends VObject
? object
: T[K] extends Schema
? SchemaToProp<T[K]>
: never
}
type ResultSet = {
hasError: boolean
messages: string[]
results: ValidateResult[]
}
type Done<Env extends Partial<Environment>> = (
resultSet: ResultSet,
context: Context<string, Env>
) => Response | void
type ValidationFunction<T, Env extends Partial<Environment>> = (
v: Validator,
c: Context<string, Env>
) => T
type MiddlewareHandler<
Data extends ValidatedData = ValidatedData,
Env extends Partial<Environment> = Environment
> = (c: Context<string, Env, Data>, next: Next) => Promise<void> | Promise<Response | undefined>
export const validatorMiddleware = <T extends Schema, Env extends Partial<Environment>>(
validationFunction: ValidationFunction<T, Env>,
options?: { done?: Done<Env> }
) => {
const v = new Validator()
const handler: MiddlewareHandler<SchemaToProp<T>, Env> = async (c, next) => {
const resultSet: ResultSet = {
hasError: false,
messages: [],
results: [],
}
const validatorList = getValidatorList(validationFunction(v, c))
for (const [keys, validator] of validatorList) {
const result = await validator.validate(c.req)
if (result.isValid) {
// Set data on request object
c.req.valid(keys, result.value)
} else {
resultSet.hasError = true
if (result.message !== undefined) {
resultSet.messages.push(result.message)
}
}
resultSet.results.push(result)
}
if (options && options.done) {
const res = options.done(resultSet, c)
if (res) {
return res
}
}
if (resultSet.hasError) {
return c.text(resultSet.messages.join('\n'), 400)
}
await next()
}
return handler
}
function getValidatorList<T extends Schema>(schema: T): [string[], VBase][] {
const map: [string[], VBase][] = []
for (const [key, value] of Object.entries(schema)) {
if (value instanceof VBase) {
map.push([[key], value])
} else {
const children = getValidatorList(value)
for (const [keys, validator] of children) {
map.push([[key, ...keys], validator])
}
}
}
return map
}