import { ValidationError , ObjectType , Type , schema , isConfigSchema } from '@osd/config-schema' ; import { Stream } from 'stream' ; import { RouteValidationError } from './validator_error' ; /**
 * Validation result factory to be used in the custom validation function to return the valid data or validation errors
 *
 * See  RouteValidationFunction .
 *
 *  public export interface RouteValidationResultFactory { ok : < T > ( value : T ) => { value : T ; } ; badRequest : ( error : Error | string , path ? : string [ ] ) => { error : RouteValidationError ; } ; } example public export type RouteValidationFunction < T > = ( data : any , validationResult : RouteValidationResultFactory ) => { value : T ; error ? : never ; } | { value ? : never ; error : RouteValidationError ; } ; /**
 * Allowed property validation options: either @osd/config-schema validations or custom validation functions
 *
 * See  RouteValidationFunction for custom validation.
 *
 *  public export type RouteValidationSpec < T > = ObjectType | Type < T > | RouteValidationFunction < T > ; type RouteValidationResultType < T extends RouteValidationSpec < any > | undefined > = NonNullable < T extends RouteValidationFunction < any > ? ReturnType < T > [ 'value' ] : T extends Type < any > ? T [ 'type' ] : undefined > ; public export interface RouteValidatorConfig < P , Q , B > { public params ? : RouteValidationSpec < P > ; public query ? : RouteValidationSpec < Q > ; public body ? : RouteValidationSpec < B > ; } public export interface RouteValidatorOptions { public unsafe ? : { params ? : boolean ; query ? : boolean ; body ? : boolean ; } ; } public export type RouteValidatorFullConfig < P , Q , B > = RouteValidatorConfig < P , Q , B > & RouteValidatorOptions ; internal export class RouteValidator < P = { } , Q = { } , B = { } > { public static from < P = { } , Q = { } , B = { } > ( opts ) { if ( opts instanceof RouteValidator ) { return opts ; } const { params , query , body , ... options } = opts ; return new RouteValidator ( { params , query , body } , options ) ; } private static ResultFactory : RouteValidationResultFactory = { ok : < T > ( value ) => ( { value } ) , badRequest : ( error , path ? ) => ( { error : new RouteValidationError ( error , path ) , } ) , } ; private constructor ( private readonly config , private readonly options = { } ) { } internal public getParams ( data , namespace ? ) { return this . validate ( this . config . params , this . options . unsafe ?. params , data , namespace ) as P ; } internal public getQuery ( data , namespace ? ) { return this . validate ( this . config . query , this . options . unsafe ?. query , data , namespace ) as Q ; } internal public getBody ( data , namespace ? ) { return this . validate ( this . config . body , this . options . unsafe ?. body , data , namespace ) as B ; } internal public hasBody ( ) { return typeof this . config . body !== 'undefined' ; } private validate < T > ( validationRule ? , unsafe ? , data ? , namespace ? ) { if ( typeof validationRule === 'undefined' ) { return { } ; } let precheckedData = this . preValidateSchema ( data ) . validate ( data , { } , namespace ) ; if ( unsafe !== true ) { precheckedData = this . safetyPrechecks ( precheckedData , namespace ) ; } const customCheckedData = this . customValidation ( validationRule , precheckedData , namespace ) ; if ( unsafe === true ) { return customCheckedData ; } return this . safetyPostchecks ( customCheckedData , namespace ) ; } private safetyPrechecks < T > ( data , namespace ? ) { return data ; } private safetyPostchecks < T > ( data , namespace ? ) { return data ; } private customValidation < T > ( validationRule , data ? , namespace ? ) { if ( isConfigSchema ( validationRule ) ) { return validationRule . validate ( data , { } , namespace ) ; } else if ( typeof validationRule === 'function' ) { return this . validateFunction ( validationRule , data , namespace ) ; } else { throw new ValidationError ( new RouteValidationError ( `The validation rule provided in the handler is not valid` ) , namespace ) ; } } private validateFunction < T > ( validateFn , data , namespace ? ) { let result ; try { result = validateFn ( data , RouteValidator . ResultFactory ) ; } catch ( err ) { result = { error : new RouteValidationError ( err ) } ; } if ( result . error ) { throw new ValidationError ( result . error , namespace ) ; } return result . value ; } private preValidateSchema ( data ) { if ( Buffer . isBuffer ( data ) ) { return schema . buffer ( ) ; } else if ( data instanceof Stream ) { return schema . stream ( ) ; } else { return schema . maybe ( schema . nullable ( schema . any ( { } ) ) ) ; } } }