New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

openapi-enforcer

Package Overview
Dependencies
Maintainers
1
Versions
131
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openapi-enforcer - npm Package Compare versions

Comparing version 0.11.5 to 1.0.0

bin/assert.js

236

bin/exception.js

@@ -18,75 +18,213 @@ /**

'use strict';
const util = require('util');
module.exports = OpenAPIException;
let inspect = util.inspect.custom || 'inspect';
module.exports = EnforcerException;
/**
* Create an EnforcerException instance
* @param {string} [header]
* @returns {EnforcerException}
* @constructor
*/
function EnforcerException (header) {
if (!(this instanceof EnforcerException)) return new EnforcerException(header);
const callbacks = {};
this.emit = function (type, payload) {
if (callbacks[type]) callbacks[type].forEach(callback => callback(payload));
};
const prototype = {
nest: function(header, meta) {
const exception = new OpenAPIException(header, meta);
this.children.push(exception);
return exception;
},
this.on = function (type, handler) {
if (!callbacks[type]) callbacks[type] = [];
callbacks[type].push(handler);
};
push: function(message) {
this.children.push(message);
},
this.header = header;
this.cache = undefined;
this.children = {
at: {},
nest: [],
message: []
};
}
flatten: function() {
return flatten([], '', this);
},
EnforcerException.prototype.at = function (key) {
const at = this.children.at;
if (!at[key]) {
at[key] = new EnforcerException('');
at[key].on('cache-clear', () => this.clearCache());
this.clearCache();
}
return at[key];
};
toString: function() {
return OpenAPIException.hasException(this)
? toString('', this)
: '';
EnforcerException.prototype.clearCache = function () {
const children = this.children;
const at = children.at;
const emit = arguments.length ? arguments[0] : true;
Object.keys(at).forEach(key => at[key].clearCache(false));
children.nest.forEach(child => child.clearCache(false));
this.cache = undefined;
if (emit) this.emit('cache-clear');
return this;
};
EnforcerException.prototype[inspect] = function () {
if (this.hasException) {
return '[ EnforcerException: ' + toString(this, null, ' ') + ' ]';
} else {
return '[ EnforcerException ]';
}
};
function OpenAPIException(header, meta) {
const exception = message => exception.push(message);
Object.assign(exception, prototype, { header: header, children: [], meta: meta });
Object.defineProperty(exception, 'isOpenAPIException', {
value: true,
writable: false,
configurable: false
});
EnforcerException.prototype.nest = function (header) {
const exception = new EnforcerException(header);
exception.on('cache-clear', () => this.clearCache());
this.children.nest.push(exception);
return exception;
}
};
OpenAPIException.hasException = function(exception) {
const children = exception.children;
const length = children.length;
for (let i = 0; i < length; i++) {
if (typeof children[i] === 'string') return true;
if (OpenAPIException.hasException(children[i])) return true;
EnforcerException.prototype.merge = function (exception) {
const thisChildren = this.children;
const thatChildren = exception.children;
const at = thisChildren.at;
let addedMessage = false;
Object.keys(thatChildren.at).forEach(key => {
if (!at[key]) {
at[key] = thatChildren.at[key];
at[key].on('cache-clear', () => this.clearCache());
} else {
at[key].merge(thatChildren.at[key]);
}
});
thatChildren.nest.forEach(exception => {
thisChildren.nest.push(exception);
});
thatChildren.message.forEach(message => {
thisChildren.message.push(message);
addedMessage = true;
});
if (addedMessage) this.clearCache();
return this;
};
EnforcerException.prototype.message = function (message) {
this.children.message.push(message);
this.clearCache();
return this;
};
EnforcerException.prototype.push = function (value) {
const type = typeof value;
if (type === 'string' && value.length) {
this.children.message.push(value);
this.clearCache();
} else if (type === 'object' && value instanceof EnforcerException) {
this.children.nest.push(value);
this.clearCache();
} else {
throw Error('Can only push string or EnforcerException instance');
}
return false;
return this;
};
EnforcerException.prototype.toString = function () {
return toString(this, null, '');
};
function flatten(errors, prefix, exception) {
if (!OpenAPIException.hasException(exception)) return errors;
Object.defineProperties(EnforcerException.prototype, {
count: {
get: function () {
if (!this.cache) this.cache = {};
if (!this.cache.count) {
const children = this.children;
this.cache.count = children.message.length +
children.nest.reduce((count, exception) => count + exception.count, 0) +
Object.keys(children.at).reduce((count, key) => count + children.at[key].count, 0);
}
return this.cache.count;
}
},
exception.children.forEach(child => {
if (typeof child === 'string') {
errors.push(prefix + exception.header + ': ' + child);
hasException: {
get: function () {
if (!this.cache) this.cache = {};
const cache = this.cache;
if (!cache.hasOwnProperty('hasException')) {
const children = this.children;
// if this has messages then an exception exists
cache.hasException = false;
if (children.message.length) {
cache.hasException = true;
} else {
// if nested objects have exception then exception exists
const nest = children.nest;
const length = nest.length;
for (let i = 0; i < length; i++) {
if (nest[i].hasException) {
cache.hasException = true;
break;
}
}
// if nested ats have exception then exception exists
if (!cache.hasException) {
const keys = Object.keys(children.at);
const length = keys.length;
for (let i = 0; i < length; i++) {
if (children.at[keys[i]].hasException) {
cache.hasException = true;
break;
}
}
}
}
}
return cache.hasException;
}
}
});
function toString (context, parent, prefix) {
if (!context.hasException) return '';
const prefixPlus = prefix + ' ';
const children = context.children;
let result = '';
if (context.header) result += (parent ? prefix : '') + context.header;
const at = children.at;
const atKeys = Object.keys(at).filter(key => at[key].hasException);
const singleAtKey = atKeys.length === 1;
atKeys.forEach(key => {
const exception = children.at[key];
if (context.header || !singleAtKey || children.nest.length > 0 || children.message.length > 0) {
result += '\n' + prefixPlus + 'at: ' + key + toString(exception, context, prefixPlus)
} else {
flatten(errors, prefix + exception.header + ': ', child);
result += ' > ' + key + toString(exception, context, prefix)
}
});
return errors;
}
function toString(prefix, exception) {
let result = exception.header + ':';
exception.children.forEach(child => {
if (typeof child === 'string') {
result += '\n ' + prefix + child;
} else if (OpenAPIException.hasException(child)) {
result += '\n ' + prefix + toString(prefix + ' ', child);
}
children.nest.forEach(exception => {
if (exception.hasException) result += '\n' + toString(exception, context, prefixPlus);
});
children.message.forEach(message => {
result += '\n' + prefixPlus + message;
});
return result;
}

@@ -25,2 +25,4 @@ /**

exports.integer = /^-?\d+$/;
exports.number = /^-?\d+(?:\.\d+)?$/;
exports.number = /^-?\d+(?:\.\d+)?$/;
exports['date-time'] = exports.dateTime;

@@ -18,28 +18,79 @@ /**

'use strict';
const Exception = require('./exception');
const queryString = require('querystring');
const rx = require('./rx');
const rxMediaType = /^([\s\S]+?)\/(?:([\s\S]+?)\+)?([\s\S]+?)$/;
const punctuation = ',,,,,,,,,,.................................:;!?';
const punctuationCount = punctuation.length;
const words = "lorem ipsum dolor sit amet consectetur adipiscing elit suspendisse sollicitudin felis pretium laoreet tortor facilisis a integer eu metus velit praesent varius sed erat quis ornare nunc porttitor nulla at ultrices nam ac vestibulum metus maecenas malesuada lectus leo blandit a congue gravida phasellus consectetur libero et tincidunt diam pellentesque lacus neque eros sed porta nunc id lobortis eget ligula mollis nulla nunc maximus gravida felis finibus est ullamcorper pellentesque ex in turpis pharetra dictum in fermentum arcu mauris odio molestie iaculis accumsan nec convallis nec nunc vestibulum nisl curabitur tristique non porttitor vivamus dui ipsum orci eget vulputate lacus interdum suscipit massa elementum sodales at interdum fames ante primis in faucibus duis mi pulvinar accumsan donec odio enim sed dignissim turpis quisque vitae turpis ut nibh tincidunt aliquam magna semper aliquam feugiat sapien justo egestas condimentum metus tincidunt odio volutpat vehicula pulvinar arcu diam bibendum sem leo sodales eleifend vehicula fusce faucibus quam lorem rhoncus amet hendrerit rhoncus augue mattis commodo lobortis urna consequat hendrerit enim risus placerat eros euismod ligula tellus tempus condimentum ac lectus erat ultrices mi lacus nisi scelerisque vehicula cursus cras enim elit aenean aliquam tempor ullamcorper est proin aliquet orci et augue posuere viverra massa augue purus orci purus neque ut elit pretium molestie vel tellus ex consequat tristique urna fringilla dignissim ex lectus imperdiet lobortis potenti efficitur feugiat facilisi placerat posuere bibendum velit volutpat dapibus donec".split(' ');
const wordCount = words.length;
exports.arrayPushMany = function(target, source) {
source.forEach(item => target.push(item));
module.exports = {
arrayRemoveItem,
copy: value => {
const map = new Map();
return copy(map, value);
},
edgeSlashes,
findMediaMatch,
getDateFromValidDateString,
getDefinitionType,
isDate,
isNumber,
isInteger,
isPlainObject,
isObject,
isObjectStringMap,
lowerCaseObjectProperties,
mapObject,
parseCookieString,
parseQueryString,
randomNumber,
randomOneOf,
randomText,
reject,
rxStringToRx,
same,
smart,
ucFirst,
validateMaxMin
};
/**
* Copies Dates, Buffers, Arrays, plain Objects, and Primitives
* @param {*} value
* @returns {*}
*/
exports.copy = function(value) {
const map = new Map();
return copy(map, value);
};
function arrayRemoveItem(array, item) {
const index = array.indexOf(item);
if (index !== -1) array.splice(index, 1);
return array;
}
/**
* Decide whether edges of a string should have slashes or not.
* @param {string} value
* @param {boolean} start
* @param {boolean} end
* @returns {string}
*/
exports.edgeSlashes = function(value, start, end) {
function copy(map, value) {
if (value instanceof Date) {
return new Date(+value);
} else if (value instanceof Buffer) {
return value.slice(0);
} else if (Array.isArray(value)) {
let result = map.get(value);
if (result) return result;
result = [];
map.set(value, result);
value.forEach(v => result.push(copy(map, v)));
return result;
} else if (isPlainObject(value)) {
let result = map.get(value);
if (result) return result;
result = {};
map.set(value, result);
Object.keys(value).forEach(key => result[key] = copy(map, value[key]));
return result;
} else {
return value;
}
}
function edgeSlashes (value, start, end) {
value = value.replace(/^\//, '').replace(/\/$/, '');

@@ -50,40 +101,11 @@ if (value.length === 0 && (start || end)) return '/';

return value;
};
}
exports.Error = function(meta, message) {
if (arguments.length === 1) {
meta = {};
message = arguments[0];
} else if (typeof meta === 'string') {
meta = { code: meta };
}
const err = Error(message.replace(/\s+/g, ' '));
Object.assign(err, meta);
return err;
};
exports.errorHandler = function(useThrow, exception, value) {
const hasErrors = Exception.hasException(exception);
if (hasErrors && useThrow) {
const err = Error(exception);
err.code = 'OPEN_API_EXCEPTION';
Object.assign(err, exception.meta);
throw err;
} else if (useThrow) {
return value;
} else {
return {
error: hasErrors ? exception : null,
value: hasErrors ? null : value
};
}
};
/**
* Provide an accept media / mime type string and possible matches and get the match.
* @param {string} input
* @param {string[]} store
* @returns {string[]} The media type matches.
* @param {string} input The allowed media type string. Example: text/html, application/xhtml+xml, application/xml;q=0.9, text/*;q=0.8
* @param {string[]} store An array of media types to search through (no quality number)
* @returns {string[]} The media type matches in order of best match first.
*/
exports.findMediaMatch = function(input, store) {
function findMediaMatch(input, store) {
const accepts = input

@@ -151,9 +173,76 @@ .split(/, */)

return unique;
};
}
exports.isDate = function (value) {
function getDateFromValidDateString (format, string) {
const date = new Date(string);
const match = rx[format].exec(string);
const year = +match[1];
const month = +match[2] - 1;
const day = +match[3];
const hour = +match[4] || 0;
const minute = +match[5] || 0;
const second = +match[6] || 0;
const millisecond = +match[7] || 0;
return date.getUTCFullYear() === year &&
date.getUTCMonth() === month &&
date.getUTCDate() === day &&
date.getUTCHours() === hour &&
date.getUTCMinutes() === minute &&
date.getUTCSeconds() === second &&
date.getUTCMilliseconds() === millisecond ? date : null;
}
function getDefinitionType (definition) {
if (Array.isArray(definition)) return 'array';
if (isPlainObject(definition)) return 'object';
if (definition === null) return 'null';
const type = typeof definition;
return type === 'object' ? 'decoratedObject' : type;
}
function isDate (value) {
return value && !isNaN(value) && value instanceof Date;
};
}
exports.lowerCaseProperties = function(obj) {
function isNumber (value) {
return typeof value === 'number' && !isNaN(value);
}
function isInteger (value) {
return !isNaN(value) && typeof value === 'number' && value === Math.round(value);
}
function isObject(v) {
return v && typeof v === 'object' && Object.prototype.toString.call(v) === '[object Object]';
}
function isPlainObject (value) {
if (!isObject(value)) return false;
// check for modified constructor
const constructor = value.constructor;
if (typeof constructor !== 'function') return false;
// check for modified prototype
const prototype = constructor.prototype;
if (!isObject(prototype)) return false;
// check constructor for Object-specific method
return prototype.hasOwnProperty('isPrototypeOf');
}
// check to see if its an object with properties as strings
function isObjectStringMap (obj) {
if (!isPlainObject(obj)) return false;
const keys = Object.keys(obj);
const length = keys.length;
for (let i = 0; i < length; i++) {
if (typeof keys[i] !== 'string' || typeof obj[keys[i]] !== 'string') return false;
}
return true;
}
// create shallow copy of the object but make all property names lower case
function lowerCaseObjectProperties (obj) {
const result = {};

@@ -164,46 +253,126 @@ Object.keys(obj).forEach(key => {

return result;
};
}
/**
* If a property does not exist then set it to the value.
* @param {object} obj
* @param {string|Symbol} property
* @param {*} value
*/
exports.propertyDefault = function(obj, property, value) {
if (!obj.hasOwnProperty(property)) obj[property] = value;
};
function mapObject (object, callback) {
const result = {};
Object.keys(object).forEach(key => {
result[key] = callback(object[key], key);
});
return result;
}
exports.queryParamsByName = function (name, value) {
const rx = RegExp('(?:^|&)' + name + '=([^&]*)', 'g');
const results = [];
let match;
while (match = rx.exec(value)) results.push(decodeURIComponent(match[1]));
return results.length ? results : null;
};
function parseCookieString(str) {
const result = {};
str.split(/; */).forEach(pair => {
const [key, value] = pair.split('=');
if (!result[key]) result[key] = [];
result[key].push(value || '');
});
return result;
}
exports.queryParamNames = function(value, objValue) {
const retObject = arguments.length >= 2;
const names = {};
const boolean = !!objValue;
value.split('&').forEach(pair => {
const kv = pair.split('=');
const name = kv[0];
if (name) names[name] = boolean;
function parseQueryString (str, delimiter) {
const query = queryString.parse(str, delimiter);
Object.keys(query).forEach(key => {
const value = query[key];
if (!Array.isArray(value)) query[key] = [ value ];
});
return retObject ? names : Object.keys(names);
};
return Object.assign({}, query);
}
exports.randomNumber = function(integer, min, max) {
if (!min) min = Math.random();
if (!max) max = Math.random();
};
function randomNumber ({ min, max, multipleOf, exclusiveMin = false, exclusiveMax = false, decimalPlaces = 0, spread = 1000 } = {}) {
const minIsNumber = isNumber(min);
const maxIsNumber = isNumber(max);
/**
* Do a deep equal on two values.
* @param {*} v1
* @param {*} v2
* @returns {boolean}
*/
exports.same = function same(v1, v2) {
if (typeof multipleOf === 'number' && multipleOf > 0) {
min = Math.ceil(min / multipleOf);
max = Math.floor(max / multipleOf);
const index = Math.round(Math.random() * (max - min) / multipleOf);
return index * multipleOf;
} else {
const multiplier = minIsNumber && maxIsNumber ? max - min : spread;
let num = Math.random() * multiplier;
if (minIsNumber) num += min;
decimalPlaces = Math.round(decimalPlaces);
if (decimalPlaces === 0) {
num = Math.round(num);
} else if (decimalPlaces > 0) {
const dec = Math.pow(10, decimalPlaces);
if (dec > 1) num = Math.round(num * dec) / dec;
}
if (minIsNumber) {
if (num < min) num = min;
if (num === min && exclusiveMin) num += Math.pow(10, -1 * decimalPlaces);
}
if (maxIsNumber) {
if (num > max) num = max;
if (num === max && exclusiveMax) num -= Math.pow(10, -1 * decimalPlaces);
}
if (minIsNumber && (num < min || (num === min && exclusiveMin))) return undefined;
if (maxIsNumber && (num > max || (num === max && exclusiveMax))) return undefined;
return num;
}
}
function randomOneOf (choices) {
const index = Math.floor(Math.random() * choices.length);
return choices[index];
}
function randomText ({ minLength = 1, maxLength = 250 } = {}) {
const length = randomNumber({ min: minLength, max: maxLength }) + 1;
let result = '';
let punctuationIndex = 1;
let uc = true;
while (result.length < length) {
const index = Math.floor(Math.random() * wordCount);
let word = words[index];
if (uc) word = ucFirst(word);
uc = false;
result += word;
if (Math.random() >= punctuationIndex) {
punctuationIndex = 1;
const index = Math.floor(Math.random() * punctuationCount);
const punct = punctuation[index];
if (/[.!?]/.test(punct)) uc = true;
result += punct;
} else {
punctuationIndex *= .9;
}
result += ' ';
}
result = result.trim();
result = result.replace(/[,.:;!?]$/, ''); // if ends in punctuation then remove it
if (maxLength > 5) {
if (result.length >= maxLength) result = result.substr(0, maxLength - 1);
result += '.';
} else if (result.length > maxLength) {
result = result.substr(0, maxLength);
}
return result;
}
function reject (message) {
return Promise.reject(typeof message === 'string' ? Error(message) : Error(message.toString()));
}
function rxStringToRx (value) {
if (typeof value === 'string') {
const rx = /^\/([\s\S]+?)\/(\w*)?$/;
const match = rx.exec(value);
return match
? RegExp(match[1], match[2] || '')
: RegExp(value);
} else if (value instanceof RegExp) {
return value;
} else {
throw Error('Cannot convert value to RegExp instance');
}
}
function same (v1, v2) {
if (v1 === v2) return true;

@@ -229,4 +398,4 @@

} else if (exports.isDate(v1)) {
return exports.isDate(v2) && +v2 === +v1;
} else if (isDate(v1)) {
return isDate(v2) && +v2 === +v1;

@@ -250,114 +419,63 @@ } else if (v1 && type === 'object') {

}
};
}
/**
* Determine the schema type using the schema. It isn't always specified but enough
* information is generally provided to determine it.
* @param {object} schema
* @returns {string}
*/
exports.schemaType = function(schema) {
if (schema.type) return schema.type; // TODO: remove this function - validator will require all types be specified
if (schema.items) return 'array';
if (schema.properties || schema.additionalProperties) return 'object';
};
/**
* Determine the schema format using the schema. It isn't always specified but enough
* information is generally provided to determine it.
* @param {object} schema
* @returns {string, undefined}
*/
exports.schemaFormat = function(schema) {
const type = exports.schemaType(schema);
switch (type) {
case 'boolean':
case 'integer':
case 'number':
return type;
case 'string':
switch (schema.format) {
case 'binary':
case 'byte':
case 'date':
case 'date-time':
return schema.format;
default:
return 'string';
}
default:
return;
function smart (value) {
const type = typeof value;
if (type === 'string') return '"' + value.replace(/"/g, '\\"') + '"';
if (value instanceof Date) return isNaN(value) ? 'invalid date object' : value.toISOString();
if (Array.isArray(value)) {
let result = '[' + value.toString() + ']';
const length = result.length;
if (length > 15) {
const excess = length - 15;
const offTop = Math.floor(excess / 2);
const offBottom = excess - offTop;
const middle = Math.ceil(length / 2);
result = result.substr(0, middle - offBottom) + '...' + result.substr(middle + offTop)
}
return result;
}
};
/**
* Wrap with quotations if the value is a string.
* @param value
* @returns {*}
*/
exports.smart = function(value) {
if (typeof value === 'string') return '"' + value.replace(/"/g, '\\"') + '"';
if (value instanceof Date) return value.toISOString();
if (value && type === 'object') {
const name = value.constructor.name;
return '[object' + (name ? ' ' + name : '') + ']';
}
return String(value);
};
}
exports.traverse = function(object, callback) {
const map = new Map();
traverse(map, '', null, null, object, callback);
};
function ucFirst (value) {
return value[0].toUpperCase() + value.substr(1);
}
exports.tryRequire = function(path) {
try {
return require(path);
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') return null;
throw err;
function validateMaxMin(exception, schema, type, maxProperty, minProperty, exclusives, value, maximum, minimum) {
if (schema.hasOwnProperty(maxProperty)) {
if (exclusives && schema.exclusiveMaximum && value >= maximum) {
let bound = schema.serialize(schema[maxProperty]).value || schema[maxProperty];
let val = schema.serialize(value).value || value;
exception.message('Expected ' + type + ' to be less than ' +
smart(bound) + '. Received: ' +
smart(val));
} else if (value > maximum) {
let bound = schema.serialize(schema[maxProperty]).value || schema[maxProperty];
let val = schema.serialize(value).value || value;
exception.message('Expected ' + type + ' to be less than or equal to ' +
smart(bound) + '. Received: ' +
smart(val));
}
}
};
function copy(map, value) {
if (value instanceof Date) {
return new Date(+value);
} else if (value instanceof Buffer) {
return value.slice(0);
} else if (Array.isArray(value)) {
let result = map.get(value);
if (result) return result;
result = [];
map.set(value, result);
value.forEach(v => result.push(copy(map, v)));
return result;
} else if (value && typeof value === 'object') {
let result = map.get(value);
if (result) return result;
result = {};
map.set(value, result);
Object.keys(value).forEach(key => result[key] = copy(map, value[key]));
return result;
} else {
return value;
if (schema.hasOwnProperty(minProperty)) {
if (exclusives && schema.exclusiveMinimum && value <= minimum) {
let bound = schema.serialize(schema[minProperty]).value || schema[minProperty];
let val = schema.serialize(value).value || value;
exception.message('Expected ' + type + ' to be greater than ' +
smart(bound) + '. Received: ' +
smart(val));
} else if (value < minimum) {
let bound = schema.serialize(schema[minProperty]).value || schema[minProperty];
let val = schema.serialize(value).value || value;
exception.message('Expected ' + type + ' to be greater than or equal to ' +
smart(bound) + '. Received: ' +
smart(val));
}
}
}
function traverse(map, path, property, parent, value, callback) {
// avoid endless loop
if (map.has(value)) return;
map.set(value, true);
callback(value, parent, property, path);
if (Array.isArray(value)) {
value.forEach((v, i) => traverse(map, path + '/' + i, i, value, v, callback));
} else if (value && typeof value === 'object') {
Object.keys(value).forEach(key => {
traverse(map, path + '/' + key, key, value, value[key], callback)
});
}
}

@@ -0,2 +1,157 @@

/**
* @license
* Copyright 2018 Brigham Young University
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
'use strict';
module.exports = require('./bin/openapi');
module.exports = Enforcer;
const dataTypeFormats = require('./bin/data-type-formats');
const Exception = require('./bin/exception');
const RefParser = require('json-schema-ref-parser');
const Result = require('./bin/result');
const Super = require('./bin/super');
const util = require('./bin/util');
/**
* Create an Enforcer instance.
* @param {string, object} definition
* @param {object} [options]
* @param {boolean} [options.hideWarnings=false] Set to true to hide warnings from the console.
* @returns {Promise<OpenApiEnforcer>}
*/
async function Enforcer(definition, options) {
let openapi;
let warnings;
// normalize options
options = Object.assign({}, options);
if (!options.hasOwnProperty('hideWarnings')) options.hideWarnings = false;
const refParser = new RefParser();
definition = util.copy(definition);
definition = await refParser.dereference(definition);
let exception = Exception('One or more errors exist in the OpenAPI definition');
const hasSwagger = definition.hasOwnProperty('swagger');
if (!hasSwagger && !definition.hasOwnProperty('openapi')) {
exception('Missing required "openapi" or "swagger" property');
} else {
const match = /^(\d+)(?:\.(\d+))(?:\.(\d+))?$/.exec(definition.swagger || definition.openapi);
if (!match) {
exception.at(hasSwagger ? 'swagger' : 'openapi')('Invalid value');
} else {
const major = +match[1];
const validator = major === 2
? Enforcer.v2_0.Swagger
: Enforcer.v3_0.OpenApi;
[ openapi, exception, warnings ] = validator(definition, refParser);
}
}
if (!options.hideWarnings && warnings) console.warn(warnings.toString());
if (exception && exception.hasException) throw Error(exception.toString());
return openapi;
}
Enforcer.dereference = function (definition) {
const refParser = new RefParser();
return refParser.dereference(definition);
};
Enforcer.Enforcer = Enforcer;
Enforcer.Exception = Exception;
Enforcer.Result = Result;
const v2_0 = Enforcer.v2_0 = {};
Object.defineProperty(v2_0, 'version', { value: '2.0' });
Object.assign(v2_0, {
Contact: Super(v2_0, 'Contact'),
Example: Super(v2_0, 'Example'),
ExternalDocumentation: Super(v2_0, 'ExternalDocumentation'),
Header: Super(v2_0, 'Header'),
Info: Super(v2_0, 'Info'),
License: Super(v2_0, 'License'),
Operation: Super(v2_0, 'Operation'),
Parameter: Super(v2_0, 'Parameter'),
PathItem: Super(v2_0, 'PathItem'),
Paths: Super(v2_0, 'Paths'),
Reference: Super(v2_0, 'Reference'),
Response: Super(v2_0, 'Response'),
Responses: Super(v2_0, 'Responses'),
Schema: Super(v2_0, 'Schema'),
SecurityRequirement: Super(v2_0, 'SecurityRequirement'),
SecurityScheme: Super(v2_0, 'SecurityScheme'),
Swagger: Super(v2_0, 'Swagger'),
Tag: Super(v2_0, 'Tag'),
Xml: Super(v2_0, 'Xml')
});
const v3_0 = Enforcer.v3_0 = {};
Object.defineProperty(v3_0, 'version', { value: '3.0' });
Object.assign(v3_0, {
Callback: Super(v3_0, 'Callback'),
Components: Super(v3_0, 'Components'),
Contact: Super(v3_0, 'Contact'),
Encoding: Super(v3_0, 'Encoding'),
Example: Super(v3_0, 'Example'),
ExternalDocumentation: Super(v3_0, 'ExternalDocumentation'),
Header: Super(v3_0, 'Header'),
Info: Super(v3_0, 'Info'),
License: Super(v3_0, 'License'),
Link: Super(v3_0, 'Link'),
MediaType: Super(v3_0, 'MediaType'),
OAuthFlow: Super(v3_0, 'OAuthFlow'),
OAuthFlows: Super(v3_0, 'OAuthFlows'),
OpenApi: Super(v3_0, 'OpenApi'),
Operation: Super(v3_0, 'Operation'),
Parameter: Super(v3_0, 'Parameter'),
PathItem: Super(v3_0, 'PathItem'),
Paths: Super(v3_0, 'Paths'),
Reference: Super(v3_0, 'Reference'),
RequestBody: Super(v3_0, 'RequestBody'),
Response: Super(v3_0, 'Response'),
Responses: Super(v3_0, 'Responses'),
Schema: Super(v3_0, 'Schema'),
SecurityRequirement: Super(v3_0, 'SecurityRequirement'),
SecurityScheme: Super(v3_0, 'SecurityScheme'),
Server: Super(v3_0, 'Server'),
ServerVariable: Super(v3_0, 'ServerVariable'),
Tag: Super(v3_0, 'Tag'),
Xml: Super(v3_0, 'Xml')
});
Enforcer.v2_0.Schema.defineDataTypeFormat('integer', 'int32', null);
Enforcer.v2_0.Schema.defineDataTypeFormat('integer', 'int64', null);
Enforcer.v2_0.Schema.defineDataTypeFormat('number', 'float', null);
Enforcer.v2_0.Schema.defineDataTypeFormat('number', 'double', null);
Enforcer.v2_0.Schema.defineDataTypeFormat('string', 'binary', dataTypeFormats.binary);
Enforcer.v2_0.Schema.defineDataTypeFormat('string', 'binary', dataTypeFormats.binary);
Enforcer.v2_0.Schema.defineDataTypeFormat('string', 'byte', dataTypeFormats.byte);
Enforcer.v2_0.Schema.defineDataTypeFormat('string', 'date', dataTypeFormats.date);
Enforcer.v2_0.Schema.defineDataTypeFormat('string', 'date-time', dataTypeFormats.dateTime);
Enforcer.v3_0.Schema.defineDataTypeFormat('integer', 'int32', null);
Enforcer.v3_0.Schema.defineDataTypeFormat('integer', 'int64', null);
Enforcer.v3_0.Schema.defineDataTypeFormat('number', 'float', null);
Enforcer.v3_0.Schema.defineDataTypeFormat('number', 'double', null);
Enforcer.v3_0.Schema.defineDataTypeFormat('string', 'binary', dataTypeFormats.binary);
Enforcer.v3_0.Schema.defineDataTypeFormat('string', 'byte', dataTypeFormats.byte);
Enforcer.v3_0.Schema.defineDataTypeFormat('string', 'date', dataTypeFormats.date);
Enforcer.v3_0.Schema.defineDataTypeFormat('string', 'date-time', dataTypeFormats.dateTime);
{
"_from": "openapi-enforcer",
"_id": "openapi-enforcer@0.10.0",
"_inBundle": false,
"_integrity": "sha512-RuF9HzkPUAmPEM08npqWicXgYKZJ7sO6LiXBb/L0PInyw+TnqAU0DdWRK2f4oQrAWMZbGKlduSy3s27Wy27rIQ==",
"_location": "/openapi-enforcer",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "openapi-enforcer",
"name": "openapi-enforcer",
"escapedName": "openapi-enforcer",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/openapi-enforcer/-/openapi-enforcer-0.10.0.tgz",
"_shasum": "b22bf90d30e2abdf96944749a7e95786773d6382",
"_spec": "openapi-enforcer",
"_where": "/home/james/temp",
"author": {
"name": "James Speirs"
},
"bugs": {
"url": "https://github.com/byu-oit/openapi-enforcer/issues"
},
"bundleDependencies": false,
"dependencies": {},
"deprecated": false,
"name": "openapi-enforcer",
"version": "1.0.0",
"description": "Library for validating, parsing, and formatting data against open api schemas.",
"devDependencies": {
"body-parser": "^1.18.3",
"chai": "^3.5.0",
"chokidar-cli": "^1.2.0",
"connect-busboy": "0.0.2",
"connect-multiparty": "^2.1.0",
"coveralls": "^3.0.1",
"express": "^4.16.3",
"formidable": "^1.2.1",
"js-yaml": "^3.12.0",
"json-schema-ref-parser": "^4.1.1",
"mocha": "^5.2.0",
"multer": "^1.3.0",
"multiparty": "^4.1.4",
"nyc": "^12.0.2",
"request": "^2.87.0",
"request-promise-native": "^1.0.5",
"swagger-parser": "^3.4.2"
},
"main": "index.js",
"directories": {
"test": "tests"
},
"homepage": "https://github.com/byu-oit/openapi-enforcer#readme",
"scripts": {
"test": "mocha --recursive test",
"coverage": "nyc --reporter=html npm test",
"coverage:report": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"coverage:watch": "chokidar 'test/**/*' 'bin/**/*' -c 'npm run coverage'"
},
"repository": {
"type": "git",
"url": "git+https://github.com/byu-oit/openapi-enforcer.git"
},
"keywords": [
"openapi",
"swagger",
"validate",
"response",
"build",
"compose",
"validate",
"parse",
"format"
"format",
"deserialize",
"serialize"
],
"author": "James Speirs",
"license": "Apache-2.0",
"main": "index.js",
"name": "openapi-enforcer",
"repository": {
"type": "git",
"url": "git+https://github.com/byu-oit/openapi-enforcer.git"
"bugs": {
"url": "https://github.com/byu-oit/openapi-enforcer/issues"
},
"scripts": {
"coverage": "nyc --reporter=html npm test",
"coverage:report": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"coverage:watch": "chokidar 'test/**/*' 'bin/**/*' -c 'npm run coverage'",
"test": "mocha --recursive test"
"homepage": "https://github.com/byu-oit/openapi-enforcer#readme",
"devDependencies": {
"chai": "^4.2.0",
"chokidar-cli": "^1.2.1",
"coveralls": "^3.0.2",
"mocha": "^5.2.0",
"nyc": "^13.1.0"
},
"types": "index.d.ts",
"version": "0.11.5"
"dependencies": {
"json-schema-ref-parser": "^6.0.1"
}
}

@@ -1,932 +0,19 @@

# OpenAPI-Enforcer
**Supports OpenAPI 2.0 (formerly Swagger) and OpenAPI 3.x**
Tools for using the Open API Specification (OAS)
Features
**Supports OAS 2.0 (formerly Swagger) and OAS 3.x.x**
- Validate a value against a schema
- Determine the schema for a provided path (allows path parameters)
- Serialization and deserialization for interprocess or network communication
- Request parsing and validating
- Response building, serializing, and validating
- Generate random valid values from a schema
## Features
# Table of Contents
- Validate your OAS documents.
- Serialize, deserialize, and validate values against OAS schemas.
- Identify the operation associated with a request.
- Parse, deserialize, and validate request parameters.
- Facilitated response building.
- Generate random valid values for a schema.
- Plugin environment for custom document validation and extended functionality including custom data type formats.
- [Constructor](#constructor)
- [Enforcer.prototype.deserialize](#enforcerprototypedeserialize)
- [Enforcer.prototype.errors](#enforcerprototypeerrors)
- [Enforcer.prototype.path](#enforcerprototypepath)
- [Enforcer.prototype.populate](#enforcerprototypepopulate)
- [Enforcer.prototype.random](#enforcerprototyperandom)
- [Enforcer.prototype.request](#enforcerprototyperequest)
- [Enforcer.prototype.response](#enforcerprototyperesponse)
- [Enforcer.prototype.schema](#enforcerprototypeschema)
- [Enforcer.prototype.serialize](#enforcerprototypeserialize)
- [Enforcer.prototype.validate](#enforcerprototypevalidate)
- [Appendix](#appendix)
- [About default, x-template, and x-variable](#about-default-x-template-and-x-variable)
- [Error Throwing vs Reporting](#error-throwing-vs-reporting)
- [Parameter Replacement](#parameter-replacement)
## Documentation
# Example
```js
const Enforcer = require('openapi-enforcer')
// define enforcer instance
const enforcer = new Enforcer('3.0.0')
// define the user schema
const userSchema = {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1
},
birthday: {
type: 'string',
format: 'date',
}
}
}
// check a value for any schema errors
const errors = enforcer.errors(userSchema, {
name: 'Bob',
birthday: new Date('2000-01-01')
});
```
# Constructor
Create an OpenAPI enforcer instance.
`new Enforcer ( definition [, options ] )`
| Parameter | Description | Type | Default |
| --------- | ----------- | ---- | ------- |
| definition | An openapi document or a string representing the version to use. | `string` or `object` |
| options | The options to use for all functions within the instance. This options can be overwritten per function called. | `object` |
| options.deserialize | The default options to apply to deserialize functions |
| options.populate | The default [options to apply to populate](#populate-options) functions. | |
| options.request | The default options to apply to request functions | |
| options.serialize | The default options to apply to serialize functions | |
| Deserialize Option | Description | Default |
| ------------------ | ----------- | ------- |
| throw | Whether errors should be [thrown or reported](#error-throwing-vs-reporting). | `true` |
| options.deserialize.throw | Set to `true` to throw errors
deserialize: {
throw: true
},
errors: {
prefix: ''
},
populate: {
copy: false,
defaults: true,
ignoreMissingRequired: true,
oneOf: true,
replacement: 'handlebar',
templateDefaults: true,
templates: true,
throw: true,
variables: true
},
request: {
throw: true
},
serialize: {
throw: true
}
**Returns** an instance of the OpenAPI Enforcer
**Example 1 - Version as parameter**
```js
const Enforcer = require('openapi-enforcer');
const enforcer = new Enforcer('2.0'); // create an enforcer for OpenAPI version 2.0
```
**Example 2 - Object as parameter**
```js
const Enforcer = require('openapi-enforcer');
const enforcer = new Enforcer({ openapi: '3.0.0' }); // create an enforcer for OpenAPI version 3.0.0
```
**Example 3 - Using Discriminators**
If your OpenAPI document is using discriminators then you'll want to either include the entire swagger document as the input parameter or include at least the component schemas (OpenAPI 3.x) or definitions (OpenAPI 2.0).
```js
const Enforcer = require('openapi-enforcer');
const enforcer2 = new Enforcer({
swagger: '2.0',
definitions: {
// ... named schemas here
}
});
const enforcer3 = new Enforcer({
openapi: '3.0.0',
components: {
schemas: {
// ... named schemas here
}
}
});
```
## Enforcer.prototype.deserialize
When a value is sent over HTTP it is in a serialized state. Calling this function will deserialize a value into its deserialized equivalent.
`Enforcer.prototype.deserialize ( schema, value [, options ] )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to use to convert serialized values. | `object` |
| value | The value to deserialize. | Any |
| options | The deserialize options | `object` |
Returns the deserialized [value or the report](#error-throwing-vs-reporting).
```js
const Enforcer = require('openapi-enforcer');
// create the enforcer instance
const enforcer = new Enforcer({ openapi: '3.0.0' });
// define a schema that defines deserialization instructions
const schema = {
type: 'object',
additionalProperties: false,
properties: {
integers: {
type: 'array',
items: {
type: 'integer'
}
},
date: {
type: 'string',
format: 'date-time'
}
}
};
const serializedValue = {
integers: [1, '2', 3.1, '3.8'],
date: '2000-01-01T01:02:03:456Z'
};
const deserialized = enforcer.deserialize(schema, serializedValue);
// {
// integers: [1, 2, 3, 4],
// date: <Date Object>
// }
```
### Deserialize Options
The [Enforcer.prototype.deserialize](#enforcerprototypedeserialize) `options` parameter can define these properties:
| Option | Description | Default |
| ------ | ----------- | ------- |
| throw | Whether errors should be [thrown or reported](#error-throwing-vs-reporting). | `true` |
### Default Deserialize Options
You can change the global defaults for how the [Enforcer.prototype.deserialize](#enforcerprototypedeserialize) works:
```js
const Enforcer = require('openapi-enforcer');
Enforcer.defaults.deserialize = {
throw: true
}
```
## Enforcer.prototype.errors
Validate a value against a schema and receive a detailed report where errors exist and why.
`Enforcer.prototype.errors ( schema, value )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to validate the value against. | `object` |
| value | The value to validate. | Any |
Returns: An array of strings where each item in the array describes one error that was encountered.
```js
const Enforcer = require('openapi-enforcer');
// create the enforcer instance
const enforcer = new Enforcer({ openapi: '3.0.0' });
// define a schema to validate values against
const schema = {
type: 'object',
additionalProperties: false,
properties: {
names: {
type: 'array',
items: {
type: 'string',
minLength: 1
}
},
date: {
type: 'string',
format: 'date-time',
maximum: '2000-01-01T00:00:00.000Z'
}
}
};
// get any errors and log to console
const errors = enforcer.errors(schema, {
names: [ 'Bob', 'Jan', '' ],
date: '2010-01-01T00:00:00.000Z',
num: 8
});
// errors ==> [
// /names/2: String length below minimum length of 1 with length of 0: ''
// /date: Expected date-time to be less than or equal to 2000-01-01T00:00:00.000Z. Received: 2010-01-01T00:00:00.000Z
// /num: Property not allowed
// ]
```
## Enforcer.prototype.path
Get the matching path's path parameters and schema.
**This function will not work if you haven't defined paths in your OpenAPI document that was passed into the constructor.**
`Enforcer.prototype.path ( path )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| path | The lookup path (optionally with path parameters) | `string` |
Returns: An object with the following properties:
- *path* - The path as defined in the OpenAPI document.
- *params* - The path parameters (still serialized)
- *schema* - The path schema as defined in the OpenAPI document.
```js
const pathItem = {
get: {},
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'number'
}
}
]
};
const definition = {
openapi: '3.0.0',
paths: {
'/path/{id}': pathItem
}
}
// create the enforcer instance
const enforcer = new Enforcer(definition);
const match = enforcer.path('/path/25');
// {
// path: '/path/{id}',
// params: {
// id: 25
// },
// schema: { ... } <== operation object
// }
```
## Enforcer.prototype.populate
Build a value from a schema. While traversing the schema the final populated value may be derived from the provided value in combination with the schema's `default` value, the `x-template` value, or the `x-variable` value.
`Enforcer.prototype.populate ( schema, params, value, options )`
This function takes one parameter (an `object`) with the following properties:
| Parameters | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to build a value from. This property is required. | `object` |
| params | A map of keys to values. These values are used to help build the final value | `object` |
| value | An initial value to start with. Set to `undefined` if you do not want to provide a value. | Any |
| options | The options to apply during the build phase. Any options specified here will overwrite defaults. | `object` |
Returns: The populated value.
### Populate Options
The [`Enforcer.prototype.populate`](#enforcerprototypepopulate) `options` parameter can define these properties:
| Option | Description | Default |
| ------ | ----------- | ------- |
| copy | When executing [`Enforcer.prototype.populate`](#enforcerprototypepopulate) and providing the `value` property, you have the option to either mutate (modify) that value or to create a copy of the value and mutate that. Mutation is faster, but if you do not want to change the passed in `value` then you should set this value to `true`. | `false` |
| defaults | Allow populated values to be built from a schema's `default` value. | `true` |
| ignoreMissingRequired | When executing [`Enforcer.prototype.populate`](#enforcerprototypepopulate) there will be times where an object with required properties is missing values for those required properties. If this value is set to `false` then [`Enforcer.prototype.populate`](#enforcerprototypepopulate) will not add the object to the populated value. If set to `true` then partially completed objects will still be added to the populated value. | `true` |
| replacement | The template [parameter replacement](#parameter-replacement) format to use. This can be one of `"handlebar"`, `"doubleHandlebar"`, or `"colon"`. | `"handlebar"` |
| templateDefaults | If this is set to `true` and a default is being use to populate a value and the default value is a string then the value will act as an `x-template` value. This can be useful because `default` values generally appear in generated documentation but you may still want to perform an `x-template` transformation on the value. | `true` |
| templates | Allow populated values to be built from a schema's `x-template` value. [More about default, x-template, and x-variable](#about-default-x-template-and-x-variable). | `true` |
| throw | Whether errors should be [thrown or reported](#error-throwing-vs-reporting). | `true` |
| variables | Allow populated values to be built from a schema's `x-variable` value. [More about default, x-template, and x-variable](#about-default-x-template-and-x-variable). | `true` |
### Default Populate Options
You can change the global defaults for how the [`Enforcer.prototype.populate`](#enforcerprototypepopulate) works like this:
```js
const Enforcer = require('openapi-enforcer');
Enforcer.defaults.populate = {
copy: false,
defaults: true,
ignoreMissingRequired: true,
replacement: 'handlebar',
templateDefaults: true,
templates: true,
throw: true,
variables: true
}
```
Alternatively you can specify the same options in the [constructor](#constructor) or when calling the [`Enforcer.prototype.populate`](#enforcerprototypepopulate) function.
## Enforcer.prototype.random
Create a value that is randomly generated but that meets the constraints of a provided schema. Works on simple primitives and complex objects.
`Enforcer.prototype.random ( schema )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to use to generate the random value. | `object` |
Returns: A random value that adheres to the provided schema.
## Enforcer.prototype.request
Parse and validate input parameters for a request.
`Enforcer.prototype.request ( { body, cookies, headers, method = 'get', path } )`
This function takes one parameter, an `object`, with the following properties:
| Property | Description | Type |
| --------- | ----------- | ---- |
| body | The request body. | `string` or `object` |
| cookies | The request cookies. An `object` with cookie names as keys and cookie values as the values. | `object` |
| headers | The request headers. An `object` with cookie names as keys and cookie values as the values. | `object` |
| method | The HTTP method. Defaults to `"get"`. | `string` |
| path | The request path. This should include the query string parameters if applicable. | `string` |
Returns an object with the following properties:
- *errors* - If any errors occurred this will be an array of strings, otherwise it will be `null`.
- *path* - The path as defined in the OpenAPI document.
- *request* - The request object, serialized and validated. If an error occurred this value will be `null`.
- *response* - The same function as [`enforcer.prototype.response`](#enforcerprototyperesponse) except that the `path` and `method` properties are already specified..
- *schema* - The path operation schema as defined in the OpenAPI document.
## Enforcer.prototype.response
Get functions that provide details and functionality that is associated to a request and its response.
`Enforcer.prototype.response ( { code, contentType, method = 'get', path } )`
This function takes one parameter, an `object`, with the following properties:
| Property | Description | Required |
| --------- | ----------- | ---- |
| code | The HTTP response status code to use. If omitted and the "default" response is defined then "default" will be used, otherwise it will use the first defined response status code. | No |
| contentType | The content type of the schema to use for the response. This can use wild cards or even specify multiple acceptable content types. For example: `"*/json"`, `"application/*"`, or `"text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8"`. If omitted then the first content type defined for the specified response code will be used. | No |
| path | The request path. This value can include path parameters. | Yes |
| method | The request method. Defaults to `"GET"`. | No |
Returns an object with the following properties:
- *data* - An object with the normalized response data. In the case that the `code` or `contentType` were either not specified or used indeterminate values, this object will have the determined values as well as the response body schema to be used. In short, it is an object with these properties:
- *code* - The determined HTTP response code that aligns with the OpenAPI document.
- *contentType* - The determined content type that aligns with the OpenAPI document.
- *schema* - The response body schema to use for this response.
Alternatively this value may be `undefined` or partially populated if the OpenAPI document does not have a response or content type match.
```js
const response = enforcer.response({
code: 200,
contentType: 'application/json',
path: '/'
});
console.log(response.data);
```
- *errors* - A function that returns an array of errors for the body and headers.
Signature: `errors ( { body, headers })`
Takes a configuration object as it's parameter with the following properties:
| Property | Description |
| ---------| ----------- |
| body | The response body to check for errors. |
| headers | An object with key value pairs where the key is the header name and the value is the header value. Each header name that has an associated OpenAPI schema will be checked for errors. |
Returns an array of strings if one or more errors occurred, otherwise returns `null`.
```js
const response = enforcer.response({
code: 200,
contentType: 'application/json',
path: '/'
});
const errors = response.errors({
body: {},
headers: {
'x-date': new Date()
}
});
if (errors) console.error(errors.join('\n'));
```
- *example* - A function that produces an example response. If the OpenAPI document has one or more examples then one of those will be used, otherwise it will be generated using the [`Enforcer.prototype.random`](#enforcerprototyperandom) function.
Signature: `example ( { name, ignoreDocumentExample=false } )`
Takes a configuration object as it's parameter with the following properties:
| Property | Description |
| ---------| ----------- |
| name | The name of the example to use when pulling from a named OpenAPI 3.x document example. Not relevant for OpenAPI 2.0. If the `ignoreDocumentExample` property is set to `true` then this value will be ignored. |
| ignoreDocumentExample | If set to `true` then even if an example exists in the OpenAPI document, a random one will be generated instead. Defaults to `false` |
Returns a value that can be used as an example.
```js
const response = enforcer.response({
code: 200,
contentType: 'application/json',
path: '/'
});
const example = response.example();
```
- *populate* - A function that uses [`enforcer.prototype.populate`](#enforcerprototypepopulate) to build a response value using the response schema and a parameter map.
Signature: `populate ( { body, headers, options, params } )`
Takes a configuration object as it's parameter with the following properties:
| Property | Description |
| ---------| ----------- |
| body | The initial body value. Omit this value if you want the body to be built from scratch. |
| headers | An initial header object with header names and values as key value pairs. If the headers object does not define the `'content-type'` header then it will be set to the same value as the `contentType` option specified by the data object. |
| options | Options to pass to the [`enforcer.prototype.populate`](#enforcerprototypepopulate) function. |
| params | The parameter map to pass to [`enforcer.prototype.populate`](#enforcerprototypepopulate) |
Returns an object with `headers` and `body` properties.
```js
const response = enforcer.response({
code: 200,
contentType: 'application/json',
path: '/'
});
const populated = response.populate({
params: {},
body: {},
headers: {}
});
```
- *serialize* - A function that takes the status code, body, and headers and then validates and then serializes the body and headers to prepare them to send as an HTTP response. If validation fails an error will be thrown.
Signature: `serialize ( { body, headers, options, skipValidation } )`
Takes a configuration object as it's parameter with the following properties:
| Property | Description |
| ---------| ----------- |
| body | The initial body value. Omit this value if you want the body to be built from scratch. |
| headers | An initial header object with header names and values as key value pairs. If the headers object does not define the `'content-type'` header then it will be set to the same value as the contentType option. |
| options | Options to pass to the [`enforcer.prototype.serialize`](#enforcerprototypeserialize) function. |
| skipValidation | Skip validation prior to seraialization. This can save processing cycles if you have already used `Enforcer.prototype.response().errors()` to check for errors and have found none. Skipping validation when errors exist may still cause errors to occur. Defaults to `false`. |
Returns an object with `headers` and `body` properties.
```js
const response = enforcer.response({
code: 200,
contentType: 'application/json',
path: '/'
});
const data = response.serialize({
body: {
num: 1,
date: new Date()
},
headers: { 'x-header': 'some value' }
});
```
## Enforcer.prototype.serialize
Serialize a value according to the schema. This works for primitives, arrays, and objects. Arrays and objects will be traversed and their values also be serialized recursively.
`Enforcer.prototype.serialize ( schema, value )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to serialize to. | `object` |
| value | The value to format. | Any |
Returns: The serialized value.
Can serialize:
- arrays and objects recursively
- binary from boolean, number, string, or Buffer
- boolean from any value
- byte from boolean, number, string, or Buffer
- date from Date, string, or number
- date-time from Date, string, or number
- integer from anything that !isNaN(value)
- number from anything that !isNaN(value)
- string from string, number, boolean, object, or Date
```js
const Enforcer = require('openapi-enforcer');
const enforcer = new Enforcer('3.0.0');
const schema = {
type: 'object',
properties: {
time: {
type: 'string',
format: 'date-time'
},
public: {
type: 'boolean'
},
seatsAvailable: {
type: 'integer'
}
}
};
const value = enforcer.serialize(schema, {
time: new Date(2000, 0, 1, 11), // formatted to ISO Date
public: 1, // formatted to true
seatsAvailable: 23.7 // formatted to integer
});
// value ==> {
// startTime: '2000-01-01T11:00:00.000Z',
// public: true,
// seatsAvailable: 24
// }
```
## Enforcer.prototype.request
Pass in an object that is representative of an HTTP request to have it validated, parsed, and deserialized. The path must match one of the definition paths.
`Enforcer.prototype.request ( req )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| request | The request. If a string is provided then it will represent the request path and all other request properties will use defaults. | `object`, `string` |
**Request Object**
| Property | Description | Default | Type |
| -------- | ----------- | ------- | ---- |
| body | The parsed request body. This value will still be deserialized and validated, but not parsed. | `undefined` | Any |
| cookie | An object of cookie key value pairs where each value is a string. | `{}` | `object` |
| header | An object of header key value pairs where each value is a string. | `{}` | `object` |
| method | The HTTP method. | `"get"` | `string` |
| path | The request path, including query string parameters. | `""` | `string` |
Returns: A parsed, deserialized, and validated request object.
```js
// create the enforcer instance
const enforcer = new Enforcer({
openapi: '3.0.0',
paths: {
'/path/{id}': {
put: {
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
x: {
type: 'number'
},
y: {
type: 'integer'
},
d: {
type: 'string',
format: 'date-time'
}
}
}
}
}
}
},
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'number'
}
},
{
name: 'date',
in: 'query',
explode: true,
schema: {
type: 'string',
format: 'date'
}
}
]
}
}
});
// parse, deserialize, and validate request
const req = enforcer.request({
body: {
x: '123.4', // value will be deserialized
y: 2, // already deserialized is OK too
d: '2000-01-01T01:02:03.456Z' // will be deserialized to Date object
},
header: {
'content-type': 'application/json' // needed to identify body schema
},
method: 'put',
path: '/path/25?date=2000-01-01&date=2000-01-02'
});
/*
req => {
body: {
x: 123.4,
y: 2,
d: <Date Object>
},
header: {
'content-type': 'application/json'
},
method: 'put',
path: {
id: 25
},
query: {
date: [
<Date Object>,
<Date Object>
]
}
}
*/
```
## Enforcer.prototype.serialize
Serialize a value for sending via HTTP. This function does not validate the value prior to serialization so you may want to use [`Enforcer.prototype.errors`](#enforcerprototypeerrors) or [`Enforcer.prototype.validate`](#enforcerprototypevalidate) prior to serialization.
Signature: `Enforcer.prototype.validate ( schema, value )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to serialize from | `object` |
| value | The value to serialize | Any |
Returns a serialized value.
## Enforcer.prototype.validate
Validate that the value adheres to the schema or throw an `Error`. This function calls [`enforcer.errors`](#enforcererrors--schema-value-) and if any errors occur then it packages them into a single `Error` instance and throws the `Error`.
`Enforcer.prototype.validate ( schema, value )`
| Parameter | Description | Type |
| --------- | ----------- | ---- |
| schema | The schema to build from | `object` |
| params | A map of keys to values. These values are used to help build the final value | `object` |
| value | An initial value to start with. | Any |
Returns `undefined`.
```js
const OpenApiEnforcer = require('../index');
const enforcer = new OpenApiEnforcer({ openapi: '3.0.0' });
const schema = {
type: 'object',
additionalProperties: false,
properties: {
names: {
type: 'array',
items: {
type: 'string',
minLength: 1
}
},
date: {
type: 'string',
format: 'date-time',
maximum: '2000-01-01T00:00:00.000Z'
}
}
};
enforcer.validate(schema, {
names: [ 'Bob', 'Jan', '' ],
date: '2010-01-01T00:00:00.000Z',
num: 8
});
// Error: One or more errors found during schema validation:
// /names/2: String length below minimum length of 1 with length of 0: ''
// /date: Expected date-time to be less than or equal to 2000-01-01T00:00:00.000Z. Received: 2010-01-01T00:00:00.000Z
// /num: Property not allowed
// at ...
```
## Appendix
### About default, x-template, and x-variable
The `default` attribute is part of the OpenAPI specification. The type of it's value must be the same as the schema type. For example, if the schema is of type string, default cannot be a number. When `default` is a string [it can behave](#options-populate-templatedefaults) like `x-template` and [substitute parameters](#parameter-replacement) into the string. The advantage of using `default` over `x-template` in this scenario is that the `default` value will often appear in OpenAPI documentation generators.
The `x-template` value must be a string that will have [parameter replacement](#parameter-replacement) occur on it. Parameters in the string may use handlebars, double handlebars, or colons depending on how the Enforcer instance has been [configured](#optionspopulatereplacement).
The `x-variable` will perform value substitution only.
If a conflict arises between the provided value, `default`, `x-template`, or `x-variable` then the following priority is observed:
1. The provided value
2. `x-variable`
3. `x-template`
4. `default`
```js
const Enforcer = require('openapi-enforcer');
const enforcer = new Enforcer('3.0.0');
const schema = {
type: 'object',
properties: {
firstName: {
type: 'string',
'x-variable': 'firstName'
},
lastName: {
type: 'string',
'x-variable': 'lastName'
},
fullName: {
type: 'string',
'x-template': '{firstName} {lastName}'
},
profileUrl: {
type: 'string',
default: 'https://your-domain.com/users/{id}'
}
}
};
const params = {
id: 12345,
firstName: 'Jan',
lastName: 'Smith'
}
const value = enforcer.populate(schema, params);
// value ==> {
// firstName: 'Jan',
// lastName: 'Smith',
// fullName: 'Jan Smith',
// profileUrl: 'https://your-domain.com/users/12345'
// }
```
### Error Throwing vs Reporting
In some cases it is useful to receive an error in the return value instead of having it thrown. To this end, some of the functions in this library have the ability to either throw an error or return it. The functions that support this functionality will have a `throw` option that defaults to `true`.
If `throw` is set to `true` then an encountered error will be thrown. If no error is encountered then the value will returned normally.
If `throw` is set to `false` then it will return an object with two properties: `error` and `value`. If an error occured then `value` will be `null`, otherwise `error` will be `null`.
```js
const data = enforcer.deserialize(schema, value, { throw: false })
if (data.error) {
console.error('Error: ' + data.error)
} else {
console.log('Value:' + value)
}
```
### Parameter Replacement
Parameter replacement is when part of a string is populated with parameters. This applies to a schema's `x-template` value and potentially `default` value. There are three types of replacement:
1. handlebar (default)
```js
const Enforcer = require('openapi-enforcer');
const options = {
populate: { replacement: 'handlebar' }
};
const enforcer = new Enforcer('3.0.0', options);
const schema = {
type: 'string',
'x-template': '{name} is {age} years old'
};
const value = enforcer.populate(schema, { name: 'Bob', age: 25 });
// value ===> 'Bob is 25 years old
```
2. doubleHandlebar
```js
const Enforcer = require('openapi-enforcer');
const options = {
populate: { replacement: 'doubleHandlebar' }
};
const enforcer = new Enforcer('3.0.0', options);
const schema = {
type: 'string',
'x-template': '{{name}} is {{age}} years old'
};
const value = enforcer.populate(schema, { name: 'Bob', age: 25 });
// value ===> 'Bob is 25 years old
```
3. colon
```js
const Enforcer = require('openapi-enforcer');
const options = {
populate: { replacement: 'colon' }
};
const enforcer = new Enforcer('3.0.0', options);
const schema = {
type: 'string',
'x-template': ':name is :age years old'
};
const value = enforcer.populate(schema, { name: 'Bob', age: 25 });
// value ===> 'Bob is 25 years old
```
https://github.com/byu-oit/openapi-enforcer/tree/master/docs

@@ -1,11 +0,5 @@

const Module = require('module');
const Enforcer = require('./')
const originalRequire = Module.prototype.require;
Module.prototype.require = function() {
console.log(this.filename + ' loading ' + arguments[0]);
return originalRequire.apply(this, arguments);
};
const x = Enforcer.Exception('asdf')
const v3 = require('./bin/definition/_v3-parameter-base');
console.log(v3.errors);
x.message('hi').me

@@ -18,51 +18,99 @@ /**

'use strict';
const Exception = require('../bin/exception');
const expect = require('chai').expect;
const Exception = require('../bin/exception');
describe('OpenAPIException', () => {
describe('exception', () => {
it('header only', () => {
const exception = new Exception('header');
expect('' + exception).to.equal('');
});
describe('no error', () => {
it('nested strings', () => {
const exception = new Exception('header');
exception.push('one');
exception.push('two');
exception.push('three');
expect(exception.toString()).to.equal('header:\n one\n two\n three');
});
it('no children', () => {
const exception = Exception('title');
expect(exception.hasException).to.be.false;
expect(exception.toString()).to.equal('');
});
it('nested exceptions', () => {
const exception = new Exception('header');
exception.nest('one');
exception.nest('two');
expect(String(exception)).to.equal('');
it('"at" children', () => {
const exception = Exception('title');
exception.at('a');
expect(exception.hasException).to.be.false;
expect(exception.toString()).to.equal('');
});
it('"nest" children', () => {
const exception = Exception('title');
exception.nest('a');
expect(exception.hasException).to.be.false;
expect(exception.toString()).to.equal('');
});
});
it('deep nested exceptions', () => {
const exception = new Exception('header');
exception.push('one');
const n2 = exception.nest('two');
const n3 = n2.nest('a');
n3.push('x');
exception.push('three');
expect('' + exception).to.equal('header:\n one\n two:\n a:\n x\n three');
describe('single error', () => {
it('no children', () => {
const exception = Exception('title');
exception.message('hello');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n hello');
});
it('"at" children', () => {
const exception = Exception('title');
exception.at('a').message('hello');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n at: a\n hello');
});
it('"nest" children', () => {
const exception = Exception('title');
exception.nest('a').message('hello');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n a\n hello');
});
});
it('flattened', () => {
const exception = new Exception('header');
exception.push('one');
const n2 = exception.nest('two');
const n3 = n2.nest('a');
n3.push('x');
exception.push('three');
expect(exception.flatten()).to.deep.equal([
'header: one',
'header: two: a: x',
'header: three'
]);
describe('"at" chain', () => {
it('joins two chained "at"s into one', () => {
const exception = Exception('title');
exception.at('a').at('b').message('hello');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n at: a > b\n hello');
});
it('joins 3 chained "at"s into one', () => {
const exception = Exception('title');
exception.at('some key').at('anotherLongKey').at('abc').message('hello');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n at: some key > anotherLongKey > abc\n hello');
});
it('can have multi-level split chaining', () => {
const exception = Exception('title');
const child = exception.at('a').at('b');
child.at('c').message('one');
child.message('two');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n at: a > b\n at: c\n one\n two');
});
it('can have multi-level split chaining with "nest"', () => {
const exception = Exception('title');
const child = exception.at('a').at('b');
child.at('c').message('one');
child.nest('nest').message('two');
expect(exception.hasException).to.be.true;
expect(exception.toString()).to.equal('title\n at: a > b\n at: c\n one\n nest\n two');
});
it('will link two at with same name', () => {
const exception = Exception('title');
exception.at('a').message('one');
exception.at('a').message('two');
expect(exception.toString()).to.equal('title\n at: a\n one\n two');
});
});
});

@@ -18,118 +18,32 @@ /**

'use strict';
const expect = require('chai').expect;
const util = require('../bin/util');
const expect = require('chai').expect;
const util = require('../bin/util');
describe('util', () => {
describe('copy', () => {
describe('randomNumber', () => {
const randomNumber = util.randomNumber;
it('can copy primitive', () => {
expect(util.copy(5)).to.equal(5);
it('does not require any parameters', () => {
const num = randomNumber();
expect(num).to.be.a('number');
});
it('can copy Date', () => {
const date = new Date();
const copy = util.copy(date);
expect(copy).to.not.equal(date);
expect(copy.toISOString()).to.equal(date.toISOString());
it('can have just a minimum', () => {
const num = randomNumber({ min: 1000 });
expect(num).to.be.at.least(1000);
});
it('can copy buffer', () => {
const buffer = Buffer.from ? Buffer.from('abcdefg') : new Buffer('abcdefg');
const copy = util.copy(buffer);
expect(copy).to.not.equal(buffer);
expect(copy.toString()).to.equal(buffer.toString());
it('can have just a maximum', () => {
const num = randomNumber({ max: 0 });
expect(num).to.be.at.most(0);
});
it('can copy object', () => {
const value = {
a: 1,
b: {
x: 'x',
y: {
y1: [ 1, 2, 3 ]
}
}
};
const copy = util.copy(value);
expect(copy).to.not.equal(value);
expect(copy.b).to.not.equal(value.b);
expect(copy.b.y).to.not.equal(value.b.y);
expect(copy.b.y.y1).to.not.equal(value.b.y.y1);
expect(copy).to.deep.equal(value);
it('can specify a spread', () => {
const num = randomNumber({ spread: 0 });
expect(num).to.equal(0)
});
it('can copy array', () => {
const value = [ 1, {}, [ 2, 'a' ] ];
const copy = util.copy(value);
expect(copy).to.not.equal(value);
expect(copy[1]).to.not.equal(value[1]);
expect(copy[2]).to.not.equal(value[2]);
expect(copy).to.deep.equal(value);
});
it('can copy circular', () => {
const value = { array: [] };
value.object = value;
value.array.push(value.array);
const copy = util.copy(value);
expect(copy).to.not.equal(value);
expect(copy.object).to.not.equal(value.object);
expect(copy.array).to.not.equal(value.array);
expect(copy).to.deep.equal(value);
});
});
describe('media match', () => {
it('no match', () => {
const matches = util.findMediaMatch('text/plain', ['x/y', 'x/z']);
expect(matches.length).to.equal(0);
});
it('text match', () => {
const accept = 'text/html, application/xhtml';
const store = ['text/plain', 'text/html'];
const matches = util.findMediaMatch(accept, store);
expect(matches).to.deep.equal(['text/html']);
});
it('wildcard subtype match', () => {
const accept = 'text/*';
const store = ['text/plain', 'text/html'];
const matches = util.findMediaMatch(accept, store);
expect(matches).to.deep.equal(['text/plain', 'text/html']);
});
it('wildcard type match', () => {
const accept = '*/plain';
const store = ['text/plain', 'something/plain'];
const matches = util.findMediaMatch(accept, store);
expect(matches).to.deep.equal(['text/plain', 'something/plain']);
});
it('multiple accept', () => {
const accept = 'text/html, application/abc+json, application/xml;q=0.9, */*;q=0.8';
const store = ['text/plain', 'text/html', 'application/json', 'application/xml'];
const matches = util.findMediaMatch(accept, store);
expect(matches).to.deep.equal(['text/html', 'application/json', 'application/xml', 'text/plain']);
});
it('specified extension match', () => {
const accept = 'application/abc+json';
const store = ['application/xyz+json', 'application/json', 'application/abc+json'];
const matches = util.findMediaMatch(accept, store);
expect(matches).to.deep.equal(['application/abc+json', 'application/json']);
});
it('non specified extension match', () => {
const accept = 'application/json';
const store = ['application/xyz+json', 'application/json', 'application/abc+json'];
const matches = util.findMediaMatch(accept, store);
expect(matches).to.deep.equal(['application/json', 'application/xyz+json', 'application/abc+json']);
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc