Skip to content

Commit

Permalink
Improve object generation compliance with JSON Schema (#159)
Browse files Browse the repository at this point in the history
* added angular-jsf section in readme

* angular-jsf demo page updated

* chore(package): update fs-extra to version 0.29.0

https://greenkeeper.io/

* chore(package): update fs-extra to version 0.30.0 (#147)

https://greenkeeper.io/

* readme & contribution updated

* tslint introduced, eslint reduced

* minor improvements

* chore(package): update chance to version 1.0.3 (#149)

https://greenkeeper.io/

* Fixed deps

* Fixed #150 so far :D

* 0.3.2

* dependency update

* chore(package): update browserify to version 13.0.1 (#151)

https://greenkeeper.io/

* Allow option to use default values (#156)

* 0.3.3

* Improved object generation compliance with JSON Schema spec

* improved tests for schema objects

* replaced es6 Object.assign by a more traditional for loop

* updated es5 object.assign polyfill to not shadow-var key

* rewrote the object generation again to better support patternProperties

* updated object.ts to match object.js
  • Loading branch information
JonathanMontane authored and pateketrueke committed Jun 3, 2016
1 parent bfd1b54 commit 61c4646
Show file tree
Hide file tree
Showing 50 changed files with 540 additions and 144 deletions.
1 change: 0 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"browser": true
},
"rules": {
"quotes": [2, "single", "avoid-escape"],
"semi": [2, "always"],
"no-unused-vars": 2,
"no-extra-semi": 2,
Expand Down
5 changes: 3 additions & 2 deletions CONTRIBUTING.md → .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ If you're submitting a bug, please clearly state what is:
## Releasing new versions

* Include a new entry in [CHANGELOG.md](CHANGELOG.md) file,
* Be followed by rebuilding and re-releasing new online demo, see [gh-pages branch](https://github.com/json-schema-faker/json-schema-faker/tree/gh-pages).
* Be followed by re-building and re-releasing online demo, see [`website-jsf` gh-pages branch](https://github.com/json-schema-faker/website-jsf/tree/gh-pages).

## Development tasks

* `npm run dev` — Run the tests and watch (preferred during development)
* `npm run dev:lint` — Run eslint on all sources
* `npm run dev:spec` — Run jasmine-node
* `npm run dev:unit` — Run `jasmine-node` on unit tests
* `npm run dev:schema` — Run `jasmine-node` on schema tests
* `npm run cover` — Run istanbul + jasmine-node
* `npm run cover:up` — Upload to coveralls (CI only)
* `npm run dist` — Prepare all assets with locales for CDN support
Expand Down
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Use [JSON Schema](http://json-schema.org/) along with fake generators to provide
- [Overview](#overview)
- [Example usage](#example-usage)
- [Gist demos](#gist-demos)
- [Automation](#automation)
- [Angular-jsf (AngularJS plugin)](#angular-jsf)
- [Grunt plugin](#grunt-plugin)
- Advanced
- [JSON Schema specification support](#json-schema-specification-support)
- [Supported keywords](#supported-keywords)
Expand All @@ -36,8 +39,6 @@ Use [JSON Schema](http://json-schema.org/) along with fake generators to provide
- [Inferred Types](#inferred-types)
- [Swagger extensions](#swagger-extensions)
- [Bundling](#bundling)
- [Automation](#automation)
- [Grunt plugin](#grunt-plugin)
- Misc
- [Contribution](#contribution)
- [Technical Documentation](#technical-documentation)
Expand Down Expand Up @@ -69,11 +70,11 @@ Install `json-schema-faker` with bower:
JSON-Schema-faker is also available at [cdnjs.com](https://www.cdnjs.com/libraries/json-schema-faker). This means you can just include the script file into your HTML:

# remember to update the version number!
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/json-schema-faker/0.2.8/json-schema-faker.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/json-schema-faker/0.3.1/json-schema-faker.min.js"></script>

It will be fetched from the [Content Delivery Network](https://en.wikipedia.org/wiki/Content_delivery_network) without installing any node.js package.

You can see [an example JS fiddle based on `jsf` loaded from cdnjs](https://jsfiddle.net/ftzhnmzq/).
You can see [an example JS fiddle based on `jsf` loaded from cdnjs](https://jsfiddle.net/ftzhnmzq/4/).

## Overview

Expand Down Expand Up @@ -135,6 +136,17 @@ Clone these gists and execute them locally (each gist has its own readme with in
* [jsf console](https://gist.github.com/ducin/9f2364ccde2e9248fbcd) - minimal example of jsf working directly under command line
* [jsf grunt](https://gist.github.com/ducin/87e0b55bddd1801d3d99) - example of jsf working under grunt.js

## Automation

### angular-jsf

Use [`angular-jsf`](https://github.com/json-schema-faker/angular-jsf) module (installable via `npm` and `bower`) to get **`jsf` working in your angular app out of the box**! And check out [angular-jsf demo](http://angular-jsf.js.org/).

### Grunt plugin

Use [grunt-jsonschema-faker](https://github.com/json-schema-faker/grunt-jsonschema-faker)
to automate running `json-schema-faker` against your JSON schemas.

## JSON Schema specification support

Currently `jsf` supports the JSON-Schema specification **draft-04** only.
Expand Down Expand Up @@ -434,13 +446,6 @@ However, you may want to bundle a smaller package of `jsf`, because:
* or for any other reason...
In that case you may bundle the distribution yourself manually. It's easily achievable: just modify the [`lib/util/container.js`](lib/util/container.js) file and either remove o rmodify the `require` calls (they're directly used by browserify to include dependencies). Automation of this feature is expected in near future.

## Automation

### Grunt plugin

Use [grunt-jsonschema-faker](https://github.com/json-schema-faker/grunt-jsonschema-faker)
to automate running `json-schema-faker` against your JSON schemas.

## Contribution

* [Alvaro Cabrera](https://twitter.com/pateketrueke)
Expand Down
1 change: 1 addition & 0 deletions lib/api/extend.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var container = require('../class/Container');
/**
* Extending dependencies
Expand Down
1 change: 1 addition & 0 deletions lib/api/format.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var Registry = require('../class/Registry');
// instantiate
var registry = new Registry();
Expand Down
1 change: 1 addition & 0 deletions lib/api/option.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var OptionRegistry = require('../class/OptionRegistry');
// instantiate
var registry = new OptionRegistry();
Expand Down
3 changes: 2 additions & 1 deletion lib/class/Container.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var randexp = require('randexp');
/**
* Container is used to wrap external libraries (faker, chance, randexp) that are used among the whole codebase. These
Expand Down Expand Up @@ -53,7 +54,7 @@ var Container = (function () {
};
};
return Container;
})();
}());
// TODO move instantiation somewhere else (out from class file)
// instantiate
var container = new Container();
Expand Down
4 changes: 3 additions & 1 deletion lib/class/OptionRegistry.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
Expand All @@ -13,7 +14,8 @@ var OptionRegistry = (function (_super) {
_super.call(this);
this.data['failOnInvalidTypes'] = true;
this.data['defaultInvalidTypeProduct'] = null;
this.data['useDefaultValue'] = false;
}
return OptionRegistry;
})(Registry);
}(Registry));
module.exports = OptionRegistry;
3 changes: 2 additions & 1 deletion lib/class/Registry.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
/**
* This class defines a registry for custom formats used within JSF.
*/
Expand Down Expand Up @@ -37,5 +38,5 @@ var Registry = (function () {
return this.data;
};
return Registry;
})();
}());
module.exports = Registry;
3 changes: 2 additions & 1 deletion lib/core/error.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
Expand All @@ -14,5 +15,5 @@ var ParseError = (function (_super) {
this.path = path;
}
return ParseError;
})(Error);
}(Error));
module.exports = ParseError;
1 change: 1 addition & 0 deletions lib/core/infer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var inferredProperties = {
array: [
'additionalItems',
Expand Down
7 changes: 5 additions & 2 deletions lib/core/random.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// <reference path="../index.d.ts" />
"use strict";
/**
* Returns random element of a collection
*
Expand All @@ -15,9 +16,11 @@ function pick(collection) {
* @returns {T[]}
*/
function shuffle(collection) {
var copy = collection.slice(), length = collection.length;
var tmp, key, copy = collection.slice(), length = collection.length;
for (; length > 0;) {
var key = Math.floor(Math.random() * length), tmp = copy[--length];
key = Math.floor(Math.random() * length);
// swap
tmp = copy[--length];
copy[length] = copy[key];
copy[key] = tmp;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/core/run.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"use strict";
var deref = require('deref');
var traverse = require('./traverse');
var random = require('./random');
var utils = require('./utils');
function isKey(prop) {
return prop === 'enum' || prop === 'required' || prop === 'definitions';
return prop === 'enum' || prop === 'default' || prop === 'required' || prop === 'definitions';
}
// TODO provide types
function run(schema, refs, ex) {
Expand Down Expand Up @@ -44,7 +45,7 @@ function run(schema, refs, ex) {
utils.merge(sub, random.pick(mix));
}
for (var prop in sub) {
if ((Array.isArray(sub[prop]) || typeof sub[prop] === 'object') && !isKey(prop)) {
if ((Array.isArray(sub[prop]) || typeof sub[prop] === 'object') && sub[prop] !== null && !isKey(prop)) {
sub[prop] = reduce(sub[prop]);
}
}
Expand Down
4 changes: 4 additions & 0 deletions lib/core/traverse.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var random = require('./random');
var ParseError = require('./error');
var inferType = require('./infer');
Expand Down Expand Up @@ -25,6 +26,9 @@ function traverse(schema, path, resolve) {
if (Array.isArray(schema.enum)) {
return random.pick(schema.enum);
}
if (option('useDefaultValue') && schema.default) {
return schema.default;
}
// TODO remove the ugly overcome
var type = schema.type;
if (Array.isArray(type)) {
Expand Down
1 change: 1 addition & 0 deletions lib/core/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
function getSubAttribute(obj, dotSeparatedKey) {
var keyElements = dotSeparatedKey.split('.');
while (keyElements.length) {
Expand Down
1 change: 1 addition & 0 deletions lib/generators/boolean.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
/**
* Generates randomized boolean value.
*
Expand Down
1 change: 1 addition & 0 deletions lib/generators/coreFormat.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var container = require('../class/Container');
var randexp = container.get('randexp');
var regexps = {
Expand Down
1 change: 1 addition & 0 deletions lib/generators/dateTime.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var random = require('../core/random');
/**
* Generates randomized date time ISO format string.
Expand Down
1 change: 1 addition & 0 deletions lib/generators/ipv4.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var random = require('../core/random');
/**
* Generates randomized ipv4 address.
Expand Down
1 change: 1 addition & 0 deletions lib/generators/null.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
/**
* Generates null value.
*
Expand Down
15 changes: 9 additions & 6 deletions lib/generators/thunk.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var words = require('../generators/words');
var random = require('../core/random');
function produce() {
Expand All @@ -12,13 +13,15 @@ function produce() {
function thunkGenerator(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), sample = produce();
while (sample.length < min) {
sample += produce();
var min = Math.max(0, min), max = random.number(min, max), result = produce();
// append until length is reached
while (result.length < min) {
result += produce();
}
if (sample.length > max) {
sample = sample.substr(0, max);
// cut if needed
if (result.length > max) {
result = result.substr(0, max);
}
return sample;
return result;
}
module.exports = thunkGenerator;
1 change: 1 addition & 0 deletions lib/generators/words.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var random = require('../core/random');
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'
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var container = require('./class/Container');
var format = require('./api/format');
var option = require('./api/option');
Expand Down
11 changes: 7 additions & 4 deletions lib/types/array.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var random = require('../core/random');
var utils = require('../core/utils');
var ParseError = require('../core/error');
Expand All @@ -23,7 +24,7 @@ function unique(path, items, value, sample, resolve, traverseCallback) {
return tmp;
}
// TODO provide types
function arrayType(value, path, resolve, traverseCallback) {
var arrayType = function arrayType(value, path, resolve, traverseCallback) {
var items = [];
if (!(value.items || value.additionalItems)) {
if (utils.hasProperties(value, 'minItems', 'maxItems', 'uniqueItems')) {
Expand All @@ -36,13 +37,15 @@ function arrayType(value, path, resolve, traverseCallback) {
return traverseCallback(item, path.concat(['items', key]), resolve);
}));
}
var length = random.number(value.minItems, value.maxItems, 1, 5), sample = typeof value.additionalItems === 'object' ? value.additionalItems : {};
for (var current = items.length; current < length; current += 1) {
var length = random.number(value.minItems, value.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++) {
items.push(traverseCallback(value.items || sample, path.concat(['items', current]), resolve));
}
if (value.uniqueItems) {
return unique(path.concat(['items']), items, value, sample, resolve, traverseCallback);
}
return items;
}
};
module.exports = arrayType;
1 change: 1 addition & 0 deletions lib/types/boolean.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var booleanGenerator = require('../generators/boolean');
var booleanType = booleanGenerator;
module.exports = booleanType;
5 changes: 3 additions & 2 deletions lib/types/external.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use strict";
var utils = require('../core/utils');
var container = require('../class/Container');
function externalType(value, path) {
var externalType = function externalType(value, path) {
var libraryName = value.faker ? 'faker' : 'chance', libraryModule = value.faker ? container.get('faker') : container.get('chance'), key = value.faker || value.chance, path = key, args = [];
if (typeof path === 'object') {
path = Object.keys(path)[0];
Expand All @@ -23,5 +24,5 @@ function externalType(value, path) {
contextObject = libraryModule[fakerModuleName];
}
return genFunction.apply(contextObject, args);
}
};
module.exports = externalType;
1 change: 1 addition & 0 deletions lib/types/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var _boolean = require('./boolean');
var _null = require('./null');
var _array = require('./array');
Expand Down
5 changes: 3 additions & 2 deletions lib/types/integer.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use strict";
var number = require('./number');
// 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.
function integerType(value) {
var integerType = function integerType(value) {
var generated = number(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);
}
};
module.exports = integerType;
1 change: 1 addition & 0 deletions lib/types/null.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
var nullGenerator = require('../generators/null');
var nullType = nullGenerator;
module.exports = nullType;
5 changes: 3 additions & 2 deletions lib/types/number.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use strict";
var random = require('../core/random');
var MIN_INTEGER = -100000000, MAX_INTEGER = 100000000;
function numberType(value) {
var numberType = 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;
Expand All @@ -19,5 +20,5 @@ function numberType(value) {
return Math.floor(random.number(min, max) / multipleOf) * multipleOf;
}
return random.number(min, max, undefined, undefined, true);
}
};
module.exports = numberType;
Loading

0 comments on commit 61c4646

Please sign in to comment.