@takeshape/schema 9.32.3 → 9.33.2
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/dist/schema-util.js +1 -1
- package/dist/types/types.d.ts +14 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/validate.d.ts +15 -9
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +310 -180
- package/es/schema-util.js +1 -1
- package/es/validate.js +309 -183
- package/examples/dependencies/rick-and-morty-layer.json +575 -0
- package/examples/dependencies/shopify-layer-2023-01.json +86171 -0
- package/package.json +6 -5
package/es/validate.js
CHANGED
|
@@ -12,16 +12,16 @@ import { CURRENT_SCHEMA_VERSION, LEGACY_SCHEMA_VERSION, LEGACY_API_VERSION } fro
|
|
|
12
12
|
import { defaultWorkflow } from './workflows';
|
|
13
13
|
import { allProjectSchemas } from './schemas';
|
|
14
14
|
import authSchemas from './schemas/auth-schemas.json';
|
|
15
|
-
import {
|
|
15
|
+
import { getAllRefs } from './schema-util';
|
|
16
16
|
import { builtInShapes } from './builtin-schema';
|
|
17
17
|
import { isValidTemplate } from './template-shapes';
|
|
18
18
|
import { isBasicResolver, isComposeResolver, isExtendsSchema, isObjectSchema } from './types/utils';
|
|
19
|
-
import {
|
|
19
|
+
import { refItemToShapeName, getRefShapeName, parsePropertyRef, propertyRefItemToResolverPath, createGetNamespace, propertyRefItemToPath } from './refs';
|
|
20
20
|
import { scalars } from './scalars';
|
|
21
21
|
import { isUnionSchema } from './unions';
|
|
22
22
|
import { isEnumLikeSchema } from './enum';
|
|
23
23
|
import metaSchemaV3_9_0 from './schemas/project-schema/meta-schema-v3.9.0.json';
|
|
24
|
-
import { ensureArray, isIntegerLike, isRecord } from '@takeshape/util';
|
|
24
|
+
import { ensureArray, isIntegerLike, isRecord, value } from '@takeshape/util';
|
|
25
25
|
import forOwn from 'lodash/forOwn';
|
|
26
26
|
import uniqBy from 'lodash/uniqBy';
|
|
27
27
|
import initial from 'lodash/initial';
|
|
@@ -32,6 +32,9 @@ import gte from 'semver/functions/gte';
|
|
|
32
32
|
import size from 'lodash/size';
|
|
33
33
|
import isEqual from 'lodash/isEqual';
|
|
34
34
|
import pick from 'lodash/pick';
|
|
35
|
+
import pMap from 'p-map';
|
|
36
|
+
import { flatten } from 'lodash';
|
|
37
|
+
const builtInShapeNames = new Set([...Object.keys(builtInShapes), ...scalars, 'object']);
|
|
35
38
|
|
|
36
39
|
function findDuplicates(items) {
|
|
37
40
|
const seen = {};
|
|
@@ -166,7 +169,24 @@ function getSemver(schemaVersion) {
|
|
|
166
169
|
return coerce(schemaVersion) ?? LEGACY_SCHEMA_VERSION;
|
|
167
170
|
}
|
|
168
171
|
|
|
169
|
-
function
|
|
172
|
+
function enumerateBasicResolvers(resolver, path) {
|
|
173
|
+
const results = [];
|
|
174
|
+
|
|
175
|
+
const visit = (resolver, path) => {
|
|
176
|
+
if (isComposeResolver(resolver)) {
|
|
177
|
+
resolver.compose.forEach((resolver, i) => {
|
|
178
|
+
visit(resolver, [...path, i]);
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
results.push([resolver, path]);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
visit(resolver, path);
|
|
186
|
+
return results;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function validateResolver(projectSchema, basePath, baseResolver) {
|
|
170
190
|
const errors = [];
|
|
171
191
|
/**
|
|
172
192
|
* V3.9 Resolver name enums are the set used for earlier v3 validations
|
|
@@ -210,14 +230,14 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
210
230
|
const isLessThanV3_9_0 = lt(getSemver(projectSchema.schemaVersion), '3.9.0');
|
|
211
231
|
const getNamespace = createGetNamespace(projectSchema);
|
|
212
232
|
|
|
213
|
-
const
|
|
233
|
+
for (const [resolver, path] of enumerateBasicResolvers(baseResolver, basePath)) {
|
|
214
234
|
if (isBasicResolver(resolver)) {
|
|
215
235
|
var _projectSchema$servic;
|
|
216
236
|
|
|
217
237
|
if ('service' in resolver && resolver.service !== 'takeshape:local' && !((_projectSchema$servic = projectSchema.services) !== null && _projectSchema$servic !== void 0 && _projectSchema$servic[resolver.service])) {
|
|
218
238
|
errors.push({
|
|
219
239
|
type: 'notFound',
|
|
220
|
-
path:
|
|
240
|
+
path: path.concat(['service']),
|
|
221
241
|
message: `Invalid service "${resolver.service}"`
|
|
222
242
|
});
|
|
223
243
|
}
|
|
@@ -225,7 +245,7 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
225
245
|
if (isLessThanV3_9_0 && !isValidResolverNameV3_9_0(resolver.name)) {
|
|
226
246
|
errors.push({
|
|
227
247
|
type: 'notFound',
|
|
228
|
-
path:
|
|
248
|
+
path: path.concat(['name']),
|
|
229
249
|
message: `Invalid resolver name "${resolver.name}"`
|
|
230
250
|
});
|
|
231
251
|
}
|
|
@@ -241,7 +261,7 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
241
261
|
if (!isValidShapeName(shapeName)) {
|
|
242
262
|
errors.push({
|
|
243
263
|
type: 'notFound',
|
|
244
|
-
path:
|
|
264
|
+
path: path.concat(['options', 'model']),
|
|
245
265
|
message: `Invalid Model Shape "${shapeName ?? ''}"`
|
|
246
266
|
});
|
|
247
267
|
}
|
|
@@ -256,7 +276,7 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
256
276
|
if (!isValidShapeName(shapeName)) {
|
|
257
277
|
errors.push({
|
|
258
278
|
type: 'notFound',
|
|
259
|
-
path:
|
|
279
|
+
path: path.concat(['shapeName']),
|
|
260
280
|
message: `Invalid Model Shape "${shapeName ?? ''}"`
|
|
261
281
|
});
|
|
262
282
|
}
|
|
@@ -274,7 +294,7 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
274
294
|
} else {
|
|
275
295
|
errors.push({
|
|
276
296
|
type: 'conflict',
|
|
277
|
-
path:
|
|
297
|
+
path: path.concat('to'),
|
|
278
298
|
message: `Unable to parse property ref "${resolver.to}"`
|
|
279
299
|
});
|
|
280
300
|
}
|
|
@@ -282,62 +302,115 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
282
302
|
} else {
|
|
283
303
|
errors.push({
|
|
284
304
|
type: 'notFound',
|
|
285
|
-
path
|
|
305
|
+
path,
|
|
286
306
|
message: `Invalid resolver`
|
|
287
307
|
});
|
|
288
308
|
}
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
if (isComposeResolver(resolver)) {
|
|
292
|
-
resolver.compose.forEach(validateBasicResolver);
|
|
293
|
-
} else {
|
|
294
|
-
validateBasicResolver(resolver);
|
|
295
309
|
}
|
|
296
310
|
|
|
297
311
|
return errors;
|
|
298
|
-
}
|
|
299
|
-
|
|
312
|
+
}
|
|
300
313
|
|
|
301
|
-
function
|
|
314
|
+
function validateLocalQueryConfig(projectSchema, query, operation, name) {
|
|
302
315
|
const location = operation === 'query' ? 'queries' : 'mutations';
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
template,
|
|
306
|
-
shapeName
|
|
307
|
-
} = parseReturnShape(projectSchema, query.shape);
|
|
316
|
+
return validateResolver(projectSchema, [location, name, 'resolver'], query.resolver);
|
|
317
|
+
}
|
|
308
318
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
319
|
+
const operationProps = [['query', 'queries'], ['mutation', 'mutations']];
|
|
320
|
+
|
|
321
|
+
function validateLocalQueryConfigs(projectSchema) {
|
|
322
|
+
const errors = [];
|
|
323
|
+
|
|
324
|
+
for (const [operation, prop] of operationProps) {
|
|
325
|
+
for (const name of Object.keys(projectSchema[prop])) {
|
|
326
|
+
errors.push(...validateLocalQueryConfig(projectSchema, projectSchema[prop][name], operation, name));
|
|
327
|
+
}
|
|
315
328
|
}
|
|
316
329
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
330
|
+
return errors;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function allowDisconnected(context, status) {
|
|
334
|
+
return Boolean(context.allowDisconnectedLayers) && status !== 'notAvailable';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isValidShapeReference(context, layerState, shapeName) {
|
|
338
|
+
if (layerState.status !== 'ok') {
|
|
339
|
+
return allowDisconnected(context, layerState.status);
|
|
323
340
|
}
|
|
324
341
|
|
|
342
|
+
return Boolean(layerState.schema.shapes[shapeName]);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function validateResolverReferences(context, projectSchema, basePath, baseResolver) {
|
|
346
|
+
const errors = [];
|
|
347
|
+
await pMap(enumerateBasicResolvers(baseResolver, basePath), async ([resolver, path]) => {
|
|
348
|
+
if (resolver.name === 'graphql:query' || resolver.name === 'graphql:mutation') {
|
|
349
|
+
const {
|
|
350
|
+
service,
|
|
351
|
+
fieldName
|
|
352
|
+
} = resolver;
|
|
353
|
+
const prop = resolver.name === 'graphql:query' ? 'queries' : 'mutations';
|
|
354
|
+
const layerState = await context.resolveLayer(service);
|
|
355
|
+
const valid = layerState.status === 'ok' ? Boolean(layerState.schema[prop][fieldName]) : allowDisconnected(context, layerState.status);
|
|
356
|
+
|
|
357
|
+
if (!valid) {
|
|
358
|
+
const operation = resolver.name === 'graphql:query' ? 'query' : 'mutation';
|
|
359
|
+
errors.push({
|
|
360
|
+
type: 'notFound',
|
|
361
|
+
path: path.concat('fieldName'),
|
|
362
|
+
message: `Missing ${operation} "${resolver.fieldName}" in service layer "${service}"`
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
} else if (resolver.name === 'delegate') {
|
|
366
|
+
const ref = parsePropertyRef(resolver.to);
|
|
367
|
+
|
|
368
|
+
if (ref && ref.serviceId !== 'local') {
|
|
369
|
+
const layerState = await context.resolveLayer(ref.serviceId);
|
|
370
|
+
const valid = layerState.status === 'ok' ? Boolean(get(layerState.schema, propertyRefItemToPath(value(''), ref))) : allowDisconnected(context, layerState.status);
|
|
371
|
+
|
|
372
|
+
if (!valid) {
|
|
373
|
+
errors.push({
|
|
374
|
+
type: 'notFound',
|
|
375
|
+
path: path.concat('to'),
|
|
376
|
+
message: `Missing resolver config at property ref "${resolver.to}"`
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
325
382
|
return errors;
|
|
326
383
|
}
|
|
327
384
|
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
385
|
+
async function validateQueryConfig(context, projectSchema, {
|
|
386
|
+
query,
|
|
387
|
+
name,
|
|
388
|
+
operation
|
|
389
|
+
}) {
|
|
390
|
+
const location = operation === 'query' ? 'queries' : 'mutations';
|
|
391
|
+
return validateResolverReferences(context, projectSchema, [location, name, 'resolver'], query.resolver);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function validateQueryConfigs(context, projectSchema) {
|
|
395
|
+
const isLessThanV3_9_0 = lt(getSemver(projectSchema.schemaVersion), '3.9.0');
|
|
331
396
|
|
|
332
|
-
|
|
333
|
-
|
|
397
|
+
if (isLessThanV3_9_0) {
|
|
398
|
+
return [];
|
|
334
399
|
}
|
|
335
400
|
|
|
336
|
-
|
|
337
|
-
|
|
401
|
+
const promises = [];
|
|
402
|
+
|
|
403
|
+
for (const [operation, prop] of operationProps) {
|
|
404
|
+
for (const name of Object.keys(projectSchema[prop])) {
|
|
405
|
+
promises.push(validateQueryConfig(context, projectSchema, {
|
|
406
|
+
query: projectSchema[prop][name],
|
|
407
|
+
operation,
|
|
408
|
+
name
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
338
411
|
}
|
|
339
412
|
|
|
340
|
-
return
|
|
413
|
+
return flatten(await Promise.all(promises));
|
|
341
414
|
}
|
|
342
415
|
|
|
343
416
|
function validateIndexedShapeConfig(projectSchema, shapeName, config) {
|
|
@@ -419,8 +492,6 @@ function validateIndexedShapes(projectSchema) {
|
|
|
419
492
|
return errors;
|
|
420
493
|
}
|
|
421
494
|
|
|
422
|
-
const builtInShapeNames = [...Object.keys(builtInShapes), ...scalars, 'object'];
|
|
423
|
-
|
|
424
495
|
function getModelShapeIds(shapes) {
|
|
425
496
|
return Object.values(shapes).filter(shape => shape.model).map(shape => shape.id);
|
|
426
497
|
}
|
|
@@ -435,44 +506,94 @@ function isAllOfPath(path) {
|
|
|
435
506
|
return index !== -1 && isIntegerLike(path[index + 1]);
|
|
436
507
|
}
|
|
437
508
|
|
|
438
|
-
function
|
|
509
|
+
function validateLocalRefs(projectSchema) {
|
|
439
510
|
const errors = [];
|
|
440
|
-
const shapeNames = new Set([...
|
|
441
|
-
const refs =
|
|
511
|
+
const shapeNames = new Set([...builtInShapeNames, ...Object.keys(projectSchema.shapes)]);
|
|
512
|
+
const refs = getAllRefs(projectSchema).filter(item => !item.isForeign);
|
|
513
|
+
|
|
514
|
+
for (const item of refs) {
|
|
515
|
+
if (item.template && !isValidTemplate(item.template)) {
|
|
516
|
+
errors.push({
|
|
517
|
+
type: 'notFound',
|
|
518
|
+
path: item.path,
|
|
519
|
+
message: `Invalid template "${item.template}"`
|
|
520
|
+
});
|
|
521
|
+
}
|
|
442
522
|
|
|
443
|
-
|
|
444
|
-
refs.forEach(item => {
|
|
445
|
-
const shapeName = refItemToShapeName(item);
|
|
523
|
+
const shapeName = refItemToShapeName(item);
|
|
446
524
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
525
|
+
if (!shapeNames.has(shapeName)) {
|
|
526
|
+
errors.push({
|
|
527
|
+
type: 'notFound',
|
|
528
|
+
path: item.path,
|
|
529
|
+
message: `Invalid ref "${get(projectSchema, item.path)}"`
|
|
530
|
+
});
|
|
531
|
+
} // Make sure refs inside allOf don't refer to their own Shape
|
|
454
532
|
|
|
455
533
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
534
|
+
if (item.path[1] === shapeName && isAllOfPath(item.path)) {
|
|
535
|
+
errors.push({
|
|
536
|
+
type: 'conflict',
|
|
537
|
+
path: item.path,
|
|
538
|
+
message: `allOf cannot be self-referential`
|
|
539
|
+
});
|
|
540
|
+
}
|
|
463
541
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
542
|
+
const parentPath = initial(item.path);
|
|
543
|
+
const parentSchema = get(projectSchema, parentPath);
|
|
544
|
+
const propName = last(item.path);
|
|
467
545
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
546
|
+
if (propName === '@ref' && parentSchema.$ref) {
|
|
547
|
+
errors.push({
|
|
548
|
+
type: 'conflict',
|
|
549
|
+
path: parentPath,
|
|
550
|
+
message: `Ref cannot have both @ref and $ref`
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return errors;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async function validateRefs(context, projectSchema) {
|
|
559
|
+
const {
|
|
560
|
+
resolveLayer
|
|
561
|
+
} = context;
|
|
562
|
+
const errors = [];
|
|
563
|
+
const refs = getAllRefs(projectSchema);
|
|
564
|
+
const layerIds = new Set();
|
|
565
|
+
|
|
566
|
+
for (const item of refs) {
|
|
567
|
+
if (item.serviceKey !== 'local') {
|
|
568
|
+
layerIds.add(item.serviceKey);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const layersById = Object.fromEntries(await pMap(layerIds, async layerId => [layerId, await resolveLayer(layerId)]));
|
|
573
|
+
|
|
574
|
+
for (const item of refs) {
|
|
575
|
+
if (item.template && !isValidTemplate(item.template)) {
|
|
576
|
+
errors.push({
|
|
577
|
+
type: 'notFound',
|
|
578
|
+
path: item.path,
|
|
579
|
+
message: `Invalid template "${item.path}"`
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const {
|
|
584
|
+
serviceKey: layerId
|
|
585
|
+
} = item;
|
|
586
|
+
const shapeName = refItemToShapeName(item);
|
|
587
|
+
const localShapeExists = Boolean(projectSchema.shapes[shapeName]) || builtInShapeNames.has(shapeName);
|
|
588
|
+
const valid = layerId === 'local' ? localShapeExists : localShapeExists || isValidShapeReference(context, layersById[layerId], shapeName);
|
|
589
|
+
|
|
590
|
+
if (!valid) {
|
|
591
|
+
errors.push({
|
|
592
|
+
type: 'notFound',
|
|
593
|
+
path: item.path,
|
|
594
|
+
message: `Invalid ref "${get(projectSchema, item.path)}"`
|
|
595
|
+
});
|
|
596
|
+
}
|
|
476
597
|
}
|
|
477
598
|
|
|
478
599
|
return errors;
|
|
@@ -739,9 +860,14 @@ export function formatError(error) {
|
|
|
739
860
|
path
|
|
740
861
|
};
|
|
741
862
|
}
|
|
863
|
+
|
|
864
|
+
function isValidateReferencesContext(context) {
|
|
865
|
+
return Boolean(context.resolveLayer);
|
|
866
|
+
}
|
|
867
|
+
|
|
742
868
|
const ajv = createAjv();
|
|
743
869
|
|
|
744
|
-
function validateStructure(schemaVersion, schema, ref
|
|
870
|
+
function validateStructure(schemaVersion, context, schema, ref) {
|
|
745
871
|
var _coerce, _ajv$errors;
|
|
746
872
|
|
|
747
873
|
const versionStr = (_coerce = coerce(schemaVersion)) === null || _coerce === void 0 ? void 0 : _coerce.format();
|
|
@@ -755,7 +881,9 @@ function validateStructure(schemaVersion, schema, ref, options) {
|
|
|
755
881
|
|
|
756
882
|
ajv.validate(`https://schema.takeshape.io/project-schema/v${versionStr}#${ref ?? ''}`, schema);
|
|
757
883
|
let errors = ((_ajv$errors = ajv.errors) === null || _ajv$errors === void 0 ? void 0 : _ajv$errors.map(formatError)) ?? [];
|
|
758
|
-
const
|
|
884
|
+
const {
|
|
885
|
+
suppressErrorPaths
|
|
886
|
+
} = context;
|
|
759
887
|
|
|
760
888
|
if (errors.length && suppressErrorPaths) {
|
|
761
889
|
errors = errors.filter(error => {
|
|
@@ -778,20 +906,10 @@ function validateStructure(schemaVersion, schema, ref, options) {
|
|
|
778
906
|
};
|
|
779
907
|
}
|
|
780
908
|
|
|
781
|
-
function
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
return structuralValidation;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
const schema = obj;
|
|
789
|
-
let errors = [];
|
|
790
|
-
const namespaceShapes = getAllNamespaceShapes(schema);
|
|
791
|
-
const additionalShapeNames = (options !== null && options !== void 0 && options.additionalShapeNames ? builtInShapeNames.concat(options.additionalShapeNames) : builtInShapeNames).concat(namespaceShapes);
|
|
792
|
-
const additionalModelShapeIds = options !== null && options !== void 0 && options.additionalModelShapeIds ? builtInModelShapeIds.concat(options.additionalModelShapeIds) : builtInModelShapeIds;
|
|
793
|
-
errors = errors.concat(checkShapeNames(schema.shapes)).concat(checkShapeIds(schema.shapes)).concat(validateWorkflowsV3(schema)).concat(validateQueryConfigs(schema, additionalShapeNames)).concat(validateRefs(schema, additionalShapeNames)).concat(validateDirectives(schema, additionalModelShapeIds)).concat(validateLocales(schema)).concat(checkWorkflowStepNames(schema.workflows)).concat(checkWorkflowStepKeys(schema.workflows)).concat(validateOneOfs(schema)).concat(validateIndexedShapes(schema)).concat(validateInterfaces(schema)).concat(validateInterfaceImplementations(schema));
|
|
794
|
-
const suppressErrorPaths = options === null || options === void 0 ? void 0 : options.suppressErrorPaths;
|
|
909
|
+
function formatValidationResult(context, errors, schema) {
|
|
910
|
+
const {
|
|
911
|
+
suppressErrorPaths
|
|
912
|
+
} = context;
|
|
795
913
|
|
|
796
914
|
if (suppressErrorPaths) {
|
|
797
915
|
errors = errors.filter(error => {
|
|
@@ -810,132 +928,140 @@ function validateV3X(version, obj, options) {
|
|
|
810
928
|
};
|
|
811
929
|
}
|
|
812
930
|
|
|
813
|
-
function validateV4X(version, obj, options) {
|
|
814
|
-
const structuralValidation = validateStructure(version, obj);
|
|
815
|
-
|
|
816
|
-
if (!structuralValidation.valid) {
|
|
817
|
-
return structuralValidation;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
const schema = obj;
|
|
821
|
-
|
|
822
|
-
for (const [index, layerConfig] of Object.entries(schema.layers)) {
|
|
823
|
-
var _options$resolveLayer;
|
|
824
|
-
|
|
825
|
-
const layerId = typeof layerConfig === 'string' ? layerConfig : layerConfig.id;
|
|
826
|
-
const layer = options === null || options === void 0 ? void 0 : (_options$resolveLayer = options.resolveLayer) === null || _options$resolveLayer === void 0 ? void 0 : _options$resolveLayer.call(options, layerId);
|
|
827
|
-
|
|
828
|
-
if (layer) {
|
|
829
|
-
const results = validateStructure(version, layer, '/definitions/layerSchema');
|
|
830
|
-
|
|
831
|
-
if (!results.valid) {
|
|
832
|
-
return results;
|
|
833
|
-
}
|
|
834
|
-
} else {
|
|
835
|
-
return {
|
|
836
|
-
valid: false,
|
|
837
|
-
schema: undefined,
|
|
838
|
-
errors: [{
|
|
839
|
-
path: ['layers', index],
|
|
840
|
-
type: 'undefined',
|
|
841
|
-
message: `Layer with id is undefined`
|
|
842
|
-
}]
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
return {
|
|
848
|
-
valid: true,
|
|
849
|
-
schema,
|
|
850
|
-
errors: undefined
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
|
|
854
931
|
const validators = [{
|
|
855
932
|
range: '^1.0.0',
|
|
856
933
|
|
|
857
|
-
|
|
858
|
-
const structuralValidation = validateStructure(schemaVersion, obj);
|
|
859
|
-
|
|
860
|
-
if (!structuralValidation.valid) {
|
|
861
|
-
return structuralValidation;
|
|
862
|
-
}
|
|
863
|
-
|
|
934
|
+
validateSyntax(schemaVersion, context, obj) {
|
|
864
935
|
const schemaV1 = obj;
|
|
865
936
|
let errors = [];
|
|
866
937
|
errors = errors.concat(checkContentTypeNames(schemaV1.contentTypes)).concat(validateWorkflowsV1(schemaV1)).concat(checkWorkflowStepNames(schemaV1.workflows)).concat(checkWorkflowStepKeys(schemaV1.workflows));
|
|
867
|
-
return errors
|
|
868
|
-
valid: false,
|
|
869
|
-
schema: undefined,
|
|
870
|
-
errors
|
|
871
|
-
} : {
|
|
872
|
-
valid: true,
|
|
873
|
-
schema: schemaV1,
|
|
874
|
-
errors: undefined
|
|
875
|
-
};
|
|
938
|
+
return formatValidationResult(context, errors, schemaV1);
|
|
876
939
|
}
|
|
877
940
|
|
|
878
941
|
}, {
|
|
879
942
|
range: '^3.0.0',
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
943
|
+
|
|
944
|
+
validateSyntax(schemaVersion, context, obj) {
|
|
945
|
+
const schema = obj;
|
|
946
|
+
let errors = [];
|
|
947
|
+
errors = errors.concat(checkShapeNames(schema.shapes)).concat(checkShapeIds(schema.shapes)).concat(validateWorkflowsV3(schema)).concat(validateLocalQueryConfigs(schema)).concat(validateLocalRefs(schema)).concat(validateDirectives(schema)).concat(validateLocales(schema)).concat(checkWorkflowStepNames(schema.workflows)).concat(checkWorkflowStepKeys(schema.workflows)).concat(validateOneOfs(schema)).concat(validateIndexedShapes(schema)).concat(validateInterfaces(schema)).concat(validateInterfaceImplementations(schema));
|
|
948
|
+
return formatValidationResult(context, errors, schema);
|
|
949
|
+
},
|
|
950
|
+
|
|
951
|
+
async validateReferences(schemaVersion, context, obj) {
|
|
952
|
+
const schema = obj;
|
|
953
|
+
const errors = flatten(await Promise.all([validateRefs(context, schema), validateQueryConfigs(context, schema)]));
|
|
954
|
+
return formatValidationResult(context, errors, schema);
|
|
955
|
+
}
|
|
956
|
+
|
|
884
957
|
}];
|
|
885
958
|
|
|
886
959
|
function findValidator(schemaVersion) {
|
|
887
|
-
const
|
|
888
|
-
const schemaSemVer = coerce(normalizedSchemaVersion);
|
|
960
|
+
const schemaSemVer = coerce(schemaVersion);
|
|
889
961
|
|
|
890
962
|
if (schemaSemVer) {
|
|
891
963
|
const validator = validators.find(v => satisfies(schemaSemVer, v.range));
|
|
892
964
|
|
|
893
965
|
if (validator) {
|
|
894
|
-
|
|
966
|
+
const {
|
|
967
|
+
validateSyntax,
|
|
968
|
+
validateReferences
|
|
969
|
+
} = validator;
|
|
970
|
+
return {
|
|
971
|
+
validateSyntax: validateSyntax.bind(null, schemaVersion),
|
|
972
|
+
...(validateReferences ? {
|
|
973
|
+
validateReferences: validateReferences.bind(null, schemaVersion)
|
|
974
|
+
} : {})
|
|
975
|
+
};
|
|
895
976
|
}
|
|
896
977
|
}
|
|
897
978
|
}
|
|
979
|
+
|
|
980
|
+
const schemaUndefinedResult = {
|
|
981
|
+
valid: false,
|
|
982
|
+
schema: undefined,
|
|
983
|
+
errors: [{
|
|
984
|
+
path: [],
|
|
985
|
+
type: 'undefined',
|
|
986
|
+
message: 'Schema is undefined'
|
|
987
|
+
}]
|
|
988
|
+
};
|
|
989
|
+
const invalidVersionResult = {
|
|
990
|
+
valid: false,
|
|
991
|
+
schema: undefined,
|
|
992
|
+
errors: [{
|
|
993
|
+
path: [],
|
|
994
|
+
type: 'invalidVersion',
|
|
995
|
+
message: 'Unknown schema version'
|
|
996
|
+
}]
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
function normalizeSchemaVersion(schema) {
|
|
1000
|
+
return schema.schemaVersion ?? '1';
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
export function validateSchemaSyntax(obj, options = {}) {
|
|
1004
|
+
if (isUndefined(obj)) {
|
|
1005
|
+
return schemaUndefinedResult;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const schema = obj;
|
|
1009
|
+
const schemaVersion = normalizeSchemaVersion(schema);
|
|
1010
|
+
const validator = findValidator(schemaVersion);
|
|
1011
|
+
|
|
1012
|
+
if (!validator) {
|
|
1013
|
+
return invalidVersionResult;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const structuralValidation = validateStructure(schemaVersion, options, obj);
|
|
1017
|
+
|
|
1018
|
+
if (!structuralValidation.valid) {
|
|
1019
|
+
return structuralValidation;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return validator.validateSyntax(options, schema);
|
|
1023
|
+
}
|
|
898
1024
|
/**
|
|
899
1025
|
* Validates a schema using a matching validation based on the `schemaVersion` property.
|
|
900
1026
|
*/
|
|
901
1027
|
|
|
902
|
-
|
|
903
|
-
export function validateSchema(obj, options) {
|
|
1028
|
+
export async function validateSchema(context, obj) {
|
|
904
1029
|
if (isUndefined(obj)) {
|
|
905
|
-
return
|
|
906
|
-
valid: false,
|
|
907
|
-
schema: undefined,
|
|
908
|
-
errors: [{
|
|
909
|
-
path: [],
|
|
910
|
-
type: 'undefined',
|
|
911
|
-
message: 'Schema is undefined'
|
|
912
|
-
}]
|
|
913
|
-
};
|
|
1030
|
+
return schemaUndefinedResult;
|
|
914
1031
|
}
|
|
915
1032
|
|
|
1033
|
+
const contextWithDefaults = {
|
|
1034
|
+
allowDisconnectedLayers: true,
|
|
1035
|
+
...context
|
|
1036
|
+
};
|
|
916
1037
|
const schema = obj;
|
|
917
|
-
const
|
|
1038
|
+
const schemaVersion = normalizeSchemaVersion(schema);
|
|
1039
|
+
const validator = findValidator(schemaVersion);
|
|
918
1040
|
|
|
919
1041
|
if (!validator) {
|
|
920
|
-
return
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1042
|
+
return invalidVersionResult;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
const structuralValidation = validateStructure(schemaVersion, contextWithDefaults, obj);
|
|
1046
|
+
|
|
1047
|
+
if (!structuralValidation.valid) {
|
|
1048
|
+
return structuralValidation;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const syntaxValidation = validator.validateSyntax(contextWithDefaults, schema);
|
|
1052
|
+
|
|
1053
|
+
if (!syntaxValidation.valid || !validator.validateReferences || !isValidateReferencesContext(contextWithDefaults)) {
|
|
1054
|
+
return syntaxValidation;
|
|
929
1055
|
}
|
|
930
1056
|
|
|
931
|
-
return validator(
|
|
1057
|
+
return validator.validateReferences(contextWithDefaults, obj);
|
|
932
1058
|
}
|
|
933
|
-
export function
|
|
1059
|
+
export function ensureValidLatestSchemaSyntax(obj) {
|
|
934
1060
|
const {
|
|
935
1061
|
valid,
|
|
936
1062
|
schema,
|
|
937
1063
|
errors
|
|
938
|
-
} =
|
|
1064
|
+
} = validateSchemaSyntax(obj);
|
|
939
1065
|
|
|
940
1066
|
if (!valid && errors) {
|
|
941
1067
|
throw new Error(`Invalid Schema "${errors[0].path.join(',')}": "${errors[0].message}"`);
|
|
@@ -1027,7 +1153,7 @@ export function ensureValidRoleImport(maybeRoles) {
|
|
|
1027
1153
|
* Only use when validating an imported schema! ignore fields optional when importing
|
|
1028
1154
|
*/
|
|
1029
1155
|
export function validateProjectSchemaImport(maybeSchema) {
|
|
1030
|
-
return
|
|
1156
|
+
return validateSchemaSyntax(maybeSchema, {
|
|
1031
1157
|
suppressErrorPaths: [...projectSchemaImportOptionalProps, ...legacyProjectSchemaImportOptionalProps]
|
|
1032
1158
|
});
|
|
1033
1159
|
}
|