@squiz/dx-json-schema-lib 1.2.13-alpha.2 → 1.2.13-alpha.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/jest.config.ts +7 -0
  3. package/lib/JsonValidationService.d.ts +4 -6
  4. package/lib/JsonValidationService.js +90 -28
  5. package/lib/JsonValidationService.js.map +1 -1
  6. package/lib/JsonValidationService.spec.js +276 -65
  7. package/lib/JsonValidationService.spec.js.map +1 -1
  8. package/lib/errors/SchemaValidationError.d.ts +1 -1
  9. package/lib/errors/SchemaValidationError.js +8 -1
  10. package/lib/errors/SchemaValidationError.js.map +1 -1
  11. package/lib/formatted-text/v1/formattedText.d.ts +18 -16
  12. package/lib/formatted-text/v1/formattedText.json +59 -88
  13. package/lib/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.d.ts +8 -0
  14. package/lib/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.js +21 -0
  15. package/lib/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.js.map +1 -0
  16. package/lib/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.spec.d.ts +1 -0
  17. package/lib/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.spec.js +52 -0
  18. package/lib/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.spec.js.map +1 -0
  19. package/lib/index.d.ts +1 -0
  20. package/lib/index.js +1 -0
  21. package/lib/index.js.map +1 -1
  22. package/lib/manifest/v1/Draft-07.json +155 -0
  23. package/lib/manifest/v1/DxComponentInputSchema.json +11 -8
  24. package/lib/manifest/v1/DxComponentInputSchema.spec.js +23 -9
  25. package/lib/manifest/v1/DxComponentInputSchema.spec.js.map +1 -1
  26. package/lib/manifest/v1/__test__/schemas/badFunctionInputComponent.json +2 -1
  27. package/lib/manifest/v1/__test__/schemas/badNestedFunctionInput.json +2 -1
  28. package/lib/manifest/v1/__test__/schemas/nonObjectFunctionInputComponent.json +2 -1
  29. package/lib/manifest/v1/__test__/schemas/validComponentJson.json +48 -0
  30. package/lib/manifest/v1/v1.d.ts +5 -7
  31. package/lib/manifest/v1/v1.spec.js +22 -3
  32. package/lib/manifest/v1/v1.spec.js.map +1 -1
  33. package/package.json +4 -3
  34. package/src/JsonValidationService.spec.ts +379 -70
  35. package/src/JsonValidationService.ts +112 -34
  36. package/src/errors/SchemaValidationError.ts +10 -2
  37. package/src/formatted-text/v1/formattedText.json +65 -89
  38. package/src/formatted-text/v1/formattedText.ts +19 -17
  39. package/src/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.spec.ts +58 -0
  40. package/src/formatted-text/v1/higherOrderFormattedTextToBaseFormattedText.ts +36 -0
  41. package/src/index.ts +1 -0
  42. package/src/manifest/v1/Draft-07.json +155 -0
  43. package/src/manifest/v1/DxComponentInputSchema.json +11 -8
  44. package/src/manifest/v1/DxComponentInputSchema.spec.ts +63 -35
  45. package/src/manifest/v1/__test__/schemas/badFunctionInputComponent.json +2 -1
  46. package/src/manifest/v1/__test__/schemas/badNestedFunctionInput.json +2 -1
  47. package/src/manifest/v1/__test__/schemas/nonObjectFunctionInputComponent.json +2 -1
  48. package/src/manifest/v1/__test__/schemas/validComponentJson.json +48 -0
  49. package/src/manifest/v1/v1.spec.ts +37 -3
  50. package/src/manifest/v1/v1.ts +5 -7
  51. package/tsconfig.json +3 -1
  52. package/tsconfig.tsbuildinfo +1 -1
@@ -2,14 +2,33 @@ import { JsonValidationService } from './JsonValidationService';
2
2
  import { SchemaValidationError } from './errors/SchemaValidationError';
3
3
 
4
4
  import validManifest from './manifest/v1/__test__/schemas/validComponent.json';
5
+ import validManifestJson from './manifest/v1/__test__/schemas/validComponentJson.json';
6
+ import { FormattedText } from './formatted-text/v1/formattedText';
7
+
8
+ // eslint-disable-next-line @typescript-eslint/ban-types
9
+ function expectToThrowErrorMatchingTypeAndMessage(received: Function, errorType: Function, message: string) {
10
+ let error: null | Error = null;
11
+
12
+ try {
13
+ received();
14
+ } catch (e: any) {
15
+ error = e;
16
+ }
17
+
18
+ expect(error).toBeDefined();
19
+ expect(error?.message).toEqual(message);
20
+ expect(error).toBeInstanceOf(errorType);
21
+ }
5
22
 
6
23
  describe('JsonValidationService', () => {
7
- let jsonValidationService: JsonValidationService;
8
- beforeEach(() => {
9
- jsonValidationService = new JsonValidationService();
10
- });
24
+ const jsonValidationService: JsonValidationService = new JsonValidationService();
11
25
 
12
26
  describe('validateManifest', () => {
27
+ it('should return true for a valid JSON endpoint manifest', () => {
28
+ const result = jsonValidationService.validateManifest(validManifestJson, 'v1');
29
+ expect(result).toBe(true);
30
+ });
31
+
13
32
  it('should return true for a valid manifest', () => {
14
33
  const result = jsonValidationService.validateManifest(validManifest, 'v1');
15
34
  expect(result).toBe(true);
@@ -20,18 +39,23 @@ describe('JsonValidationService', () => {
20
39
 
21
40
  delete (invalidManifest as any).name;
22
41
 
23
- expect(() => {
24
- jsonValidationService.validateManifest(invalidManifest, 'v1');
25
- }).toThrow(SchemaValidationError);
26
- expect(() => {
27
- jsonValidationService.validateManifest(invalidManifest, 'v1');
28
- }).toMatchInlineSnapshot(`[Function]`);
42
+ expectToThrowErrorMatchingTypeAndMessage(
43
+ () => {
44
+ jsonValidationService.validateManifest(invalidManifest, 'v1');
45
+ },
46
+ SchemaValidationError,
47
+ `failed validation: The required property \`name\` is missing at \`#\``,
48
+ );
29
49
  });
30
50
 
31
51
  it('should throw an error for an invalid manifest version', () => {
32
- expect(() => {
33
- jsonValidationService.validateManifest(validManifest, 'invalid-version' as any);
34
- }).toThrow(Error);
52
+ expectToThrowErrorMatchingTypeAndMessage(
53
+ () => {
54
+ jsonValidationService.validateManifest(validManifest, 'invalid-version' as any);
55
+ },
56
+ SchemaValidationError,
57
+ 'failed validation: Invalid manifest version',
58
+ );
35
59
  });
36
60
  });
37
61
 
@@ -50,8 +74,7 @@ describe('JsonValidationService', () => {
50
74
  expect(result).toBe(true);
51
75
  });
52
76
 
53
- // TODO DEVX-658
54
- it.skip('should return false for an invalid content schema, where the required property is missed from a child object', () => {
77
+ it('should return false for an invalid content schema, where the required property is missed from a child object', () => {
55
78
  const contentSchema = {
56
79
  type: 'object',
57
80
 
@@ -67,9 +90,13 @@ describe('JsonValidationService', () => {
67
90
 
68
91
  required: ['my-input'],
69
92
  };
70
- expect(() => {
71
- jsonValidationService.validateContentSchema(contentSchema);
72
- }).toThrow(SchemaValidationError);
93
+ expectToThrowErrorMatchingTypeAndMessage(
94
+ () => {
95
+ jsonValidationService.validateContentSchema(contentSchema);
96
+ },
97
+ SchemaValidationError,
98
+ 'failed validation: The required property `required` is missing at `#/properties/my-input`',
99
+ );
73
100
  });
74
101
 
75
102
  it('should throw a SchemaValidationError for an invalid content schema, top level type must be object', () => {
@@ -84,13 +111,16 @@ describe('JsonValidationService', () => {
84
111
  required: ['my-input'],
85
112
  },
86
113
  };
87
- expect(() => {
88
- jsonValidationService.validateContentSchema(contentSchema);
89
- }).toThrow(SchemaValidationError);
114
+ expectToThrowErrorMatchingTypeAndMessage(
115
+ () => {
116
+ jsonValidationService.validateContentSchema(contentSchema);
117
+ },
118
+ SchemaValidationError,
119
+ `failed validation: Expected value at \`#/type\` to be \`object\`, but value given is \`array\``,
120
+ );
90
121
  });
91
122
 
92
- // TODO DEVX-658
93
- it.skip('should throw a SchemaValidationError for an invalid content schema missing the required property', () => {
123
+ it('should throw a SchemaValidationError for an invalid content schema missing the required property', () => {
94
124
  const contentSchema = {
95
125
  type: 'object',
96
126
 
@@ -98,65 +128,344 @@ describe('JsonValidationService', () => {
98
128
  'my-input': { type: 'number' },
99
129
  },
100
130
  };
101
- expect(() => {
102
- jsonValidationService.validateContentSchema(contentSchema);
103
- }).toThrow(SchemaValidationError);
131
+ expectToThrowErrorMatchingTypeAndMessage(
132
+ () => {
133
+ jsonValidationService.validateContentSchema(contentSchema);
134
+ },
135
+ SchemaValidationError,
136
+ 'failed validation: The required property `required` is missing at `#`',
137
+ );
104
138
  });
105
139
  });
106
140
 
107
141
  describe('validateComponentInput', () => {
108
- const functionInputSchema = {
109
- type: 'object',
142
+ describe('FormattedText input', () => {
143
+ it('should handle type as an array', () => {
144
+ const functionInputSchema = {
145
+ type: 'object',
110
146
 
111
- properties: {
112
- 'my-input': { type: 'number' },
113
- },
147
+ properties: {
148
+ 'my-input': { type: ['number', 'string'] },
149
+ },
114
150
 
115
- required: ['my-input'],
116
- };
151
+ required: ['my-input'],
152
+ };
117
153
 
118
- it('should return true for valid input values', () => {
119
- const inputValue = {
120
- 'my-input': 123,
121
- };
122
- const result = jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
123
- expect(result).toBe(true);
124
- });
154
+ expect(
155
+ jsonValidationService.validateComponentInput(functionInputSchema, {
156
+ 'my-input': 'formattedText',
157
+ }),
158
+ ).toEqual(true);
125
159
 
126
- it('should throw a SchemaValidationError for invalid input type', () => {
127
- const inputValue = {
128
- 'my-input': '123',
129
- };
130
- expect(() => {
131
- jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
132
- }).toThrow(SchemaValidationError);
133
- expect(() => {
134
- jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
135
- }).toMatchInlineSnapshot(`[Function]`);
136
- });
160
+ expect(
161
+ jsonValidationService.validateComponentInput(functionInputSchema, {
162
+ 'my-input': 123,
163
+ }),
164
+ ).toEqual(true);
165
+ });
166
+
167
+ it('should handle type is an array of both string and FormattedText', () => {
168
+ const formattedText: FormattedText = [
169
+ {
170
+ tag: 'p',
171
+ type: 'tag',
172
+ children: [
173
+ { type: 'text', value: 'This is some ' },
174
+ { type: 'text', value: 'Link to asset 12345' },
175
+ { type: 'text', value: ' with an image ' },
176
+ { type: 'text', value: '.' },
177
+ ],
178
+ },
179
+ ];
180
+ const functionInputSchema = {
181
+ type: 'object',
182
+
183
+ properties: {
184
+ 'my-input': { type: ['FormattedText', 'string'] },
185
+ },
186
+
187
+ required: ['my-input'],
188
+ };
189
+
190
+ expect(
191
+ jsonValidationService.validateComponentInput(functionInputSchema, {
192
+ 'my-input': formattedText,
193
+ }),
194
+ ).toEqual(true);
195
+
196
+ expect(
197
+ jsonValidationService.validateComponentInput(functionInputSchema, {
198
+ 'my-input': 'hello',
199
+ }),
200
+ ).toEqual(true);
201
+ });
202
+
203
+ it('should return true if input is formatted text', () => {
204
+ const functionInputSchema = {
205
+ type: 'object',
206
+
207
+ properties: {
208
+ 'my-input': { type: 'FormattedText' },
209
+ },
210
+
211
+ required: ['my-input'],
212
+ };
213
+
214
+ const formattedText: FormattedText = [
215
+ {
216
+ tag: 'p',
217
+ type: 'tag',
218
+ children: [
219
+ { type: 'text', value: 'This is some ' },
220
+ { type: 'text', value: 'Link to asset 12345' },
221
+ { type: 'text', value: ' with an image ' },
222
+ { type: 'text', value: '.' },
223
+ ],
224
+ },
225
+ ];
226
+ const inputValue = {
227
+ 'my-input': formattedText,
228
+ };
229
+
230
+ expect(jsonValidationService.validateComponentInput(functionInputSchema, inputValue)).toEqual(true);
231
+ });
232
+
233
+ it('should throw an error if the FormattedText input is not formatted text', () => {
234
+ const functionInputSchema = {
235
+ type: 'object',
236
+
237
+ properties: {
238
+ 'my-input': { type: 'FormattedText' },
239
+ },
240
+
241
+ required: ['my-input'],
242
+ };
243
+ const inputValue = {
244
+ 'my-input': 123,
245
+ };
246
+ expectToThrowErrorMatchingTypeAndMessage(
247
+ () => {
248
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
249
+ },
250
+ SchemaValidationError,
251
+ `failed validation: Expected \`123\` (number) in \`#/my-input\` to be of type \`array\``,
252
+ );
253
+ });
254
+
255
+ it('should throw an error if the FormattedText input is invalid formatted text', () => {
256
+ const functionInputSchema = {
257
+ type: 'object',
258
+
259
+ properties: {
260
+ 'my-input': { type: 'FormattedText' },
261
+ },
262
+
263
+ required: ['my-input'],
264
+ };
265
+ const inputValue = {
266
+ 'my-input': {
267
+ something: 'aa',
268
+ },
269
+ };
270
+ expectToThrowErrorMatchingTypeAndMessage(
271
+ () => {
272
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
273
+ },
274
+ SchemaValidationError,
275
+ `failed validation: Expected \`[object Object]\` (object) in \`#/my-input\` to be of type \`array\``,
276
+ );
277
+ });
278
+
279
+ it('should throw an error if the FormattedText input is invalid formatted text (deeply nested)', () => {
280
+ const functionInputSchema = {
281
+ type: 'object',
282
+
283
+ properties: {
284
+ 'my-input': { type: 'FormattedText' },
285
+ },
286
+
287
+ required: ['my-input'],
288
+ };
289
+ const formattedText: FormattedText = [
290
+ {
291
+ tag: 'p',
292
+ type: 'tag',
293
+ children: [
294
+ { type: 'text', value: 'This is some ' },
295
+ { type: 'text', value: 'Link to asset 12345' },
296
+ { type: 'text', value: ' with an image ' },
297
+ { type: 'text', value: 123 as any }, // see here
298
+ { type: 'text', value: '.' },
299
+ ],
300
+ },
301
+ ];
302
+
303
+ const inputValue = {
304
+ 'my-input': formattedText,
305
+ };
306
+ expectToThrowErrorMatchingTypeAndMessage(
307
+ () => {
308
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
309
+ },
310
+ SchemaValidationError,
311
+ 'failed validation: Object at `#/my-input/0` does not match any schema',
312
+ );
313
+ });
314
+
315
+ it('should validate an array of formatted texts', () => {
316
+ const formattedText: FormattedText = [
317
+ {
318
+ tag: 'p',
319
+ type: 'tag',
320
+ children: [{ type: 'text', value: 'hello' }],
321
+ },
322
+ ];
323
+
324
+ const schema = {
325
+ type: 'object',
326
+ properties: {
327
+ arr: {
328
+ type: 'array',
329
+ items: { type: 'FormattedText' },
330
+ },
331
+ },
332
+ };
333
+ const value = {
334
+ arr: [formattedText],
335
+ };
336
+
337
+ expect(jsonValidationService.validateComponentInput(schema, value)).toEqual(true);
338
+ });
339
+
340
+ it('should throw an error if the FormattedText input is invalid formatted text (deeply, deeply nested)', () => {
341
+ const formattedText: FormattedText = [
342
+ {
343
+ tag: 'p',
344
+ type: 'tag',
345
+ children: [{ type: 'text', value: 'hello' }],
346
+ },
347
+ ];
348
+
349
+ const inputValue: any = {
350
+ 'my-input': formattedText,
351
+ deep: {
352
+ arr: [
353
+ {
354
+ prop: formattedText,
355
+ formattedTextArray: [formattedText, formattedText, { bad: 'data' }],
356
+ },
357
+ ],
358
+ },
359
+ };
137
360
 
138
- it('should throw a SchemaValidationError for invalid input missing properties', () => {
139
- const inputValue = {};
140
- expect(() => {
141
- jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
142
- }).toThrow(SchemaValidationError);
143
- expect(() => {
144
- jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
145
- }).toMatchInlineSnapshot(`[Function]`);
361
+ const functionInputSchema = {
362
+ type: 'object',
363
+
364
+ properties: {
365
+ 'my-input': { type: 'FormattedText' },
366
+ deep: {
367
+ type: 'object',
368
+ properties: {
369
+ arr: {
370
+ type: 'array',
371
+ items: {
372
+ type: 'object',
373
+ required: ['prop', 'formattedTextArray'],
374
+ properties: {
375
+ prop: 'FormattedText',
376
+ formattedTextArray: {
377
+ type: 'array',
378
+ items: {
379
+ type: 'FormattedText',
380
+ },
381
+ },
382
+ },
383
+ },
384
+ },
385
+ },
386
+ required: ['arr'],
387
+ },
388
+ },
389
+
390
+ required: ['my-input', 'deep'],
391
+ };
392
+
393
+ expectToThrowErrorMatchingTypeAndMessage(
394
+ () => {
395
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
396
+ },
397
+ SchemaValidationError,
398
+ 'failed validation: Expected `[object Object]` (object) in `#/deep/arr/0/formattedTextArray/2` to be of type `array`',
399
+ );
400
+ });
146
401
  });
147
402
 
148
- // TODO DEVX-658
149
- it.skip('should throw a SchemaValidationError for invalid input additional properties', () => {
150
- const inputValue = {
151
- 'my-input': 123,
152
- 'my-input-2': 123,
403
+ describe('standard inputs', () => {
404
+ const functionInputSchema = {
405
+ type: 'object',
406
+
407
+ properties: {
408
+ 'my-input': { type: 'number' },
409
+ },
410
+
411
+ required: ['my-input'],
153
412
  };
154
- expect(() => {
155
- jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
156
- }).toThrow(SchemaValidationError);
157
- expect(() => {
158
- jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
159
- }).toMatchInlineSnapshot();
413
+
414
+ it('should return true for valid input values', () => {
415
+ const inputValue = {
416
+ 'my-input': 123,
417
+ };
418
+ const result = jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
419
+ expect(result).toBe(true);
420
+ });
421
+
422
+ it('should throw a SchemaValidationError for invalid input type', () => {
423
+ const inputValue = {
424
+ 'my-input': '123',
425
+ };
426
+ expectToThrowErrorMatchingTypeAndMessage(
427
+ () => {
428
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
429
+ },
430
+ SchemaValidationError,
431
+ `failed validation: Expected \`123\` (string) in \`#/my-input\` to be of type \`number\``,
432
+ );
433
+ });
434
+
435
+ it('should throw a SchemaValidationError for invalid input missing properties', () => {
436
+ const inputValue = {};
437
+ expectToThrowErrorMatchingTypeAndMessage(
438
+ () => {
439
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
440
+ },
441
+ SchemaValidationError,
442
+ `failed validation: The required property \`my-input\` is missing at \`#\``,
443
+ );
444
+ });
445
+
446
+ it('should throw a SchemaValidationError with a truncated enum value list if there are more than 5 enum options', () => {
447
+ expectToThrowErrorMatchingTypeAndMessage(
448
+ () => {
449
+ jsonValidationService.validateComponentInput(
450
+ { type: 'object', properties: { enum: { type: 'string', enum: ['a', 'b', 'c', 'd', 'e', 'f', 'g'] } } },
451
+ { enum: 'z' },
452
+ );
453
+ },
454
+ SchemaValidationError,
455
+ 'failed validation: Expected given value `z` in #/enum` to be one of `[a, b, c, d, e, ... 2 more]`',
456
+ );
457
+ });
458
+
459
+ // TODO DEVX-658
460
+ it.skip('should throw a SchemaValidationError for invalid input additional properties', () => {
461
+ const inputValue = {
462
+ 'my-input': 123,
463
+ 'my-input-2': 123,
464
+ };
465
+ expect(() => {
466
+ jsonValidationService.validateComponentInput(functionInputSchema, inputValue);
467
+ }).toThrowErrorMatchingInlineSnapshot();
468
+ });
160
469
  });
161
470
  });
162
471
  });
@@ -1,54 +1,132 @@
1
- import Ajv, { AnySchemaObject, SchemaObject } from 'ajv';
2
- import addFormats from 'ajv-formats';
3
- import betterAjvErrors from 'better-ajv-errors';
4
-
5
1
  import DxComponentInputSchema from './manifest/v1/DxComponentInputSchema.json';
6
2
  import DxComponentIcons from './manifest/v1/DxComponentIcons.json';
7
3
  import DxContentMetaSchema from './manifest/v1/DxContentMetaSchema.json';
4
+ import Draft07Schema from './manifest/v1/Draft-07.json';
5
+
6
+ import FormattedText from './formatted-text/v1/formattedText.json';
7
+
8
8
  import v1 from './manifest/v1/v1.json';
9
9
  import { SchemaValidationError } from './errors/SchemaValidationError';
10
+ import { Draft07, JSONError, JSONSchema, Draft, DraftConfig } from 'json-schema-library';
11
+
12
+ import { draft07Config } from 'json-schema-library';
13
+ import { MANIFEST_MODELS } from '.';
14
+
15
+ const defaultConfig: DraftConfig = {
16
+ ...draft07Config,
17
+ errors: {
18
+ ...draft07Config.errors,
19
+
20
+ enumError(data) {
21
+ let values = '[]';
22
+
23
+ if (data.values && Array.isArray(data.values)) {
24
+ if (data.values.length < 5) {
25
+ values = `[${data.values.join(', ')}]`;
26
+ } else {
27
+ const firstFiveValues = data.values.slice(0, 5);
28
+ values = `[${firstFiveValues.join(', ')}, ... ${data.values.length - 5} more]`;
29
+ }
30
+ }
31
+
32
+ return {
33
+ type: 'error',
34
+ name: 'EnumError',
35
+ code: 'enum-error',
36
+ message: `Expected given value \`${data.value}\` in ${data.pointer}\` to be one of \`${values}\``,
37
+ data,
38
+ };
39
+ },
40
+
41
+ anyOfError(data) {
42
+ let value = `Value \`${data.value}\` at`;
43
+
44
+ if (typeof data.value == 'object') {
45
+ value = 'Object at';
46
+ }
47
+
48
+ if (Array.isArray(data.value)) {
49
+ value = 'Array at';
50
+ }
51
+
52
+ return {
53
+ type: 'error',
54
+ name: 'AnyOfError',
55
+ code: 'any-of-error',
56
+ message: `${value} \`${data.pointer}\` does not match any schema`,
57
+ data,
58
+ };
59
+ },
60
+ },
61
+ };
62
+
63
+ const FTSchema = new Draft07(FormattedText);
64
+
65
+ const ComponentInputSchema = new Draft(
66
+ {
67
+ ...defaultConfig,
68
+
69
+ resolveRef(schema, rootSchema) {
70
+ const resolvedSchema = draft07Config.resolveRef(schema, rootSchema) as MANIFEST_MODELS.v1.CoreSchemaMetaSchema;
71
+ if (!resolvedSchema) {
72
+ return resolvedSchema;
73
+ }
74
+ if (resolvedSchema.type === 'FormattedText') {
75
+ return FTSchema.rootSchema;
76
+ } else if (Array.isArray(resolvedSchema.type) && resolvedSchema.type.includes('FormattedText')) {
77
+ return {
78
+ ...schema,
79
+ ...FTSchema.rootSchema,
80
+ type: resolvedSchema.type.filter((t) => t !== 'FormattedText').concat(FormattedText.type as 'array'),
81
+ };
82
+ } else {
83
+ return resolvedSchema;
84
+ }
85
+ },
86
+ },
87
+ DxComponentInputSchema,
88
+ );
89
+ ComponentInputSchema.addRemoteSchema('DxComponentInputSchema.json/DxContentMetaSchema.json', DxContentMetaSchema);
90
+
91
+ const v1Schema = new Draft07(v1, defaultConfig);
92
+
93
+ v1Schema.addRemoteSchema('DxComponentInputSchema.json/DxContentMetaSchema.json', DxContentMetaSchema);
94
+ v1Schema.addRemoteSchema('/DxComponentInputSchema.json', ComponentInputSchema.getSchema());
95
+ v1Schema.addRemoteSchema('/DxComponentIcons.json', DxComponentIcons);
96
+ v1Schema.addRemoteSchema('http://json-schema.org/draft-07/schema', Draft07Schema);
97
+ v1Schema.addRemoteSchema('http://json-schema.org/draft-07/schema#', Draft07Schema);
98
+
99
+ const ContentSchemaSchema = new Draft07(DxComponentInputSchema, defaultConfig);
100
+ ContentSchemaSchema.addRemoteSchema('DxComponentInputSchema.json/DxContentMetaSchema.json', DxContentMetaSchema);
10
101
 
11
102
  export class JsonValidationService {
12
- ajv: Ajv;
13
- constructor() {
14
- this.ajv = new Ajv({
15
- strict: true,
16
- schemas: { DxContentMetaSchema, v1, DxComponentIcons, DxComponentInputSchema },
17
- allowUnionTypes: true,
18
- allErrors: true,
19
- formats: {
20
- 'multi-line': true,
21
- },
22
- });
23
-
24
- addFormats(this.ajv);
25
- }
26
103
  validateManifest(manifest: unknown, version: 'v1') {
27
104
  switch (version) {
28
- case 'v1':
29
- return this.doValidate(v1, manifest);
105
+ case 'v1': {
106
+ const validationResult = v1Schema.validate(manifest);
107
+ return this.processValidationResult(validationResult);
108
+ }
30
109
 
31
110
  default:
32
- throw new Error('Invalid manifest version');
111
+ throw new SchemaValidationError('Invalid manifest version');
33
112
  }
34
113
  }
35
-
36
- validateContentSchema(contentSchema: AnySchemaObject) {
37
- // This schema matches the input definition in the manifest
38
- return this.doValidate(DxComponentInputSchema, contentSchema);
114
+ validateContentSchema(contentSchema: JSONSchema) {
115
+ return this.processValidationResult(ContentSchemaSchema.validate(contentSchema));
39
116
  }
40
117
 
41
- validateComponentInput(functionInputSchema: SchemaObject, inputValue: unknown) {
42
- return this.doValidate(functionInputSchema, inputValue);
118
+ validateComponentInput(functionInputSchema: JSONSchema, inputValue: unknown) {
119
+ const inputSchema = ComponentInputSchema.compileSchema(functionInputSchema);
120
+ const errors = ComponentInputSchema.validate(inputValue, inputSchema);
121
+
122
+ return this.processValidationResult(errors);
43
123
  }
44
124
 
45
- private doValidate(schema: string | SchemaObject, value: unknown): true {
46
- const result = this.ajv.validate(schema, value);
47
- if (!result) {
48
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
49
- const betterErrors = betterAjvErrors(schema, value, this.ajv.errors!, { format: 'js' });
50
- throw new SchemaValidationError(betterErrors);
125
+ private processValidationResult(errors: JSONError[]): true {
126
+ if (errors.length > 0) {
127
+ throw new SchemaValidationError(errors.map((a) => a.message).join(',\n'));
51
128
  }
52
- return result;
129
+
130
+ return true;
53
131
  }
54
132
  }