2022-09-13 00:54:06 +02:00
|
|
|
import type { Context } from '../../context.ts'
|
2022-09-26 14:18:48 +02:00
|
|
|
import type { Environment, Next, ValidatedData } from '../../hono.ts'
|
2022-09-21 02:40:17 +02:00
|
|
|
import { VBase, Validator } from './validator.ts'
|
2022-09-30 14:56:39 +02:00
|
|
|
import type {
|
|
|
|
VString,
|
|
|
|
VNumber,
|
|
|
|
VBoolean,
|
|
|
|
VObject,
|
|
|
|
VNumberArray,
|
|
|
|
VStringArray,
|
|
|
|
VBooleanArray,
|
|
|
|
ValidateResult,
|
|
|
|
} from './validator.ts'
|
2022-09-20 03:11:34 +02:00
|
|
|
|
2022-09-21 02:40:17 +02:00
|
|
|
type Schema = {
|
2022-09-30 14:56:39 +02:00
|
|
|
[key: string]:
|
|
|
|
| VString
|
|
|
|
| VNumber
|
|
|
|
| VBoolean
|
|
|
|
| VObject
|
|
|
|
| VStringArray
|
|
|
|
| VNumberArray
|
|
|
|
| VBooleanArray
|
|
|
|
| Schema
|
2022-09-21 02:40:17 +02:00
|
|
|
}
|
2022-09-26 14:18:48 +02:00
|
|
|
|
2022-09-20 03:11:34 +02:00
|
|
|
type SchemaToProp<T> = {
|
2022-09-30 14:56:39 +02:00
|
|
|
[K in keyof T]: T[K] extends VNumberArray
|
|
|
|
? number[]
|
|
|
|
: T[K] extends VBooleanArray
|
|
|
|
? boolean[]
|
|
|
|
: T[K] extends VStringArray
|
|
|
|
? string[]
|
|
|
|
: T[K] extends VNumber
|
2022-09-21 00:55:29 +02:00
|
|
|
? number
|
|
|
|
: T[K] extends VBoolean
|
|
|
|
? boolean
|
|
|
|
: T[K] extends VString
|
|
|
|
? string
|
|
|
|
: T[K] extends VObject
|
|
|
|
? object
|
2022-09-21 02:40:17 +02:00
|
|
|
: T[K] extends Schema
|
|
|
|
? SchemaToProp<T[K]>
|
2022-09-20 03:11:34 +02:00
|
|
|
: never
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type ResultSet = {
|
|
|
|
hasError: boolean
|
|
|
|
messages: string[]
|
2022-09-20 03:11:34 +02:00
|
|
|
results: ValidateResult[]
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
|
|
|
|
2022-09-26 14:18:48 +02:00
|
|
|
type Done<Env extends Partial<Environment>> = (
|
|
|
|
resultSet: ResultSet,
|
|
|
|
context: Context<string, Env>
|
|
|
|
) => Response | void
|
2022-09-13 00:54:06 +02:00
|
|
|
|
2022-09-26 14:18:48 +02:00
|
|
|
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>
|
2022-09-21 02:40:17 +02:00
|
|
|
|
2022-09-26 14:18:48 +02:00
|
|
|
export const validatorMiddleware = <T extends Schema, Env extends Partial<Environment>>(
|
|
|
|
validationFunction: ValidationFunction<T, Env>,
|
|
|
|
options?: { done?: Done<Env> }
|
2022-09-20 03:11:34 +02:00
|
|
|
) => {
|
|
|
|
const v = new Validator()
|
2022-09-26 14:18:48 +02:00
|
|
|
const handler: MiddlewareHandler<SchemaToProp<T>, Env> = async (c, next) => {
|
2022-09-20 03:11:34 +02:00
|
|
|
const resultSet: ResultSet = {
|
|
|
|
hasError: false,
|
|
|
|
messages: [],
|
|
|
|
results: [],
|
|
|
|
}
|
2022-09-13 00:54:06 +02:00
|
|
|
|
2022-09-21 02:40:17 +02:00
|
|
|
const validatorList = getValidatorList(validationFunction(v, c))
|
2022-09-20 03:11:34 +02:00
|
|
|
|
2022-09-21 02:40:17 +02:00
|
|
|
for (const [keys, validator] of validatorList) {
|
2022-09-20 03:11:34 +02:00
|
|
|
const result = await validator.validate(c.req)
|
|
|
|
if (result.isValid) {
|
|
|
|
// Set data on request object
|
2022-09-21 02:40:17 +02:00
|
|
|
c.req.valid(keys, result.value)
|
2022-09-20 03:11:34 +02:00
|
|
|
} else {
|
|
|
|
resultSet.hasError = true
|
|
|
|
if (result.message !== undefined) {
|
|
|
|
resultSet.messages.push(result.message)
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-20 03:11:34 +02:00
|
|
|
resultSet.results.push(result)
|
|
|
|
}
|
2022-09-13 00:54:06 +02:00
|
|
|
|
2022-09-20 03:11:34 +02:00
|
|
|
if (options && options.done) {
|
|
|
|
const res = options.done(resultSet, c)
|
|
|
|
if (res) {
|
|
|
|
return res
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
2022-09-20 03:11:34 +02:00
|
|
|
}
|
2022-09-13 00:54:06 +02:00
|
|
|
|
2022-09-20 03:11:34 +02:00
|
|
|
if (resultSet.hasError) {
|
|
|
|
return c.text(resultSet.messages.join('\n'), 400)
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
2022-09-20 03:11:34 +02:00
|
|
|
await next()
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
2022-09-20 03:11:34 +02:00
|
|
|
return handler
|
2022-09-13 00:54:06 +02:00
|
|
|
}
|
2022-09-26 14:18:48 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|