to-json-schema
Version:
Convert JS object to JSON Schema
109 lines (92 loc) • 3.59 kB
JavaScript
;
var isEqual = require('lodash.isequal');
var xor = require('lodash.xor');
var keys = require('lodash.keys');
var _require = require('./jsonSchemaHelpers'),
types = _require.types,
FORMAT_REGEXPS = _require.FORMAT_REGEXPS,
isFormat = _require.isFormat;
var helpers = {
stringFormats: keys(FORMAT_REGEXPS),
isFormat: isFormat,
typeNames: ['integer', 'number', // make sure number is after integer (for proper type detection)
'string', 'array', 'object', 'boolean', 'null', 'date'],
getType: function getType(val) {
return helpers.typeNames.find(function (typeName) {
return types[typeName](val);
});
},
/**
* Tries to find the least common schema from two supplied JSON schemas. If it is unable to find
* such a schema, it returns null. Incompatibility in structure/types leads to returning null,
* except when the difference is only integer/number. Than the 'number' is used instead 'int'.
* Types/Structure incompatibility in array items only leads to schema that doesn't specify
* items structure/type.
* @param {object} schema1 - JSON schema
* @param {object} schema2 - JSON schema
* @returns {object|null}
*/
mergeSchemaObjs: function mergeSchemaObjs(schema1, schema2) {
var schema1Keys = keys(schema1);
var schema2Keys = keys(schema2);
if (!isEqual(schema1Keys, schema2Keys)) {
if (schema1.type === 'array' && schema2.type === 'array') {
// TODO optimize???
if (isEqual(xor(schema1Keys, schema2Keys), ['items'])) {
var schemaWithoutItems = schema1Keys.length > schema2Keys.length ? schema2 : schema1;
var schemaWithItems = schema1Keys.length > schema2Keys.length ? schema1 : schema2;
var isSame = keys(schemaWithoutItems).reduce(function (acc, current) {
return isEqual(schemaWithoutItems[current], schemaWithItems[current]) && acc;
}, true);
if (isSame) {
return schemaWithoutItems;
}
}
}
if (schema1.type !== 'object' || schema2.type !== 'object') {
return null;
}
}
var retObj = {};
for (var i = 0, length = schema1Keys.length; i < length; i++) {
var key = schema1Keys[i];
if (helpers.getType(schema1[key]) === 'object') {
var x = helpers.mergeSchemaObjs(schema1[key], schema2[key]);
if (!x) {
if (schema1.type === 'object' || schema2.type === 'object') {
return {
type: 'object'
};
} // special treatment for array items. If not mergeable, we can do without them
if (key !== 'items' || schema1.type !== 'array' || schema2.type !== 'array') {
return null;
}
} else {
retObj[key] = x;
}
} else {
// simple value schema properties (not defined by object)
if (key === 'type') {
// eslint-disable-line no-lonely-if
if (schema1[key] !== schema2[key]) {
if (schema1[key] === 'integer' && schema2[key] === 'number' || schema1[key] === 'number' && schema2[key] === 'integer') {
retObj[key] = 'number';
} else {
return null;
}
} else {
retObj[key] = schema1[key];
}
} else {
if (!isEqual(schema1[key], schema2[key])) {
// TODO Is it even possible to take this path?
return null;
}
retObj[key] = schema1[key];
}
}
}
return retObj;
}
};
module.exports = helpers;