diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9d1a8c8ecee2867d357e86df24908a951af8fd91..35755d4b07cb6e8d34e7ba4573c90dc12dddbe0a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -12,12 +12,12 @@ If you are submitting a bug report, please include the following: * When submitting *bug reports*, *paste your schema example* whenever possible. This will save us from having to ask you for it later. * When submitting *Pull Requests (PRs)*, you should request to merge your changes into our `develop` branch. -* Our approach to testing is described in [spec/README.md](spec/README.md). +* Our approach to testing is described in [spec/README.md](/spec/README.md). * PRs without specs will not be merged anytime soon! ## Releasing new versions -* Create a new entry in the [CHANGELOG.md](CHANGELOG.md) file, +* Create a new entry in the [CHANGELOG](/CHANGELOG) file, * Re-build and re-release the online demo, see [`website-jsf` gh-pages branch](https://github.com/json-schema-faker/website-jsf/tree/gh-pages). ## Development tasks diff --git a/CHANGELOG b/CHANGELOG index 23dd33776f026a747eb50e012ca92863eb988164..9706954f6672e44746fd19664674ebf09b55505b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +v0.5.0-rc15 + date: 2018-04-07 + changes: + - Introduced option: "optionalsProbability". v0.5.0-rc1 date: 2017-04-10 changes: diff --git a/README.md b/README.md index 44f26d8d21e317ea3a60c25a40a3ab2289bdcaa4..366674b851099a8ccbd8022f61e7b94417863f99 100644 --- a/README.md +++ b/README.md @@ -446,6 +446,7 @@ You may define following options for `jsf` that alter its behavior: - `maxLength`: number - Configure a maximum length to allow generating strings for. This will override the maximum length found inside a JSON Schema. - `random`: Function - a replacement for `Math.random` to support pseudorandom number generation. - `alwaysFakeOptionals`: boolean - When true, all object-properties will be generated regardless they're `required` or not. +- `optionalsProbability`: number - A decimal number from 0 to 1 that indicates the probability to fake a non-required object property (default: 0). When `0.0`, only `required` properties will be generated; when `1.0`, all properties are generated. This option is overwritten to 1 when `alwaysFakeOptionals = true`. Set options just as below: diff --git a/bower.json b/bower.json index 47cd202e3b09ee5d73afc2c4278074cc6bf05cb8..930d8b254fa09edaa1b0075068005c6151175a06 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "json-schema-faker", - "version": "0.5.0-rc14", + "version": "0.5.0-rc15", "description": "JSON-Schema + fake data generators", "homepage": "http://json-schema-faker.js.org", "main": "dist/json-schema-faker.js", diff --git a/dist/json-schema-faker.js b/dist/json-schema-faker.js index 35a5a792b7bf0ef713e9cc106abb1dfa9b853471..4d3f593ed30afc6e933a703e43084f9cac93ef15 100644 Binary files a/dist/json-schema-faker.js and b/dist/json-schema-faker.js differ diff --git a/dist/json-schema-faker.min.js b/dist/json-schema-faker.min.js index 2452d1ebfbe774e113aae6a2d549cc8b2ba8902a..19b9d3cb6c81c770e3b857b29564f25ef4bfbd50 100644 Binary files a/dist/json-schema-faker.min.js and b/dist/json-schema-faker.min.js differ diff --git a/lib/index.js b/lib/index.js index 4f070437953d277ff6febf40a6f5a1046dd2cdd1..6625c0c1d83aea197cccc9236c50895ef2298cfe 100644 Binary files a/lib/index.js and b/lib/index.js differ diff --git a/package-lock.json b/package-lock.json index e1baef03129794efee79af31fb6c309b240af36d..88cab3940e0cbedb80d38baf7a2c167f50ba071a 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index 5c3692bf3371574502c02792e96612f1c2e327b1..92097c6b43689c1e8281e8693ab7ea6935a2aca1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-schema-faker", - "version": "0.5.0-rc14", + "version": "0.5.0-rc15", "description": "JSON-Schema + fake data generators", "homepage": "http://json-schema-faker.js.org", "main": "lib/index.js", diff --git a/spec/schema/core/option/alwaysFakeOptionals.js b/spec/schema/core/option/alwaysFakeOptionals.js new file mode 100644 index 0000000000000000000000000000000000000000..035bd0f9879963ff2cd20bf7c79e527e50958e97 --- /dev/null +++ b/spec/schema/core/option/alwaysFakeOptionals.js @@ -0,0 +1,8 @@ +module.exports = { + register: function(jsf) { + return jsf.option({ + 'useDefaultValue': true, + 'alwaysFakeOptionals': true + }); + } +}; diff --git a/spec/schema/core/option/alwaysFakeOptionals.json b/spec/schema/core/option/alwaysFakeOptionals.json new file mode 100644 index 0000000000000000000000000000000000000000..609d6e3a1220bed337c5b32d5da648d7a898c208 --- /dev/null +++ b/spec/schema/core/option/alwaysFakeOptionals.json @@ -0,0 +1,44 @@ +[ + { + "description": "alwaysFakeOptionals option", + "tests": [ + { + "description": "should handle alwaysFakeOptionals option (= true) for objects", + "schema": { + "type": "object", + "properties": { + "optionalProperty1": { "type": "number", "default": 1 }, + "optionalProperty2": { "type": "number", "default": 1 }, + "optionalProperty3": { "type": "number", "default": 1 }, + "optionalProperty4": { "type": "number", "default": 1 }, + "optionalProperty5": { "type": "number", "default": 1 } + } + }, + "valid": true, + "equal": { + "optionalProperty1": 1, + "optionalProperty2": 1, + "optionalProperty3": 1, + "optionalProperty4": 1, + "optionalProperty5": 1 + }, + "require": "core/option/alwaysFakeOptionals" + }, + { + "description": "should handle alwaysFakeOptionals option (= true) for arrays", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": ["a"] + }, + "maxItems": 5, + "minItems": 0 + }, + "valid": true, + "equal": ["a", "a", "a", "a", "a"], + "require": "core/option/alwaysFakeOptionals" + } + ] + } +] diff --git a/spec/schema/core/option/optionalsProbability.json b/spec/schema/core/option/optionalsProbability.json new file mode 100644 index 0000000000000000000000000000000000000000..da53c1c970d6a797d603beb6eabfb8f892f0b2f2 --- /dev/null +++ b/spec/schema/core/option/optionalsProbability.json @@ -0,0 +1,96 @@ +[ + { + "description": "optionalsProbability option", + "tests": [ + { + "description": "should handle optionalsProbability option (default = 0) for objects", + "schema": { + "type": "object", + "properties": { + "optionalProperty1": { "type": "number", "default": 1 }, + "optionalProperty2": { "type": "number", "default": 1 }, + "optionalProperty3": { "type": "number", "default": 1 }, + "optionalProperty4": { "type": "number", "default": 1 }, + "optionalProperty5": { "type": "number", "default": 1 } + } + }, + "valid": true, + "equal": {} + }, + { + "description": "should handle optionalsProbability option (= 1) for objects", + "schema": { + "type": "object", + "properties": { + "optionalProperty1": { "type": "number", "default": 1 }, + "optionalProperty2": { "type": "number", "default": 1 }, + "optionalProperty3": { "type": "number", "default": 1 }, + "optionalProperty4": { "type": "number", "default": 1 }, + "optionalProperty5": { "type": "number", "default": 1 } + } + }, + "valid": true, + "equal": { + "optionalProperty1": 1, + "optionalProperty2": 1, + "optionalProperty3": 1, + "optionalProperty4": 1, + "optionalProperty5": 1 + }, + "require": "core/option/optionalsProbabilityEquals1" + }, + { + "description": "should handle optionalsProbability option (= 1) for arrays", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": ["a"] + }, + "maxItems": 5, + "minItems": 0 + }, + "valid": true, + "equal": ["a", "a", "a", "a", "a"], + "require": "core/option/optionalsProbabilityEquals1" + }, + { + "description": "should handle optionalsProbability option (= 0) overwritten by alwaysFakeOptionals for objects", + "schema": { + "type": "object", + "properties": { + "optionalProperty1": { "type": "number", "default": 1 }, + "optionalProperty2": { "type": "number", "default": 1 }, + "optionalProperty3": { "type": "number", "default": 1 }, + "optionalProperty4": { "type": "number", "default": 1 }, + "optionalProperty5": { "type": "number", "default": 1 } + } + }, + "valid": true, + "equal": { + "optionalProperty1": 1, + "optionalProperty2": 1, + "optionalProperty3": 1, + "optionalProperty4": 1, + "optionalProperty5": 1 + }, + "require": "core/option/optionalsProbabilityOverwritten" + }, + { + "description": "should handle optionalsProbability option (= 0) overwritten by alwaysFakeOptionals for objects", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": ["a"] + }, + "maxItems": 5, + "minItems": 0 + }, + "valid": true, + "equal": ["a", "a", "a", "a", "a"], + "require": "core/option/optionalsProbabilityOverwritten" + } + ] + } +] diff --git a/spec/schema/core/option/optionalsProbabilityEquals1.js b/spec/schema/core/option/optionalsProbabilityEquals1.js new file mode 100644 index 0000000000000000000000000000000000000000..c269a57e63ccfad948f098071ffb9ae2a3868d1a --- /dev/null +++ b/spec/schema/core/option/optionalsProbabilityEquals1.js @@ -0,0 +1,8 @@ +module.exports = { + register: function(jsf) { + return jsf.option({ + 'useDefaultValue': true, + 'optionalsProbability': 1.0 + }); + } +}; diff --git a/spec/schema/core/option/optionalsProbabilityOverwritten.js b/spec/schema/core/option/optionalsProbabilityOverwritten.js new file mode 100644 index 0000000000000000000000000000000000000000..d8aabc2010d2cad95fee694f40fe502b06f8af26 --- /dev/null +++ b/spec/schema/core/option/optionalsProbabilityOverwritten.js @@ -0,0 +1,9 @@ +module.exports = { + register: function(jsf) { + return jsf.option({ + 'useDefaultValue': true, + 'alwaysFakeOptionals': true, + 'optionalsProbability': 0.0 + }); + } +}; diff --git a/spec/schema/core/option/random.json b/spec/schema/core/option/random.json index f2bd48c35bf5e1e2118ed5576aebee0d01c0521a..2af07c2e4593660219b9b579b003f200bd8e632d 100644 --- a/spec/schema/core/option/random.json +++ b/spec/schema/core/option/random.json @@ -23,9 +23,9 @@ }, "valid": true, "equal": { - "a": 62, - "b": "amet voluptate qui reprehenderi", - "c": "fw538" + "a": 92, + "b": "enim veniam", + "c": "ff353" }, "repeat": 1, "require": "core/option/random" diff --git a/spec/schema/core/types/object.json b/spec/schema/core/types/object.json index 1eeb036d4baffa70d08baf911d91635329a670e7..01ba47f89f991ac9c1f4774fe117a9b304241684 100644 --- a/spec/schema/core/types/object.json +++ b/spec/schema/core/types/object.json @@ -2,81 +2,81 @@ { "description": "object generator", "tests": [ - { - "description": "addP === false && !props && !patternProps", - "schema": { - "type": "object", - "additionalProperties": false - }, - "valid": true + { + "description": "addP === false && !props && !patternProps", + "schema": { + "type": "object", + "additionalProperties": false }, - { - "description": "addP === false && props && !patternProps", - "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer" - } - }, - "additionalProperties": false + "valid": true + }, + { + "description": "addP === false && props && !patternProps", + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + } }, - "valid": true + "additionalProperties": false }, - { - "description": "addP === false && !props && patternProps", - "schema": { - "type": "object", - "patternProperties": { - "count": { - "type": "integer" - } - }, - "additionalProperties": false + "valid": true + }, + { + "description": "addP === false && !props && patternProps", + "schema": { + "type": "object", + "patternProperties": { + "count": { + "type": "integer" + } }, - "valid": true + "additionalProperties": false }, - { - "description": "addP === false && props && patternProps", - "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer" - } - }, - "patternProperties": { - "v-[0-3]": { - "type": "integer" - } - }, - "additionalProperties": false + "valid": true + }, + { + "description": "addP === false && props && patternProps", + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + } }, - "valid": true - }, - { - "description": "addP === false && !props && !patternProps && minProperties", - "schema": { - "type": "object", - "minProperties": 2, - "additionalProperties": false + "patternProperties": { + "v-[0-3]": { + "type": "integer" + } }, - "throws": "missing properties for:\n[\\s\\S]+? in \\/" + "additionalProperties": false }, - { - "description": "addP === undef || props", - "schema": { - "type": "object", - "minProperties": 2, - "properties": { - "count": { - "type": "integer" - } - } - }, - "valid": true + "valid": true + }, + { + "description": "addP === false && !props && !patternProps && minProperties", + "schema": { + "type": "object", + "minProperties": 2, + "additionalProperties": false + }, + "throws": "missing properties for:\n[\\s\\S]+? in \\/" + }, + { + "description": "addP === undef || props", + "schema": { + "type": "object", + "minProperties": 2, + "properties": { + "count": { + "type": "integer" + } + } }, - { + "valid": true + }, + { "description": "should skip some non-required properties", "schema": { "type": "object", diff --git a/ts/class/OptionRegistry.ts b/ts/class/OptionRegistry.ts index 3994c37e56471ba7e7ae330e898550d12d01b8df..bc52f0bc3d40dd65d82080bc4efa93155985f950 100644 --- a/ts/class/OptionRegistry.ts +++ b/ts/class/OptionRegistry.ts @@ -23,6 +23,7 @@ class OptionRegistry extends Registry<Option> { data['failOnInvalidFormat'] = true; data['alwaysFakeOptionals'] = false; + data['optionalsProbability'] = 0.0; data['useDefaultValue'] = false; data['requiredOnly'] = false; diff --git a/ts/types/array.ts b/ts/types/array.ts index 76c7679546f8e726eab87f404f7bb2a46bbb5f1f..ee38168626ab0afde1824c0803533096791f79fc 100644 --- a/ts/types/array.ts +++ b/ts/types/array.ts @@ -79,8 +79,10 @@ var arrayType: FTypeGenerator = function arrayType(value: IArraySchema, path: Sc } } - var length: number = (maxItems != null && optionAPI('alwaysFakeOptionals')) ? - maxItems : random.number(minItems, maxItems, 1, 5), + var optionalsProbability = optionAPI('alwaysFakeOptionals') === true ? 1.0 : optionAPI('optionalsProbability'); + var length: number = (maxItems != null && optionalsProbability) + ? Math.round(maxItems * optionalsProbability) + : random.number(minItems, maxItems, 1, 5), // TODO below looks bad. Should additionalItems be copied as-is? sample: Object = typeof value.additionalItems === 'object' ? value.additionalItems : {}; diff --git a/ts/types/object.ts b/ts/types/object.ts index 1690e86e0a4c27800cbe8887d9396281c2f4f575..137399338374d8e3cca652178b052e6ebf746a08 100644 --- a/ts/types/object.ts +++ b/ts/types/object.ts @@ -18,6 +18,11 @@ var objectType: FTypeGenerator = function objectType(value: IObjectSchema, path, var propertyKeys = Object.keys(properties); var patternPropertyKeys = Object.keys(patternProperties); + var optionalProperties = propertyKeys.concat(patternPropertyKeys).reduce(function(_response, _key) { + if (requiredProperties.indexOf(_key) === -1) _response.push(_key); + return _response; + }, []); + var allProperties = requiredProperties.concat(optionalProperties); var additionalProperties = allowsAdditional ? (value.additionalProperties === true ? {} : value.additionalProperties) @@ -43,22 +48,20 @@ var objectType: FTypeGenerator = function objectType(value: IObjectSchema, path, return traverseCallback(props, path.concat(['properties']), resolve); } + var optionalsProbability = optionAPI('alwaysFakeOptionals') === true ? 1.0 : optionAPI('optionalsProbability'); + var ignoreProperties = optionAPI('ignoreProperties') || []; var min = Math.max(value.minProperties || 0, requiredProperties.length); - var max = Math.max(value.maxProperties || random.number(min, propertyKeys.length)); + var max = Math.max(value.maxProperties || allProperties.length); - random.shuffle(patternPropertyKeys).forEach(function(_key) { - if (requiredProperties.indexOf(_key) === -1) { - requiredProperties.push(_key); - } + var neededExtras = Math.round((min - requiredProperties.length) + optionalsProbability * (max - min)); + var extraPropertiesRandomOrder = random.shuffle(optionalProperties).slice(0, neededExtras); + var extraProperties = optionalProperties.filter(function(_item) { + return extraPropertiesRandomOrder.indexOf(_item) !== -1; }); - var fakeOptionals = optionAPI('alwaysFakeOptionals'); - var ignoreProperties = optionAPI('ignoreProperties') || []; - // properties are read from right-to-left - var _props = fakeOptionals ? propertyKeys - : requiredProperties.slice(0, max); + var _props = requiredProperties.concat(extraProperties).slice(0, max); var skipped = []; var missing = [];