@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.
- package/CHANGELOG.md +12 -0
- package/jest.config.ts +13 -2
- package/lib/JsonSchemaService.d.ts +34 -0
- package/lib/JsonSchemaService.js +140 -0
- package/lib/JsonSchemaService.js.map +1 -0
- package/lib/JsonSchemaService.spec.d.ts +1 -0
- package/lib/JsonSchemaService.spec.js +1807 -0
- package/lib/JsonSchemaService.spec.js.map +1 -0
- package/lib/JsonValidationService.d.ts +11 -33
- package/lib/JsonValidationService.js +43 -194
- package/lib/JsonValidationService.js.map +1 -1
- package/lib/JsonValidationService.spec.js +19 -1800
- package/lib/JsonValidationService.spec.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/jsonTypeResolution/TypeResolver.d.ts +4 -2
- package/lib/jsonTypeResolution/TypeResolver.js +0 -3
- package/lib/jsonTypeResolution/TypeResolver.js.map +1 -1
- package/lib/jsonTypeResolution/TypeResolver.spec.js +10 -0
- package/lib/jsonTypeResolution/TypeResolver.spec.js.map +1 -1
- package/lib/manifest/v1/DxComponentInputSchema.spec.js +18 -0
- package/lib/manifest/v1/DxComponentInputSchema.spec.js.map +1 -1
- package/lib/manifest/v1/DxContentMetaSchema.json +24 -32
- package/lib/manifest/v1/JobV1.d.ts +23 -18
- package/lib/manifest/v1/__test__/schemas/invalidUiMetadata.json +31 -0
- package/lib/manifest/v1/v1.d.ts +23 -18
- package/lib/manifest/v1/v1.spec.js +3 -66
- package/lib/manifest/v1/v1.spec.js.map +1 -1
- package/lib/primitiveTypes/SquizImage.d.ts +2 -1
- package/lib/primitiveTypes/SquizImage.js.map +1 -1
- package/lib/processValidationResult.d.ts +2 -0
- package/lib/processValidationResult.js +24 -0
- package/lib/processValidationResult.js.map +1 -0
- package/lib/validators/customKeywordValidators.d.ts +2 -0
- package/lib/validators/customKeywordValidators.js +47 -0
- package/lib/validators/customKeywordValidators.js.map +1 -0
- package/lib/validators/customKeywordValidators.spec.d.ts +1 -0
- package/lib/validators/customKeywordValidators.spec.js +100 -0
- package/lib/validators/customKeywordValidators.spec.js.map +1 -0
- package/package.json +2 -5
- package/src/JsonSchemaService.spec.ts +2198 -0
- package/src/JsonSchemaService.ts +159 -0
- package/src/JsonValidationService.spec.ts +26 -2195
- package/src/JsonValidationService.ts +64 -226
- package/src/index.ts +1 -0
- package/src/jsonTypeResolution/TypeResolver.spec.ts +12 -0
- package/src/jsonTypeResolution/TypeResolver.ts +5 -5
- package/src/manifest/v1/DxComponentInputSchema.spec.ts +21 -0
- package/src/manifest/v1/DxContentMetaSchema.json +24 -32
- package/src/manifest/v1/JobV1.ts +23 -23
- package/src/manifest/v1/__test__/schemas/invalidUiMetadata.json +31 -0
- package/src/manifest/v1/v1.spec.ts +3 -73
- package/src/manifest/v1/v1.ts +23 -23
- package/src/primitiveTypes/SquizImage.ts +2 -1
- package/src/processValidationResult.ts +20 -0
- package/src/validators/customKeywordValidators.spec.ts +171 -0
- package/src/validators/customKeywordValidators.ts +53 -0
- 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
|
13
|
-
import
|
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 {
|
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 {
|
21
|
-
import {
|
22
|
-
|
23
|
-
|
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
|
-
*
|
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
|
-
|
312
|
-
return processValidationResult(validationResult);
|
173
|
+
return this.componentManifestSchemaService.validateInput(manifest);
|
313
174
|
}
|
314
175
|
case 'JobV1': {
|
315
|
-
|
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
|
185
|
+
return this.componentInputSchemaService.validateInput(contentSchema);
|
325
186
|
}
|
326
187
|
|
327
188
|
validateComponentInput(functionInputSchema: JSONSchema, inputValue: unknown) {
|
328
|
-
|
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
|
-
|
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
|
});
|
@@ -35,7 +35,11 @@ export function ResolvableType<SHAPE, TITLE extends string>(
|
|
35
35
|
}
|
36
36
|
export type AnyResolvableType = ResolvableType<string, any>;
|
37
37
|
|
38
|
-
export type
|
38
|
+
export type ResolverContext = {
|
39
|
+
baseUrl?: string;
|
40
|
+
};
|
41
|
+
|
42
|
+
export type Resolver<INPUT, OUTPUT> = (input: INPUT, ctx?: ResolverContext) => MaybePromise<OUTPUT>;
|
39
43
|
|
40
44
|
/**
|
41
45
|
* A JSON Type Resolver class which stores the primitive and resolvable JSON Schema types and their resolvers
|
@@ -74,10 +78,6 @@ export class TypeResolver<P extends AnyPrimitiveType, R extends AnyResolvableTyp
|
|
74
78
|
return this.primitives.has(type);
|
75
79
|
}
|
76
80
|
|
77
|
-
isPrimitiveSchema(schema: JSONSchema): schema is P {
|
78
|
-
return this.isPrimitiveType(schema.title);
|
79
|
-
}
|
80
|
-
|
81
81
|
isResolvableSchema(schema: JSONSchema): schema is R {
|
82
82
|
return this.resolvables.has(schema.title);
|
83
83
|
}
|
@@ -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
|
-
"
|
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": {
|
package/src/manifest/v1/JobV1.ts
CHANGED
@@ -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).
|
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:
|
376
|
+
'failed validation: ui:metadata property collapsedByDefault is only valid for object properties.',
|
447
377
|
);
|
448
378
|
});
|
449
379
|
});
|