diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b16413e --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +identifier=com.luckymarmot.PawExtensions.JSONSchemaFakerDynamicValue +extensions_dir=$(HOME)/Library/Containers/com.luckymarmot.Paw/Data/Library/Application Support/com.luckymarmot.Paw/Extensions/ + +build: + npm run build + cp README.md LICENSE ./build/$(identifier)/ + +clean: + rm -Rf ./build/ + +local-copy: + mkdir -p "$(extensions_dir)$(identifier)/" + cp -r ./build/$(identifier)/* "$(extensions_dir)$(identifier)/" + +local: local-deploy +local-deploy: clean build local-copy + + +install: + npm install + +test: + npm test + +lint: + npm run lint + +check: test lint + +archive: build + cd ./build/; zip -r JsonWebTokenDynamicValue.zip "$(identifier)/" diff --git a/src/JSONSchemaFakerDynamicValue.js b/src/JSONSchemaFakerDynamicValue.js index a738355..028ad35 100755 --- a/src/JSONSchemaFakerDynamicValue.js +++ b/src/JSONSchemaFakerDynamicValue.js @@ -1,3 +1,6 @@ +if (typeof Error.captureStackTrace === 'undefined') { + Error.captureStackTrace = () => {} +} import jsf from 'json-schema-faker' import { @@ -20,10 +23,21 @@ export default class JSONSchemaFakerDynamicValue { 'Resolve References', 'Checkbox', { defaultValue: true } + ), + new InputField( + 'predict', + 'Guess Formats', + 'Checkbox', + { defaultValue: true } ) - ]; + ] evaluate(context) { + jsf.option({ + failOnInvalidTypes: false, + defaultInvalidTypeProduct: null + }) + let resolveRefs = this.resolveRefs let _schema = this.schema let mainKey = '@undefined' @@ -41,8 +55,20 @@ export default class JSONSchemaFakerDynamicValue { Object.assign(schema, schemas) + if (this.predict) { + schema = this._guessFormats(schema) + } + let generated = (jsf(schema) || {}).$$schema - return JSON.stringify(generated, null, ' ') + + let result + if (typeof generated === 'object') { + result = JSON.stringify(generated, null, ' ') + } + else { + result = generated + } + return result } _getSchemaDict(context, _schema, resolveRefs, mainKey) { @@ -179,4 +205,97 @@ export default class JSONSchemaFakerDynamicValue { return baseObj } + + _guessFormats(_schema, key) { + let schema = _schema + + if (typeof schema !== 'object') { + return schema + } + + if (schema.type && schema.type === 'string') { + schema = this._guessStringFormat(schema, key) + } + else if ( + schema.type && + schema.type === 'integer' || + schema.type === 'number' + ) { + delete schema.format + } + + let obj + if (Array.isArray(schema)) { + obj = [] + let index = 0 + for (let sub of schema) { + obj.push(this._guessFormats(sub, index)) + index += 1 + } + } + else { + obj = {} + for (let _key of Object.keys(schema)) { + obj[_key] = this._guessFormats(schema[_key], _key) + } + } + + return obj + } + + _guessStringFormat(schema, key) { + if (schema.faker) { + return schema + } + + if (schema.format) { + let format = schema.format + delete schema.format + if (format === 'email') { + schema.faker = 'internet.email' + } + else if (format === 'password') { + schema.faker = 'internet.password' + } + else if (format === 'date-time') { + schema.faker = 'date.recent' + } + else if (format === 'url') { + schema.faker = 'internet.url' + } + else if (format === 'date' && !schema.pattern) { + schema.pattern = '^20[0-2][0-9]-0[1-9]-[0-2][1-8]$' + } + else if (format === 'byte' && !schema.pattern) { + schema.pattern = '^(?:[A-Za-z0-9+/]{4})*' + + '(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$' + } + else if (format === 'binary' && !schema.pattern) { + schema.pattern = '^.*$' + } + return schema + } + + if (key.match(/email/i)) { + schema.faker = 'internet.email' + } + else if (key.match(/name/i)) { + schema.faker = 'name.findName' + } + else if (key.match(/phone/i)) { + schema.faker = 'phone.phoneNumberFormat' + } + else if (key.match(/url/i)) { + schema.faker = 'internet.url' + } + else if ( + typeof schema.pattern === 'undefined' && + typeof schema.minLength === 'undefined' && + typeof schema.maxLength === 'undefined' + ) { + schema.faker = 'company.bsNoun' + } + + return schema + } } diff --git a/src/__tests__/JSONSchemaFakerDynamicValue-test.js b/src/__tests__/JSONSchemaFakerDynamicValue-test.js index 73988cc..a864332 100644 --- a/src/__tests__/JSONSchemaFakerDynamicValue-test.js +++ b/src/__tests__/JSONSchemaFakerDynamicValue-test.js @@ -1,9 +1,9 @@ import { - UnitTest, registerTest, against, targets, desc + UnitTest, registerTest, against, targets, desc } from '../__utils__/TestUtils' import { - ClassMock + ClassMock } from '../__mocks__/Mocks' import { @@ -92,7 +92,7 @@ export class TestJSONSchemaFakerDynamicValue extends UnitTest { return {} }) - const expected = '3' + const expected = 3 const result = dv.evaluate(ctx) this.assertEqual(expected, result) @@ -128,7 +128,7 @@ export class TestJSONSchemaFakerDynamicValue extends UnitTest { } }) - const expected = '3' + const expected = 3 const result = dv.evaluate(ctx) this.assertEqual(expected, result) @@ -572,6 +572,76 @@ export class TestJSONSchemaFakerDynamicValue extends UnitTest { this.assertEqual(expected, result) } + @targets('_guessFormats') + @desc('_guessFormats removes unusable integer formats') + testGuessFormatsRemovesUnusableIntegerFormats() { + const dv = this.__init() + const schema = { + type: 'integer', + minimum: 3, + maximum: 4, + format: 'uint32' + } + + const expected = { + type: 'integer', + minimum: 3, + maximum: 4 + } + + const result = dv._guessFormats(schema) + + this.assertEqual(expected, result) + } + + @targets('_guessFormats') + @desc('_guessFormats removes unusable number formats') + testGuessFormatsRemovesUnusableNumberFormats() { + const dv = this.__init() + const schema = { + type: 'number', + minimum: 3, + maximum: 4, + format: 'float' + } + + const expected = { + type: 'number', + minimum: 3, + maximum: 4 + } + + const result = dv._guessFormats(schema) + + this.assertEqual(expected, result) + } + + @targets('_guessFormats') + @desc('_guessFormats calls _guessStringFormat') + testGuessFormatsCallsGuessStringFormat() { + const dv = this.__init() + + dv.spyOn('_guessStringFormat', () => { + return {} + }) + + const schema = { + type: 'string', + minimum: 3, + maximum: 4 + } + + dv._guessFormats(schema) + + this.assertEqual(dv.spy._guessStringFormat.count, 1) + } + + @targets('_guessStringFormat') + @desc('_ignored') + _testGuessStringFormat() { + // TODO + } + __init(schema, resolveRefs = true, domainName) { const dv = new ClassMock(new JSONSchemaFakerDynamicValue(domainName)) dv.schema = schema