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

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +12 -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 +4 -2
  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/primitiveTypes/SquizImage.d.ts +2 -1
  31. package/lib/primitiveTypes/SquizImage.js.map +1 -1
  32. package/lib/processValidationResult.d.ts +2 -0
  33. package/lib/processValidationResult.js +24 -0
  34. package/lib/processValidationResult.js.map +1 -0
  35. package/lib/validators/customKeywordValidators.d.ts +2 -0
  36. package/lib/validators/customKeywordValidators.js +47 -0
  37. package/lib/validators/customKeywordValidators.js.map +1 -0
  38. package/lib/validators/customKeywordValidators.spec.d.ts +1 -0
  39. package/lib/validators/customKeywordValidators.spec.js +100 -0
  40. package/lib/validators/customKeywordValidators.spec.js.map +1 -0
  41. package/package.json +2 -5
  42. package/src/JsonSchemaService.spec.ts +2198 -0
  43. package/src/JsonSchemaService.ts +159 -0
  44. package/src/JsonValidationService.spec.ts +26 -2195
  45. package/src/JsonValidationService.ts +64 -226
  46. package/src/index.ts +1 -0
  47. package/src/jsonTypeResolution/TypeResolver.spec.ts +12 -0
  48. package/src/jsonTypeResolution/TypeResolver.ts +5 -5
  49. package/src/manifest/v1/DxComponentInputSchema.spec.ts +21 -0
  50. package/src/manifest/v1/DxContentMetaSchema.json +24 -32
  51. package/src/manifest/v1/JobV1.ts +23 -23
  52. package/src/manifest/v1/__test__/schemas/invalidUiMetadata.json +31 -0
  53. package/src/manifest/v1/v1.spec.ts +3 -73
  54. package/src/manifest/v1/v1.ts +23 -23
  55. package/src/primitiveTypes/SquizImage.ts +2 -1
  56. package/src/processValidationResult.ts +20 -0
  57. package/src/validators/customKeywordValidators.spec.ts +171 -0
  58. package/src/validators/customKeywordValidators.ts +53 -0
  59. 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, ResolverContext, 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, ctx: ResolverContext = {}) {
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, ctx))
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
+ }