import {Container, Injectable, InjectParam} from '../di' import {Request} from '../http/lifecycle/Request' import {Valid, ValidationRules} from './rules/types' import {Validator} from './Validator' import {AppClass} from '../lifecycle/AppClass' import {DataContainer} from '../http/lifecycle/Request' /** * Base class for defining reusable validators for request routes. * If instantiated with a container, it must be a request-level container, * but the type interface allows any data-container to be used when creating * manually. * * You should mark implementations of this class as singleton to avoid * re-validating the input data every time it is accessed. * * @example * ```ts * // Instantiate with the request: * const data = request.make(MyFormRequest) * * // Instantiate with some container: * const data = new MyFormRequest(someDataContainer) * ``` */ @Injectable() export abstract class FormRequest extends AppClass { /** The cached validation result. */ protected cachedResult?: Valid constructor( @InjectParam(Request) protected readonly data: DataContainer, ) { super() } protected container(): Container { return (this.data as unknown) as Container } /** * The validation rules that should be applied to the request to guarantee * that it contains the given data type. * @protected */ protected abstract getRules(): ValidationRules | Promise /** * Validate and get the request input. Throws a validation error on fail. * Internally, caches the result after the first validation. So, singleton * validators will avoid re-processing their rules every time. */ public async get(): Promise> { if ( !this.cachedResult ) { const validator = > this.make(Validator, await this.getRules()) this.cachedResult = await validator.validate(this.data.input()) } return this.cachedResult } }