From e2b2536a346c23c82de416c8c16c8d2c9b58758f Mon Sep 17 00:00:00 2001 From: philosomniac Date: Wed, 28 Feb 2024 15:23:20 -0600 Subject: [PATCH] add schema validation support for more json-schema version refs (closes #2354) --- kafka-ui-react-app/package.json | 1 + kafka-ui-react-app/pnpm-lock.yaml | 14 +++++++++++ .../Topic/SendMessage/__test__/utils.spec.ts | 22 +++++++++++++++++ .../Topics/Topic/SendMessage/utils.ts | 24 +++++++++++++++++-- 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/kafka-ui-react-app/package.json b/kafka-ui-react-app/package.json index 172ec4466ab..a0253977d35 100644 --- a/kafka-ui-react-app/package.json +++ b/kafka-ui-react-app/package.json @@ -16,6 +16,7 @@ "@types/testing-library__jest-dom": "^5.14.5", "ace-builds": "^1.7.1", "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", "ajv-formats": "^2.1.1", "classnames": "^2.2.6", "fetch-mock": "^9.11.0", diff --git a/kafka-ui-react-app/pnpm-lock.yaml b/kafka-ui-react-app/pnpm-lock.yaml index 01862dd3bbe..ce8e3d0326d 100644 --- a/kafka-ui-react-app/pnpm-lock.yaml +++ b/kafka-ui-react-app/pnpm-lock.yaml @@ -41,6 +41,9 @@ dependencies: ajv: specifier: ^8.6.3 version: 8.8.2 + ajv-draft-04: + specifier: ^1.0.0 + version: 1.0.0(ajv@8.8.2) ajv-formats: specifier: ^2.1.1 version: 2.1.1(ajv@8.8.2) @@ -2126,6 +2129,17 @@ packages: - supports-color dev: true + /ajv-draft-04@1.0.0(ajv@8.8.2): + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.8.2 + dev: false + /ajv-formats@2.1.1(ajv@8.8.2): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: diff --git a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts index 6dbfe5fb6f2..504a96bbc5b 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts +++ b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts @@ -81,5 +81,27 @@ describe('SendMessage utils', () => { 'Error in parsing the "content" field value', ]); }); + + // https://github.com/provectus/kafka-ui/issues/2354 + it('should successfully validate when schema references json-schema version draft-07', () => { + const schema = `{"type":"object","title":"A","description":"A","$id":"https://example.com/A.json","$schema":"http://json-schema.org/draft-07/schema#","required":["a"],"properties":{"a":{"type":"number"}}}`; + expect(validateBySchema('{"a": 6}', schema, 'key')).toHaveLength(0); + }); + it('should successfully validate when schema references json-schema version draft-06', () => { + const schema = `{"type":"object","title":"A","description":"A","$id":"https://example.com/A.json","$schema":"http://json-schema.org/draft-06/schema#","required":["a"],"properties":{"a":{"type":"number"}}}`; + expect(validateBySchema('{"a": 6}', schema, 'key')).toHaveLength(0); + }); + it('should successfully validate when schema references json-schema version draft-04', () => { + const schema = `{"type":"object","title":"A","description":"A","id":"https://example.com/A.json","$schema":"http://json-schema.org/draft-04/schema#","required":["a"],"properties":{"a":{"type":"number"}}}`; + expect(validateBySchema('{"a": 6}', schema, 'key')).toHaveLength(0); + }); + it('should successfully validate when schema references json-schema version draft-2019-09', () => { + const schema = `{"type":"object","title":"A","description":"A","$id":"https://example.com/A.json","$schema":"https://json-schema.org/draft/2019-09/schema","required":["a"],"properties":{"a":{"type":"number"}}}`; + expect(validateBySchema('{"a": 6}', schema, 'key')).toHaveLength(0); + }); + it('should successfully validate when schema references json-schema version draft-2020-12', () => { + const schema = `{"type":"object","title":"A","description":"A","$id":"https://example.com/A.json","$schema":"https://json-schema.org/draft/2020-12/schema","required":["a"],"properties":{"a":{"type":"number"}}}`; + expect(validateBySchema('{"a": 6}', schema, 'key')).toHaveLength(0); + }); }); }); diff --git a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/utils.ts b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/utils.ts index c8161b0c823..19ef1e6d716 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/utils.ts +++ b/kafka-ui-react-app/src/components/Topics/Topic/SendMessage/utils.ts @@ -5,7 +5,11 @@ import { } from 'generated-sources'; import jsf from 'json-schema-faker'; import { compact } from 'lodash'; -import Ajv, { DefinedError } from 'ajv/dist/2020'; +import Ajv, { DefinedError } from 'ajv'; +import Ajv2019 from 'ajv/dist/2019'; +import Ajv2020 from 'ajv/dist/2020'; +import * as draft6MetaSchema from 'ajv/dist/refs/json-schema-draft-06.json'; +import AjvDraft4 from 'ajv-draft-04'; import addFormats from 'ajv-formats'; import upperFirst from 'lodash/upperFirst'; @@ -54,6 +58,21 @@ export const getSerdeOptions = (items: SerdeDescription[]) => { return compact(options); }; +const getAjvVersionForSchemaRef = (schemaRef: string) => { + switch (schemaRef) { + case 'https://json-schema.org/draft/2019-09/schema': + return new Ajv2019(); + case 'https://json-schema.org/draft/2020-12/schema': + return new Ajv2020(); + case 'http://json-schema.org/draft-06/schema#': + return new Ajv().addMetaSchema(draft6MetaSchema); + case 'http://json-schema.org/draft-04/schema#': + return new AjvDraft4(); + default: + return new Ajv(); + } +}; + export const validateBySchema = ( value: string, schema: string | undefined, @@ -82,7 +101,8 @@ export const validateBySchema = ( return [`Error in parsing the "${type}" field value`]; } try { - const ajv = new Ajv(); + const schemaRef = parsedSchema.$schema; + const ajv = getAjvVersionForSchemaRef(schemaRef); addFormats(ajv); const validate = ajv.compile(parsedSchema); validate(parsedValue);