UNPKG

json-schema-mock

Version:

data mocker for json-schema.

294 lines (235 loc) 8.57 kB
/** * 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;