@squiz/dx-json-schema-lib 1.33.1-alpha.4 → 1.33.1-alpha.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.uat.matrix.squiz.cloud//abc123';
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: value "not-valid://canary.uat.matrix.squiz.cloud//abc123" isn't a valid matrix asset uri`,
157
- undefined,
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: value "matrix://" isn't a valid matrix asset uri`,
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 `[object Object]` (array) in `#/my-input` to be of type `string`',
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: 'Expected `[object Object]` (array) in `#/my-input` to be of type `string`',
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 this.processValidationResult(errors);
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 this.processValidationResult(validationResult);
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 this.processValidationResult(ComponentInputSchema.validate(contentSchema));
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 this.processValidationResult(errors);
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 this.processValidationResult(errors);
307
+ return processValidationResult(errors);
294
308
  }
309
+ }
295
310
 
296
- private processValidationResult(errors: JSONError[]): true {
297
- if (errors.length > 0) {
298
- const ValidationDataMap: ValidationDataMap = errors.reduce((acc: ValidationDataMap, error: JSONError) => {
299
- const pointer = error?.data?.pointer;
300
- const validationData: ValidationData = {
301
- message: error.message,
302
- data: error.data,
303
- };
304
- Object.keys(acc).includes(pointer) && Array.isArray(acc[pointer])
305
- ? acc[pointer].push(validationData)
306
- : (acc[pointer] = [validationData]);
307
- return acc;
308
- }, {});
309
- throw new SchemaValidationError(errors.map((a) => a.message).join(',\n'), undefined, ValidationDataMap);
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(`value "${value}" isn't a valid matrix asset uri`);
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
+ }