yapi/common/json-schema-mockjs.js

1153 lines
32 KiB
JavaScript
Raw Normal View History

2017-10-05 09:12:44 +08:00
function _interopDefault(ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var $RefParser = _interopDefault(require('json-schema-ref-parser'));
var deref = _interopDefault(require('deref'));
var tslib_1 = require('tslib');
// dynamic proxy for custom generators
function proxy(gen) {
return function (value, schema, property) {
var fn = value;
var args = [];
// support for nested object, first-key is the generator
if (typeof value === 'object') {
fn = Object.keys(value)[0];
// treat the given array as arguments,
if (Array.isArray(value[fn])) {
// if the generator is expecting arrays they should be nested, e.g. `[[1, 2, 3], true, ...]`
args = value[fn];
}
else {
args.push(value[fn]);
}
}
// support for keypaths, e.g. "internet.email"
var props = fn.split('.');
// retrieve a fresh dependency
var ctx = gen();
while (props.length > 1) {
ctx = ctx[props.shift()];
}
// retrieve last value from context object
value = typeof ctx === 'object' ? ctx[props[0]] : ctx;
// invoke dynamic generators
if (typeof value === 'function') {
value = value.apply(ctx, args);
}
// test for pending callbacks
if (Object.prototype.toString.call(value) === '[object Object]') {
for (var key in value) {
if (typeof value[key] === 'function') {
throw new Error('Cannot resolve value for "' + property + ': ' + fn + '", given: ' + value);
}
}
}
return value;
};
}
/**
* Container is used to wrap external generators (faker, chance, casual, etc.) and its dependencies.
*
* - `jsf.extend('faker')` will enhance or define the given dependency.
* - `jsf.define('faker')` will provide the "faker" keyword support.
*
* RandExp is not longer considered an "extension".
*/
var Container = (function () {
function Container() {
// dynamic requires - handle all dependencies
// they will NOT be included on the bundle
this.registry = {};
this.support = {};
}
/**
* Override dependency given by name
* @param name
* @param callback
*/
Container.prototype.extend = function (name, callback) {
var _this = this;
this.registry[name] = callback(this.registry[name]);
// built-in proxy (can be overridden)
if (!this.support[name]) {
this.support[name] = proxy(function () { return _this.registry[name]; });
}
};
/**
* Set keyword support by name
* @param name
* @param callback
*/
Container.prototype.define = function (name, callback) {
this.support[name] = callback;
};
/**
* Returns dependency given by name
* @param name
* @returns {Dependency}
*/
Container.prototype.get = function (name) {
if (typeof this.registry[name] === 'undefined') {
throw new ReferenceError('"' + name + '" dependency doesn\'t exist.');
}
return this.registry[name];
};
/**
* Apply a custom keyword
* @param schema
*/
Container.prototype.wrap = function (schema) {
var keys = Object.keys(schema);
var length = keys.length;
while (length--) {
var fn = keys[length].replace(/^x-/, '');
var gen = this.support[fn];
if (typeof gen === 'function') {
schema.generate = function () { return gen(schema[keys[length]], schema, keys[length]); };
break;
}
}
return schema;
};
return Container;
}());
/**
* This class defines a registry for custom formats used within JSF.
*/
var Registry = (function () {
function Registry() {
// empty by default
this.data = {};
}
/**
* Registers custom format
*/
Registry.prototype.register = function (name, callback) {
this.data[name] = callback;
};
/**
* Register many formats at one shot
*/
Registry.prototype.registerMany = function (formats) {
for (var name in formats) {
this.data[name] = formats[name];
}
};
/**
* Returns element by registry key
*/
Registry.prototype.get = function (name) {
var format = this.data[name];
return format;
};
/**
* Returns the whole registry content
*/
Registry.prototype.list = function () {
return this.data;
};
return Registry;
}());
// instantiate
var registry = new Registry();
/**
* Custom format API
*
* @see https://github.com/json-schema-faker/json-schema-faker#custom-formats
* @param nameOrFormatMap
* @param callback
* @returns {any}
*/
function formatAPI$1(nameOrFormatMap, callback) {
if (typeof nameOrFormatMap === 'undefined') {
return registry.list();
}
else if (typeof nameOrFormatMap === 'string') {
if (typeof callback === 'function') {
registry.register(nameOrFormatMap, callback);
}
else {
return registry.get(nameOrFormatMap);
}
}
else {
registry.registerMany(nameOrFormatMap);
}
}
/**
* This class defines a registry for custom settings used within JSF.
*/
var OptionRegistry = (function (_super) {
tslib_1.__extends(OptionRegistry, _super);
function OptionRegistry() {
var _this = _super.call(this) || this;
_this.data['failOnInvalidTypes'] = true;
_this.data['defaultInvalidTypeProduct'] = null;
_this.data['failOnInvalidFormat'] = true;
_this.data['useDefaultValue'] = false;
_this.data['requiredOnly'] = false;
_this.data['maxItems'] = null;
_this.data['maxLength'] = null;
_this.data['defaultMinItems'] = 0;
_this.data['defaultRandExpMax'] = 10;
_this.data['alwaysFakeOptionals'] = false;
_this.data['random'] = Math.random;
return _this;
}
return OptionRegistry;
}(Registry));
// instantiate
var registry$1 = new OptionRegistry();
/**
* Custom option API
*
* @param nameOrOptionMap
* @returns {any}
*/
function optionAPI(nameOrOptionMap) {
if (typeof nameOrOptionMap === 'string') {
return registry$1.get(nameOrOptionMap);
}
else {
return registry$1.registerMany(nameOrOptionMap);
}
}
var RandExp = require('randexp');
// set maximum default, see #193
RandExp.prototype.max = 10;
// same implementation as the original except using our random
RandExp.prototype.randInt = function (a, b) {
return a + Math.floor(optionAPI('random')() * (1 + b - a));
};
function _randexp(value) {
var re = new RandExp(value);
// apply given setting
re.max = optionAPI('defaultRandExpMax');
return re.gen();
}
function getSubAttribute(obj, dotSeparatedKey) {
var keyElements = dotSeparatedKey.split('.');
while (keyElements.length) {
var prop = keyElements.shift();
if (!obj[prop]) {
break;
}
obj = obj[prop];
}
return obj;
}
/**
* Returns true/false whether the object parameter has its own properties defined
*
* @param obj
* @param properties
* @returns {boolean}
*/
function hasProperties(obj) {
var properties = [];
for (var _i = 1; _i < arguments.length; _i++) {
properties[_i - 1] = arguments[_i];
}
return properties.filter(function (key) {
return typeof obj[key] !== 'undefined';
}).length > 0;
}
/**
* Returns typecasted value.
* External generators (faker, chance, casual) may return data in non-expected formats, such as string, when you might expect an
* integer. This function is used to force the typecast.
*
* @param value
* @param targetType
* @returns {any}
*/
function typecast(value, schema) {
// FIXME this function should cover most cases and should be reused within generators
switch (schema.type) {
case 'integer':
return parseInt(value, 10);
case 'number':
return parseFloat(value);
case 'string':
value = String(value);
var min = Math.max(schema.minLength || 0, 0);
var max = Math.min(schema.maxLength || Infinity, Infinity);
while (value.length < min) {
value += ' ' + value;
}
if (value.length > max) {
value = value.substr(0, max);
}
return value;
case 'boolean':
return !!value;
default:
return value;
}
}
function merge(a, b) {
for (var key in b) {
if (typeof b[key] !== 'object' || b[key] === null) {
a[key] = b[key];
}
else if (Array.isArray(b[key])) {
a[key] = a[key] || [];
// fix #292 - skip duplicated values from merge object (b)
b[key].forEach(function (value) {
if (a[key].indexOf(value)) {
a[key].push(value);
}
});
}
else if (typeof a[key] !== 'object' || a[key] === null || Array.isArray(a[key])) {
a[key] = merge({}, b[key]);
}
else {
a[key] = merge(a[key], b[key]);
}
}
return a;
}
function clean(obj, isArray, requiredProps) {
if (!obj || typeof obj !== 'object') {
return obj;
}
if (Array.isArray(obj)) {
obj = obj
.map(function (value) { return clean(value, true); })
.filter(function (value) { return typeof value !== 'undefined'; });
return obj;
}
Object.keys(obj).forEach(function (k) {
if (!requiredProps || requiredProps.indexOf(k) === -1) {
if (Array.isArray(obj[k]) && !obj[k].length) {
delete obj[k];
}
}
else {
obj[k] = clean(obj[k]);
}
});
if (!Object.keys(obj).length && isArray) {
return undefined;
}
return obj;
}
function short(schema) {
var s = JSON.stringify(schema);
var l = JSON.stringify(schema, null, 2);
return s.length > 400 ? l.substr(0, 400) + '...' : l;
}
var utils = {
getSubAttribute: getSubAttribute,
hasProperties: hasProperties,
typecast: typecast,
merge: merge,
clean: clean,
short: short,
randexp: _randexp
};
/// <reference path="../index.d.ts" />
/**
* Returns random element of a collection
*
* @param collection
* @returns {T}
*/
function pick(collection) {
return collection[Math.floor(optionAPI('random')() * collection.length)];
}
/**
* Returns shuffled collection of elements
*
* @param collection
* @returns {T[]}
*/
function shuffle(collection) {
var tmp, key, copy = collection.slice(), length = collection.length;
for (; length > 0;) {
key = Math.floor(optionAPI('random')() * length);
// swap
tmp = copy[--length];
copy[length] = copy[key];
copy[key] = tmp;
}
return copy;
}
/**
* These values determine default range for random.number function
*
* @type {number}
*/
var MIN_NUMBER = -100;
var MAX_NUMBER = 100;
/**
* Returns a random integer between min (inclusive) and max (inclusive)
* Using Math.round() will give you a non-uniform distribution!
* @see http://stackoverflow.com/a/1527820/769384
*/
function getRandom(min, max) {
return optionAPI('random')() * (max - min) + min;
}
/**
* Generates random number according to parameters passed
*
* @param min
* @param max
* @param defMin
* @param defMax
* @param hasPrecision
* @returns {number}
*/
function number(min, max, defMin, defMax, hasPrecision) {
if (hasPrecision === void 0) { hasPrecision = false; }
defMin = typeof defMin === 'undefined' ? MIN_NUMBER : defMin;
defMax = typeof defMax === 'undefined' ? MAX_NUMBER : defMax;
min = typeof min === 'undefined' ? defMin : min;
max = typeof max === 'undefined' ? defMax : max;
if (max < min) {
max += min;
}
var result = getRandom(min, max);
if (!hasPrecision) {
return Math.round(result);
}
return result;
}
var random = {
pick: pick,
shuffle: shuffle,
number: number,
};
var ParseError = (function (_super) {
tslib_1.__extends(ParseError, _super);
function ParseError(message, path) {
var _this = _super.call(this) || this;
_this.path = path;
if (Error.captureStackTrace) {
Error.captureStackTrace(_this, _this.constructor);
}
_this.name = 'ParseError';
_this.message = message;
_this.path = path;
return _this;
}
return ParseError;
}(Error));
var inferredProperties = {
array: [
'additionalItems',
'items',
'maxItems',
'minItems',
'uniqueItems'
],
integer: [
'exclusiveMaximum',
'exclusiveMinimum',
'maximum',
'minimum',
'multipleOf'
],
object: [
'additionalProperties',
'dependencies',
'maxProperties',
'minProperties',
'patternProperties',
'properties',
'required'
],
string: [
'maxLength',
'minLength',
'pattern'
]
};
inferredProperties.number = inferredProperties.integer;
var subschemaProperties = [
'additionalItems',
'items',
'additionalProperties',
'dependencies',
'patternProperties',
'properties'
];
/**
* Iterates through all keys of `obj` and:
* - checks whether those keys match properties of a given inferred type
* - makes sure that `obj` is not a subschema; _Do not attempt to infer properties named as subschema containers. The
* reason for this is that any property name within those containers that matches one of the properties used for
* inferring missing type values causes the container itself to get processed which leads to invalid output. (Issue 62)_
*
* @returns {boolean}
*/
function matchesType(obj, lastElementInPath, inferredTypeProperties) {
return Object.keys(obj).filter(function (prop) {
var isSubschema = subschemaProperties.indexOf(lastElementInPath) > -1, inferredPropertyFound = inferredTypeProperties.indexOf(prop) > -1;
if (inferredPropertyFound && !isSubschema) {
return true;
}
}).length > 0;
}
/**
* Checks whether given `obj` type might be inferred. The mechanism iterates through all inferred types definitions,
* tries to match allowed properties with properties of given `obj`. Returns type name, if inferred, or null.
*
* @returns {string|null}
*/
function inferType(obj, schemaPath) {
for (var typeName in inferredProperties) {
var lastElementInPath = schemaPath[schemaPath.length - 1];
if (matchesType(obj, lastElementInPath, inferredProperties[typeName])) {
return typeName;
}
}
}
/**
* Generates randomized boolean value.
*
* @returns {boolean}
*/
function booleanGenerator() {
return optionAPI('random')() > 0.5;
}
var booleanType = booleanGenerator;
/**
* Generates null value.
*
* @returns {null}
*/
function nullGenerator() {
return null;
}
var nullType = nullGenerator;
// TODO provide types
function unique(path, items, value, sample, resolve, traverseCallback) {
var tmp = [], seen = [];
function walk(obj) {
var json = JSON.stringify(obj);
if (seen.indexOf(json) === -1) {
seen.push(json);
tmp.push(obj);
}
}
items.forEach(walk);
// TODO: find a better solution?
var limit = 100;
while (tmp.length !== items.length) {
walk(traverseCallback(value.items || sample, path, resolve));
if (!limit--) {
break;
}
}
return tmp;
}
// TODO provide types
var arrayType = function arrayType(value, path, resolve, traverseCallback) {
var items = [];
if (!(value.items || value.additionalItems)) {
if (utils.hasProperties(value, 'minItems', 'maxItems', 'uniqueItems')) {
throw new ParseError('missing items for ' + utils.short(value), path);
}
return items;
}
// see http://stackoverflow.com/a/38355228/769384
// after type guards support subproperties (in TS 2.0) we can simplify below to (value.items instanceof Array)
// so that value.items.map becomes recognized for typescript compiler
var tmpItems = value.items;
if (tmpItems instanceof Array) {
return Array.prototype.concat.call(items, tmpItems.map(function (item, key) {
var itemSubpath = path.concat(['items', key + '']);
return traverseCallback(item, itemSubpath, resolve);
}));
}
var minItems = value.minItems;
//var maxItems = value.maxItems;
var maxItems = 1;
if (optionAPI('defaultMinItems') && minItems === undefined) {
// fix boundaries
minItems = !maxItems
? optionAPI('defaultMinItems')
: Math.min(optionAPI('defaultMinItems'), maxItems);
}
if (optionAPI('maxItems')) {
// Don't allow user to set max items above our maximum
if (maxItems && maxItems > optionAPI('maxItems')) {
maxItems = optionAPI('maxItems');
}
// Don't allow user to set min items above our maximum
if (minItems && minItems > optionAPI('maxItems')) {
minItems = maxItems;
}
}
var length = (maxItems != null && optionAPI('alwaysFakeOptionals')) ?
maxItems : random.number(minItems, maxItems, 1, 5),
// TODO below looks bad. Should additionalItems be copied as-is?
sample = typeof value.additionalItems === 'object' ? value.additionalItems : {};
for (var current = items.length; current < length; current++) {
var itemSubpath = path.concat(['items', current + '']);
var element = traverseCallback(value.items || sample, itemSubpath, resolve);
items.push(element);
}
if (value.uniqueItems) {
return unique(path.concat(['items']), items, value, sample, resolve, traverseCallback);
}
return items;
};
var MIN_INTEGER = -100000000;
var MAX_INTEGER = 100000000;
var numberType$1 = function numberType(value) {
var min = typeof value.minimum === 'undefined' ? MIN_INTEGER : value.minimum, max = typeof value.maximum === 'undefined' ? MAX_INTEGER : value.maximum, multipleOf = value.multipleOf;
if (multipleOf) {
max = Math.floor(max / multipleOf) * multipleOf;
min = Math.ceil(min / multipleOf) * multipleOf;
}
if (value.exclusiveMinimum && value.minimum && min === value.minimum) {
min += multipleOf || 1;
}
if (value.exclusiveMaximum && value.maximum && max === value.maximum) {
max -= multipleOf || 1;
}
if (min > max) {
return NaN;
}
if (multipleOf) {
return Math.floor(random.number(min, max) / multipleOf) * multipleOf;
}
return random.number(min, max, undefined, undefined, true);
};
// The `integer` type is just a wrapper for the `number` type. The `number` type
// returns floating point numbers, and `integer` type truncates the fraction
// part, leaving the result as an integer.
var integerType = function integerType(value) {
var generated = numberType$1(value);
// whether the generated number is positive or negative, need to use either
// floor (positive) or ceil (negative) function to get rid of the fraction
return generated > 0 ? Math.floor(generated) : Math.ceil(generated);
};
var LIPSUM_WORDS = ('Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod tempor incididunt ut labore'
+ ' et dolore magna aliqua Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea'
+ ' commodo consequat Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla'
+ ' pariatur Excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt mollit anim id est'
+ ' laborum').split(' ');
/**
* Generates randomized array of single lorem ipsum words.
*
* @param length
* @returns {Array.<string>}
*/
function wordsGenerator$1(length) {
var words = random.shuffle(LIPSUM_WORDS);
return words.slice(0, length);
}
// fallback generator
var anyType = { type: ['string', 'number', 'integer', 'boolean'] };
// TODO provide types
var objectType = function objectType(value, path, resolve, traverseCallback) {
var props = {};
var properties = value.properties || {};
2017-10-15 17:46:53 +08:00
var propertyKeys = value.required = Object.keys(properties);
2017-10-05 09:12:44 +08:00
var patternProperties = value.patternProperties || {};
var requiredProperties = (value.required || []).slice();
var allowsAdditional = value.additionalProperties === false ? false : true;
2017-10-15 17:46:53 +08:00
2017-10-05 09:12:44 +08:00
var patternPropertyKeys = Object.keys(patternProperties);
var additionalProperties = allowsAdditional
? (value.additionalProperties === true ? {} : value.additionalProperties)
: null;
if (!allowsAdditional &&
propertyKeys.length === 0 &&
patternPropertyKeys.length === 0 &&
utils.hasProperties(value, 'minProperties', 'maxProperties', 'dependencies', 'required')) {
throw new ParseError('missing properties for:\n' + utils.short(value), path);
}
if (optionAPI('requiredOnly') === true) {
requiredProperties.forEach(function (key) {
if (properties[key]) {
props[key] = properties[key];
}
});
return traverseCallback(props, path.concat(['properties']), resolve);
}
var min = Math.max(value.minProperties || 0, requiredProperties.length);
var max = Math.max(value.maxProperties || random.number(min, min + 5));
random.shuffle(patternPropertyKeys.concat(propertyKeys)).forEach(function (_key) {
if (requiredProperties.indexOf(_key) === -1) {
requiredProperties.push(_key);
}
});
// properties are read from right-to-left
var _props = optionAPI('alwaysFakeOptionals') ? requiredProperties
: requiredProperties.slice(0, random.number(min, max));
_props.forEach(function (key) {
// first ones are the required properies
if (properties[key]) {
props[key] = properties[key];
}
else {
var found;
// then try patternProperties
patternPropertyKeys.forEach(function (_key) {
if (key.match(new RegExp(_key))) {
found = true;
props[utils.randexp(key)] = patternProperties[_key];
}
});
if (!found) {
// try patternProperties again,
var subschema = patternProperties[key] || additionalProperties;
if (subschema) {
// otherwise we can use additionalProperties?
props[patternProperties[key] ? utils.randexp(key) : key] = subschema;
}
}
}
});
var current = Object.keys(props).length;
while (true) {
if (!(patternPropertyKeys.length || allowsAdditional)) {
break;
}
if (current >= min) {
break;
}
if (allowsAdditional) {
var word = wordsGenerator$1(1) + utils.randexp('[a-f\\d]{1,3}');
if (!props[word]) {
props[word] = additionalProperties || anyType;
current += 1;
}
}
patternPropertyKeys.forEach(function (_key) {
var word = utils.randexp(_key);
if (!props[word]) {
props[word] = patternProperties[_key];
current += 1;
}
});
}
if (!allowsAdditional && current < min) {
throw new ParseError('properties constraints were too strong to successfully generate a valid object for:\n' +
utils.short(value), path);
}
return traverseCallback(props, path.concat(['properties']), resolve);
};
/**
* Helper function used by thunkGenerator to produce some words for the final result.
*
* @returns {string}
*/
function produce() {
var length = random.number(1, 5);
return wordsGenerator$1(length).join(' ');
}
/**
* Generates randomized concatenated string based on words generator.
*
* @returns {string}
*/
function thunkGenerator$1(min, max) {
if (min === void 0) { min = 0; }
if (max === void 0) { max = 140; }
var min = Math.max(0, min), max = random.number(min, max), result = produce();
// append until length is reached
while (result.length < min) {
result += produce();
}
// cut if needed
if (result.length > max) {
result = result.substr(0, max);
}
return result;
}
/**
* Generates randomized ipv4 address.
*
* @returns {string}
*/
function ipv4Generator() {
return [0, 0, 0, 0].map(function () {
return random.number(0, 255);
}).join('.');
}
var MOST_NEAR_DATETIME = 2524608000000;
/**
* Generates randomized date time ISO format string.
*
* @returns {string}
*/
function dateTimeGenerator$1() {
var date = new Date();
var days = random.number(-1000, MOST_NEAR_DATETIME);
date.setTime(date.getTime() - days);
return date.toISOString();
}
/**
* Predefined core formats
* @type {[key: string]: string}
*/
var regexps = {
email: '[a-zA-Z\\d][a-zA-Z\\d-]{1,13}[a-zA-Z\\d]@{hostname}',
hostname: '[a-zA-Z]{1,33}\\.[a-z]{2,4}',
ipv6: '[a-f\\d]{4}(:[a-f\\d]{4}){7}',
uri: '[a-zA-Z][a-zA-Z0-9+-.]*'
};
/**
* Generates randomized string basing on a built-in regex format
*
* @param coreFormat
* @returns {string}
*/
function coreFormatGenerator(coreFormat) {
return utils.randexp(regexps[coreFormat]).replace(/\{(\w+)\}/, function (match, key) {
return utils.randexp(regexps[key]);
});
}
function generateFormat(value, invalid) {
var callback = formatAPI$1(value.format);
if (typeof callback === 'function') {
return callback(value);
}
switch (value.format) {
case 'date-time':
return dateTimeGenerator$1();
case 'ipv4':
return ipv4Generator();
case 'regex':
// TODO: discuss
return '.+?';
case 'email':
case 'hostname':
case 'ipv6':
case 'uri':
return coreFormatGenerator(value.format);
default:
if (typeof callback === 'undefined') {
if (optionAPI('failOnInvalidFormat')) {
throw new Error('unknown registry key ' + utils.short(value.format));
}
else {
return invalid();
}
}
throw new Error('unsupported format "' + value.format + '"');
}
}
var stringType = function stringType(value) {
var output;
var minLength = value.minLength;
var maxLength = value.maxLength;
if (optionAPI('maxLength')) {
// Don't allow user to set max length above our maximum
if (maxLength && maxLength > optionAPI('maxLength')) {
maxLength = optionAPI('maxLength');
}
// Don't allow user to set min length above our maximum
if (minLength && minLength > optionAPI('maxLength')) {
minLength = optionAPI('maxLength');
}
}
if (value.format) {
output = generateFormat(value, function () { return thunkGenerator$1(minLength, maxLength); });
}
else if (value.pattern) {
output = utils.randexp(value.pattern);
}
else {
output = thunkGenerator$1(minLength, maxLength);
}
while (output.length < minLength) {
output += optionAPI('random')() > 0.7 ? thunkGenerator$1() : utils.randexp('.+');
}
if (output.length > maxLength) {
output = output.substr(0, maxLength);
}
return output;
};
// var typeMap = {
// boolean: booleanType,
// null: nullType,
// array: arrayType,
// integer: integerType,
// number: numberType$1,
// object: objectType,
// string: stringType
// };
var typeMap = {
boolean: () => '@boolean',
null: nullType,
array: arrayType,
integer: () => '@integer',
number: () => '@natural',
object: objectType,
string: () => '@string',
};
// TODO provide types
function traverse(schema, path, resolve) {
schema = resolve(schema);
if (!schema) {
return;
}
if (Array.isArray(schema.enum)) {
return random.pick(schema.enum);
}
// thunks can return sub-schemas
if (typeof schema.thunk === 'function') {
return traverse(schema.thunk(), path, resolve);
}
if (typeof schema.generate === 'function') {
return utils.typecast(schema.generate(), schema);
}
if (optionAPI('useDefaultValue') && 'default' in schema) {
return schema.default;
}
// TODO remove the ugly overcome
var type = schema.type;
if (Array.isArray(type)) {
type = random.pick(type);
}
else if (typeof type === 'undefined') {
// Attempt to infer the type
type = inferType(schema, path) || type;
}
if (typeof type === 'string') {
if (!typeMap[type]) {
if (optionAPI('failOnInvalidTypes')) {
throw new ParseError('unknown primitive ' + utils.short(type), path.concat(['type']));
}
else {
return optionAPI('defaultInvalidTypeProduct');
}
}
else {
try {
return utils.clean(typeMap[type](schema, path, resolve, traverse), null, schema.required);
}
catch (e) {
if (typeof e.path === 'undefined') {
throw new ParseError(e.message, path);
}
throw e;
}
}
}
var copy = {};
if (Array.isArray(schema)) {
copy = [];
}
for (var prop in schema) {
if (typeof schema[prop] === 'object' && prop !== 'definitions') {
copy[prop] = traverse(schema[prop], path.concat([prop]), resolve);
}
else {
copy[prop] = schema[prop];
}
}
return copy;
}
function isKey(prop) {
return prop === 'enum' || prop === 'default' || prop === 'required' || prop === 'definitions';
}
// TODO provide types
function run(refs, schema, container) {
try {
return traverse(schema, [], function reduce(sub, maxReduceDepth) {
if (typeof maxReduceDepth === 'undefined') {
maxReduceDepth = random.number(1, 3);
}
if (!sub) {
return null;
}
// cleanup
if (sub.id && typeof sub.id === 'string') {
delete sub.id;
delete sub.$schema;
}
if (typeof sub.$ref === 'string') {
if (sub.$ref.indexOf('#/') === -1) {
var ref = deref.util.findByRef(sub.$ref, refs);
if (!ref) {
throw new Error('Reference not found: ' + sub.$ref);
}
return ref;
}
// just remove the reference
delete sub.$ref;
return sub;
}
if (Array.isArray(sub.allOf)) {
var schemas = sub.allOf;
delete sub.allOf;
// this is the only case where all sub-schemas
// must be resolved before any merge
schemas.forEach(function (subSchema) {
var _sub = reduce(subSchema, maxReduceDepth + 1);
// call given thunks if present
utils.merge(sub, typeof _sub.thunk === 'function'
? _sub.thunk()
: _sub);
});
}
if (Array.isArray(sub.oneOf || sub.anyOf)) {
var mix = sub.oneOf || sub.anyOf;
delete sub.anyOf;
delete sub.oneOf;
return {
thunk: function () {
var copy = utils.merge({}, sub);
utils.merge(copy, random.pick(mix));
return copy;
},
};
}
for (var prop in sub) {
if ((Array.isArray(sub[prop]) || typeof sub[prop] === 'object') && !isKey(prop)) {
sub[prop] = reduce(sub[prop], maxReduceDepth);
}
}
return container.wrap(sub);
});
}
catch (e) {
if (e.path) {
throw new Error(e.message + ' in ' + '/' + e.path.join('/'));
}
else {
throw e;
}
}
}
var container = new Container();
function getRefs(refs) {
var $refs = {};
if (Array.isArray(refs)) {
refs.map(deref.util.normalizeSchema).forEach(function (schema) {
$refs[schema.id] = schema;
});
}
else {
$refs = refs || {};
}
return $refs;
}
var jsf = function (schema, refs) {
var $ = deref();
var $refs = getRefs(refs);
return run($refs, $(schema, $refs, true), container);
};
jsf.resolve = function (schema, refs, cwd) {
if (typeof refs === 'string') {
cwd = refs;
refs = {};
}
// normalize basedir (browser aware)
cwd = cwd || (typeof process !== 'undefined' ? process.cwd() : '');
cwd = cwd.replace(/\/+$/, '') + '/';
var $refs = getRefs(refs);
// identical setup as json-schema-sequelizer
var fixedRefs = {
order: 300,
canRead: true,
read: function (file, callback) {
callback(null, deref.util.findByRef(cwd !== '/'
? file.url.replace(cwd, '')
: file.url, $refs));
},
};
return $RefParser
.dereference(cwd, schema, {
resolve: { fixedRefs: fixedRefs },
dereference: {
circular: 'ignore',
},
}).then(function (sub) { return jsf(sub, refs); });
};
jsf.utils = utils;
jsf.format = formatAPI$1;
jsf.option = optionAPI;
// built-in support
container.define('pattern', utils.randexp);
// returns itself for chaining
jsf.extend = function (name, cb) {
container.extend(name, cb);
return jsf;
};
jsf.define = function (name, cb) {
container.define(name, cb);
return jsf;
};
jsf.locate = function (name) {
return container.get(name);
};
var VERSION = "0.5.0-rc11";
jsf.version = VERSION;
module.exports = jsf;
2017-10-15 17:46:53 +08:00
var a = {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"username": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"phone": {
"type": "string"
},
"userStatus": {
"type": "integer",
"format": "int32",
"description": "User Status"
}
},
"xml": {
"name": "User"
}
}
}
console.log(jsf(a))