From 6712471942eefe4695af1672c6e7e846ead7f7c7 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Fri, 13 Dec 2019 13:21:49 +0100 Subject: [PATCH] refactor: removes "amanda" BREAKING CHANGE: Removes JSON Schema Draft 3 support --- README.md | 6 + lib/meta-schema-v3.js | 174 ------------------ lib/validators/json-schema-legacy.js | 97 +--------- lib/validators/json-schema-next.js | 22 +-- lib/validators/json-schema.js | 2 +- package-lock.json | 5 - package.json | 1 - test/fixtures/invalid-schema-v3.json | 19 -- test/fixtures/valid-schema-v3.json | 18 -- test/unit/utils/schema-v4-generator.test.js | 7 - .../validators/json-schema-legacy.test.js | 89 --------- test/unit/validators/json-schema.test.js | 96 +--------- 12 files changed, 21 insertions(+), 515 deletions(-) delete mode 100644 lib/meta-schema-v3.js delete mode 100644 test/fixtures/invalid-schema-v3.json delete mode 100644 test/fixtures/valid-schema-v3.json diff --git a/README.md b/README.md index f3e865a1..93d8f386 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,12 @@ The validation `result` against the given JSON Schema will look as follows: } ``` +### Supported JSON Schema versions + +- [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7) +- [JSON Schema Draft 6](https://json-schema.org/specification-links.html#draft-6) +- [JSON Schema Draft 4](https://json-schema.org/specification-links.html#draft-4) + ## Examples Take a look at the [Gherkin](https://cucumber.io/docs/gherkin/) specification, which describes on examples how validation of each field behaves: diff --git a/lib/meta-schema-v3.js b/lib/meta-schema-v3.js deleted file mode 100644 index f0c3986b..00000000 --- a/lib/meta-schema-v3.js +++ /dev/null @@ -1,174 +0,0 @@ -module.exports = { - $schema: 'http://json-schema.org/draft-03/schema#', - id: 'http://json-schema.org/draft-03/schema#', - type: 'object', - - properties: { - type: { - type: ['string', 'array'], - items: { - type: ['string', { $ref: '#' }], - }, - uniqueItems: true, - default: 'any', - }, - - properties: { - type: 'object', - additionalProperties: { $ref: '#' }, - default: {}, - }, - - patternProperties: { - type: 'object', - additionalProperties: { $ref: '#' }, - default: {}, - }, - - additionalProperties: { - type: [{ $ref: '#' }, 'boolean'], - default: {}, - }, - - items: { - type: [{ $ref: '#' }, 'array'], - items: { $ref: '#' }, - default: {}, - }, - - additionalItems: { - type: [{ $ref: '#' }, 'boolean'], - default: {}, - }, - - required: { - type: 'boolean', - default: false, - }, - - dependencies: { - type: 'object', - additionalProperties: { - type: ['string', 'array', { $ref: '#' }], - items: { - type: 'string', - }, - }, - default: {}, - }, - - minimum: { - type: 'number', - }, - - maximum: { - type: 'number', - }, - - exclusiveMinimum: { - type: 'boolean', - default: false, - }, - - exclusiveMaximum: { - type: 'boolean', - default: false, - }, - - minItems: { - type: 'integer', - minimum: 0, - default: 0, - }, - - maxItems: { - type: 'integer', - minimum: 0, - }, - - uniqueItems: { - type: 'boolean', - default: false, - }, - - pattern: { - type: 'string', - format: 'regex', - }, - - minLength: { - type: 'integer', - minimum: 0, - default: 0, - }, - - maxLength: { - type: 'integer', - }, - - enum: { - type: 'array', - minItems: 1, - uniqueItems: true, - }, - - default: { - type: 'any', - }, - - title: { - type: 'string', - }, - - description: { - type: 'string', - }, - - format: { - type: 'string', - }, - - divisibleBy: { - type: 'number', - minimum: 0, - exclusiveMinimum: true, - default: 1, - }, - - disallow: { - type: ['string', 'array'], - items: { - type: ['string', { $ref: '#' }], - }, - uniqueItems: true, - }, - - extends: { - type: [{ $ref: '#' }, 'array'], - items: { $ref: '#' }, - default: {}, - }, - - id: { - type: 'string', - format: 'uri', - }, - - $ref: { - type: 'string', - format: 'uri', - }, - - $schema: { - type: 'string', - format: 'uri', - }, - }, - - dependencies: { - exclusiveMinimum: 'minimum', - exclusiveMaximum: 'maximum', - }, - - default: {}, -} diff --git a/lib/validators/json-schema-legacy.js b/lib/validators/json-schema-legacy.js index bef19fc5..38fbaf79 100644 --- a/lib/validators/json-schema-legacy.js +++ b/lib/validators/json-schema-legacy.js @@ -1,68 +1,17 @@ -const amanda = require('amanda'); const tv4 = require('tv4'); const jsonPointer = require('json-pointer'); const { JsonSchemaValidator, META_SCHEMA } = require('./json-schema-next'); const { ValidationErrors } = require('./validation-errors'); const toGavelResult = require('../utils/to-gavel-result'); -const warnOnDeprecation = require('../utils/warn-on-deprecation'); - -/** - * Returns a proper article for a given string. - * @param {string} str - * @returns {string} - */ -function getArticle(str) { - return ['a', 'e', 'i', 'o', 'u'].includes(str.toLowerCase()) ? 'an' : 'a'; -} - -const jsonSchemaOptions = { - singleError: false, - messages: { - minLength: (prop, val, validator) => - `The ${prop} property must be at least ${validator} characters long (currently ${val.length} characters long).`, - maxLength: (prop, val, validator) => - `The ${prop} property must not exceed ${validator} characters (currently${val.length} characters long).`, - length: (prop, val, validator) => - `The ${prop} property must be exactly ${validator} characters long (currently ${val.length} characters long).`, - format: (prop, val, validator) => - `The ${prop} property must be ${getArticle( - validator[0] - )} ${validator} (current value is ${JSON.stringify(val)}).`, - type: (prop, val, validator) => - `The ${prop} property must be ${getArticle( - validator[0] - )} ${validator} (current value is ${JSON.stringify(val)})."`, - except: (prop, val) => `The ${prop} property must not be ${val}.`, - minimum: (prop, val, validator) => - `The minimum value of the ${prop} must be ${validator} (current value is ${JSON.stringify( - val - )}).`, - maximum: (prop, val, validator) => - `The maximum value of the ${prop} must be ${validator} (current value is ${JSON.stringify( - val - )}).`, - pattern: (prop, val, validator) => - `The ${prop} value (${val}) does not match the ${validator} pattern.`, - maxItems: (prop, val, validator) => - `The ${prop} property must not contain more than ${validator} items (currently contains ${val.length} items).`, - minItems: (prop, val, validator) => - `The ${prop} property must contain at least ${validator} items (currently contains ${val.length} items).`, - divisibleBy: (prop, val, validator) => - `The ${prop} property is not divisible by ${validator} (current value is ${JSON.stringify( - val - )}).`, - uniqueItems: (prop) => `All items in the ${prop} property must be unique.` - } -}; class JsonSchemaLegacy extends JsonSchemaValidator { validateSchema() { const { jsonSchema, jsonMetaSchema } = this; - // In case schema version is unidentified, - // assume JSON Schema Draft V3. - const metaSchema = jsonMetaSchema || META_SCHEMA.draftV3; + // Set the default JSON Schema version if no explicit + // version is provided. + const metaSchema = jsonMetaSchema || META_SCHEMA.draftV4; tv4.reset(); tv4.addSchema('', metaSchema); @@ -76,12 +25,6 @@ class JsonSchemaLegacy extends JsonSchemaValidator { const parsedData = this.parseData(data); switch (this.jsonSchemaVersion) { - case 'draftV3': - warnOnDeprecation( - 'JSON Schema Draft V3 is deprecated. Please use a newer version of JSON Schema (Draft V4-7).' - ); - - return this.validateUsingAmanda(parsedData); case 'draftV4': return this.validateUsingTV4(parsedData); default: @@ -91,40 +34,6 @@ class JsonSchemaLegacy extends JsonSchemaValidator { } } - validateUsingAmanda(data) { - let errors = { - length: 0, - errorMessages: {} - }; - - try { - amanda.validate(data, this.jsonSchema, jsonSchemaOptions, (error) => { - if (error && error.length > 0) { - for (let i = 0; i < error.length; i++) { - if (error[i].property === '') { - error[i].property = []; - } - } - - errors = new ValidationErrors(error); - } - }); - } catch (internalError) { - errors = new ValidationErrors({ - '0': { - property: [], - attributeValue: true, - message: `Validator internal error: ${internalError.message}`, - validatorName: 'error' - }, - length: 1, - errorMessages: {} - }); - } - - return toGavelResult(errors); - } - validateUsingTV4(data) { const result = tv4.validateMultiple(data, this.jsonSchema); const validationErrors = result.errors.concat(result.missing); diff --git a/lib/validators/json-schema-next.js b/lib/validators/json-schema-next.js index 908febca..50f3e8ba 100644 --- a/lib/validators/json-schema-next.js +++ b/lib/validators/json-schema-next.js @@ -5,19 +5,16 @@ const metaSchemaV6 = require('ajv/lib/refs/json-schema-draft-06.json'); const metaSchemaV7 = require('ajv/lib/refs/json-schema-draft-07.json'); const metaSchemaV4 = require('../meta-schema-v4'); -const metaSchemaV3 = require('../meta-schema-v3'); const errors = require('../errors'); const parseJson = require('../utils/parseJson'); const SCHEMA_VERSIONS = { - draftV3: 'http://json-schema.org/draft-03/schema', draftV4: 'http://json-schema.org/draft-04/schema', draftV6: 'http://json-schema.org/draft-06/schema', draftV7: 'http://json-schema.org/draft-07/schema' }; const META_SCHEMA = { - draftV3: metaSchemaV3, draftV4: metaSchemaV4, draftV6: metaSchemaV6, draftV7: metaSchemaV7 @@ -46,16 +43,15 @@ const getImplicitSchemaVersion = (jsonSchema) => { * without the explicit version. */ const getImplicitLegacySchemaVersion = (jsonSchema) => { - const [schemaVersion] = [ - ['draftV3', metaSchemaV3], - ['draftV4', metaSchemaV4] - ].find(([_, metaSchema]) => { - tv4.reset(); - tv4.addSchema('', metaSchema); - tv4.addSchema(metaSchema.$schema, metaSchema); - const validationResult = tv4.validateResult(jsonSchema, metaSchema); - return validationResult.valid; - }) || [null, null]; + const [schemaVersion] = [['draftV4', metaSchemaV4]].find( + ([_, metaSchema]) => { + tv4.reset(); + tv4.addSchema('', metaSchema); + tv4.addSchema(metaSchema.$schema, metaSchema); + const validationResult = tv4.validateResult(jsonSchema, metaSchema); + return validationResult.valid; + } + ) || [null, null]; return schemaVersion; }; diff --git a/lib/validators/json-schema.js b/lib/validators/json-schema.js index 178f9622..3f3a838b 100644 --- a/lib/validators/json-schema.js +++ b/lib/validators/json-schema.js @@ -29,7 +29,7 @@ class JsonSchema { constructor(jsonSchema) { const resolvedJsonSchema = resolveJsonSchema(jsonSchema); const jsonSchemaVersion = getSchemaVersion(resolvedJsonSchema); - const isLegacySchema = ['draftV3', 'draftV4'].includes(jsonSchemaVersion); + const isLegacySchema = ['draftV4'].includes(jsonSchemaVersion); // Instantiate different JSON Schema validators // based on the JSON Schema Draft version. diff --git a/package-lock.json b/package-lock.json index 5fa6f8f7..14a3e0f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -565,11 +565,6 @@ "uri-js": "^4.2.2" } }, - "amanda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amanda/-/amanda-1.0.1.tgz", - "integrity": "sha512-DJZMA1t7skqQgH5yq4NxBfqun+jDQUSYJRYGg+AqnKc3I0/hs8eX3PDdlfgiADkHgo4HlesD5QuoqFcCYA280Q==" - }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", diff --git a/package.json b/package.json index 667c375b..5eee9e07 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ }, "dependencies": { "ajv": "6.10.2", - "amanda": "1.0.1", "caseless": "0.12.0", "clone": "2.1.2", "commander": "3.0.2", diff --git a/test/fixtures/invalid-schema-v3.json b/test/fixtures/invalid-schema-v3.json deleted file mode 100644 index d99bccd7..00000000 --- a/test/fixtures/invalid-schema-v3.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-03/schema#", - "description": "Any validation failures are shown in the right-hand Messages pane.", - "type": "object", - "required": ["foo"], - "properties": { - "foo": { - "type": "number" - }, - "bar": { - "type": "string", - "enum": [ - "a", - "b", - "c" - ] - } - } -} \ No newline at end of file diff --git a/test/fixtures/valid-schema-v3.json b/test/fixtures/valid-schema-v3.json deleted file mode 100644 index d57c325b..00000000 --- a/test/fixtures/valid-schema-v3.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-03/schema#", - "description": "Any validation failures are shown in the right-hand Messages pane.", - "type": "object", - "properties": { - "foo": { - "type": "number" - }, - "bar": { - "type": "string", - "enum": [ - "a", - "b", - "c" - ] - } - } -} \ No newline at end of file diff --git a/test/unit/utils/schema-v4-generator.test.js b/test/unit/utils/schema-v4-generator.test.js index ef36e892..1064c527 100644 --- a/test/unit/utils/schema-v4-generator.test.js +++ b/test/unit/utils/schema-v4-generator.test.js @@ -1,5 +1,4 @@ const { assert } = require('chai'); -const amanda = require('amanda'); const tv4 = require('tv4'); const { @@ -64,12 +63,6 @@ describe('SchemaV4Generator', () => { assert.doesNotThrow(() => JSON.stringify(schema)); }); - it('which is valid json schema for amanda (doesNotThrow)', () => { - assert.doesNotThrow(() => - amanda.validate(JSON.parse(sampleJson), sg.schema, () => {}) - ); - }); - it('which is expected strict schema', () => { assert.deepEqual(JSON.parse(sampleJsonSchema), sg.schema); }); diff --git a/test/unit/validators/json-schema-legacy.test.js b/test/unit/validators/json-schema-legacy.test.js index 479207a1..a5212947 100644 --- a/test/unit/validators/json-schema-legacy.test.js +++ b/test/unit/validators/json-schema-legacy.test.js @@ -4,99 +4,10 @@ const { } = require('../../../lib/validators/json-schema-legacy'); const errorTypes = require('../../../lib/errors'); -const invalidJsonSchema3 = require('../../fixtures/invalid-schema-v3'); -const validJsonSchema3 = require('../../fixtures/valid-schema-v3'); const invalidJsonSchema4 = require('../../fixtures/invalid-schema-v4'); const validJsonSchema4 = require('../../fixtures/valid-schema-v4'); describe('JSON Schema (legacy)', () => { - /** - * V3 - */ - describe('given JSON Schema Draft 3', () => { - describe('and the schema is invalid', () => { - let init; - - before(() => { - init = () => new JsonSchemaLegacy(invalidJsonSchema3); - }); - - it('should throw an error about the invalid schema', () => { - expect(init).to.throw(errorTypes.JsonSchemaNotValid); - }); - }); - - describe('and the schema is valid', () => { - let init; - let validator; - - before(() => { - init = () => { - validator = new JsonSchemaLegacy(validJsonSchema3); - }; - }); - - it('should not throw any errors', () => { - expect(init).not.to.throw(); - }); - - it('should recognize schema version as v3', () => { - expect(validator).to.have.property('jsonSchemaVersion', 'draftV3'); - }); - - describe('given validating data', () => { - describe('and the data is invalid', () => { - let errors; - const data = { - foo: 'wrong value type' - }; - - before(() => { - errors = validator.validate(data); - }); - - describe('should return validation errors', () => { - it('should have exactly 1 error', () => { - expect(errors).to.have.lengthOf(1); - }); - - it('should have an error message', () => { - expect(errors[0].message).to.match( - /^The foo property must be a number \(current value is "wrong value type"\)/ - ); - }); - - it('should have a pointer', () => { - expect(errors[0]).to.have.nested.property( - 'location.pointer', - '/foo' - ); - }); - - it('should have a property name', () => { - expect(errors[0].location.property).to.deep.equal(['foo']); - }); - }); - }); - - describe('and the data is valid', () => { - let errors; - const data = { - foo: 123 - }; - - before(() => { - errors = validator.validate(data); - }); - - it('should return no errors', () => { - expect(errors).to.have.lengthOf(0); - }); - }); - }); - }); - }); - /** * V4 */ diff --git a/test/unit/validators/json-schema.test.js b/test/unit/validators/json-schema.test.js index 5aeb5c2d..2a5e81aa 100644 --- a/test/unit/validators/json-schema.test.js +++ b/test/unit/validators/json-schema.test.js @@ -148,79 +148,7 @@ describe('JsonSchema', () => { }); }); - describe('validate an object to check json_schema_options passed to Amanda', () => { - let errors = null; - let messagesLength = null; - - before(() => { - messagesLength = Object.keys( - fixtures.sampleJsonBodyTestingAmandaMessages - ).length; - validator = new JsonSchema( - fixtures.sampleJsonSchemaTestingAmandaMessages - ); - errors = validator.validate( - fixtures.sampleJsonBodyTestingAmandaMessages - ); - }); - - it('contains all those schema defined messages', () => { - assert.lengthOf(errors, messagesLength); - assert.lengthOf( - Object.keys( - fixtures.sampleJsonSchemaTestingAmandaMessages.properties - ), - messagesLength - ); - assert.lengthOf(errors, messagesLength); - }); - }); - describe('validateSchema', () => { - describe('with schema v3', () => { - describe('when invalid schema provided', () => { - let fn = null; - - before(() => { - const invalidSchema = require('../../fixtures/invalid-schema-v3'); - fn = () => { - validator = new JsonSchema(invalidSchema); - }; - }); - - it('should throw an error', () => { - assert.throw(fn); - }); - - it('should mention schema v3 in the message', () => { - try { - fn(); - } catch (e) { - assert.include(e.message, 'draftV3'); - } - }); - }); - - describe('when valid v3 schema provided', () => { - let fn = null; - before(() => { - const validSchema = require('../../fixtures/valid-schema-v3'); - fn = () => { - validator = new JsonSchema(validSchema); - }; - }); - - it('should not throw any error', () => { - assert.doesNotThrow(fn); - }); - - it('should set @jsonSchemaVersion to v3', () => { - fn(); - assert.equal(validator.jsonSchemaVersion, 'draftV3'); - }); - }); - }); - describe('with schema v4', () => { describe('when invalid v4 schema provided', () => { let fn = null; @@ -264,26 +192,6 @@ describe('JsonSchema', () => { }); describe('with not identified version of schema', () => { - describe('valid against v3 metaschema', () => { - let fn = null; - before(() => { - validSchema = require('../../fixtures/valid-schema-v3'); - delete validSchema.$schema; - fn = () => { - validator = new JsonSchema(validSchema); - }; - }); - - it('should not throw any error', () => { - assert.doesNotThrow(fn); - }); - - it('should set @jsonSchemaVersion to v3', () => { - fn(); - assert.equal(validator.jsonSchemaVersion, 'draftV3'); - }); - }); - describe('valid against v4 metaschema', () => { let fn = null; before(() => { @@ -318,12 +226,12 @@ describe('JsonSchema', () => { assert.throw(fn); }); - it('should mention both v3 and v4 in the error message', () => { + it('should mention only v4 in the error message', () => { try { fn(); } catch (error) { - assert.include(error.message, 'draftV3'); assert.include(error.message, 'draftV4'); + assert.notInclude(error.message, 'draftV3'); } }); });