Skip to content

Commit

Permalink
fix: request schema preprocessor and multipart middleware custom form…
Browse files Browse the repository at this point in the history
…ats (#452)

* fix: request schema preprocessor unknown format error

* fix: multipart middleware ajv options formats and types

* common ajv options handler

Co-authored-by: Carmine DiMascio <cdimascio@gmail.com>
  • Loading branch information
Guillaume and cdimascio committed Nov 9, 2020
1 parent bc30bb6 commit cf37281
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 62 deletions.
5 changes: 3 additions & 2 deletions src/framework/types.ts
Expand Up @@ -31,8 +31,9 @@ export type SecurityHandlers = {
) => boolean | Promise<boolean>;
};

export interface MultipartOpts extends ajv.Options {
multerOpts: any;
export interface MultipartOpts {
multerOpts: boolean | multer.Options;
ajvOpts: ajv.Options;
}

export interface RequestValidatorOptions
Expand Down
4 changes: 1 addition & 3 deletions src/middlewares/openapi.multipart.ts
Expand Up @@ -19,9 +19,7 @@ export function multipart(
options: MultipartOpts,
): OpenApiRequestHandler {
const mult = multer(options.multerOpts);
const Ajv = createRequestAjv(apiDoc, {
unknownFormats: options.unknownFormats,
});
const Ajv = createRequestAjv(apiDoc, { ...options.ajvOpts });
return (req, res, next) => {
// TODO check that format: binary (for upload) else do not use multer.any()
// use multer.none() if no binary parameters exist
Expand Down
127 changes: 70 additions & 57 deletions src/openapi.validator.ts
@@ -1,4 +1,5 @@
import ono from 'ono';
import ajv = require('ajv');
import * as express from 'express';
import * as _uniq from 'lodash.uniq';
import * as cloneDeep from 'lodash.clonedeep';
Expand All @@ -15,6 +16,7 @@ import {
OpenApiRequestMetadata,
ValidateSecurityOpts,
OpenAPIV3,
RequestValidatorOptions,
} from './framework/types';
import { defaultResolver } from './resolvers';
import { OperationHandlerOptions } from './framework/types';
Expand All @@ -35,6 +37,7 @@ export {

export class OpenApiValidator {
readonly options: OpenApiValidatorOpts;
readonly ajvOpts: AjvOptions;

constructor(options: OpenApiValidatorOpts) {
this.validateOptions(options);
Expand Down Expand Up @@ -82,6 +85,7 @@ export class OpenApiValidator {
}

this.options = options;
this.ajvOpts = new AjvOptions(options);
}

installMiddleware(spec: Promise<Spec>): OpenApiRequestHandler[] {
Expand All @@ -90,14 +94,10 @@ export class OpenApiValidator {
const responseApiDoc = this.options.validateResponses
? cloneDeep(spec.apiDoc)
: null;
new RequestSchemaPreprocessor(spec.apiDoc, {
nullable: true,
coerceTypes: this.options.coerceTypes,
removeAdditional: false,
useDefaults: true,
unknownFormats: this.options.unknownFormats,
format: this.options.validateFormats,
}).preProcess();
new RequestSchemaPreprocessor(
spec.apiDoc,
this.ajvOpts.preprocessor,
).preProcess();

return {
context: new OpenApiContext(spec, this.options.ignorePaths),
Expand Down Expand Up @@ -249,7 +249,7 @@ export class OpenApiValidator {
private multipartMiddleware(apiDoc: OpenAPIV3.Document) {
return middlewares.multipart(apiDoc, {
multerOpts: this.options.fileUploader,
unknownFormats: this.options.unknownFormats,
ajvOpts: this.ajvOpts.multipart,
});
}

Expand All @@ -261,59 +261,18 @@ export class OpenApiValidator {
}

private requestValidationMiddleware(apiDoc: OpenAPIV3.Document) {
const {
coerceTypes,
unknownFormats,
validateRequests,
validateFormats,
formats,
} = this.options;
const { allowUnknownQueryParameters } = <ValidateRequestOpts>(
validateRequests
const requestValidator = new middlewares.RequestValidator(
apiDoc,
this.ajvOpts.request,
);
const requestValidator = new middlewares.RequestValidator(apiDoc, {
nullable: true,
coerceTypes,
removeAdditional: false,
useDefaults: true,
unknownFormats,
allowUnknownQueryParameters,
format: validateFormats,
formats: formats.reduce((acc, f) => {
acc[f.name] = {
type: f.type,
validate: f.validate,
};
return acc;
}, {}),
});
return (req, res, next) => requestValidator.validate(req, res, next);
}

private responseValidationMiddleware(apiDoc: OpenAPIV3.Document) {
const {
coerceTypes,
unknownFormats,
validateResponses,
validateFormats,
formats,
} = this.options;
const { removeAdditional } = <ValidateResponseOpts>validateResponses;

return new middlewares.ResponseValidator(apiDoc, {
nullable: true,
coerceTypes,
removeAdditional,
unknownFormats,
format: validateFormats,
formats: formats.reduce((acc, f) => {
acc[f.name] = {
type: f.type,
valdiate: f.validate,
};
return acc;
}, {}),
}).validate();
return new middlewares.ResponseValidator(
apiDoc,
this.ajvOpts.response,
).validate();
}

installOperationHandlers(baseUrl: string, context: OpenApiContext): Router {
Expand Down Expand Up @@ -394,3 +353,57 @@ export class OpenApiValidator {
}
}
}

class AjvOptions {
private options: OpenApiValidatorOpts;
constructor(options: OpenApiValidatorOpts) {
this.options = options;
}
get preprocessor(): ajv.Options {
return this.baseOptions();
}

get response(): ajv.Options {
const { removeAdditional } = <ValidateResponseOpts>(
this.options.validateResponses
);
return {
...this.baseOptions(),
useDefaults: false,
removeAdditional,
};
}

get request(): RequestValidatorOptions {
const { allowUnknownQueryParameters } = <ValidateRequestOpts>(
this.options.validateRequests
);
return {
...this.baseOptions(),
allowUnknownQueryParameters,
};
}

get multipart(): ajv.Options {
return this.baseOptions();
}

private baseOptions(): ajv.Options {
const { coerceTypes, unknownFormats, validateFormats } = this.options;
return {
nullable: true,
coerceTypes,
useDefaults: true,
removeAdditional: false,
unknownFormats,
format: validateFormats,
formats: this.options.formats.reduce((acc, f) => {
acc[f.name] = {
type: f.type,
validate: f.validate,
};
return acc;
}, {}),
};
}
}

0 comments on commit cf37281

Please sign in to comment.