@squiz/dx-json-schema-lib 1.33.1-alpha.4 → 1.33.1-alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- package/.npm/_logs/{2023-05-12T02_45_11_782Z-debug-0.log → 2023-05-15T01_10_06_696Z-debug-0.log} +15 -15
- package/lib/JsonValidationService.d.ts +0 -2
- package/lib/JsonValidationService.js +40 -29
- package/lib/JsonValidationService.js.map +1 -1
- package/lib/JsonValidationService.spec.js +262 -16
- package/lib/JsonValidationService.spec.js.map +1 -1
- package/lib/validators/customFormatValidators.js +5 -1
- package/lib/validators/customFormatValidators.js.map +1 -1
- package/lib/validators/utils/getValidationDataForMatrixUri.d.ts +8 -0
- package/lib/validators/utils/getValidationDataForMatrixUri.js +51 -0
- package/lib/validators/utils/getValidationDataForMatrixUri.js.map +1 -0
- package/lib/validators/utils/getValidationDataForMatrixUri.spec.d.ts +1 -0
- package/lib/validators/utils/getValidationDataForMatrixUri.spec.js +91 -0
- package/lib/validators/utils/getValidationDataForMatrixUri.spec.js.map +1 -0
- package/package.json +2 -2
- package/src/JsonValidationService.spec.ts +280 -7
- package/src/JsonValidationService.ts +43 -29
- package/src/validators/customFormatValidators.ts +8 -1
- package/src/validators/utils/getValidationDataForMatrixUri.spec.ts +92 -0
- package/src/validators/utils/getValidationDataForMatrixUri.ts +55 -0
- package/tsconfig.tsbuildinfo +1 -1
@@ -13,7 +13,7 @@ import {
|
|
13
13
|
ResolvableType,
|
14
14
|
TypeResolver,
|
15
15
|
} from './jsonTypeResolution/TypeResolver';
|
16
|
-
import { FormattedTextType } from './primitiveTypes';
|
16
|
+
import { FormattedTextType, SquizImageType } from './primitiveTypes';
|
17
17
|
|
18
18
|
function expectToThrowErrorMatchingTypeAndMessage(
|
19
19
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
@@ -145,7 +145,7 @@ describe('JsonValidationService', () => {
|
|
145
145
|
).toEqual(true);
|
146
146
|
});
|
147
147
|
it('should throw error for invalid matrix-asset-uri format', () => {
|
148
|
-
const invalidMatrixAssetUri = 'not-valid://canary
|
148
|
+
const invalidMatrixAssetUri = 'not-valid://canary uat matrix.squiz.cloud//abc123';
|
149
149
|
expectToThrowErrorMatchingTypeAndMessage(
|
150
150
|
() => {
|
151
151
|
jsonValidationService.validateRenderInput(functionInputSchema, {
|
@@ -153,8 +153,21 @@ describe('JsonValidationService', () => {
|
|
153
153
|
});
|
154
154
|
},
|
155
155
|
SchemaValidationError,
|
156
|
-
`failed validation:
|
157
|
-
|
156
|
+
`failed validation: matrix asset uri "not-valid://canary uat matrix.squiz.cloud//abc123" isn't a valid matrix asset uri`,
|
157
|
+
{
|
158
|
+
assetId: {
|
159
|
+
data: { expected: /^\d+(?::.+)?$/, received: '/abc123' },
|
160
|
+
message: 'Matrix Asset Id has invalid format, must match /^d+(?::.+)?$/',
|
161
|
+
},
|
162
|
+
identifier: {
|
163
|
+
data: { expected: /^[a-zA-Z0-9.-]+$/, received: 'canary uat matrix.squiz.cloud' },
|
164
|
+
message: 'Matrix Identifier has invalid format, must match /^[a-zA-Z0-9.-]+$/',
|
165
|
+
},
|
166
|
+
scheme: {
|
167
|
+
data: { expected: 'matrix-asset', received: 'not-valid' },
|
168
|
+
message: 'Uri scheme is invalid, must match "matrix-asset"',
|
169
|
+
},
|
170
|
+
},
|
158
171
|
);
|
159
172
|
});
|
160
173
|
it('should throw error for invalid matrix-asset-uri type number', () => {
|
@@ -186,7 +199,21 @@ describe('JsonValidationService', () => {
|
|
186
199
|
});
|
187
200
|
},
|
188
201
|
SchemaValidationError,
|
189
|
-
`failed validation:
|
202
|
+
`failed validation: matrix asset uri "matrix://" isn't a valid matrix asset uri`,
|
203
|
+
{
|
204
|
+
assetId: {
|
205
|
+
data: { expected: /^\d+(?::.+)?$/, received: '' },
|
206
|
+
message: 'Matrix Asset Id has invalid format, must match /^d+(?::.+)?$/',
|
207
|
+
},
|
208
|
+
identifier: {
|
209
|
+
data: { expected: /^[a-zA-Z0-9.-]+$/, received: '' },
|
210
|
+
message: 'Matrix Identifier has invalid format, must match /^[a-zA-Z0-9.-]+$/',
|
211
|
+
},
|
212
|
+
scheme: {
|
213
|
+
data: { expected: 'matrix-asset', received: 'matrix' },
|
214
|
+
message: 'Uri scheme is invalid, must match "matrix-asset"',
|
215
|
+
},
|
216
|
+
},
|
190
217
|
);
|
191
218
|
});
|
192
219
|
});
|
@@ -223,11 +250,12 @@ describe('JsonValidationService', () => {
|
|
223
250
|
jsonValidationService.validateRenderInput(schema, value);
|
224
251
|
},
|
225
252
|
SchemaValidationError,
|
226
|
-
'failed validation: Expected `[
|
253
|
+
'failed validation: Expected `Array [{"type":"text","value":"hello"}]` (array) in `#/my-input` to be of type `string`',
|
227
254
|
{
|
228
255
|
'#/my-input': [
|
229
256
|
{
|
230
|
-
message:
|
257
|
+
message:
|
258
|
+
'Expected `Array [{"type":"text","value":"hello"}]` (array) in `#/my-input` to be of type `string`',
|
231
259
|
data: {
|
232
260
|
received: 'array',
|
233
261
|
expected: 'string',
|
@@ -353,6 +381,14 @@ describe('JsonSchemaService', () => {
|
|
353
381
|
},
|
354
382
|
SchemaValidationError,
|
355
383
|
'failed validation: The required property `required` is missing at `#/properties/my-input`',
|
384
|
+
{
|
385
|
+
'#/properties/my-input': [
|
386
|
+
{
|
387
|
+
data: { key: 'required', pointer: '#/properties/my-input' },
|
388
|
+
message: 'The required property `required` is missing at `#/properties/my-input`',
|
389
|
+
},
|
390
|
+
],
|
391
|
+
},
|
356
392
|
);
|
357
393
|
});
|
358
394
|
|
@@ -374,6 +410,14 @@ describe('JsonSchemaService', () => {
|
|
374
410
|
},
|
375
411
|
SchemaValidationError,
|
376
412
|
`failed validation: Expected value at \`#/type\` to be \`object\`, but value given is \`array\``,
|
413
|
+
{
|
414
|
+
'#/type': [
|
415
|
+
{
|
416
|
+
data: { expected: 'object', pointer: '#/type', value: 'array' },
|
417
|
+
message: 'Expected value at `#/type` to be `object`, but value given is `array`',
|
418
|
+
},
|
419
|
+
],
|
420
|
+
},
|
377
421
|
);
|
378
422
|
});
|
379
423
|
|
@@ -391,6 +435,83 @@ describe('JsonSchemaService', () => {
|
|
391
435
|
},
|
392
436
|
SchemaValidationError,
|
393
437
|
'failed validation: The required property `required` is missing at `#`',
|
438
|
+
{
|
439
|
+
'#': [
|
440
|
+
{ data: { key: 'required', pointer: '#' }, message: 'The required property `required` is missing at `#`' },
|
441
|
+
],
|
442
|
+
},
|
443
|
+
);
|
444
|
+
});
|
445
|
+
});
|
446
|
+
|
447
|
+
describe('SquizImage input', () => {
|
448
|
+
it('should validate a squizImage input', () => {
|
449
|
+
const schema = {
|
450
|
+
type: 'object',
|
451
|
+
properties: {
|
452
|
+
image: {
|
453
|
+
type: 'SquizImage',
|
454
|
+
},
|
455
|
+
},
|
456
|
+
};
|
457
|
+
|
458
|
+
const value: SquizImageType['__shape__'] = {
|
459
|
+
name: 'My Image',
|
460
|
+
alt: 'My Image that did not load',
|
461
|
+
caption: 'This above is a loaded image',
|
462
|
+
imageVariations: {
|
463
|
+
original: {
|
464
|
+
url: 'https://picsum.photos/200/300',
|
465
|
+
width: 100,
|
466
|
+
height: 100,
|
467
|
+
byteSize: 1000,
|
468
|
+
mimeType: 'image/jpeg',
|
469
|
+
aspectRatio: '1:1',
|
470
|
+
sha1Hash: '1234567890abcdef1234567890abcdef12345678',
|
471
|
+
},
|
472
|
+
},
|
473
|
+
};
|
474
|
+
expect(jsonSchemaService.validateInput(value, schema)).toEqual(true);
|
475
|
+
});
|
476
|
+
|
477
|
+
it('should error if SquizImage type is missing required properties', async () => {
|
478
|
+
const schema = {
|
479
|
+
type: 'object',
|
480
|
+
properties: {
|
481
|
+
myProp: {
|
482
|
+
type: 'SquizImage',
|
483
|
+
},
|
484
|
+
},
|
485
|
+
};
|
486
|
+
|
487
|
+
const value: { myProp: SquizImageType['__shape__'] } = {
|
488
|
+
// @ts-expect-error - missing required properties
|
489
|
+
myProp: {
|
490
|
+
alt: 'alt',
|
491
|
+
caption: 'caption',
|
492
|
+
name: 'name',
|
493
|
+
},
|
494
|
+
};
|
495
|
+
expectToThrowErrorMatchingTypeAndMessage(
|
496
|
+
() => {
|
497
|
+
jsonSchemaService.validateInput(value, schema);
|
498
|
+
},
|
499
|
+
SchemaValidationError,
|
500
|
+
'failed validation: Expected `Object {"alt":"alt","caption":"caption","name":"name"}` (object) in `#/myProp` to be of type `SquizImage`',
|
501
|
+
{
|
502
|
+
'#/myProp': [
|
503
|
+
{
|
504
|
+
data: {
|
505
|
+
expected: 'SquizImage',
|
506
|
+
pointer: '#/myProp',
|
507
|
+
received: 'object',
|
508
|
+
value: { alt: 'alt', caption: 'caption', name: 'name' },
|
509
|
+
},
|
510
|
+
message:
|
511
|
+
'Expected `Object {"alt":"alt","caption":"caption","name":"name"}` (object) in `#/myProp` to be of type `SquizImage`',
|
512
|
+
},
|
513
|
+
],
|
514
|
+
},
|
394
515
|
);
|
395
516
|
});
|
396
517
|
});
|
@@ -517,6 +638,27 @@ describe('JsonSchemaService', () => {
|
|
517
638
|
},
|
518
639
|
SchemaValidationError,
|
519
640
|
'failed validation: Value `123` in `#/my-input` does not match any given oneof schema',
|
641
|
+
{
|
642
|
+
'#/my-input': [
|
643
|
+
{
|
644
|
+
data: {
|
645
|
+
errors: [
|
646
|
+
{
|
647
|
+
code: 'type-error',
|
648
|
+
data: { expected: 'array', pointer: '#/my-input', received: 'number', value: 123 },
|
649
|
+
message: 'Expected `123` (number) in `#/my-input` to be of type `array`',
|
650
|
+
name: 'TypeError',
|
651
|
+
type: 'error',
|
652
|
+
},
|
653
|
+
],
|
654
|
+
oneOf: [{ $ref: 'FormattedText.json' }],
|
655
|
+
pointer: '#/my-input',
|
656
|
+
value: '123',
|
657
|
+
},
|
658
|
+
message: 'Value `123` in `#/my-input` does not match any given oneof schema',
|
659
|
+
},
|
660
|
+
],
|
661
|
+
},
|
520
662
|
);
|
521
663
|
});
|
522
664
|
|
@@ -541,6 +683,27 @@ describe('JsonSchemaService', () => {
|
|
541
683
|
},
|
542
684
|
SchemaValidationError,
|
543
685
|
'failed validation: Value `{"something":"aa"}` in `#/my-input` does not match any given oneof schema',
|
686
|
+
{
|
687
|
+
'#/my-input': [
|
688
|
+
{
|
689
|
+
data: {
|
690
|
+
errors: [
|
691
|
+
{
|
692
|
+
code: 'type-error',
|
693
|
+
data: { expected: 'array', pointer: '#/my-input', received: 'object', value: { something: 'aa' } },
|
694
|
+
message: 'Expected `Object {"something":"aa"}` (object) in `#/my-input` to be of type `array`',
|
695
|
+
name: 'TypeError',
|
696
|
+
type: 'error',
|
697
|
+
},
|
698
|
+
],
|
699
|
+
oneOf: [{ $ref: 'FormattedText.json' }],
|
700
|
+
pointer: '#/my-input',
|
701
|
+
value: '{"something":"aa"}',
|
702
|
+
},
|
703
|
+
message: 'Value `{"something":"aa"}` in `#/my-input` does not match any given oneof schema',
|
704
|
+
},
|
705
|
+
],
|
706
|
+
},
|
544
707
|
);
|
545
708
|
});
|
546
709
|
|
@@ -577,6 +740,46 @@ describe('JsonSchemaService', () => {
|
|
577
740
|
},
|
578
741
|
SchemaValidationError,
|
579
742
|
'failed validation: Value `[{"tag":"p","type":"tag","children":[{"type":"text","value":"This is some "},{"type":"text","value":"Link to asset 12345"},{"type":"text","value":" with an image "},{"type":"text","value":123},{"type":"text","value":"."}]}]` in `#/my-input` does not match any given oneof schema',
|
743
|
+
{
|
744
|
+
'#/my-input': [
|
745
|
+
{
|
746
|
+
data: {
|
747
|
+
errors: [
|
748
|
+
{
|
749
|
+
code: 'any-of-error',
|
750
|
+
data: {
|
751
|
+
anyOf: [
|
752
|
+
{ $ref: '#/definitions/HigherOrderFormattedNodes' },
|
753
|
+
{ $ref: '#/definitions/BaseFormattedNodes' },
|
754
|
+
],
|
755
|
+
pointer: '#/my-input/0',
|
756
|
+
value: {
|
757
|
+
children: [
|
758
|
+
{ type: 'text', value: 'This is some ' },
|
759
|
+
{ type: 'text', value: 'Link to asset 12345' },
|
760
|
+
{ type: 'text', value: ' with an image ' },
|
761
|
+
{ type: 'text', value: 123 },
|
762
|
+
{ type: 'text', value: '.' },
|
763
|
+
],
|
764
|
+
tag: 'p',
|
765
|
+
type: 'tag',
|
766
|
+
},
|
767
|
+
},
|
768
|
+
message: 'Object at `#/my-input/0` does not match any schema',
|
769
|
+
name: 'AnyOfError',
|
770
|
+
type: 'error',
|
771
|
+
},
|
772
|
+
],
|
773
|
+
oneOf: [{ $ref: 'FormattedText.json' }],
|
774
|
+
pointer: '#/my-input',
|
775
|
+
value:
|
776
|
+
'[{"tag":"p","type":"tag","children":[{"type":"text","value":"This is some "},{"type":"text","value":"Link to asset 12345"},{"type":"text","value":" with an image "},{"type":"text","value":123},{"type":"text","value":"."}]}]',
|
777
|
+
},
|
778
|
+
message:
|
779
|
+
'Value `[{"tag":"p","type":"tag","children":[{"type":"text","value":"This is some "},{"type":"text","value":"Link to asset 12345"},{"type":"text","value":" with an image "},{"type":"text","value":123},{"type":"text","value":"."}]}]` in `#/my-input` does not match any given oneof schema',
|
780
|
+
},
|
781
|
+
],
|
782
|
+
},
|
580
783
|
);
|
581
784
|
});
|
582
785
|
|
@@ -664,6 +867,34 @@ describe('JsonSchemaService', () => {
|
|
664
867
|
},
|
665
868
|
SchemaValidationError,
|
666
869
|
'failed validation: Value `{"bad":"data"}` in `#/deep/arr/0/formattedTextArray/2` does not match any given oneof schema',
|
870
|
+
{
|
871
|
+
'#/deep/arr/0/formattedTextArray/2': [
|
872
|
+
{
|
873
|
+
data: {
|
874
|
+
errors: [
|
875
|
+
{
|
876
|
+
code: 'type-error',
|
877
|
+
data: {
|
878
|
+
expected: 'array',
|
879
|
+
pointer: '#/deep/arr/0/formattedTextArray/2',
|
880
|
+
received: 'object',
|
881
|
+
value: { bad: 'data' },
|
882
|
+
},
|
883
|
+
message:
|
884
|
+
'Expected `Object {"bad":"data"}` (object) in `#/deep/arr/0/formattedTextArray/2` to be of type `array`',
|
885
|
+
name: 'TypeError',
|
886
|
+
type: 'error',
|
887
|
+
},
|
888
|
+
],
|
889
|
+
oneOf: [{ $ref: 'FormattedText.json' }],
|
890
|
+
pointer: '#/deep/arr/0/formattedTextArray/2',
|
891
|
+
value: '{"bad":"data"}',
|
892
|
+
},
|
893
|
+
message:
|
894
|
+
'Value `{"bad":"data"}` in `#/deep/arr/0/formattedTextArray/2` does not match any given oneof schema',
|
895
|
+
},
|
896
|
+
],
|
897
|
+
},
|
667
898
|
);
|
668
899
|
});
|
669
900
|
|
@@ -714,6 +945,27 @@ describe('JsonSchemaService', () => {
|
|
714
945
|
},
|
715
946
|
SchemaValidationError,
|
716
947
|
'failed validation: Value `{"bad":"data"}` in `#/my-input` does not match any given oneof schema',
|
948
|
+
{
|
949
|
+
'#/my-input': [
|
950
|
+
{
|
951
|
+
data: {
|
952
|
+
errors: [
|
953
|
+
{
|
954
|
+
code: 'type-error',
|
955
|
+
data: { expected: 'array', pointer: '#/my-input', received: 'object', value: { bad: 'data' } },
|
956
|
+
message: 'Expected `Object {"bad":"data"}` (object) in `#/my-input` to be of type `array`',
|
957
|
+
name: 'TypeError',
|
958
|
+
type: 'error',
|
959
|
+
},
|
960
|
+
],
|
961
|
+
oneOf: [{ $ref: 'FormattedText.json' }],
|
962
|
+
pointer: '#/my-input',
|
963
|
+
value: '{"bad":"data"}',
|
964
|
+
},
|
965
|
+
message: 'Value `{"bad":"data"}` in `#/my-input` does not match any given oneof schema',
|
966
|
+
},
|
967
|
+
],
|
968
|
+
},
|
717
969
|
);
|
718
970
|
});
|
719
971
|
|
@@ -801,6 +1053,14 @@ describe('JsonSchemaService', () => {
|
|
801
1053
|
},
|
802
1054
|
SchemaValidationError,
|
803
1055
|
`failed validation: Expected \`123\` (string) in \`#/my-input\` to be of type \`number\``,
|
1056
|
+
{
|
1057
|
+
'#/my-input': [
|
1058
|
+
{
|
1059
|
+
data: { expected: 'number', pointer: '#/my-input', received: 'string', value: '123' },
|
1060
|
+
message: 'Expected `123` (string) in `#/my-input` to be of type `number`',
|
1061
|
+
},
|
1062
|
+
],
|
1063
|
+
},
|
804
1064
|
);
|
805
1065
|
});
|
806
1066
|
|
@@ -812,6 +1072,11 @@ describe('JsonSchemaService', () => {
|
|
812
1072
|
},
|
813
1073
|
SchemaValidationError,
|
814
1074
|
`failed validation: The required property \`my-input\` is missing at \`#\``,
|
1075
|
+
{
|
1076
|
+
'#': [
|
1077
|
+
{ data: { key: 'my-input', pointer: '#' }, message: 'The required property `my-input` is missing at `#`' },
|
1078
|
+
],
|
1079
|
+
},
|
815
1080
|
);
|
816
1081
|
});
|
817
1082
|
|
@@ -825,6 +1090,14 @@ describe('JsonSchemaService', () => {
|
|
825
1090
|
},
|
826
1091
|
SchemaValidationError,
|
827
1092
|
'failed validation: Expected given value `z` in #/enum` to be one of `[a, b, c, d, e, ... 2 more]`',
|
1093
|
+
{
|
1094
|
+
'#/enum': [
|
1095
|
+
{
|
1096
|
+
data: { pointer: '#/enum', value: 'z', values: ['a', 'b', 'c', 'd', 'e', 'f', 'g'] },
|
1097
|
+
message: 'Expected given value `z` in #/enum` to be one of `[a, b, c, d, e, ... 2 more]`',
|
1098
|
+
},
|
1099
|
+
],
|
1100
|
+
},
|
828
1101
|
);
|
829
1102
|
});
|
830
1103
|
|
@@ -67,6 +67,28 @@ const defaultConfig: DraftConfig = {
|
|
67
67
|
data,
|
68
68
|
};
|
69
69
|
},
|
70
|
+
|
71
|
+
typeError(data) {
|
72
|
+
let value = `${data.value}`;
|
73
|
+
|
74
|
+
if (data.received == 'object') {
|
75
|
+
value = `Object ${JSON.stringify(data.value)}`;
|
76
|
+
}
|
77
|
+
|
78
|
+
if (data.received == 'array') {
|
79
|
+
value = `Array ${JSON.stringify(data.value)}`;
|
80
|
+
}
|
81
|
+
|
82
|
+
const expected = JSON.stringify(data.expected).replace(/"/g, '`');
|
83
|
+
|
84
|
+
return {
|
85
|
+
type: 'error',
|
86
|
+
name: 'TypeError',
|
87
|
+
code: 'type-error',
|
88
|
+
message: `Expected \`${value}\` (${data.received}) in \`${data.pointer}\` to be of type ${expected}`,
|
89
|
+
data,
|
90
|
+
};
|
91
|
+
},
|
70
92
|
},
|
71
93
|
};
|
72
94
|
|
@@ -204,15 +226,7 @@ export class JSONSchemaService<P extends AnyPrimitiveType, R extends AnyResolvab
|
|
204
226
|
public validateInput(input: unknown, inputSchema: JSONSchema = this.schema.rootSchema): true | never {
|
205
227
|
inputSchema = this.schema.compileSchema(inputSchema);
|
206
228
|
const errors = this.schema.validate(input, inputSchema);
|
207
|
-
return
|
208
|
-
}
|
209
|
-
|
210
|
-
private processValidationResult(errors: JSONError[]): true {
|
211
|
-
if (errors.length > 0) {
|
212
|
-
throw new SchemaValidationError(errors.map((a) => a.message).join(',\n'));
|
213
|
-
}
|
214
|
-
|
215
|
-
return true;
|
229
|
+
return processValidationResult(errors);
|
216
230
|
}
|
217
231
|
|
218
232
|
/**
|
@@ -269,7 +283,7 @@ export class JsonValidationService {
|
|
269
283
|
switch (version) {
|
270
284
|
case 'v1': {
|
271
285
|
const validationResult = v1Schema.validate(manifest);
|
272
|
-
return
|
286
|
+
return processValidationResult(validationResult);
|
273
287
|
}
|
274
288
|
|
275
289
|
default:
|
@@ -277,37 +291,37 @@ export class JsonValidationService {
|
|
277
291
|
}
|
278
292
|
}
|
279
293
|
validateContentSchema(contentSchema: JSONSchema) {
|
280
|
-
return
|
294
|
+
return processValidationResult(ComponentInputSchema.validate(contentSchema));
|
281
295
|
}
|
282
296
|
|
283
297
|
validateComponentInput(functionInputSchema: JSONSchema, inputValue: unknown) {
|
284
298
|
const inputSchema = ComponentInputSchema.compileSchema(functionInputSchema);
|
285
299
|
const errors = ComponentInputSchema.validate(inputValue, inputSchema);
|
286
|
-
return
|
300
|
+
return processValidationResult(errors);
|
287
301
|
}
|
288
302
|
|
289
303
|
validateRenderInput(functionInputSchema: JSONSchema, inputValue: unknown) {
|
290
304
|
const inputSchema = RenderInputSchema.compileSchema(functionInputSchema);
|
291
305
|
const errors = RenderInputSchema.validate(inputValue, inputSchema);
|
292
306
|
|
293
|
-
return
|
307
|
+
return processValidationResult(errors);
|
294
308
|
}
|
309
|
+
}
|
295
310
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
}
|
311
|
-
return true;
|
311
|
+
function processValidationResult(errors: JSONError[]): true {
|
312
|
+
if (errors.length > 0) {
|
313
|
+
const ValidationDataMap: ValidationDataMap = errors.reduce((acc: ValidationDataMap, error: JSONError) => {
|
314
|
+
const pointer = error?.data?.pointer;
|
315
|
+
const validationData: ValidationData = {
|
316
|
+
message: error.message,
|
317
|
+
data: error.data,
|
318
|
+
};
|
319
|
+
Object.keys(acc).includes(pointer) && Array.isArray(acc[pointer])
|
320
|
+
? acc[pointer].push(validationData)
|
321
|
+
: (acc[pointer] = [validationData]);
|
322
|
+
return acc;
|
323
|
+
}, {});
|
324
|
+
throw new SchemaValidationError(errors.map((a) => a.message).join(',\n'), undefined, ValidationDataMap);
|
312
325
|
}
|
326
|
+
return true;
|
313
327
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { Draft, JSONError, JSONSchema } from '@squiz/json-schema-library';
|
2
2
|
import { SchemaValidationError } from '../errors/SchemaValidationError';
|
3
3
|
import { isMatrixAssetUri } from './utils/matrixAssetValidator';
|
4
|
+
import { getValidationDataForMatrixUri } from './utils/getValidationDataForMatrixUri';
|
4
5
|
|
5
6
|
export const customFormatValidators: Record<
|
6
7
|
string,
|
@@ -8,7 +9,13 @@ export const customFormatValidators: Record<
|
|
8
9
|
> = {
|
9
10
|
'matrix-asset-uri': (core, schema, value): undefined => {
|
10
11
|
if (isMatrixAssetUri(value) === false) {
|
11
|
-
throw new SchemaValidationError(`
|
12
|
+
if (typeof value !== 'string') throw new SchemaValidationError(`matrix asset uri "${value}" isn't a string`);
|
13
|
+
const validationData = getValidationDataForMatrixUri(value);
|
14
|
+
throw new SchemaValidationError(
|
15
|
+
`matrix asset uri "${value}" isn't a valid matrix asset uri`,
|
16
|
+
undefined,
|
17
|
+
validationData,
|
18
|
+
);
|
12
19
|
}
|
13
20
|
return;
|
14
21
|
},
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import { getValidationDataForMatrixUri } from './getValidationDataForMatrixUri';
|
2
|
+
|
3
|
+
describe('getInvalidPartFromMatrixUri', () => {
|
4
|
+
it('should return invalid part of matrix asset uri - invalid scheme', () => {
|
5
|
+
const part = getValidationDataForMatrixUri('matrix-link://dx-data.asset.com/123');
|
6
|
+
expect(part).toEqual({
|
7
|
+
scheme: {
|
8
|
+
message: 'Uri scheme is invalid, must match "matrix-asset"',
|
9
|
+
data: {
|
10
|
+
expected: 'matrix-asset',
|
11
|
+
received: 'matrix-link',
|
12
|
+
},
|
13
|
+
},
|
14
|
+
});
|
15
|
+
});
|
16
|
+
it('should return invalid part of matrix asset uri - invalid identifier', () => {
|
17
|
+
const part = getValidationDataForMatrixUri('matrix-asset://dx team id/123');
|
18
|
+
expect(part).toEqual({
|
19
|
+
identifier: {
|
20
|
+
message: 'Matrix Identifier has invalid format, must match /^[a-zA-Z0-9.-]+$/',
|
21
|
+
data: {
|
22
|
+
expected: /^[a-zA-Z0-9.-]+$/,
|
23
|
+
received: 'dx team id',
|
24
|
+
},
|
25
|
+
},
|
26
|
+
});
|
27
|
+
});
|
28
|
+
it('should return invalid part of matrix asset uri - invalid asset id', () => {
|
29
|
+
const part = getValidationDataForMatrixUri('matrix-asset://dx-data.asset.com/asset/1234');
|
30
|
+
expect(part).toEqual({
|
31
|
+
assetId: {
|
32
|
+
message: 'Matrix Asset Id has invalid format, must match /^d+(?::.+)?$/',
|
33
|
+
data: {
|
34
|
+
expected: /^\d+(?::.+)?$/,
|
35
|
+
received: 'asset/1234',
|
36
|
+
},
|
37
|
+
},
|
38
|
+
});
|
39
|
+
});
|
40
|
+
|
41
|
+
it('should return invalid part of matrix asset uri - invalid identifier and asset id', () => {
|
42
|
+
const part = getValidationDataForMatrixUri('matrix-asset://dx team id/asset/1234');
|
43
|
+
expect(part).toEqual({
|
44
|
+
identifier: {
|
45
|
+
message: 'Matrix Identifier has invalid format, must match /^[a-zA-Z0-9.-]+$/',
|
46
|
+
data: {
|
47
|
+
expected: /^[a-zA-Z0-9.-]+$/,
|
48
|
+
received: 'dx team id',
|
49
|
+
},
|
50
|
+
},
|
51
|
+
assetId: {
|
52
|
+
message: 'Matrix Asset Id has invalid format, must match /^d+(?::.+)?$/',
|
53
|
+
data: {
|
54
|
+
expected: /^\d+(?::.+)?$/,
|
55
|
+
received: 'asset/1234',
|
56
|
+
},
|
57
|
+
},
|
58
|
+
});
|
59
|
+
});
|
60
|
+
|
61
|
+
it('should return invalid part of matrix asset uri - invalid scheme, identifier and asset id', () => {
|
62
|
+
const part = getValidationDataForMatrixUri('matrix-link://dx team id/asset/1234');
|
63
|
+
expect(part).toEqual({
|
64
|
+
scheme: {
|
65
|
+
message: 'Uri scheme is invalid, must match "matrix-asset"',
|
66
|
+
data: {
|
67
|
+
expected: 'matrix-asset',
|
68
|
+
received: 'matrix-link',
|
69
|
+
},
|
70
|
+
},
|
71
|
+
identifier: {
|
72
|
+
message: 'Matrix Identifier has invalid format, must match /^[a-zA-Z0-9.-]+$/',
|
73
|
+
data: {
|
74
|
+
expected: /^[a-zA-Z0-9.-]+$/,
|
75
|
+
received: 'dx team id',
|
76
|
+
},
|
77
|
+
},
|
78
|
+
assetId: {
|
79
|
+
message: 'Matrix Asset Id has invalid format, must match /^d+(?::.+)?$/',
|
80
|
+
data: {
|
81
|
+
expected: /^\d+(?::.+)?$/,
|
82
|
+
received: 'asset/1234',
|
83
|
+
},
|
84
|
+
},
|
85
|
+
});
|
86
|
+
});
|
87
|
+
|
88
|
+
it('should return undefined if matrix asset uri is valid', () => {
|
89
|
+
const part = getValidationDataForMatrixUri('matrix-asset://dx-data.asset.com/123');
|
90
|
+
expect(part).toEqual(undefined);
|
91
|
+
});
|
92
|
+
});
|
@@ -0,0 +1,55 @@
|
|
1
|
+
type matrixAssetUriValidationData = { message: string; data: any };
|
2
|
+
type uriParts = 'scheme' | 'identifier' | 'assetId';
|
3
|
+
type matrixAssetUriValidationDataMap = Record<uriParts, matrixAssetUriValidationData> | object;
|
4
|
+
|
5
|
+
export function getValidationDataForMatrixUri(uri: string): matrixAssetUriValidationDataMap | undefined {
|
6
|
+
let validationDataMap: matrixAssetUriValidationDataMap = {};
|
7
|
+
const [scheme, path] = uri.split('://');
|
8
|
+
|
9
|
+
if (scheme !== 'matrix-asset') {
|
10
|
+
const validationData: matrixAssetUriValidationData = {
|
11
|
+
message: 'Uri scheme is invalid, must match "matrix-asset"',
|
12
|
+
data: {
|
13
|
+
expected: 'matrix-asset',
|
14
|
+
received: scheme,
|
15
|
+
},
|
16
|
+
};
|
17
|
+
validationDataMap = {
|
18
|
+
...validationDataMap,
|
19
|
+
scheme: validationData,
|
20
|
+
};
|
21
|
+
}
|
22
|
+
|
23
|
+
const [identifier, ...assetIdArray] = path.split('/');
|
24
|
+
const assetId = assetIdArray.join('/');
|
25
|
+
|
26
|
+
const identifierRegex = /^[a-zA-Z0-9.-]+$/;
|
27
|
+
if (identifierRegex.test(identifier) === false) {
|
28
|
+
validationDataMap = {
|
29
|
+
...validationDataMap,
|
30
|
+
identifier: {
|
31
|
+
message: 'Matrix Identifier has invalid format, must match /^[a-zA-Z0-9.-]+$/',
|
32
|
+
data: {
|
33
|
+
expected: /^[a-zA-Z0-9.-]+$/,
|
34
|
+
received: identifier,
|
35
|
+
},
|
36
|
+
},
|
37
|
+
};
|
38
|
+
}
|
39
|
+
|
40
|
+
const assetIdRegex = /^\d+(?::.+)?$/;
|
41
|
+
if (assetIdRegex.test(assetId) === false) {
|
42
|
+
validationDataMap = {
|
43
|
+
...validationDataMap,
|
44
|
+
assetId: {
|
45
|
+
message: 'Matrix Asset Id has invalid format, must match /^d+(?::.+)?$/',
|
46
|
+
data: {
|
47
|
+
expected: /^\d+(?::.+)?$/,
|
48
|
+
received: assetId,
|
49
|
+
},
|
50
|
+
},
|
51
|
+
};
|
52
|
+
}
|
53
|
+
|
54
|
+
return Object.keys(validationDataMap).length > 0 ? validationDataMap : undefined;
|
55
|
+
}
|