diff --git a/frontend/package.json b/frontend/package.json index 6c55031f5..605709f29 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "@tanstack/react-table": "8.16.0", "ace-builds": "1.33.0", "ajv": "8.8.2", + "ajv-draft-04": "^1.0.0", "ajv-formats": "2.1.1", "json-schema-faker": "0.5.6", "jsonpath-plus": "10.2.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d23c01a88..aaa0eb140 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -51,6 +51,9 @@ importers: ajv: specifier: 8.8.2 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) @@ -1381,6 +1384,14 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -5461,6 +5472,10 @@ snapshots: transitivePeerDependencies: - supports-color + ajv-draft-04@1.0.0(ajv@8.8.2): + optionalDependencies: + ajv: 8.8.2 + ajv-formats@2.1.1(ajv@8.8.2): optionalDependencies: ajv: 8.8.2 diff --git a/frontend/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts b/frontend/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts index 6dbfe5fb6..757cb5fa5 100644 --- a/frontend/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts +++ b/frontend/src/components/Topics/Topic/SendMessage/__test__/utils.spec.ts @@ -81,5 +81,31 @@ describe('SendMessage utils', () => { 'Error in parsing the "content" field value', ]); }); + + 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/frontend/src/components/Topics/Topic/SendMessage/utils.ts b/frontend/src/components/Topics/Topic/SendMessage/utils.ts index ddc3c8d68..e457e43b5 100644 --- a/frontend/src/components/Topics/Topic/SendMessage/utils.ts +++ b/frontend/src/components/Topics/Topic/SendMessage/utils.ts @@ -4,7 +4,11 @@ import { TopicSerdeSuggestion, } from 'generated-sources'; import jsf from 'json-schema-faker'; -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'; jsf.option('fillProperties', false); @@ -56,6 +60,21 @@ function upperFirst(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } +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, @@ -84,7 +103,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);