@squiz/dx-json-schema-lib 1.79.0 → 1.80.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/jest.config.ts +13 -2
  3. package/lib/JsonSchemaService.d.ts +34 -0
  4. package/lib/JsonSchemaService.js +140 -0
  5. package/lib/JsonSchemaService.js.map +1 -0
  6. package/lib/JsonSchemaService.spec.d.ts +1 -0
  7. package/lib/JsonSchemaService.spec.js +1807 -0
  8. package/lib/JsonSchemaService.spec.js.map +1 -0
  9. package/lib/JsonValidationService.d.ts +11 -33
  10. package/lib/JsonValidationService.js +43 -194
  11. package/lib/JsonValidationService.js.map +1 -1
  12. package/lib/JsonValidationService.spec.js +19 -1800
  13. package/lib/JsonValidationService.spec.js.map +1 -1
  14. package/lib/index.d.ts +1 -0
  15. package/lib/index.js +1 -0
  16. package/lib/index.js.map +1 -1
  17. package/lib/jsonTypeResolution/TypeResolver.d.ts +0 -1
  18. package/lib/jsonTypeResolution/TypeResolver.js +0 -3
  19. package/lib/jsonTypeResolution/TypeResolver.js.map +1 -1
  20. package/lib/jsonTypeResolution/TypeResolver.spec.js +10 -0
  21. package/lib/jsonTypeResolution/TypeResolver.spec.js.map +1 -1
  22. package/lib/manifest/v1/DxComponentInputSchema.spec.js +18 -0
  23. package/lib/manifest/v1/DxComponentInputSchema.spec.js.map +1 -1
  24. package/lib/manifest/v1/DxContentMetaSchema.json +24 -32
  25. package/lib/manifest/v1/JobV1.d.ts +23 -18
  26. package/lib/manifest/v1/__test__/schemas/invalidUiMetadata.json +31 -0
  27. package/lib/manifest/v1/v1.d.ts +23 -18
  28. package/lib/manifest/v1/v1.spec.js +3 -66
  29. package/lib/manifest/v1/v1.spec.js.map +1 -1
  30. package/lib/processValidationResult.d.ts +2 -0
  31. package/lib/processValidationResult.js +24 -0
  32. package/lib/processValidationResult.js.map +1 -0
  33. package/lib/validators/customKeywordValidators.d.ts +2 -0
  34. package/lib/validators/customKeywordValidators.js +47 -0
  35. package/lib/validators/customKeywordValidators.js.map +1 -0
  36. package/lib/validators/customKeywordValidators.spec.d.ts +1 -0
  37. package/lib/validators/customKeywordValidators.spec.js +100 -0
  38. package/lib/validators/customKeywordValidators.spec.js.map +1 -0
  39. package/package.json +1 -1
  40. package/src/JsonSchemaService.spec.ts +2198 -0
  41. package/src/JsonSchemaService.ts +159 -0
  42. package/src/JsonValidationService.spec.ts +26 -2195
  43. package/src/JsonValidationService.ts +64 -226
  44. package/src/index.ts +1 -0
  45. package/src/jsonTypeResolution/TypeResolver.spec.ts +12 -0
  46. package/src/jsonTypeResolution/TypeResolver.ts +0 -4
  47. package/src/manifest/v1/DxComponentInputSchema.spec.ts +21 -0
  48. package/src/manifest/v1/DxContentMetaSchema.json +24 -32
  49. package/src/manifest/v1/JobV1.ts +23 -23
  50. package/src/manifest/v1/__test__/schemas/invalidUiMetadata.json +31 -0
  51. package/src/manifest/v1/v1.spec.ts +3 -73
  52. package/src/manifest/v1/v1.ts +23 -23
  53. package/src/processValidationResult.ts +20 -0
  54. package/src/validators/customKeywordValidators.spec.ts +171 -0
  55. package/src/validators/customKeywordValidators.ts +53 -0
  56. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,159 @@
1
+ import JSONQuery, { Input } from '@sagold/json-query';
2
+
3
+ import DxComponentInputSchema from './manifest/v1/DxComponentInputSchema.json';
4
+ import DxComponentIcons from './manifest/v1/DxComponentIcons.json';
5
+ import DxContentMetaSchema from './manifest/v1/DxContentMetaSchema.json';
6
+ import MatrixAssetSchema from './manifest/v1/MatrixAssetSchema.json';
7
+
8
+ import Draft07Schema from './manifest/v1/Draft-07.json';
9
+
10
+ import JobV1 from './manifest/v1/JobV1.json';
11
+ import v1 from './manifest/v1/v1.json';
12
+ import { JSONSchema, Draft } from '@squiz/json-schema-library';
13
+
14
+ import { draft07Config } from '@squiz/json-schema-library';
15
+ import { AnyPrimitiveType, AnyResolvableType, TypeResolver } from './jsonTypeResolution/TypeResolver';
16
+ import { JsonResolutionError } from './errors/JsonResolutionError';
17
+ import { processValidationResult } from './processValidationResult';
18
+ import { defaultConfig } from '.';
19
+
20
+ export const ComponentInputMetaSchema: MetaSchemaInput = {
21
+ root: DxComponentInputSchema,
22
+ remotes: {
23
+ 'DxComponentInputSchema.json/DxContentMetaSchema.json': DxContentMetaSchema,
24
+ },
25
+ };
26
+
27
+ export const RenderInputMetaSchema: MetaSchemaInput = {
28
+ root: Draft07Schema,
29
+ };
30
+
31
+ export const ManifestV1MetaSchema: MetaSchemaInput = {
32
+ root: v1,
33
+ remotes: {
34
+ 'DxComponentInputSchema.json/DxContentMetaSchema.json': DxContentMetaSchema,
35
+ '/DxComponentInputSchema.json': DxComponentInputSchema,
36
+ '/DxComponentIcons.json': DxComponentIcons,
37
+ '/MatrixAssetSchema.json': MatrixAssetSchema,
38
+ 'http://json-schema.org/draft-07/schema': Draft07Schema,
39
+ 'http://json-schema.org/draft-07/schema#': Draft07Schema,
40
+ },
41
+ };
42
+
43
+ export const JobV1MetaSchema: MetaSchemaInput = {
44
+ root: JobV1,
45
+ remotes: {
46
+ 'DxComponentInputSchema.json/DxContentMetaSchema.json': DxContentMetaSchema,
47
+ '/DxComponentInputSchema.json': DxComponentInputSchema,
48
+ 'http://json-schema.org/draft-07/schema': Draft07Schema,
49
+ 'http://json-schema.org/draft-07/schema#': Draft07Schema,
50
+ },
51
+ };
52
+
53
+ interface MetaSchemaInput {
54
+ root: JSONSchema;
55
+ remotes?: Record<string, JSONSchema>;
56
+ }
57
+ /**
58
+ * A service that can be used to validate and resolve JSON against a schema.
59
+ */
60
+ export class JSONSchemaService<P extends AnyPrimitiveType, R extends AnyResolvableType> {
61
+ schema: Draft;
62
+ constructor(private typeResolver: TypeResolver<P, R>, metaSchema: MetaSchemaInput) {
63
+ this.schema = new Draft(
64
+ {
65
+ ...defaultConfig,
66
+ resolveRef: (schema, rootSchema) => this.doResolveRef(schema, rootSchema),
67
+ validate: (core, data, schema, pointer) => defaultConfig.validate(core, data, schema, pointer),
68
+ resolveOneOf: (core, data, schema, pointer) => defaultConfig.resolveOneOf(core, data, schema, pointer),
69
+ },
70
+ metaSchema.root,
71
+ );
72
+
73
+ for (const [key, value] of Object.entries(metaSchema.remotes || {})) {
74
+ this.schema.addRemoteSchema(key, value);
75
+ }
76
+
77
+ for (const schema of this.typeResolver.validationSchemaDefinitions) {
78
+ // Please find a better way of doing this.
79
+ this.schema.addRemoteSchema(`/${schema.title}.json`, schema as JSONSchema);
80
+ this.schema.addRemoteSchema(`#/${schema.title}.json`, schema as JSONSchema);
81
+ }
82
+ }
83
+
84
+ private doResolveRef(schema: JSONSchema, rootSchema: JSONSchema): JSONSchema {
85
+ const initialRef = draft07Config.resolveRef(schema, rootSchema);
86
+ if (!initialRef) return initialRef;
87
+ if (!this.typeResolver.isPrimitiveType(initialRef.type)) return initialRef;
88
+
89
+ const validationSchemas = this.typeResolver.getValidationSchemaForPrimitive(initialRef.type);
90
+ // All validation schemas are pre-compiled as remote schemas and are referenced below
91
+ const fullValidationSchema = {
92
+ oneOf: validationSchemas.map((schema) => ({ $ref: `${schema.title}.json` })),
93
+ };
94
+ return this.schema.compileSchema(fullValidationSchema);
95
+ }
96
+
97
+ /**
98
+ * Validate an input value against a specified schema
99
+ * @throws {SchemaValidationError} if the input is invalid
100
+ * @returns true if the input is valid
101
+ */
102
+ public validateInput(input: unknown, inputSchema: JSONSchema = this.schema.rootSchema): true | never {
103
+ inputSchema = this.schema.compileSchema(inputSchema);
104
+ const errors = this.schema.validate(input, inputSchema);
105
+ return processValidationResult(errors);
106
+ }
107
+
108
+ /**
109
+ * Resolve an input object by replacing all resolvable shapes with their resolved values
110
+ * @param input any input object which matches the input schema
111
+ * @param inputSchema a JSONSchema which provides type information about the input object
112
+ * @returns the input object with all resolvable shapes resolved
113
+ */
114
+ public async resolveInput(input: Input, inputSchema: JSONSchema) {
115
+ const setters: Array<Promise<(input: Input) => Input>> = [];
116
+ this.schema.each(
117
+ input,
118
+ async (schema, value, pointer) => {
119
+ // Bug in library for Array item schemas which won't resolve the oneOf schema
120
+ if (Array.isArray(schema?.oneOf)) {
121
+ const oldSchema = schema;
122
+ schema = this.schema.resolveOneOf(value, schema);
123
+ schema.oneOfSchema = oldSchema;
124
+ }
125
+ if (!this.typeResolver.isResolvableSchema(schema)) return;
126
+ // If its a resolvable schema, it should exist in a oneOf array with other schemas
127
+ // Including a primitive schema
128
+ const allPossibleSchemaTitles: Array<string> = schema.oneOfSchema.oneOf.map((o: JSONSchema) =>
129
+ o.$ref.replace('.json', ''),
130
+ );
131
+
132
+ const primitiveSchema = allPossibleSchemaTitles.find((title) => this.typeResolver.isPrimitiveType(title));
133
+ if (!primitiveSchema) return;
134
+ const resolver = this.typeResolver.tryGetResolver(primitiveSchema, schema as any);
135
+ if (!resolver) return;
136
+ const setResolvedData = Promise.resolve()
137
+ .then(() => resolver(value))
138
+ .then((resolvedData) => (item: typeof input) => JSONQuery.set(item, pointer, resolvedData, 'replace' as any))
139
+ .catch((e) => Promise.reject(new JsonResolutionError(e, pointer, value)));
140
+ setters.push(setResolvedData);
141
+ },
142
+ inputSchema,
143
+ );
144
+
145
+ const potentialResolutionErrors = [];
146
+ for (const resolveResult of await Promise.allSettled(setters)) {
147
+ if (resolveResult.status === 'rejected') {
148
+ potentialResolutionErrors.push(resolveResult.reason);
149
+ continue;
150
+ }
151
+
152
+ input = resolveResult.value(input);
153
+ }
154
+ if (potentialResolutionErrors.length) {
155
+ throw new Error(`Error(s) occurred when resolving JSON:\n${potentialResolutionErrors.join('\n')}`);
156
+ }
157
+ return input;
158
+ }
159
+ }