diff --git a/src/framework/types.ts b/src/framework/types.ts index 7614350e..74e1cec9 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -2,6 +2,22 @@ import ajv = require('ajv'); import { Request, Response, NextFunction } from 'express'; export { OpenAPIFrameworkArgs }; +export type BodySchema = + | OpenAPIV3.ReferenceObject + | OpenAPIV3.SchemaObject + | {}; + +export interface ParametersSchema { + query: object; + headers: object; + params: object; + cookies: object; +} + +export interface ValidationSchema extends ParametersSchema { + body: BodySchema; +} + export interface OpenAPIFrameworkInit { apiDoc: OpenAPIV3.Document; basePaths: string[]; @@ -41,7 +57,7 @@ export interface OpenApiValidatorOpts { unknownFormats?: true | string[] | 'ignore'; multerOpts?: {}; $refParser?: { - mode: 'bundle' | 'dereference', + mode: 'bundle' | 'dereference'; }; } @@ -363,7 +379,7 @@ interface OpenAPIFrameworkArgs { apiDoc: OpenAPIV3.Document | string; validateApiDoc?: boolean; $refParser?: { - mode: 'bundle' | 'dereference', + mode: 'bundle' | 'dereference'; }; } diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 0899104d..5ee219ba 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -9,6 +9,7 @@ import { import ono from 'ono'; import { NextFunction, RequestHandler, Response } from 'express'; import { + ValidationSchema, OpenAPIV3, OpenApiRequest, RequestValidatorOptions, @@ -82,18 +83,18 @@ export class RequestValidator { ): RequestHandler { const apiDoc = this.apiDoc; const schemaParser = new ParametersSchemaParser(apiDoc); - const parametersSchema = schemaParser.parse(path, reqSchema.parameters); - const securityQueryParam = Security.queryParam(apiDoc, reqSchema); const bodySchemaParser = new BodySchemaParser(this.ajv, apiDoc); - - // TODO bodyParser.parse should return OpenAPIV3.SchemaObject instead of BodySchema + const parameters = schemaParser.parse(path, reqSchema.parameters); + const securityQueryParam = Security.queryParam(apiDoc, reqSchema); const body = bodySchemaParser.parse(path, reqSchema, contentType); + + const properties: ValidationSchema = { ...parameters, body: body }; const required = (body).required ? ['body'] : []; // $schema: "http://json-schema.org/draft-04/schema#", const schema = { required: ['query', 'headers', 'params'].concat(required), - properties: { ...parametersSchema, body: body }, + properties, }; const validator = this.ajv.compile(schema); @@ -106,8 +107,7 @@ export class RequestValidator { req.params = openapi.pathParams ?? req.params; } - const parameters = reqSchema.parameters; - const mutator = new RequestParameterMutator(apiDoc, path, parameters); + const mutator = new RequestParameterMutator(apiDoc, path, properties); mutator.modifyRequest(req); diff --git a/src/middlewares/parsers/body.parse.ts b/src/middlewares/parsers/body.parse.ts index 0f78861f..15aa3616 100644 --- a/src/middlewares/parsers/body.parse.ts +++ b/src/middlewares/parsers/body.parse.ts @@ -1,12 +1,7 @@ import { Ajv } from 'ajv'; import { ContentType, validationError } from '../util'; -import { OpenAPIV3 } from '../../framework/types'; - -export type BodySchema = - | OpenAPIV3.ReferenceObject - | OpenAPIV3.SchemaObject - | {}; +import { OpenAPIV3, BodySchema } from '../../framework/types'; export class BodySchemaParser { private _apiDoc: OpenAPIV3.Document; diff --git a/src/middlewares/parsers/req.parameter.mutator.ts b/src/middlewares/parsers/req.parameter.mutator.ts index dcac07b1..b4cac883 100644 --- a/src/middlewares/parsers/req.parameter.mutator.ts +++ b/src/middlewares/parsers/req.parameter.mutator.ts @@ -1,5 +1,10 @@ import { Request } from 'express'; -import { OpenAPIV3 } from '../../framework/types'; +import { + OpenAPIV3, + OpenApiRequest, + OpenApiRequestMetadata, + ValidationSchema, +} from '../../framework/types'; import { validationError } from '../util'; import { dereferenceParameter, normalizeParameter } from './util'; import * as mediaTypeParser from 'media-typer'; @@ -31,17 +36,17 @@ type Parameter = ReferenceObject | ParameterObject; */ export class RequestParameterMutator { private _apiDocs: OpenAPIV3.Document; - private parameters: Parameter[]; private path: string; + private parsedSchema: ValidationSchema; constructor( apiDocs: OpenAPIV3.Document, path: string, - parameters: Parameter[] = [], + parsedSchema: ValidationSchema, ) { this._apiDocs = apiDocs; this.path = path; - this.parameters = parameters; + this.parsedSchema = parsedSchema; } /** @@ -49,8 +54,9 @@ export class RequestParameterMutator { * req values may be parsed/mutated as a JSON object, JSON Exploded Object, JSON Array, or JSON Exploded Array * @param req */ - public modifyRequest(req: Request): void { - this.parameters.forEach(p => { + public modifyRequest(req: OpenApiRequest): void { + const { parameters } = (req.openapi).schema; + parameters.forEach(p => { const parameter = dereferenceParameter(this._apiDocs, p); const { name, schema } = normalizeParameter(parameter); const { type } = schema; @@ -195,13 +201,12 @@ export class RequestParameterMutator { req[field][name] = {}; properties.forEach(property => { if (req[field][property]) { - // const type = schema.properties[field].properties[name] - // .properties?.[property]?.type; - // const value = req[field][property]; - // const coercedValue = - // type === 'array' && !Array.isArray(value) ? [value] : value; - // req[field][name][property] = coercedValue; - req[field][name][property] = req[field][property]; + const schema = this.parsedSchema[field]; + const type = schema.properties[name].properties?.[property]?.type; + const value = req[field][property]; + const coercedValue = + type === 'array' && !Array.isArray(value) ? [value] : value; + req[field][name][property] = coercedValue; delete req[field][property]; } }); diff --git a/src/middlewares/parsers/schema.parse.ts b/src/middlewares/parsers/schema.parse.ts index 8b0ef18d..f006a7c9 100644 --- a/src/middlewares/parsers/schema.parse.ts +++ b/src/middlewares/parsers/schema.parse.ts @@ -1,4 +1,4 @@ -import { OpenAPIV3 } from '../../framework/types'; +import { OpenAPIV3, ParametersSchema } from '../../framework/types'; import { validationError } from '../util'; import { dereferenceParameter, normalizeParameter } from './util'; @@ -11,13 +11,6 @@ const PARAM_TYPE = { type Parameter = OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject; -export interface ParametersSchema { - query: object; - headers: object; - params: object; - cookies: object; -} - /** * A class top arse incoing parameters and populate a list of request fields e.g. id and field types e.g. query * whose value must later be parsed as a JSON object, JSON Exploded Object, JSON Array, or JSON Exploded Array