@oas-tools/core
Advanced tools
Comparing version 3.0.1 to 3.0.2
{ | ||
"name": "@oas-tools/core", | ||
"version": "3.0.1", | ||
"version": "3.0.2", | ||
"description": "OAS Tools core library", | ||
@@ -5,0 +5,0 @@ "type": "module", |
import _ from "lodash"; | ||
import { schema } from "../../utils/index.js"; | ||
import { OASBase, logger } from "@oas-tools/commons"; | ||
@@ -12,9 +13,15 @@ | ||
return new OASParams(oasFile, (req, res, next) => { | ||
const methodParams = oasFile.paths[req.route.path]?.[req.method.toLowerCase()]?.parameters; | ||
const oasRequest = oasFile.paths[req.route.path]?.[req.method.toLowerCase()]; | ||
const pathParams = oasFile.paths[req.route.path]?.parameters; | ||
const methodParams = oasRequest?.parameters; | ||
const params = _.merge(pathParams, methodParams); | ||
const paramsObj = {}; | ||
let body = req.body; | ||
if (oasRequest.requestBody) { | ||
const contentType = Object.keys(oasRequest.requestBody.content)[0]; | ||
body = schema.parseBody(body, oasRequest.requestBody.content[contentType].schema); | ||
} | ||
Object.values(params).forEach((param) => paramsObj[param.name] = _getParameterValue(req, param)); | ||
res.defaultSend = res.send; // save original send for error handling | ||
res.locals.oas = { params: paramsObj, body: req.body }; | ||
res.locals.oas = { params: paramsObj, body: body }; | ||
if(req.file || req.files && req.files.length > 0) res.locals.oas.files = [req.files, req.file].flat().filter((file) => file !== undefined); | ||
@@ -21,0 +28,0 @@ next() |
import { OASBase, errors, logger, validator } from "@oas-tools/commons"; | ||
import { commons } from "../../utils/index.js"; | ||
import { commons, schema as schemaUtils } from "../../utils/index.js"; | ||
import MIMEtype from "whatwg-mimetype"; | ||
@@ -23,5 +23,5 @@ | ||
const contentType = Object.keys(oasRequest.requestBody.content)[0]; | ||
const schema = oasRequest.requestBody.content[contentType].schema; | ||
const schema = schemaUtils.parseSchema(oasRequest.requestBody.content[contentType].schema, "request"); | ||
const body = res.locals.oas.body; | ||
/* On multipart requests insert files in body for validation */ | ||
@@ -52,7 +52,14 @@ if (contentType.toLocaleLowerCase() === 'multipart/form-data' && res.locals.oas.files) { | ||
/* Check parameters */ | ||
const commonParams = oasFile.paths[req.route.path].parameters; | ||
const methodParams = oasRequest.parameters ?? commonParams; | ||
const parameters = commonParams ? [...new Set([...methodParams, ...commonParams])] : methodParams; | ||
// Check for extraneous query params | ||
const missingParams = Object.keys(req.query ?? {}).filter((qp) => !parameters?.filter((p) => p.in === "query").map((p) => p.name).includes(qp)); | ||
if (missingParams.length > 0) { | ||
commons.handle(RequestValidationError, "Extraneous parameter found in request query:\n" + missingParams.map((p) => ` - Missing declaration for "${p}"`).join("\n"), config.strict); | ||
} | ||
// Validate against schema | ||
if (res.locals.oas.params && Object.keys(res.locals.oas.params).length > 0) { | ||
const commonParams = oasFile.paths[req.route.path].parameters; | ||
const methodParams = oasRequest.parameters ?? commonParams; | ||
const parameters = commonParams ? [...new Set([...methodParams, ...commonParams])] : methodParams; | ||
parameters.forEach((param) => { | ||
@@ -118,4 +125,5 @@ const value = res.locals.oas.params[param.name]; | ||
const schemaContentType = Object.keys(expectedResponse.content)[0]; | ||
const schema = expectedResponse.content[schemaContentType].schema; | ||
const {validate, valid} = validator.validate(data, schema, oasFile.openapi); | ||
const schema = schemaUtils.parseSchema(expectedResponse.content[schemaContentType].schema, "response"); | ||
const parsedData = schemaUtils.parseBody(data, schema); | ||
const {validate, valid} = validator.validate(parsedData, schema, oasFile.openapi); | ||
@@ -125,8 +133,7 @@ if (!valid) { | ||
} | ||
oldSend.call(res, contentType.essence === "application/json" ? JSON.stringify(data) : data); | ||
} | ||
} else { | ||
logger.debug("Response content is not defined in the OAS Document for this response"); | ||
oldSend.call(res, contentType.essence === "application/json" ? JSON.stringify(data) : data); | ||
} | ||
oldSend.call(res, contentType.essence === "application/json" ? JSON.stringify(data) : data); | ||
} | ||
@@ -133,0 +140,0 @@ next(); |
@@ -17,2 +17,67 @@ import _ from 'lodash'; | ||
/** | ||
* Parse the request body taking defaults into account | ||
* @param {string} body - Request body. | ||
* @param {string} schema - OAS schema declaration for request body. | ||
*/ | ||
export function parseBody(body, schema) { | ||
const bodyType = Array.isArray(body) ? 'array' : typeof body; | ||
if (bodyType === 'array' && schema.type === "array") { | ||
const newBody = body ?? []; | ||
return newBody.map((item) => parseBody(item, schema.items)); | ||
} else if ((bodyType === 'object' || bodyType === "undefined") && schema.type === "object") { | ||
const newBody = body ?? {}; | ||
// Parse properties | ||
Object.keys(schema.properties ?? {}).map((field) => | ||
newBody[field] = parseBody(newBody[field], schema.properties[field]) | ||
); | ||
// Parse additionalProperties | ||
if (schema.additionalProperties) { | ||
Object.keys(newBody) | ||
.filter((field) => !Object.keys(schema.properties ?? {}).includes(field)) | ||
.map((field) => newBody[field] = parseBody(newBody[field], schema.additionalProperties)); | ||
} | ||
return _.omitBy(newBody, _.isUndefined); | ||
} else if (_.isUndefined(body)) { | ||
return body ?? schema.default; | ||
} else { | ||
return body ?? (schema.default ?? null); | ||
} | ||
} | ||
/** | ||
* Parse schemas depending on the scope | ||
* @param {string} schema - OAS Schema declaration. | ||
* @param {string} scope - Scope, can be `request` or `response`. | ||
*/ | ||
export function parseSchema(schema, scope) { | ||
const newSchema = JSON.parse(JSON.stringify(schema)); | ||
if (schema.oneOf || schema.anyOf || schema.allOf) { | ||
const keyword = Object.keys(schema)[0]; | ||
newSchema[keyword] = schema[keyword].map((sch) => parseSchema(sch, scope)); | ||
} else if (schema.not) { | ||
newSchema.not = parseSchema(schema.not, scope); | ||
} else if (schema.type === "array") { | ||
newSchema.items = parseSchema(schema.items, scope); | ||
} else if (schema.type === "object") { | ||
Object.keys(schema.properties ?? {}).forEach((prop) => { | ||
const requiredIdx = schema.required?.indexOf(prop); | ||
if (scope === "request" && requiredIdx > -1 && schema.properties[prop].readOnly) { | ||
newSchema.required.splice(requiredIdx, 1); | ||
} else if (scope === "response" && requiredIdx > -1 && schema.properties[prop].writeOnly) { | ||
newSchema.required.splice(requiredIdx, 1); | ||
} | ||
newSchema.properties[prop] = parseSchema(newSchema.properties[prop], scope); | ||
}); | ||
} | ||
return _.omitBy(newSchema, (i) => Array.isArray(i) && i.length === 0); | ||
} | ||
59761
951