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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }