json-schema-mock
Version:
data mocker for json-schema.
294 lines (235 loc) • 8.57 kB
JavaScript
/**
* datamocker
* @author neekey <ni184775761@gmail.com>
*/
var _ = require( 'lodash' );
var Mockjs = require( 'mockjs' );
var MockRandom = Mockjs.Random;
var FormatMocker = require( './format' );
var TitleStringMockData = require( '../MOCK_DATA/title_string.json').join( ',' );
var DataMocker = function( schema ){
return Mockers._mocker( schema );
};
// todo 支持 正则 到随机数据的生成
var Mockers = {
_mocker: function( schema ){
schema = _.cloneDeep( schema );
/**
* 若为allOf, anyOf, oneOf, not之类 也先处理掉
* 由于XOf 中还可能嵌套 XOf,因此这里要循环下
*
*/
while( schema.allOf || schema.anyOf || schema.oneOf ){
var extraSchema = {};
var tmpAllOf = schema.allOf;
var tmpAnyOf = schema.anyOf;
var tmpOneOf = schema.oneOf;
delete schema.allOf;
delete schema.anyOf;
delete schema.oneOf;
// 如果是allOf 就全部合并
if( tmpAllOf && tmpAllOf.length ){
tmpAllOf.forEach( function( s ){
_.merge( extraSchema, s );
});
}
// 如果是oneOf 则随机选择其中一个
if( tmpOneOf && tmpOneOf.length ){
_.merge( extraSchema, tmpOneOf[ MockRandom.integer( 0, tmpOneOf.length - 1 )] );
}
// 如果是anyOf 则随机选择其中的N个合并
if( tmpAnyOf && tmpAnyOf.length ){
tmpAnyOf.forEach(function( s ){
if( MockRandom.boolean() ){
_.merge( extraSchema, s );
}
});
}
_.merge( schema, extraSchema );
// todo not 先不管了...
}
// 若给到了默认值,就直接返回默认值
if( schema.default ){
return schema.default;
}
/**
* 制定性越强的关键词越先被执行,mocker不处理 $ref,$ref的获取和组装应该在将schema传递给mocker之前进行
* todo 处理行内的$ref
*/
if( schema.enum && schema.enum.length > 0 ){
return schema.enum[ _.random( 0, schema.enum.length - 1 ) ];
}
/**
* 若存在format,则交给对应format mocker 处理
*/
if( schema.format ){
var formatRet = FormatMocker( schema.format, schema );
// 若该format是没有定义过的,则不会返回任何值,则无视
if( formatRet !== undefined ){
return formatRet;
}
}
/**
* 目前仅支持一种type的情况
*/
var type = schema.type;
if(_.isArray( type ) ){
type = type[ 0 ];
}
var ret = this[ type + 'Mocker' ] ? this[ type + 'Mocker' ]( schema ) : {};
return ret;
},
objectMocker: function( schema ){
var ret = {};
var self = this;
/** 检查关键词,先仅仅对 properties 进行支持
* maxProperties
* minProperties
* required
* additionalProperties, properties and patternProperties
* dependences
*/
if( schema.properties ){
_.each( schema.properties, function( value, key ){
ret[ key ] = self._mocker( value );
});
}
return ret;
},
arrayMocker: function( schema ){
var ret = [];
var self = this;
/**
* 检查关键词,先仅仅对 items 和 maxItems 和 minItems 进行支持
* additionalItems and items
* maxItems
* minItems
* uniqueItems
*/
// 若为数组
if( _.isArray( schema.items ) ){
_.each( schema.items, function( item ) {
ret.push( self._mocker( item ) );
});
}
// 若为对象
else if( _.isObject( schema.items ) ) {
var size = _.random( schema.minItems || 1, schema.maxItems || 5 );
_.times( size, function(){
ret.push( self._mocker( schema.items ) );
});
}
return ret;
},
stringMocker: function( schema ){
/**
* 先仅仅对maxLength,和minLength进行支持
* maxLength
* minLength
* enum
* pattern
* @type {string}
*/
var ret = null;
var strLen = MockRandom.integer( schema.minLength || 1, schema.maxLength || ( ( schema.minLength || 0 ) < 50 ? 50 : schema.minLength ) );
var beginIndex = MockRandom.integer( 0, TitleStringMockData.length - 1 - strLen );
ret = TitleStringMockData.substring( beginIndex, beginIndex + strLen );
return ret;
},
/**
* 最多保留数字的多少位小数
* @param number
* @param len
* @private
*/
_toFloat: function( number, len ){
var num = String( number );
var dotIndex = num.indexOf( '.' );
// 检查是否包含小数
if( dotIndex > 0 ){
num = num.substring( 0, dotIndex + len + 1 );
}
return parseFloat( num );
},
/**
* 获取最小需要的小数位,如 给定一个整数,需要和它进行较量,最好有一位小数,总之比传入的数的小数位多一位
* @param num
* @returns {number}
* @private
*/
_getMinFloat: function( num ){
var ret = /\.(0*)\d*$/.exec( num );
return ret ? ret[1].length + 1 : 1;
},
/**
* 对数字进行mock
* @param schema
* @param {Boolean} floating 模拟数据是否可以带有小数
* @returns {null}
* @private
*/
_numberMocker: function( schema, floating ){
var ret = null;
/**
* multipleOf
* maximum and exclusiveMaximum
* minimum and exclusiveMinimum
*/
if( schema.multipleOf ){
// 检查条件的合理性,若不合理则返回0
var multipleMin = 1;
var multipleMax = 5;
if( schema.maximum !== undefined ){
if( ( schema.maximum == schema.multipleOf && !schema.exclusiveMaximum ) || ( schema.maximum > schema.multipleOf ) ){
multipleMax = Math.floor( schema.maximum / schema.multipleOf );
}
else {
// 若最大值小于个值
multipleMin = 0;
multipleMax = 0;
}
}
ret = schema.multipleOf * MockRandom.integer( multipleMin, multipleMax );
}
else {
var minimum = schema.minimum || 0;
var maximum = schema.maximum || 9999;
// 最大最小值之间的差额
var gap = maximum - minimum;
/**
* 根据最大最小值,计算最小需要的浮点数,为何需要计算?看下面例子:
* - min: 0.000006
* - max: 0.000009
* 如果计算的时候对随机数直接保留2位小数,则就无法继续在最大最小值区间里面进行计算了
*/
var minFloat = this._getMinFloat( minimum );
minFloat = minFloat < this._getMinFloat( maximum ) ? this._getMinFloat( maximum ) : minFloat;
var maxFloat = minFloat + _.random( 0, 2 );
/**
* 一个很小,小于最大值和最小值差额的值,用于出现临界情况时进行修正的值
*/
var littleGap = this._toFloat( _.random( 0, gap, floating ),_.random( minFloat, maxFloat ) ) / 10;
ret = this._toFloat( _.random( minimum, maximum, floating ), _.random( minFloat, maxFloat ) );
if( ret === schema.maximum && schema.exclusiveMaximum ){
ret -= littleGap;
}
if( ret === schema.minimum && schema.exclusiveMinimum ) {
ret += littleGap;
}
}
return ret;
},
numberMocker: function( schema ){
return this._numberMocker( schema, true );
},
integerMocker: function( schema ){
return this._numberMocker( schema, false );
},
booleanMocker: function( schema ){
return MockRandom.boolean();
},
nullMocker: function( schema ){
return null;
}
};
module.exports = DataMocker;