express-openapi-validator
Advanced tools
Comparing version 4.11.0-beta.2 to 4.11.0
@@ -22,2 +22,20 @@ "use strict"; | ||
if (request) { | ||
if (options.serDesMap) { | ||
ajv.addKeyword('x-eov-serdes', { | ||
modifying: true, | ||
compile: (sch) => { | ||
if (sch) { | ||
return function validate(data, path, obj, propName) { | ||
if (typeof data === 'object') | ||
return true; | ||
if (!!sch.deserialize) { | ||
obj[propName] = sch.deserialize(data); | ||
} | ||
return true; | ||
}; | ||
} | ||
return () => true; | ||
}, | ||
}); | ||
} | ||
ajv.removeKeyword('readOnly'); | ||
@@ -49,17 +67,20 @@ ajv.addKeyword('readOnly', { | ||
// response | ||
ajv.addKeyword('x-eov-serializer', { | ||
modifying: true, | ||
compile: (sch) => { | ||
if (sch) { | ||
const isDate = ['date', 'date-time'].includes(sch.format); | ||
return function validate(data, path, obj, propName) { | ||
if (typeof data === 'string' && isDate) | ||
if (options.serDesMap) { | ||
ajv.addKeyword('x-eov-serdes', { | ||
modifying: true, | ||
compile: (sch) => { | ||
if (sch) { | ||
return function validate(data, path, obj, propName) { | ||
if (typeof data === 'string') | ||
return true; | ||
if (!!sch.serialize) { | ||
obj[propName] = sch.serialize(data); | ||
} | ||
return true; | ||
obj[propName] = sch.serialize(data); | ||
return true; | ||
}; | ||
} | ||
return () => true; | ||
}, | ||
}); | ||
}; | ||
} | ||
return () => true; | ||
}, | ||
}); | ||
} | ||
ajv.removeKeyword('writeOnly'); | ||
@@ -66,0 +87,0 @@ ajv.addKeyword('writeOnly', { |
@@ -22,7 +22,7 @@ "use strict"; | ||
}, new Set())); | ||
const validateApiDoc = 'validateApiDoc' in args ? !!args.validateApiDoc : true; | ||
const validateApiSpec = 'validateApiSpec' in args ? !!args.validateApiSpec : true; | ||
const validator = new openapi_schema_validator_1.OpenAPISchemaValidator({ | ||
version: apiDoc.openapi, | ||
}); | ||
if (validateApiDoc) { | ||
if (validateApiSpec) { | ||
const apiDocValidation = validator.validate(apiDoc); | ||
@@ -56,15 +56,8 @@ if (apiDocValidation.errors.length) { | ||
const origCwd = process.cwd(); | ||
const specDir = path.resolve(origCwd, path.dirname(filePath)); | ||
const absolutePath = path.resolve(origCwd, filePath); | ||
if (fs.existsSync(absolutePath)) { | ||
// Get document, or throw exception on error | ||
try { | ||
process.chdir(specDir); | ||
return $refParser.mode === 'dereference' | ||
? $RefParser.dereference(absolutePath) | ||
: $RefParser.bundle(absolutePath); | ||
} | ||
finally { | ||
process.chdir(origCwd); | ||
} | ||
return $refParser.mode === 'dereference' | ||
? $RefParser.dereference(absolutePath) | ||
: $RefParser.bundle(absolutePath); | ||
} | ||
@@ -71,0 +64,0 @@ else { |
@@ -31,3 +31,3 @@ /* istanbul ignore file */ | ||
return res; | ||
if (!mungError && res.statusCode >= 400) | ||
if (!mungError && res.statusCode >= 500) | ||
return original.call(this, json); | ||
@@ -34,0 +34,0 @@ // Run the munger |
@@ -27,3 +27,3 @@ import * as ajv from 'ajv'; | ||
export interface Options extends ajv.Options { | ||
schemaObjectMapper?: object; | ||
serDesMap?: SerDesMap; | ||
} | ||
@@ -35,5 +35,6 @@ export interface RequestValidatorOptions extends Options, ValidateRequestOpts { | ||
coerceTypes?: boolean | 'array'; | ||
removeAdditional?: boolean | 'all' | 'failing'; | ||
}; | ||
export declare type ValidateResponseOpts = { | ||
removeAdditional?: 'failing' | boolean; | ||
removeAdditional?: boolean | 'all' | 'failing'; | ||
coerceTypes?: boolean | 'array'; | ||
@@ -54,8 +55,25 @@ onError?: (err: InternalServerError, json: any) => void; | ||
}; | ||
export declare type Serializer = { | ||
export declare type SerDes = { | ||
format: string; | ||
serialize: (o: unknown) => string; | ||
serialize?: (o: unknown) => string; | ||
deserialize?: (s: string) => unknown; | ||
}; | ||
export declare class SerDesSingleton implements SerDes { | ||
serializer: SerDes; | ||
deserializer: SerDes; | ||
format: string; | ||
serialize?: (o: unknown) => string; | ||
deserialize?: (s: string) => unknown; | ||
constructor(param: { | ||
format: string; | ||
serialize: (o: unknown) => string; | ||
deserialize: (s: string) => unknown; | ||
}); | ||
} | ||
export declare type SerDesMap = { | ||
[format: string]: SerDes; | ||
}; | ||
export interface OpenApiValidatorOpts { | ||
apiSpec: OpenAPIV3.Document | string; | ||
validateApiSpec?: boolean; | ||
validateResponses?: boolean | ValidateResponseOpts; | ||
@@ -68,2 +86,3 @@ validateRequests?: boolean | ValidateRequestOpts; | ||
unknownFormats?: true | string[] | 'ignore'; | ||
serDes?: SerDes[]; | ||
formats?: Format[]; | ||
@@ -400,3 +419,3 @@ fileUploader?: boolean | multer.Options; | ||
apiDoc: OpenAPIV3.Document | string; | ||
validateApiDoc?: boolean; | ||
validateApiSpec?: boolean; | ||
$refParser?: { | ||
@@ -422,3 +441,3 @@ mode: 'bundle' | 'dereference'; | ||
export interface OpenApiRequest extends Request { | ||
openapi?: OpenApiRequestMetadata | {}; | ||
openapi?: OpenApiRequestMetadata; | ||
} | ||
@@ -425,0 +444,0 @@ export declare type OpenApiRequestHandler = (req: OpenApiRequest, res: Response, next: NextFunction) => any; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Forbidden = exports.Unauthorized = exports.UnsupportedMediaType = exports.InternalServerError = exports.RequestEntityTooLarge = exports.BadRequest = exports.MethodNotAllowed = exports.NotAcceptable = exports.NotFound = exports.HttpError = void 0; | ||
exports.Forbidden = exports.Unauthorized = exports.UnsupportedMediaType = exports.InternalServerError = exports.RequestEntityTooLarge = exports.BadRequest = exports.MethodNotAllowed = exports.NotAcceptable = exports.NotFound = exports.HttpError = exports.SerDesSingleton = void 0; | ||
class SerDesSingleton { | ||
constructor(param) { | ||
this.format = param.format; | ||
this.serialize = param.serialize; | ||
this.deserialize = param.deserialize; | ||
this.deserializer = { | ||
format: param.format, | ||
deserialize: param.deserialize | ||
}; | ||
this.serializer = { | ||
format: param.format, | ||
serialize: param.serialize | ||
}; | ||
} | ||
} | ||
exports.SerDesSingleton = SerDesSingleton; | ||
; | ||
class HttpError extends Error { | ||
@@ -5,0 +22,0 @@ constructor(err) { |
@@ -17,3 +17,3 @@ import * as res from './resolvers'; | ||
}; | ||
export * as serdes from './framework/base.serdes'; | ||
declare function openapiValidator(options: OpenApiValidatorOpts): import("./framework/types").OpenApiRequestHandler[]; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.error = exports.middleware = exports.resolvers = void 0; | ||
exports.serdes = exports.error = exports.middleware = exports.resolvers = void 0; | ||
const cloneDeep = require("lodash.clonedeep"); | ||
const res = require("./resolvers"); | ||
@@ -22,2 +23,3 @@ const openapi_validator_1 = require("./openapi.validator"); | ||
}; | ||
exports.serdes = require("./framework/base.serdes"); | ||
function openapiValidator(options) { | ||
@@ -27,3 +29,4 @@ const oav = new openapi_validator_1.OpenApiValidator(options); | ||
return oav.installMiddleware(new openapi_spec_loader_1.OpenApiSpecLoader({ | ||
apiDoc: options.apiSpec, | ||
apiDoc: cloneDeep(options.apiSpec), | ||
validateApiSpec: options.validateApiSpec, | ||
$refParser: options.$refParser, | ||
@@ -30,0 +33,0 @@ }).load()); |
@@ -6,2 +6,3 @@ "use strict"; | ||
const path_to_regexp_1 = require("path-to-regexp"); | ||
const types_1 = require("../framework/types"); | ||
function applyOpenApiMetadata(openApiContext, responseApiDoc) { | ||
@@ -19,2 +20,8 @@ return (req, res, next) => { | ||
const { expressRoute, openApiRoute, pathParams, schema } = matched; | ||
if (!schema) { | ||
throw new types_1.MethodNotAllowed({ | ||
path: req.path, | ||
message: `${req.method} method not allowed`, | ||
}); | ||
} | ||
req.openapi = { | ||
@@ -33,3 +40,6 @@ expressRoute: expressRoute, | ||
else if (openApiContext.isManagedRoute(path)) { | ||
req.openapi = {}; | ||
throw new types_1.NotFound({ | ||
path: req.path, | ||
message: 'not found', | ||
}); | ||
} | ||
@@ -36,0 +46,0 @@ next(); |
@@ -31,15 +31,3 @@ "use strict"; | ||
const path = openapi.expressRoute; | ||
if (!path) { | ||
throw new types_1.NotFound({ | ||
path: req.path, | ||
message: 'not found', | ||
}); | ||
} | ||
const reqSchema = openapi.schema; | ||
if (!reqSchema) { | ||
throw new types_1.MethodNotAllowed({ | ||
path: req.path, | ||
message: `${req.method} method not allowed`, | ||
}); | ||
} | ||
// cache middleware by combining method, path, and contentType | ||
@@ -46,0 +34,0 @@ const contentType = util_1.ContentType.from(req); |
@@ -17,18 +17,2 @@ "use strict"; | ||
const openapi = req.openapi; | ||
const expressRoute = openapi.expressRoute; | ||
if (!expressRoute) { | ||
return next(new types_1.NotFound({ | ||
path: req.path, | ||
message: 'not found', | ||
})); | ||
} | ||
const pathSchema = openapi.schema; | ||
if (!pathSchema) { | ||
// add openapi metadata to make this case more clear | ||
// its not obvious that missig schema means methodNotAllowed | ||
return next(new types_1.MethodNotAllowed({ | ||
path: req.path, | ||
message: `${req.method} method not allowed`, | ||
})); | ||
} | ||
// use the local security object or fallbac to api doc's security or undefined | ||
@@ -52,3 +36,3 @@ const securities = (_d = openapi.schema.security) !== null && _d !== void 0 ? _d : apiDoc.security; | ||
function checkErrorWithOr(res) { | ||
return res.forEach(r => { | ||
return res.forEach((r) => { | ||
if (r.success) { | ||
@@ -64,3 +48,3 @@ success = true; | ||
let allSuccess = false; | ||
res.forEach(r => { | ||
res.forEach((r) => { | ||
if (!r.success) { | ||
@@ -80,3 +64,3 @@ allSuccess = false; | ||
} | ||
results.forEach(result => { | ||
results.forEach((result) => { | ||
if (Array.isArray(result) && result.length > 1) { | ||
@@ -136,3 +120,8 @@ checkErrorsWithAnd(result); | ||
const scheme = this.securitySchemes[securityKey]; | ||
const handler = (_b = (_a = this.securityHandlers) === null || _a === void 0 ? void 0 : _a[securityKey]) !== null && _b !== void 0 ? _b : fallbackHandler; | ||
const handler = (_b = | ||
(_a = this.securityHandlers) === null || _a === void 0 | ||
? void 0 | ||
: _a[securityKey]) !== null && _b !== void 0 | ||
? _b | ||
: fallbackHandler; | ||
const scopesTmp = s[securityKey]; | ||
@@ -139,0 +128,0 @@ const scopes = Array.isArray(scopesTmp) ? scopesTmp : []; |
@@ -1,3 +0,2 @@ | ||
import ajv = require('ajv'); | ||
import { OpenAPIV3, ValidateResponseOpts } from '../../framework/types'; | ||
import { OpenAPIV3, Options, ValidateResponseOpts } from '../../framework/types'; | ||
export declare class SchemaPreprocessor { | ||
@@ -7,4 +6,5 @@ private ajv; | ||
private apiDocRes; | ||
private serDesMap; | ||
private responseOpts; | ||
constructor(apiDoc: OpenAPIV3.Document, ajvOptions: ajv.Options, validateResponsesOpts: ValidateResponseOpts); | ||
constructor(apiDoc: OpenAPIV3.Document, ajvOptions: Options, validateResponsesOpts: ValidateResponseOpts); | ||
preProcess(): { | ||
@@ -11,0 +11,0 @@ apiDoc: OpenAPIV3.Document; |
@@ -19,14 +19,2 @@ "use strict"; | ||
} | ||
const dateTime = { | ||
format: 'date-time', | ||
serialize: (d) => { | ||
return d && d.toISOString(); | ||
}, | ||
}; | ||
const date = { | ||
format: 'date', | ||
serialize: (d) => { | ||
return d && d.toISOString().split('T')[0]; | ||
}, | ||
}; | ||
if (!Array.prototype['flatMap']) { | ||
@@ -53,2 +41,3 @@ // polyfill flatMap | ||
this.apiDoc = apiDoc; | ||
this.serDesMap = ajvOptions.serDesMap; | ||
this.responseOpts = validateResponsesOpts; | ||
@@ -59,3 +48,3 @@ } | ||
const r = this.gatherSchemaNodesFromPaths(); | ||
// Now that we've processed paths, clonse the spec | ||
// Now that we've processed paths, clone a response spec if we are validating responses | ||
this.apiDocRes = !!this.responseOpts ? cloneDeep(this.apiDoc) : null; | ||
@@ -117,16 +106,20 @@ const schemaNodes = { | ||
traverseSchemas(nodes, visit) { | ||
const seen = new Set(); | ||
const recurse = (parent, node, opts) => { | ||
const schema = this.resolveSchema(node.schema); | ||
if (node.schema._seen || !schema) { | ||
// if we can't dereference a path within the schema, skip preprocessing | ||
// TODO handle refs like below during preprocessing | ||
// #/paths/~1subscription/get/requestBody/content/application~1json/schema/properties/subscription | ||
const schema = node.schema; | ||
if (!schema || seen.has(schema)) | ||
return; | ||
seen.add(schema); | ||
if (schema.$ref) { | ||
const resolvedSchema = this.resolveSchema(schema); | ||
const path = schema.$ref.split('/').slice(1); | ||
opts.req.originalSchema = schema; | ||
opts.res.originalSchema = schema; | ||
visit(parent, node, opts); | ||
recurse(node, new Node(schema, resolvedSchema, path), opts); | ||
return; | ||
} | ||
node.schema._seen = true; | ||
// Save the original schema so we can check if it was a $ref | ||
opts.req.originalSchema = node.schema; | ||
opts.res.originalSchema = node.schema; | ||
// TODO mark visited, and skip visited | ||
// TODO Visit api docs | ||
opts.req.originalSchema = schema; | ||
opts.res.originalSchema = schema; | ||
visit(parent, node, opts); | ||
@@ -151,4 +144,4 @@ if (schema.allOf) { | ||
} | ||
else if (node.schema.properties) { | ||
Object.entries(node.schema.properties).forEach(([id, cschema]) => { | ||
else if (schema.properties) { | ||
Object.entries(schema.properties).forEach(([id, cschema]) => { | ||
const path = [...node.path, 'properties', id]; | ||
@@ -190,3 +183,3 @@ const child = new Node(node, cschema, path); | ||
options.path = node.path; | ||
if (nschema) { | ||
if (nschema) { // This null check should no longer be necessary | ||
this.handleSerDes(pschema, nschema, options); | ||
@@ -255,15 +248,5 @@ this.handleReadonly(pschema, nschema, options); | ||
handleSerDes(parent, schema, state) { | ||
if (state.kind === 'res') { | ||
if (schema.type === 'string' && !!schema.format) { | ||
switch (schema.format) { | ||
case 'date-time': | ||
schema.type = ['object', 'string']; | ||
schema['x-eov-serializer'] = dateTime; | ||
break; | ||
case 'date': | ||
schema.type = ['object', 'string']; | ||
schema['x-eov-serializer'] = date; | ||
break; | ||
} | ||
} | ||
if (schema.type === 'string' && !!schema.format && this.serDesMap[schema.format]) { | ||
schema.type = ['object', 'string']; | ||
schema['x-eov-serdes'] = this.serDesMap[schema.format]; | ||
} | ||
@@ -270,0 +253,0 @@ } |
@@ -10,2 +10,3 @@ "use strict"; | ||
const resolvers_1 = require("./resolvers"); | ||
const base_serdes_1 = require("./framework/base.serdes"); | ||
const schema_preprocessor_1 = require("./middlewares/parsers/schema.preprocessor"); | ||
@@ -262,3 +263,23 @@ var types_1 = require("./framework/types"); | ||
normalizeOptions(options) { | ||
// Modify the request | ||
if (!options.serDes) { | ||
options.serDes = base_serdes_1.defaultSerDes; | ||
} | ||
else { | ||
if (!Array.isArray(options.unknownFormats)) { | ||
options.unknownFormats = Array(); | ||
} | ||
options.serDes.forEach(currentSerDes => { | ||
if (options.unknownFormats.indexOf(currentSerDes.format) === -1) { | ||
options.unknownFormats.push(currentSerDes.format); | ||
} | ||
}); | ||
base_serdes_1.defaultSerDes.forEach(currentDefaultSerDes => { | ||
let defautSerDesOverride = options.serDes.find(currentOptionSerDes => { | ||
return currentDefaultSerDes.format === currentOptionSerDes.format; | ||
}); | ||
if (!defautSerDesOverride) { | ||
options.serDes.push(currentDefaultSerDes); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -288,5 +309,6 @@ isOperationHandlerOptions(value) { | ||
get request() { | ||
const { allowUnknownQueryParameters, coerceTypes } = (this.options.validateRequests); | ||
const { allowUnknownQueryParameters, coerceTypes, removeAdditional } = (this.options.validateRequests); | ||
return Object.assign(Object.assign({}, this.baseOptions()), { allowUnknownQueryParameters, | ||
coerceTypes }); | ||
coerceTypes, | ||
removeAdditional }); | ||
} | ||
@@ -297,3 +319,17 @@ get multipart() { | ||
baseOptions() { | ||
const { coerceTypes, unknownFormats, validateFormats } = this.options; | ||
const { coerceTypes, unknownFormats, validateFormats, serDes } = this.options; | ||
const serDesMap = {}; | ||
for (const serDesObject of serDes) { | ||
if (!serDesMap[serDesObject.format]) { | ||
serDesMap[serDesObject.format] = serDesObject; | ||
} | ||
else { | ||
if (serDesObject.serialize) { | ||
serDesMap[serDesObject.format].serialize = serDesObject.serialize; | ||
} | ||
if (serDesObject.deserialize) { | ||
serDesMap[serDesObject.format].deserialize = serDesObject.deserialize; | ||
} | ||
} | ||
} | ||
return { | ||
@@ -313,2 +349,3 @@ nullable: true, | ||
}, {}), | ||
serDesMap: serDesMap, | ||
}; | ||
@@ -315,0 +352,0 @@ } |
{ | ||
"name": "express-openapi-validator", | ||
"version": "4.11.0-beta.2", | ||
"version": "4.11.0", | ||
"description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", | ||
@@ -37,3 +37,3 @@ "main": "dist/index.js", | ||
"content-type": "^1.0.4", | ||
"json-schema-ref-parser": "^9.0.6", | ||
"json-schema-ref-parser": "^9.0.7", | ||
"lodash.clonedeep": "^4.5.0", | ||
@@ -44,2 +44,3 @@ "lodash.get": "^4.4.2", | ||
"media-typer": "^1.1.0", | ||
"@types/multer": "^1.4.5", | ||
"multer": "^1.4.2", | ||
@@ -55,3 +56,2 @@ "ono": "^7.1.3", | ||
"@types/morgan": "^1.9.1", | ||
"@types/multer": "^1.4.4", | ||
"@types/node": "^14.14.2", | ||
@@ -58,0 +58,0 @@ "@types/supertest": "^2.0.10", |
@@ -18,3 +18,3 @@ # 🦋 express-openapi-validator | ||
- 👮 security validation / custom security functions | ||
- 👽 3rd party / custom formats | ||
- 👽 3rd party / custom formats / custom data serialization-deserialization | ||
- 🧵 optionally auto-map OpenAPI endpoints to Express handler functions | ||
@@ -26,2 +26,4 @@ - ✂️ **\$ref** support; split specs over multiple files | ||
[Koa](https://github.com/cdimascio/express-openapi-validator/tree/lerna-fastify/packages/koa-openapi-validator) and [Fastify](https://github.com/cdimascio/express-openapi-validator/tree/lerna-fastify/packages/fastify-openapi-validator) now available! 🚀 | ||
## Install | ||
@@ -114,3 +116,2 @@ | ||
validateResponses: true, // <-- to validate responses | ||
// unknownFormats: ['my-format'] // <-- to provide custom formats | ||
}), | ||
@@ -482,2 +483,3 @@ ); | ||
validateResponses: true, | ||
validateApiSpec: true, | ||
validateSecurity: { | ||
@@ -497,2 +499,11 @@ handlers: { | ||
unknownFormats: ['phone-number', 'uuid'], | ||
serDes: [{ | ||
OpenApiValidator.serdes.dateTime, | ||
OpenApiValidator.serdes.date, | ||
{ | ||
format: 'mongo-objectid', | ||
deserialize: (s) => new ObjectID(s), | ||
serialize: (o) => o.toString(), | ||
}, | ||
}], | ||
operationHandlers: false | 'operations/base/path' | { ... }, | ||
@@ -503,3 +514,3 @@ ignorePaths: /.*\/pets$/, | ||
mode: 'bundle' | ||
} | ||
}, | ||
}); | ||
@@ -589,3 +600,20 @@ ``` | ||
``` | ||
**removeAdditional:** | ||
Determines whether to keep or remove additional properties in request body or to fail validation if schema has `additionalProperties` set to `false`. For futher details, refer to [AJV documentation](https://ajv.js.org/docs/validation.html#removing-additional-properties) | ||
- `false` (**default**) - not to remove additional properties | ||
- `"all"` - all additional properties are removed, regardless of additionalProperties keyword in schema (and no validation is made for them). | ||
- `true` - only additional properties with additionalProperties keyword equal to false are removed. | ||
- `"failing"` - additional properties that fail request schema validation will be removed (where additionalProperties keyword is false or schema). | ||
For example: | ||
```javascript | ||
validateRequests: { | ||
removeAdditional: true; | ||
} | ||
``` | ||
### ▪️ validateResponses (optional) | ||
@@ -655,2 +683,10 @@ | ||
### ▪️ validateApiSpec (optional) | ||
Determines whether the validator should validate the OpenAPI specification. Useful if you are certain that the api spec is syntactically correct and want to bypass this check. | ||
- `true` (**default**) - validate the OpenAPI specification. | ||
- `false` - do not validate the OpenAPI specification. | ||
### ▪️ formats (optional) | ||
@@ -714,2 +750,45 @@ | ||
### ▪️ serDes (optional) | ||
Defines custom serialization and deserialization behavior for schemas of type `string` that declare a `format`. By default, `Date` objects are serialized as `string` when a schema's `type` is `string` and `format` is `date` or `date-time`. | ||
e.g. | ||
```javascript | ||
// If `serDes` is not specified, the following behavior is default | ||
serDes: [{ | ||
OpenApiValidator.serdes.dateTime.serializer, | ||
OpenApiValidator.serdes.date.serializer, | ||
}], | ||
``` | ||
To create custom serializers and/or deserializers, define: | ||
- `format` (required) - a custom 'unknown' format that triggers the serializer and/or deserializer | ||
- `deserialize` (optional) - upon receiving a request, transform a string property to an object. Deserialization occurs _after_ request schema validation. | ||
- `serialize` (optional) - before sending a response, transform an object to string. Serialization occurs _after_ response schema validation | ||
e.g. | ||
```javascript | ||
serDes: [{ | ||
// installs dateTime serializer and deserializer | ||
OpenApiValidator.serdes.dateTime, | ||
// installs date serializer and deserializer | ||
OpenApiValidator.serdes.date, | ||
// custom serializer and deserializer for the custom format, mongo-objectid | ||
{ | ||
format: 'mongo-objectid', | ||
deserialize: (s) => new ObjectID(s), | ||
serialize: (o) => o.toString(), | ||
} | ||
}], | ||
``` | ||
The mongo serializers will trigger on the following schema: | ||
```yaml | ||
type: string | ||
format: mongo-objectid | ||
``` | ||
See [mongo-serdes-js](https://github.com/pilerou/mongo-serdes-js) for additional (de)serializers including MongoDB `ObjectID`, `UUID`, ... | ||
### ▪️ operationHandlers (optional) | ||
@@ -969,3 +1048,3 @@ | ||
See [OpenAPI 3](https://swagger.io/docs/specification/authentication/) authentication for `securityScheme` and `security` documentation | ||
See [examples](https://github.com/cdimascio/express-openapi-validator/blob/security/test/security.spec.ts#L17) from unit tests | ||
See [examples](https://github.com/cdimascio/express-openapi-validator/blob/master/test/security.handlers.spec.ts#L21) from unit tests | ||
@@ -1147,2 +1226,8 @@ ## Example: Multiple Validators and API specs | ||
## Related Projects | ||
- [koa-openapi-validator](https://github.com/cdimascio/express-openapi-validator/tree/lerna-fastify/packages/koa-openapi-validator) | ||
- [fastify-openapi-validator](https://github.com/cdimascio/express-openapi-validator/tree/lerna-fastify/packages/fastify-openapi-validator) | ||
_Note: koa and fastify does not (yet) support response validation or operation handlers | ||
## Contributors ✨ | ||
@@ -1149,0 +1234,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
275986
23
5620
1
1315
12
+ Added@types/multer@^1.4.5
+ Added@types/body-parser@1.19.5(transitive)
+ Added@types/connect@3.4.38(transitive)
+ Added@types/express@5.0.0(transitive)
+ Added@types/express-serve-static-core@5.0.1(transitive)
+ Added@types/http-errors@2.0.4(transitive)
+ Added@types/mime@1.3.5(transitive)
+ Added@types/multer@1.4.12(transitive)
+ Added@types/node@22.9.0(transitive)
+ Added@types/qs@6.9.17(transitive)
+ Added@types/range-parser@1.2.7(transitive)
+ Added@types/send@0.17.4(transitive)
+ Added@types/serve-static@1.15.7(transitive)
+ Addedundici-types@6.19.8(transitive)