openapi-request-coercer
Version:
Coerce request properties according to an openapi parameter list.
264 lines • 11.3 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
var ts_log_1 = require("ts-log");
var OpenAPIRequestCoercer = /** @class */ (function () {
function OpenAPIRequestCoercer(args) {
var loggingKey = args && args.loggingKey ? "".concat(args.loggingKey, ": ") : '';
if (!args) {
throw new Error("".concat(loggingKey, "missing args argument"));
}
var logger = args.logger || ts_log_1.dummyLogger;
if (!Array.isArray(args.parameters)) {
throw new Error("".concat(loggingKey, "args.parameters must be an Array"));
}
if (!args.coercionStrategy) {
args.coercionStrategy = {};
}
var extensionBase = args && args.extensionBase ? args.extensionBase : 'x-openapi-coercion';
var strictExtensionName = "".concat(extensionBase, "-strict");
var enableObjectCoercion = !!args.enableObjectCoercion;
this.coerceHeaders = buildCoercer({
params: args.parameters,
property: 'header',
isHeaders: true,
logger: logger,
loggingKey: loggingKey,
strictExtensionName: strictExtensionName,
enableObjectCoercion: enableObjectCoercion,
coercionStrategy: args.coercionStrategy
});
this.coerceParams = buildCoercer({
params: args.parameters,
property: 'path',
isHeaders: false,
logger: logger,
loggingKey: loggingKey,
strictExtensionName: strictExtensionName,
enableObjectCoercion: enableObjectCoercion,
coercionStrategy: args.coercionStrategy
});
this.coerceQuery = buildCoercer({
params: args.parameters,
property: 'query',
isHeaders: false,
logger: logger,
loggingKey: loggingKey,
strictExtensionName: strictExtensionName,
enableObjectCoercion: enableObjectCoercion,
coercionStrategy: args.coercionStrategy
});
this.coerceFormData = buildCoercer({
params: args.parameters,
requestBody: args.requestBody,
property: 'formData',
isHeaders: false,
logger: logger,
loggingKey: loggingKey,
strictExtensionName: strictExtensionName,
enableObjectCoercion: enableObjectCoercion,
coercionStrategy: args.coercionStrategy
});
}
OpenAPIRequestCoercer.prototype.coerce = function (request) {
if (request.headers && this.coerceHeaders) {
this.coerceHeaders(request.headers);
}
if (request.params && this.coerceParams) {
this.coerceParams(request.params);
}
if (request.query && this.coerceQuery) {
this.coerceQuery(request.query);
}
if (request.body && this.coerceFormData) {
this.coerceFormData(request.body);
}
};
return OpenAPIRequestCoercer;
}());
exports["default"] = OpenAPIRequestCoercer;
function buildCoercer(args) {
var _a, _b, _c;
if (!args.params.length && !args.requestBody) {
return;
}
var l = args.isHeaders ? function (name) { return name.toLowerCase(); } : function (name) { return name; };
var properties = args.params.filter(byLocation(args.property));
if (args.property === 'formData' && args.requestBody) {
var openapiv3formData_1 = (_c = (_b = (_a = args.requestBody) === null || _a === void 0 ? void 0 : _a.content['application/x-www-form-urlencoded']) === null || _b === void 0 ? void 0 : _b.schema) === null || _c === void 0 ? void 0 : _c.properties;
if (openapiv3formData_1) {
properties = properties.concat(Object.keys(openapiv3formData_1).map(function (k) {
return __assign(__assign({}, openapiv3formData_1[k]), { name: k });
}));
}
}
var coercers = properties.reduce(function (acc, param) {
acc[l(param.name)] = buildCoercerForParam(args, param);
return acc;
}, {});
return function (obj) {
for (var paramName in obj) {
if (coercers.hasOwnProperty(l(paramName))) {
obj[paramName] = coercers[l(paramName)](obj[paramName]);
}
}
};
}
function buildCoercerForParam(args, param) {
var logger = args.logger, loggingKey = args.loggingKey, enableObjectCoercion = args.enableObjectCoercion, _a = args.coercionStrategy, customStrategy = _a === void 0 ? {} : _a;
var strict = !!param[args.strictExtensionName];
function getCoercer(type) {
var OBJECT_FORMAT_COERCER = {
"default": function (schema, input) { return JSON.parse(input); },
deepObject: function (schema, input) {
for (var _i = 0, _a = Object.keys(input); _i < _a.length; _i++) {
var key = _a[_i];
var propertySchema = schema.properties
? schema.properties[key]
: schema.additionalProperties;
if (propertySchema) {
input[key] = getCoercer(propertySchema.type)(propertySchema, input[key]);
}
}
return input;
}
};
var COERCION_STRATEGIES = {
array: function (schema, input) {
if (!Array.isArray(input)) {
var collectionFormat = param.collectionFormat;
// OpenAPI 3.0 has replaced collectionFormat with a style property
// https://swagger.io/docs/specification/serialization/
if (param.style) {
if (param.style === 'form' && param["in"] === 'query') {
collectionFormat = param.explode ? 'multi' : 'csv';
}
else if (param.style === 'simple' &&
(param["in"] === 'path' || param["in"] === 'header')) {
collectionFormat = 'csv';
}
else if (param.style === 'spaceDelimited' &&
param["in"] === 'query') {
collectionFormat = 'ssv';
}
else if (param.style === 'pipeDelimited' &&
param["in"] === 'query') {
collectionFormat = 'pipes';
}
}
var sep = pathsep(collectionFormat || 'csv');
input = input.split(sep);
}
return input.map(function (v, i) {
var itemSchema = schema.items.schema
? schema.items.schema
: schema.items;
return getCoercer(itemSchema.type)(itemSchema, v);
});
},
object: function (schema, input) {
if (!enableObjectCoercion) {
return input;
}
// Similar to arrays, objects support formats. In OpenAPI 3.0, the format is called "style".
// Currently this coercer only automatically supports the deepObject style, and a simple
// JSON format, though the OpenAPI 3.0 specification defines many styles similar to arrays.
var style = param.style || schema.format;
var objectCoercer = OBJECT_FORMAT_COERCER[style] || OBJECT_FORMAT_COERCER["default"];
return objectCoercer(schema, input);
},
boolean: function (schema, input) {
if (typeof input === 'boolean') {
return input;
}
if (input === 'false') {
return false;
}
else {
return true;
}
},
integer: function (schema, input) {
var result = Math.floor(Number(input));
return isNaN(result) ? input : result;
},
number: function (schema, input) {
var result = Number(input);
return isNaN(result) ? input : result;
},
string: function (schema, input) { return String(input); }
};
var STRICT_COERCION_STRATEGIES = {
boolean: function (schema, input) {
if (typeof input === 'boolean') {
return input;
}
if (input.toLowerCase() === 'false') {
return false;
}
else if (input.toLowerCase() === 'true') {
return true;
}
else {
return null;
}
}
};
if (customStrategy[type] !== undefined) {
return function (schema, input) { return customStrategy[type](input); };
}
if (strict && STRICT_COERCION_STRATEGIES[type] !== undefined) {
return STRICT_COERCION_STRATEGIES[type];
}
if (COERCION_STRATEGIES[type] !== undefined) {
return COERCION_STRATEGIES[type];
}
var msg = type === undefined
? 'No type has been defined'
: "No proper coercion strategy has been found for type '".concat(type, "'");
logger.warn(loggingKey, "".concat(msg, ". A default 'identity' strategy has been set."));
return function (schema, input) { return input; };
}
// OpenAPI (Swagger) 2.0 has type and format information as direct properties
// of the param object. OpenAPI 3.0 has type and format information in a
// schema object property. Use a schema value to normalize the change across
// both versions so coercer works properly.
var paramOrSchema = param.schema || param;
if (paramOrSchema.type === 'array') {
if (!paramOrSchema.items) {
throw new Error("".concat(args.loggingKey, "items is a required property with type array"));
}
if (paramOrSchema.items.type === 'array' ||
(paramOrSchema.items.schema &&
paramOrSchema.items.schema.type === 'array')) {
throw new Error("".concat(args.loggingKey, "nested arrays are not allowed (items was of type array)"));
}
}
return getCoercer(paramOrSchema.type).bind(null, paramOrSchema);
}
function byLocation(location) {
return function (param) { return param["in"] === location; };
}
function pathsep(format) {
switch (format) {
case 'csv':
return ',';
case 'ssv':
return ' ';
case 'tsv':
return '\t';
case 'pipes':
return '|';
}
}
//# sourceMappingURL=index.js.map