Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/framework/types.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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;
}, {}),
};
}
}