Skip to content

Commit 85f9db7

Browse files
authored
452 fix issue with validation with schemas with external references (#894)
* rename documentation utility * fix error with schemas with external references in validation mode * apply formatting changes --------- Co-authored-by: Logende <[email protected]>
1 parent f73e7ef commit 85f9db7

6 files changed

Lines changed: 60 additions & 10 deletions

File tree

meta_configurator/src/data/managedValidation.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {sizeOf} from '@/utility/sizeOf';
88

99
// Import worker as ESM
1010
import ValidationWorker from '@/workers/validationWorker?worker';
11+
import {removeExternalReferences} from '@/schema/removeExternalReferences.ts';
1112

1213
export class ManagedValidation {
1314
private worker: Worker;
@@ -70,14 +71,17 @@ export class ManagedValidation {
7071
this.isValidationOngoing = true;
7172

7273
const data = getDataForMode(this.mode).data.value;
73-
const schema = getSchemaForMode(this.mode).schemaRaw.value;
7474

7575
if (sizeOf(data) > useSettings().value.performance.maxDocumentSizeForValidation) {
7676
this.currentValidationResult.value = new ValidationResult([]);
7777
this.isValidationOngoing = false;
7878
return;
7979
}
8080

81+
// duplicate the schema and remove external references to avoid ajv trying to fetch them and throwing an error if they are not available
82+
const schema = JSON.parse(JSON.stringify(getSchemaForMode(this.mode).schemaRaw.value));
83+
removeExternalReferences(schema);
84+
8185
try {
8286
this.currentValidationResult.value = await this.validateWithWorker(data, schema);
8387
} catch (err) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {isExternalRef} from '@/schema/externalReferences.ts';
2+
3+
export function removeExternalReferences(schema: any): number {
4+
let externalRefsRemoved = 0;
5+
6+
// recursively go over while schema and remove all external refs
7+
if (typeof schema === 'object' && schema !== null) {
8+
if (
9+
schema.$ref !== undefined &&
10+
typeof schema.$ref === 'string' &&
11+
isExternalRef(schema.$ref)
12+
) {
13+
// remove external ref
14+
delete schema.$ref;
15+
externalRefsRemoved++;
16+
}
17+
// recursively check all properties of the schema
18+
for (const key in schema) {
19+
if (schema.hasOwnProperty(key)) {
20+
externalRefsRemoved += removeExternalReferences(schema[key]);
21+
}
22+
}
23+
}
24+
// handle arrays
25+
if (Array.isArray(schema)) {
26+
for (const item of schema) {
27+
externalRefsRemoved += removeExternalReferences(item);
28+
}
29+
}
30+
31+
return externalRefsRemoved;
32+
}

meta_configurator/src/schema/resolveReferences.ts renamed to meta_configurator/src/schema/resolveReferencesForDocumentation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {doesSchemaHaveType} from '@/schema/schemaReadingUtils';
88
import {useErrorService} from '@/utility/errorServiceInstance';
99

1010
// this code is not recursive and also mainly developed for the documentation view, not sure how generally applicable it is
11-
export function resolveReferences(subSchema: any, rootSchema: TopLevelSchema): any {
11+
export function resolveReferencesForDocumentation(subSchema: any, rootSchema: TopLevelSchema): any {
1212
if (!subSchema) {
1313
return new Set();
1414
}
@@ -19,7 +19,7 @@ export function resolveReferences(subSchema: any, rootSchema: TopLevelSchema): a
1919
// copy subSchema
2020
const subSchemaWithoutRef = cloneDeep(subSchema);
2121
delete subSchemaWithoutRef.$ref;
22-
const resolvedSchema = resolveReferences(schemaAtRef, rootSchema);
22+
const resolvedSchema = resolveReferencesForDocumentation(schemaAtRef, rootSchema);
2323
try {
2424
return mergeAllOfs([subSchemaWithoutRef, resolvedSchema]);
2525
} catch (error) {

meta_configurator/src/schema/validationService.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getTopLevelSchemaId,
1111
ValidationResult,
1212
} from '@/schema/validationUtils';
13+
import {removeExternalReferences} from '@/schema/removeExternalReferences.ts';
1314

1415
/**
1516
* Service for validating data against a JSON schema.
@@ -32,10 +33,13 @@ export class ValidationService {
3233
}
3334

3435
private initValidationFunction() {
35-
this._ajv = getMatchingAjvVersion(this.topLevelSchema);
36+
// duplicate the schema and remove external references to avoid ajv trying to fetch them and throwing an error if they are not available
37+
let schema = JSON.parse(JSON.stringify(this.topLevelSchema));
38+
removeExternalReferences(schema);
39+
this._ajv = getMatchingAjvVersion(schema);
3640
addFormats(this._ajv);
37-
const topLevelSchemaId = getTopLevelSchemaId(this.topLevelSchema);
38-
this._ajv.addSchema(this.topLevelSchema, topLevelSchemaId);
41+
const topLevelSchemaId = getTopLevelSchemaId(schema);
42+
this._ajv.addSchema(schema, topLevelSchemaId);
3943
this._validationFunction = this._ajv.getSchema(topLevelSchemaId);
4044
}
4145

meta_configurator/src/utility/documentation/documentationUtils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type {JsonSchemaObjectType, TopLevelSchema} from '@/schema/jsonSchemaType';
2-
import {collectReferences, findTargetPath, resolveReferences} from '@/schema/resolveReferences';
2+
import {
3+
collectReferences,
4+
findTargetPath,
5+
resolveReferencesForDocumentation,
6+
} from '@/schema/resolveReferencesForDocumentation.ts';
37
import type {Path} from '@/utility/path';
48
import {pathToAscii} from '@/utility/pathUtils';
59
import {formatRegistry} from '@/dataformats/formatRegistry';
@@ -96,7 +100,10 @@ export function generateSchemaInstance(
96100
visitedReferences = new Set([...visitedReferences, ...collectReferences(schema, rootSchema)]);
97101

98102
// resolve the references of the current schema if needed
99-
const resolvedSchema: JsonSchemaObjectType = resolveReferences(schema, rootSchema);
103+
const resolvedSchema: JsonSchemaObjectType = resolveReferencesForDocumentation(
104+
schema,
105+
rootSchema
106+
);
100107

101108
const type = resolvedSchema?.type ?? 'any';
102109
if (type === 'string') return '{string}';

meta_configurator/src/utility/documentation/schemaToMarkdown.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
SchemaNodeData,
1212
} from '@/schema/graph-representation/schemaGraphTypes';
1313
import type {JsonSchemaObjectType, TopLevelSchema} from '@/schema/jsonSchemaType';
14-
import {resolveReferences} from '@/schema/resolveReferences';
14+
import {resolveReferencesForDocumentation} from '@/schema/resolveReferencesForDocumentation.ts';
1515
import {hasOutgoingEdge} from '@/schema/graph-representation/graphUtils';
1616
import {
1717
escapeMarkdown,
@@ -267,7 +267,10 @@ function writeObjectNode(
267267
md.push(`</details>`);
268268
}
269269

270-
const instance = generateSchemaInstance(resolveReferences(node.schema, rootSchema), rootSchema);
270+
const instance = generateSchemaInstance(
271+
resolveReferencesForDocumentation(node.schema, rootSchema),
272+
rootSchema
273+
);
271274
if (instance) {
272275
md.push('#### Example\n');
273276
md.push('```' + useSettings().value.dataFormat);

0 commit comments

Comments
 (0)