@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
@@ -1,31 +1,46 @@
1
- import JSONQuery, { Input } from '@sagold/json-query';
2
-
3
1
  import DxComponentInputSchema from './manifest/v1/DxComponentInputSchema.json';
4
- import DxComponentIcons from './manifest/v1/DxComponentIcons.json';
5
2
  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
3
  import FormattedText from './formatted-text/v1/formattedText.json';
11
4
 
12
- import v1 from './manifest/v1/v1.json';
13
- import JobV1 from './manifest/v1/JobV1.json';
14
- import { SchemaValidationError, ValidationData, ValidationDataMap } from './errors/SchemaValidationError';
15
- import { Draft07, JSONError, JSONSchema, Draft, DraftConfig } from '@squiz/json-schema-library';
5
+ import { SchemaValidationError } from './errors/SchemaValidationError';
6
+ import { Draft07, JSONSchema, Draft, DraftConfig } from '@squiz/json-schema-library';
16
7
 
17
8
  import { draft07Config } from '@squiz/json-schema-library';
18
- import { MANIFEST_MODELS } from '.';
9
+ import {
10
+ BaseFormattedTextType,
11
+ ComponentInputFormattedTextType,
12
+ MANIFEST_MODELS,
13
+ SquizImageType,
14
+ SquizLinkType,
15
+ TypeResolverBuilder,
16
+ } from '.';
19
17
  import { customFormatValidators } from './validators/customFormatValidators';
20
- import { AnyPrimitiveType, AnyResolvableType, TypeResolver } from './jsonTypeResolution/TypeResolver';
21
- import { JsonResolutionError } from './errors/JsonResolutionError';
22
-
23
- const defaultConfig: DraftConfig = {
18
+ import { customKeywordValidators } from './validators/customKeywordValidators';
19
+ import {
20
+ ComponentInputMetaSchema,
21
+ JobV1MetaSchema,
22
+ JSONSchemaService,
23
+ ManifestV1MetaSchema,
24
+ } from './JsonSchemaService';
25
+
26
+ export const defaultConfig: DraftConfig = {
24
27
  ...draft07Config,
25
28
  validateFormat: {
26
29
  ...draft07Config.validateFormat,
27
30
  ...customFormatValidators,
28
31
  },
32
+ typeKeywords: {
33
+ ...draft07Config.typeKeywords,
34
+ array: draft07Config.typeKeywords.array.concat('ui:metadata'),
35
+ object: draft07Config.typeKeywords.object.concat('ui:metadata'),
36
+ boolean: draft07Config.typeKeywords.boolean.concat('ui:metadata'),
37
+ string: draft07Config.typeKeywords.string.concat('ui:metadata'),
38
+ number: draft07Config.typeKeywords.number.concat('ui:metadata'),
39
+ },
40
+ validateKeyword: {
41
+ ...draft07Config.validateKeyword,
42
+ ...customKeywordValidators,
43
+ },
29
44
  errors: {
30
45
  ...draft07Config.errors,
31
46
  enumError(data) {
@@ -95,6 +110,9 @@ const defaultConfig: DraftConfig = {
95
110
 
96
111
  const FTSchema = new Draft07(FormattedText, defaultConfig);
97
112
 
113
+ /**
114
+ * @deprecated Only used for FormattedText resolution
115
+ */
98
116
  export const ComponentInputSchema = new Draft(
99
117
  {
100
118
  ...defaultConfig,
@@ -122,236 +140,56 @@ export const ComponentInputSchema = new Draft(
122
140
  );
123
141
  ComponentInputSchema.addRemoteSchema('DxComponentInputSchema.json/DxContentMetaSchema.json', DxContentMetaSchema);
124
142
 
125
- export const RenderInputSchema = new Draft({
126
- ...defaultConfig,
127
- resolveRef(schema, rootSchema) {
128
- const resolvedSchema = draft07Config.resolveRef(schema, rootSchema) as MANIFEST_MODELS.v1.CoreSchemaMetaSchema;
129
- if (!resolvedSchema) {
130
- return resolvedSchema;
131
- }
132
-
133
- if (resolvedSchema.type === 'FormattedText') {
134
- return { type: 'string' };
135
- } else if (Array.isArray(resolvedSchema.type) && resolvedSchema.type.includes('FormattedText')) {
136
- return {
137
- ...schema,
138
- type: resolvedSchema.type.filter((t) => t !== 'FormattedText').concat('string'),
139
- };
140
- } else {
141
- return resolvedSchema;
142
- }
143
- },
144
- });
145
-
146
- const v1Schema = new Draft07(v1, defaultConfig);
147
-
148
- v1Schema.addRemoteSchema('DxComponentInputSchema.json/DxContentMetaSchema.json', DxContentMetaSchema);
149
- v1Schema.addRemoteSchema('/DxComponentInputSchema.json', ComponentInputSchema.getSchema());
150
- v1Schema.addRemoteSchema('/DxComponentIcons.json', DxComponentIcons);
151
- v1Schema.addRemoteSchema('/MatrixAssetSchema.json', MatrixAssetSchema);
152
- v1Schema.addRemoteSchema('http://json-schema.org/draft-07/schema', Draft07Schema);
153
- v1Schema.addRemoteSchema('http://json-schema.org/draft-07/schema#', Draft07Schema);
154
-
155
- const jobV1Schema = new Draft07(JobV1, defaultConfig);
156
- jobV1Schema.addRemoteSchema('/DxComponentInputSchema.json', ComponentInputSchema.getSchema());
157
-
158
- export const ComponentInputMetaSchema: MetaSchemaInput = {
159
- root: DxComponentInputSchema,
160
- remotes: {
161
- 'DxComponentInputSchema.json/DxContentMetaSchema.json': DxContentMetaSchema,
162
- },
163
- };
164
-
165
- export const RenderInputMetaSchema: MetaSchemaInput = {
166
- root: Draft07Schema,
167
- };
168
-
169
- export const ManifestV1MetaSchema: MetaSchemaInput = {
170
- root: v1,
171
- remotes: {
172
- 'DxComponentInputSchema.json/DxContentMetaSchema.json': DxContentMetaSchema,
173
- '/DxComponentInputSchema.json': DxComponentInputSchema,
174
- '/DxComponentIcons.json': DxComponentIcons,
175
- '/MatrixAssetSchema.json': MatrixAssetSchema,
176
- 'http://json-schema.org/draft-07/schema': Draft07Schema,
177
- 'http://json-schema.org/draft-07/schema#': Draft07Schema,
178
- },
179
- };
180
-
181
- interface MetaSchemaInput {
182
- root: JSONSchema;
183
- remotes?: Record<string, JSONSchema>;
184
- }
185
143
  /**
186
- * A service that can be used to validate and resolve JSON against a schema.
144
+ * Deprecated JSON Validation Service that wraps the JSONSchemaService
145
+ * @deprecated Use JSONSchemaService instead
187
146
  */
188
- export class JSONSchemaService<P extends AnyPrimitiveType, R extends AnyResolvableType> {
189
- schema: Draft;
190
- constructor(private typeResolver: TypeResolver<P, R>, metaSchema: MetaSchemaInput) {
191
- this.schema = new Draft(
192
- {
193
- ...defaultConfig,
194
- resolveRef: (schema, rootSchema) => this.doResolveRef(schema, rootSchema),
195
- validate: (core, data, schema, pointer) => defaultConfig.validate(core, data, schema, pointer),
196
- resolveOneOf: (core, data, schema, pointer) => defaultConfig.resolveOneOf(core, data, schema, pointer),
197
- },
198
- metaSchema.root,
199
- );
200
-
201
- for (const [key, value] of Object.entries(metaSchema.remotes || {})) {
202
- this.schema.addRemoteSchema(key, value);
203
- }
204
-
205
- for (const schema of this.typeResolver.validationSchemaDefinitions) {
206
- // Please find a better way of doing this.
207
- this.schema.addRemoteSchema(`/${schema.title}.json`, schema as JSONSchema);
208
- this.schema.addRemoteSchema(`#/${schema.title}.json`, schema as JSONSchema);
209
- }
210
- }
211
-
212
- private doResolveRef(schema: JSONSchema, rootSchema: JSONSchema): JSONSchema {
213
- const initialRef = draft07Config.resolveRef(schema, rootSchema);
214
- if (!initialRef) return initialRef;
215
- if (!this.typeResolver.isPrimitiveType(initialRef.type)) return initialRef;
216
-
217
- const validationSchemas = this.typeResolver.getValidationSchemaForPrimitive(initialRef.type);
218
- // All validation schemas are pre-compiled as remote schemas and are referenced below
219
- const fullValidationSchema = {
220
- oneOf: validationSchemas.map((schema) => ({ $ref: `${schema.title}.json` })),
221
- };
222
- return this.schema.compileSchema(fullValidationSchema);
223
- }
224
-
225
- /**
226
- * Validate an input value against a specified schema
227
- * @throws {SchemaValidationError} if the input is invalid
228
- * @returns true if the input is valid
229
- */
230
- public validateInput(input: unknown, inputSchema: JSONSchema = this.schema.rootSchema): true | never {
231
- inputSchema = this.schema.compileSchema(inputSchema);
232
- const errors = this.schema.validate(input, inputSchema);
233
- return processValidationResult(errors);
234
- }
235
-
236
- /**
237
- * Resolve an input object by replacing all resolvable shapes with their resolved values
238
- * @param input any input object which matches the input schema
239
- * @param inputSchema a JSONSchema which provides type information about the input object
240
- * @returns the input object with all resolvable shapes resolved
241
- */
242
- public async resolveInput(input: Input, inputSchema: JSONSchema) {
243
- const setters: Array<Promise<(input: Input) => Input>> = [];
244
- this.schema.each(
245
- input,
246
- async (schema, value, pointer) => {
247
- // Bug in library for Array item schemas which won't resolve the oneOf schema
248
- if (Array.isArray(schema?.oneOf)) {
249
- const oldSchema = schema;
250
- schema = this.schema.resolveOneOf(value, schema);
251
- schema.oneOfSchema = oldSchema;
252
- }
253
- if (!this.typeResolver.isResolvableSchema(schema)) return;
254
- // If its a resolvable schema, it should exist in a oneOf array with other schemas
255
- // Including a primitive schema
256
- const allPossibleSchemaTitles: Array<string> = schema.oneOfSchema.oneOf.map((o: JSONSchema) =>
257
- o.$ref.replace('.json', ''),
258
- );
259
- const primitiveSchema = allPossibleSchemaTitles.find((title) => this.typeResolver.isPrimitiveType(title));
260
- if (!primitiveSchema) return;
261
- const resolver = this.typeResolver.tryGetResolver(primitiveSchema, schema as any);
262
- if (!resolver) return;
263
- const setResolvedData = Promise.resolve()
264
- .then(() => resolver(value))
265
- .then((resolvedData) => (item: typeof input) => JSONQuery.set(item, pointer, resolvedData, 'replace' as any))
266
- .catch((e) => Promise.reject(new JsonResolutionError(e, pointer, value)));
267
- setters.push(setResolvedData);
268
- },
269
- inputSchema,
270
- );
271
-
272
- const potentialResolutionErrors = [];
273
- for (const resolveResult of await Promise.allSettled(setters)) {
274
- if (resolveResult.status === 'rejected') {
275
- potentialResolutionErrors.push(resolveResult.reason);
276
- continue;
277
- }
278
-
279
- input = resolveResult.value(input);
280
- }
281
- if (potentialResolutionErrors.length) {
282
- throw new Error(`Error(s) occurred when resolving JSON:\n${potentialResolutionErrors.join('\n')}`);
283
- }
284
- return input;
285
- }
286
-
287
- private assignResolvedData(input: Input, pointer: string, resolvedData: unknown): Input {
288
- // TODO: Revert to using JSONQuery.set once https://github.com/sagold/json-query/pull/27 is resolved.
289
- // Test "should resolve multiple primitive type array items in multi-level nested array structures" confirms
290
- // desired behaviour.
291
- JSONQuery.get(input, pointer, (unresolvedData) => {
292
- if (resolvedData === unresolvedData) {
293
- return;
294
- }
295
-
296
- for (const prop of Object.getOwnPropertyNames(unresolvedData)) {
297
- delete unresolvedData[prop];
298
- }
299
-
300
- Object.assign(unresolvedData, resolvedData);
301
- });
302
-
303
- return input;
304
- }
305
- }
306
-
307
147
  export class JsonValidationService {
148
+ private jobSchemaService = new JSONSchemaService(TypeResolverBuilder.new().build(), JobV1MetaSchema);
149
+ private componentInputSchemaService = new JSONSchemaService(
150
+ TypeResolverBuilder.new()
151
+ .addPrimitive(BaseFormattedTextType)
152
+ .addPrimitive(SquizImageType)
153
+ .addPrimitive(SquizLinkType)
154
+ .build(),
155
+ ComponentInputMetaSchema,
156
+ );
157
+ private renderInputSchemaService = new JSONSchemaService(
158
+ TypeResolverBuilder.new()
159
+ .addPrimitive(ComponentInputFormattedTextType)
160
+ .addPrimitive(SquizImageType)
161
+ .addPrimitive(SquizLinkType)
162
+ .build(),
163
+ ComponentInputMetaSchema,
164
+ );
165
+ private componentManifestSchemaService = new JSONSchemaService(
166
+ TypeResolverBuilder.new().build(),
167
+ ManifestV1MetaSchema,
168
+ );
169
+
308
170
  validateManifest(manifest: unknown, version: 'v1' | 'JobV1') {
309
171
  switch (version) {
310
172
  case 'v1': {
311
- const validationResult = v1Schema.validate(manifest);
312
- return processValidationResult(validationResult);
173
+ return this.componentManifestSchemaService.validateInput(manifest);
313
174
  }
314
175
  case 'JobV1': {
315
- const validationResult = jobV1Schema.validate(manifest);
316
- return processValidationResult(validationResult);
176
+ return this.jobSchemaService.validateInput(manifest);
317
177
  }
318
178
 
319
179
  default:
320
180
  throw new SchemaValidationError('Invalid manifest version');
321
181
  }
322
182
  }
183
+
323
184
  validateContentSchema(contentSchema: JSONSchema) {
324
- return processValidationResult(ComponentInputSchema.validate(contentSchema));
185
+ return this.componentInputSchemaService.validateInput(contentSchema);
325
186
  }
326
187
 
327
188
  validateComponentInput(functionInputSchema: JSONSchema, inputValue: unknown) {
328
- const inputSchema = ComponentInputSchema.compileSchema(functionInputSchema);
329
- const errors = ComponentInputSchema.validate(inputValue, inputSchema);
330
- return processValidationResult(errors);
189
+ return this.componentInputSchemaService.validateInput(inputValue, functionInputSchema);
331
190
  }
332
191
 
333
192
  validateRenderInput(functionInputSchema: JSONSchema, inputValue: unknown) {
334
- const inputSchema = RenderInputSchema.compileSchema(functionInputSchema);
335
- const errors = RenderInputSchema.validate(inputValue, inputSchema);
336
-
337
- return processValidationResult(errors);
338
- }
339
- }
340
-
341
- function processValidationResult(errors: JSONError[]): true {
342
- if (errors.length > 0) {
343
- const ValidationDataMap: ValidationDataMap = errors.reduce((acc: ValidationDataMap, error: JSONError) => {
344
- const pointer = error?.data?.pointer;
345
- const validationData: ValidationData = {
346
- message: error.message,
347
- data: error.data,
348
- };
349
- Object.keys(acc).includes(pointer) && Array.isArray(acc[pointer])
350
- ? acc[pointer].push(validationData)
351
- : (acc[pointer] = [validationData]);
352
- return acc;
353
- }, {});
354
- throw new SchemaValidationError(errors.map((a) => a.message).join(',\n'), undefined, ValidationDataMap);
193
+ return this.renderInputSchemaService.validateInput(inputValue, functionInputSchema);
355
194
  }
356
- return true;
357
195
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from './formatted-text/v1/resolveFormattedTextNodes';
7
7
 
8
8
  export * as SUB_SCHEMAS from './manifest/v1/subSchemas';
9
9
 
10
+ export * from './JsonSchemaService';
10
11
  export * from './JsonValidationService';
11
12
  export * from './errors/SchemaValidationError';
12
13
  export * from './errors/JsonResolutionError';
@@ -84,4 +84,16 @@ describe('getValidationSchemaForPrimitive', () => {
84
84
  }),
85
85
  ).toThrowError();
86
86
  });
87
+
88
+ it('should return undefined when try to get a resolver for missing primitive', () => {
89
+ const primitiveType = primitiveTypeFixture('MyPrimitive');
90
+ const resolvableType = resolvableTypeFixture('MyResolvable');
91
+ const resolver = new TypeResolver([primitiveType, primitiveTypeFixture('MyOtherPrimitive')], [resolvableType], {
92
+ MyOtherPrimitive: {
93
+ MyResolvable: () => null,
94
+ },
95
+ });
96
+
97
+ expect(resolver.tryGetResolver('DoesNotExist', resolvableType)).toEqual(undefined);
98
+ });
87
99
  });
@@ -74,10 +74,6 @@ export class TypeResolver<P extends AnyPrimitiveType, R extends AnyResolvableTyp
74
74
  return this.primitives.has(type);
75
75
  }
76
76
 
77
- isPrimitiveSchema(schema: JSONSchema): schema is P {
78
- return this.isPrimitiveType(schema.title);
79
- }
80
-
81
77
  isResolvableSchema(schema: JSONSchema): schema is R {
82
78
  return this.resolvables.has(schema.title);
83
79
  }
@@ -161,4 +161,25 @@ describe('DxComponentInputSchema', () => {
161
161
  }),
162
162
  ).not.toThrowError();
163
163
  });
164
+
165
+ describe('ui:metadata', () => {
166
+ describe('enumWidget', () => {
167
+ it.each(['radio', 'dropdown'])('should allow %s as a value', (v) => {
168
+ expect(() =>
169
+ jsonValidationService.validateContentSchema({
170
+ type: 'object',
171
+ required: [],
172
+ properties: {
173
+ myProp: {
174
+ enum: ['one', 'two', 'three'],
175
+ 'ui:metadata': {
176
+ enumWidget: v,
177
+ },
178
+ },
179
+ },
180
+ }),
181
+ ).not.toThrow();
182
+ });
183
+ });
184
+ });
164
185
  });
@@ -36,43 +36,34 @@
36
36
  "default": []
37
37
  },
38
38
  "ui:metadata": {
39
- "oneOf": [
40
- {
41
- "type": "object",
42
- "additionalProperties": false,
43
- "properties": {
44
- "collapsedByDefault": {
45
- "type": "boolean",
46
- "default": false
47
- }
48
- },
49
- "if": {
50
- "type": "object"
51
- }
52
- },
53
- {
54
- "type": "object",
55
- "additionalProperties": false,
56
- "properties": {
57
- "inlineEditable": {
58
- "type": "boolean",
59
- "default": false
60
- },
61
- "quickOption": {
62
- "type": "boolean",
63
- "default": false
64
- }
65
- },
66
- "if": {
67
- "type": ["number", "integer", "array", "boolean", "string"]
68
- }
69
- }
70
- ],
39
+ "type": "object",
40
+ "additionalProperties": false,
71
41
  "properties": {
72
42
  "autoReload": {
73
43
  "type": "boolean",
74
44
  "description": "Whether the component should automatically reload when the input is changed",
75
45
  "default": false
46
+ },
47
+ "inlineEditable": {
48
+ "type": "boolean",
49
+ "default": false,
50
+ "description": "Allow this property to be editable inline"
51
+ },
52
+ "collapsedByDefault": {
53
+ "type": "boolean",
54
+ "default": false,
55
+ "description": "Choose if an object grouping should be collapsed by default"
56
+ },
57
+ "quickOption": {
58
+ "type": "boolean",
59
+ "default": false,
60
+ "description": "Allow this property to be part of the Quick Options UI"
61
+ },
62
+ "enumWidget": {
63
+ "type": "string",
64
+ "enum": ["radio", "dropdown"],
65
+ "default": "dropdown",
66
+ "description": "Choose if the enum values are displayed as a radio button list or a dropdown"
76
67
  }
77
68
  }
78
69
  }
@@ -231,6 +222,7 @@
231
222
  "oneOf": { "$ref": "#/definitions/schemaArray" },
232
223
  "not": { "$ref": "#" }
233
224
  },
225
+ "ui:metadata": { "$ref": "#/definitions/ui:metadata" },
234
226
  "additionalProperties": false,
235
227
  "dependencies": {
236
228
  "properties": {
@@ -14,32 +14,10 @@ export type DxComponentInputSchema = CoreSchemaMetaSchema & {
14
14
  [k: string]: unknown;
15
15
  };
16
16
  export type CoreSchemaMetaSchema = CoreSchemaMetaSchema1 & CoreSchemaMetaSchema2;
17
- export type UiMetadata = {
18
- /**
19
- * Whether the component should automatically reload when the input is changed
20
- */
21
- autoReload?: boolean;
22
- [k: string]: unknown;
23
- } & (
24
- | {
25
- collapsedByDefault?: boolean;
26
- }
27
- | {
28
- inlineEditable?: boolean;
29
- quickOption?: boolean;
30
- }
31
- );
32
17
  export type CoreSchemaMetaSchema2 =
33
18
  | {
34
19
  $id?: string;
35
- 'ui:metadata'?:
36
- | {
37
- collapsedByDefault?: boolean;
38
- }
39
- | {
40
- inlineEditable?: boolean;
41
- quickOption?: boolean;
42
- };
20
+ 'ui:metadata'?: UiMetadata;
43
21
  $schema?: string;
44
22
  $ref?: string;
45
23
  $comment?: string;
@@ -469,3 +447,25 @@ export interface CoreSchemaMetaSchema1 {
469
447
  oneOf?: SchemaArray;
470
448
  not?: CoreSchemaMetaSchema2;
471
449
  }
450
+ export interface UiMetadata {
451
+ /**
452
+ * Whether the component should automatically reload when the input is changed
453
+ */
454
+ autoReload?: boolean;
455
+ /**
456
+ * Allow this property to be editable inline
457
+ */
458
+ inlineEditable?: boolean;
459
+ /**
460
+ * Choose if an object grouping should be collapsed by default
461
+ */
462
+ collapsedByDefault?: boolean;
463
+ /**
464
+ * Allow this property to be part of the Quick Options UI
465
+ */
466
+ quickOption?: boolean;
467
+ /**
468
+ * Choose if the enum values are displayed as a radio button list or a dropdown
469
+ */
470
+ enumWidget?: 'radio' | 'dropdown';
471
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "../../v1.json",
3
+ "description": "t",
4
+ "displayName": "t",
5
+ "name": "test",
6
+ "namespace": "other",
7
+ "mainFunction": "main",
8
+ "version": "1.0.4",
9
+ "environment": [],
10
+ "functions": [
11
+ {
12
+ "entry": "main.js",
13
+ "name": "main",
14
+ "output": {
15
+ "responseType": "html"
16
+ },
17
+ "input": {
18
+ "type": "object",
19
+ "properties": {
20
+ "prop": {
21
+ "type": "string",
22
+ "ui:metadata": {
23
+ "quickOption": true
24
+ }
25
+ }
26
+ },
27
+ "required": []
28
+ }
29
+ }
30
+ ]
31
+ }
@@ -21,9 +21,9 @@ function expectToThrowErrorMatchingTypeAndMessage(received: Function, errorType:
21
21
  error = e;
22
22
  }
23
23
 
24
- expect(error).toBeDefined();
25
- expect(error?.message).toEqual(message);
24
+ expect(error).toBeTruthy();
26
25
  expect(error).toBeInstanceOf(errorType);
26
+ expect(error?.message).toEqual(message);
27
27
  }
28
28
 
29
29
  describe('manifest/v1', () => {
@@ -276,76 +276,6 @@ describe('manifest/v1', () => {
276
276
  ),
277
277
  ).toEqual(true);
278
278
  });
279
- it('should fail if ui:metadata property object on object input group has flags other than collapsedByDefault', async () => {
280
- const manifest = await fetchTestManifest('validComponent.json');
281
- expectToThrowErrorMatchingTypeAndMessage(
282
- () => {
283
- validationService.validateManifest(
284
- {
285
- ...manifest,
286
- functions: [
287
- {
288
- name: 'main',
289
- entry: 'main.js',
290
- output: {
291
- responseType: 'html',
292
- },
293
- input: {
294
- type: 'object',
295
- properties: {
296
- 'users-contact': {
297
- type: 'object',
298
- description: 'A description of group can be provided if needed',
299
- required: ['fist-name', 'last-name', 'phone-number', 'email'],
300
- 'ui:metadata': {
301
- collapsedByDefault: true,
302
- quickOption: false,
303
- inlineEditable: true,
304
- },
305
- properties: {
306
- 'fist-name': {
307
- type: 'string',
308
- 'ui:metadata': {
309
- inlineEditable: true,
310
- quickOption: false,
311
- },
312
- },
313
- 'last-name': {
314
- type: 'string',
315
- 'ui:metadata': {
316
- inlineEditable: true,
317
- quickOption: false,
318
- },
319
- },
320
- 'phone-number': {
321
- type: 'number',
322
- 'ui:metadata': {
323
- inlineEditable: true,
324
- quickOption: false,
325
- },
326
- },
327
- email: {
328
- type: 'string',
329
- 'ui:metadata': {
330
- inlineEditable: true,
331
- quickOption: false,
332
- },
333
- },
334
- },
335
- },
336
- },
337
- required: ['users-contact'],
338
- },
339
- },
340
- ],
341
- },
342
- 'v1',
343
- );
344
- },
345
- SchemaValidationError,
346
- 'failed validation: Value `{"collapsedByDefault":true,"quickOption":false,"inlineEditable":true}` in `#/functions/0/input/properties/users-contact/ui:metadata` does not match any given oneof schema',
347
- );
348
- });
349
279
  it('should allow the autoReload field on the manifest', async () => {
350
280
  const manifest = await fetchTestManifest('validComponent.json');
351
281
 
@@ -443,7 +373,7 @@ describe('manifest/v1', () => {
443
373
  );
444
374
  },
445
375
  SchemaValidationError,
446
- 'failed validation: Value `{"inlineEditable":true,"quickOption":false,"collapsedByDefault":true}` in `#/functions/0/input/properties/users-contact/properties/fist-name/ui:metadata` does not match any given oneof schema',
376
+ 'failed validation: ui:metadata property collapsedByDefault is only valid for object properties.',
447
377
  );
448
378
  });
449
379
  });