@takeshape/schema 8.144.4 → 8.146.0
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/builtin-schema.d.ts.map +1 -1
- package/dist/builtin-schema.js +17 -0
- package/dist/get-is-leaf.d.ts +1 -1
- package/dist/get-is-leaf.d.ts.map +1 -1
- package/dist/get-is-leaf.js +3 -1
- package/dist/migration/index.d.ts +1 -0
- package/dist/migration/index.d.ts.map +1 -1
- package/dist/migration/index.js +4 -1
- package/dist/migration/to/v3.0.0.d.ts.map +1 -1
- package/dist/migration/to/v3.18.0.d.ts +5 -0
- package/dist/migration/to/v3.18.0.d.ts.map +1 -0
- package/dist/migration/to/v3.18.0.js +85 -0
- package/dist/mocks.d.ts.map +1 -1
- package/dist/mocks.js +4 -2
- package/dist/project-schema/index.d.ts +4 -1
- package/dist/project-schema/index.d.ts.map +1 -1
- package/dist/project-schema/index.js +20 -3
- package/dist/project-schema/latest.d.ts +4 -139
- package/dist/project-schema/latest.d.ts.map +1 -1
- package/dist/project-schema/migrate.d.ts.map +1 -1
- package/dist/project-schema/migrate.js +4 -0
- package/dist/project-schema/v3.18.0.d.ts +1334 -0
- package/dist/project-schema/v3.18.0.d.ts.map +1 -0
- package/dist/project-schema/v3.18.0.js +5 -0
- package/dist/refs.d.ts +14 -2
- package/dist/refs.d.ts.map +1 -1
- package/dist/refs.js +26 -0
- package/dist/relationships.d.ts +28 -2
- package/dist/relationships.d.ts.map +1 -1
- package/dist/relationships.js +234 -19
- package/dist/rewrite.d.ts.map +1 -1
- package/dist/rewrite.js +10 -2
- package/dist/schema-util.d.ts +1 -18
- package/dist/schema-util.d.ts.map +1 -1
- package/dist/schema-util.js +36 -120
- package/dist/schemas/index.d.ts +2 -2
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +6 -4
- package/dist/schemas/index.ts +4 -2
- package/dist/schemas/project-schema/latest.json +2175 -2195
- package/dist/schemas/project-schema/v3.18.0.json +2350 -0
- package/dist/schemas/project-schema.json +3 -0
- package/dist/template-shapes/templates.d.ts +5 -0
- package/dist/template-shapes/templates.d.ts.map +1 -1
- package/dist/template-shapes/templates.js +59 -28
- package/dist/template-shapes/where.d.ts +3 -4
- package/dist/template-shapes/where.d.ts.map +1 -1
- package/dist/template-shapes/where.js +32 -26
- package/dist/types/types.d.ts +42 -2
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts +2 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/utils.js +9 -1
- package/dist/unions.d.ts.map +1 -1
- package/dist/unions.js +3 -1
- package/dist/util/detect-cycles.d.ts +3 -1
- package/dist/util/detect-cycles.d.ts.map +1 -1
- package/dist/util/detect-cycles.js +37 -28
- package/dist/util/form-config.d.ts +9 -0
- package/dist/util/form-config.d.ts.map +1 -0
- package/dist/util/form-config.js +97 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +13 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +75 -45
- package/es/builtin-schema.js +17 -0
- package/es/get-is-leaf.js +2 -1
- package/es/migration/index.js +3 -1
- package/es/migration/to/v3.18.0.js +72 -0
- package/es/mocks.js +4 -2
- package/es/project-schema/index.js +3 -1
- package/es/project-schema/migrate.js +5 -1
- package/es/project-schema/v3.18.0.js +1 -0
- package/es/refs.js +22 -1
- package/es/relationships.js +215 -19
- package/es/rewrite.js +10 -2
- package/es/schema-util.js +38 -119
- package/es/schemas/index.js +4 -3
- package/es/schemas/index.ts +4 -2
- package/es/schemas/project-schema/latest.json +2175 -2195
- package/es/schemas/project-schema/v3.18.0.json +2350 -0
- package/es/schemas/project-schema.json +3 -0
- package/es/template-shapes/templates.js +55 -29
- package/es/template-shapes/where.js +29 -23
- package/es/types/utils.js +8 -2
- package/es/unions.js +2 -1
- package/es/util/detect-cycles.js +36 -28
- package/es/util/form-config.js +85 -0
- package/es/util/index.js +2 -1
- package/es/validate.js +74 -46
- package/examples/latest/betzino.json +12383 -6066
- package/examples/latest/blog-schema.json +46 -25
- package/examples/latest/brewery-schema.json +14 -9
- package/examples/latest/complex-project-schema.json +442 -244
- package/examples/latest/complex-schema.json +17205 -0
- package/examples/latest/fabric-ecommerce.json +2 -2
- package/examples/latest/frank-and-fred-schema.json +5141 -2391
- package/examples/latest/klirr-schema.json +35445 -0
- package/examples/latest/massive-schema.json +1205 -640
- package/examples/latest/mill-components-schema.json +241 -113
- package/examples/latest/one-earth.json +14429 -0
- package/examples/latest/pet-oneof-array.json +2 -2
- package/examples/latest/post-schema.json +18 -11
- package/examples/latest/pruned-shopify-product-schema.json +2 -2
- package/examples/latest/real-world-schema.json +81 -41
- package/examples/latest/recursive-repeater-schema.json +14 -9
- package/examples/latest/recursive-schema.json +14 -9
- package/examples/latest/rick-and-morty-ast.json +138 -80
- package/examples/latest/rick-and-morty-graphql.json +78 -45
- package/examples/latest/rick-and-morty-rest.json +2 -2
- package/examples/latest/schema-with-repeater-draftjs.json +38 -23
- package/examples/latest/shape-books-v3_2_0.json +138 -80
- package/examples/latest/shape-books.json +138 -80
- package/examples/latest/shopify-lookbook.json +30 -16
- package/examples/latest/shopify-namespace-schema.json +364 -0
- package/examples/latest/shopify-store-with-widget.json +2 -2
- package/examples/latest/stripe-starter-resolved.json +14 -8
- package/examples/latest/user-schema-no-required.json +2 -2
- package/examples/latest/user-schema-with-defaults.json +2 -2
- package/examples/source/complex-schema.json +12760 -0
- package/examples/source/klirr-schema.json +27716 -0
- package/examples/source/one-earth.json +11897 -0
- package/examples/source/post-schema.json +0 -1
- package/package.json +5 -4
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { TemplateVerbs } from './types';
|
|
2
2
|
import { listTypePrefix } from '../migration';
|
|
3
|
-
import { pascalCase } from '@takeshape/util';
|
|
3
|
+
import { deepClone, pascalCase, visit } from '@takeshape/util';
|
|
4
4
|
import keyBy from 'lodash/keyBy';
|
|
5
5
|
import { createShape, getShapeDependencies, isIndexedRemoteShape } from '../schema-util';
|
|
6
6
|
import { getFlattenedTemplateShapeName, getRefShapeName } from '../refs';
|
|
7
7
|
import { getWhereSearchArg } from './where';
|
|
8
8
|
import { mergeObjectSchemas, mergeSchemaProperties } from '../util';
|
|
9
9
|
import { rewriteSchema } from '../rewrite';
|
|
10
|
+
import get from 'lodash/get';
|
|
10
11
|
export const TSGetArgs = getIDQueryArgs('TSGetArgs');
|
|
11
12
|
export const TSGetSingletonArgs = getIDQueryArgs('TSGetSingletonArgs');
|
|
12
13
|
export const TSListArgs = getShapeListQueryArgs('TSListArgs', true);
|
|
@@ -188,14 +189,39 @@ export function getMutationInputShapeName(verb) {
|
|
|
188
189
|
return pascalCase([verb, shapeName, 'input']);
|
|
189
190
|
};
|
|
190
191
|
}
|
|
191
|
-
|
|
192
192
|
/**
|
|
193
|
-
* Create a shape name variant for an input.
|
|
193
|
+
* Create a shape name variant for an input.
|
|
194
194
|
*/
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
|
|
196
|
+
export function getInputShapeName(shapeName) {
|
|
197
|
+
return shapeName === 'TSRelationship' ? shapeName : `${shapeName}Input`;
|
|
198
|
+
}
|
|
199
|
+
export function getOutputShapeName(name) {
|
|
200
|
+
return name.replace(/(?:Input|PartialInput)$/, '');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function rewriteRefs(shapeSchema, rewriteShapeName) {
|
|
204
|
+
const newSchema = deepClone(shapeSchema);
|
|
205
|
+
visit(newSchema, ['$ref', '@ref'], (_, path) => {
|
|
206
|
+
const parentPath = path.slice(0, -1);
|
|
207
|
+
const value = get(newSchema, parentPath);
|
|
208
|
+
|
|
209
|
+
if ((value['@ref'] || value.$ref) && !parentPath.includes('@input') && !parentPath.includes('@output')) {
|
|
210
|
+
if (value['@ref']) {
|
|
211
|
+
const ref = value['@ref'];
|
|
212
|
+
const parts = ref.split(':');
|
|
213
|
+
value['@ref'] = `local:${rewriteShapeName(parts[1])}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (value.$ref) {
|
|
217
|
+
const ref = value.$ref;
|
|
218
|
+
const parts = ref.split('/');
|
|
219
|
+
parts[2] = rewriteShapeName(parts[2]);
|
|
220
|
+
value.$ref = parts.join('/');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return newSchema;
|
|
199
225
|
}
|
|
200
226
|
/**
|
|
201
227
|
* Construct shapes for a mutation inputs, given source shape info.
|
|
@@ -213,14 +239,19 @@ export function getMutationArgs(templateName, verb) {
|
|
|
213
239
|
const inputName = getShapeName(shape.name);
|
|
214
240
|
const isSingleton = shape.model && shape.model.type === 'single';
|
|
215
241
|
const shouldRequireId = !isSingleton && (verb === TemplateVerbs.Update || verb === TemplateVerbs.Delete || verb === TemplateVerbs.Duplicate);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
242
|
+
let rewrittenSchema;
|
|
243
|
+
let inputShapeSchema;
|
|
244
|
+
|
|
245
|
+
if (verb !== TemplateVerbs.Delete) {
|
|
246
|
+
rewrittenSchema = rewriteSchema(shape.schema, {
|
|
247
|
+
removeDefault: isUpdate,
|
|
248
|
+
removeRequired: isUpdate,
|
|
249
|
+
replaceInput: true
|
|
250
|
+
});
|
|
251
|
+
inputShapeSchema = rewriteRefs(rewrittenSchema, getInputShapeName);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const inputShape = createShape(inputName, mergeObjectSchemas(shouldRequireId ? idSchema : undefined, inputShapeSchema), {
|
|
224
255
|
description: `${verb} ${shape.name} input`
|
|
225
256
|
});
|
|
226
257
|
const argsShapeName = getFlattenedTemplateShapeName(shape.name, templateName);
|
|
@@ -241,30 +272,25 @@ export function getMutationArgs(templateName, verb) {
|
|
|
241
272
|
dependencies: {
|
|
242
273
|
[inputName]: inputShape,
|
|
243
274
|
[argsShapeName]: argsShape,
|
|
244
|
-
...(verb !== TemplateVerbs.Delete && keyBy(getShapeDependencies(projectSchema, shape
|
|
245
|
-
|
|
275
|
+
...(verb !== TemplateVerbs.Delete && keyBy(getShapeDependencies(projectSchema, // get the shape dependencies of the shape with the @input swapped
|
|
276
|
+
createShape(inputShape.name, rewrittenSchema), propSchema => {
|
|
277
|
+
if (propSchema['@resolver']) {
|
|
246
278
|
return false;
|
|
247
279
|
}
|
|
248
280
|
|
|
249
281
|
const shapeName = getRefShapeName(projectSchema, propSchema);
|
|
250
282
|
|
|
251
283
|
if (shapeName) {
|
|
252
|
-
return shapeName !== 'TSRelationship' && !projectSchema.shapes[getInputShapeName(shapeName
|
|
253
|
-
isPartial: isUpdate
|
|
254
|
-
})];
|
|
284
|
+
return shapeName !== 'TSRelationship' && !projectSchema.shapes[getInputShapeName(shapeName)];
|
|
255
285
|
}
|
|
256
286
|
|
|
257
287
|
return true;
|
|
258
288
|
}).map(shapeName => {
|
|
259
|
-
const shape = projectSchema.shapes[shapeName];
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
name,
|
|
265
|
-
id: name,
|
|
266
|
-
schema: rewrittenSchema
|
|
267
|
-
};
|
|
289
|
+
const shape = projectSchema.shapes[shapeName]; // We only allow partial input at the top-level
|
|
290
|
+
|
|
291
|
+
return createShape(getInputShapeName(shapeName), rewriteRefs(rewriteSchema(shape.schema, {
|
|
292
|
+
replaceInput: true
|
|
293
|
+
}), getInputShapeName));
|
|
268
294
|
}), 'name')),
|
|
269
295
|
...(addStructure ? {
|
|
270
296
|
ContentStructureInput
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { pascalCase, isDefined } from '@takeshape/util';
|
|
2
1
|
import unset from 'lodash/unset';
|
|
3
2
|
import isFunction from 'lodash/isFunction';
|
|
4
|
-
import { createSchemaPropertyList, createShape, getAllRefsInShapes,
|
|
3
|
+
import { createSchemaPropertyList, createShape, getAllRefsInShapes, isIndexedRemoteShape } from '../schema-util';
|
|
4
|
+
import uniqBy from 'lodash/uniqBy';
|
|
5
|
+
import md5 from 'blueimp-md5';
|
|
6
|
+
import { pascalCase, isDefined } from '@takeshape/util';
|
|
5
7
|
import { workflowsEnabled } from '../api-version';
|
|
6
8
|
import { followRef, getRef, getRefOrItemsRef, hasRefProperty, hasResolvableRef, refItemToShape, refItemToShapeName } from '../refs';
|
|
7
9
|
import { enumerateOneOfKeys, isUnionSchema } from '../unions';
|
|
8
|
-
import { isObjectSchema } from '../types';
|
|
9
|
-
import
|
|
10
|
-
import md5 from 'blueimp-md5';
|
|
11
|
-
import { isEqualRelationship } from '../relationships';
|
|
10
|
+
import { isObjectSchema, isPropertySchemaWithRelationship } from '../types/utils';
|
|
11
|
+
import { isEqualRelationship, getRelationship, getRelationshipShapes } from '../relationships';
|
|
12
12
|
export const MAX_RELATIONSHIP_DEPTH = 1;
|
|
13
13
|
export const exceededRelationshipDepth = (depth = 0) => depth >= MAX_RELATIONSHIP_DEPTH;
|
|
14
14
|
|
|
@@ -159,7 +159,7 @@ function isId(name) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
function isNonRelationshipRef(prop) {
|
|
162
|
-
return Boolean(hasRefProperty(prop) && !prop
|
|
162
|
+
return Boolean(hasRefProperty(prop) && !getRelationship(prop));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
function getPropType(projectSchema, name, prop) {
|
|
@@ -200,20 +200,17 @@ function truncateNames(names) {
|
|
|
200
200
|
// For unions of many types hash the names this reduces shape name size while still allowing reuse
|
|
201
201
|
return names.length > 80 ? md5(names) : names;
|
|
202
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Get a formatted string of related shape names from a property schema with a
|
|
205
|
+
* relationship resolver
|
|
206
|
+
*/
|
|
203
207
|
|
|
204
|
-
function getRelatedShapeNames(projectSchema, prop) {
|
|
205
|
-
var _prop$Relationship;
|
|
206
208
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
function getRelationshipShapeName(refs) {
|
|
210
|
+
return refs.map(ref => {
|
|
211
|
+
const shapeName = ref.split(':')[1];
|
|
212
|
+
return pascalCase(shapeName);
|
|
210
213
|
}).sort().join('');
|
|
211
|
-
|
|
212
|
-
if (!names) {
|
|
213
|
-
throw new Error(`Relationship property ${prop.title} is missing related shapes`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return names;
|
|
217
214
|
}
|
|
218
215
|
|
|
219
216
|
function joinShapeNames(shapes) {
|
|
@@ -232,8 +229,16 @@ function getTypeName(name, prop, shapeName, context) {
|
|
|
232
229
|
return `TSWhereID`;
|
|
233
230
|
}
|
|
234
231
|
|
|
235
|
-
|
|
236
|
-
|
|
232
|
+
const relationship = getRelationship(prop);
|
|
233
|
+
|
|
234
|
+
if (relationship) {
|
|
235
|
+
const relationshipName = getRelationshipShapeName(relationship.refs);
|
|
236
|
+
|
|
237
|
+
if (!relationshipName) {
|
|
238
|
+
throw new Error(`Relationship property "${prop.title ?? ''}" is missing related shapes`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const shapeNames = truncateNames(relationshipName);
|
|
237
242
|
return `TSWhere${shapeNames}Relationship`;
|
|
238
243
|
}
|
|
239
244
|
|
|
@@ -265,7 +270,7 @@ function skipField({
|
|
|
265
270
|
exceededRelationshipDepth,
|
|
266
271
|
isIndexedShape
|
|
267
272
|
}) {
|
|
268
|
-
return Boolean(prop['@sensitive'] || !isIndexedShape && prop['@resolver'] || prop
|
|
273
|
+
return Boolean(prop['@sensitive'] || !isIndexedShape && prop['@resolver'] || isPropertySchemaWithRelationship(prop) && exceededRelationshipDepth || (name === '_enabled' || name === '_enabledAt') && workflowsEnabled);
|
|
269
274
|
}
|
|
270
275
|
|
|
271
276
|
export function getFields(typeName, shapes, context) {
|
|
@@ -355,11 +360,12 @@ export function getPropertyComparisonType(name, prop, shapeName, context) {
|
|
|
355
360
|
|
|
356
361
|
let props;
|
|
357
362
|
const propOrItems = prop.items ?? prop;
|
|
363
|
+
const relationship = getRelationship(prop);
|
|
358
364
|
|
|
359
365
|
if (isId(name)) {
|
|
360
366
|
props = getFieldTypeComparison('id');
|
|
361
|
-
} else if (
|
|
362
|
-
const shapes =
|
|
367
|
+
} else if (relationship) {
|
|
368
|
+
const shapes = getRelationshipShapes(projectSchema.shapes, relationship.refs);
|
|
363
369
|
props = getFields(typeName, shapes, { ...context,
|
|
364
370
|
relationshipDepth: relationshipDepth + 1
|
|
365
371
|
});
|
package/es/types/utils.js
CHANGED
|
@@ -7,12 +7,12 @@ import isArray from 'lodash/isArray';
|
|
|
7
7
|
import has from 'lodash/has';
|
|
8
8
|
import { latestSchemaJson } from '../schemas';
|
|
9
9
|
import satisfies from 'semver/functions/satisfies';
|
|
10
|
+
|
|
10
11
|
/** Resolver Type Utils **/
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Only tests that the shape is right, not that the name is correct. That's a job for the validator.
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
16
|
export function isBasicResolver(resolver) {
|
|
17
17
|
return isString(resolver === null || resolver === void 0 ? void 0 : resolver.name) && isString(resolver.service);
|
|
18
18
|
}
|
|
@@ -263,4 +263,10 @@ export const getRefType = refSchema => {
|
|
|
263
263
|
if (has(refSchema, '$ref')) {
|
|
264
264
|
return RefType.$ref;
|
|
265
265
|
}
|
|
266
|
-
};
|
|
266
|
+
};
|
|
267
|
+
export function isPropertySchemaWithRelationship(schema) {
|
|
268
|
+
var _schema$Resolver;
|
|
269
|
+
|
|
270
|
+
schema = schema['@output'] ?? schema;
|
|
271
|
+
return ((_schema$Resolver = schema['@resolver']) === null || _schema$Resolver === void 0 ? void 0 : _schema$Resolver.name) === 'takeshape:getRelated';
|
|
272
|
+
}
|
package/es/unions.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getContentTransform, toName } from './content-schema-transform';
|
|
|
5
5
|
import { isRefSchema } from './types/utils';
|
|
6
6
|
import { BadDataError } from '@takeshape/errors';
|
|
7
7
|
import { getShapeById } from './schema-util';
|
|
8
|
+
import { getOutputShapeName } from './template-shapes/templates';
|
|
8
9
|
export function enumerateOneOfKeys(projectSchema, oneOf) {
|
|
9
10
|
const result = [];
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ export function enumerateOneOfKeys(projectSchema, oneOf) {
|
|
|
17
18
|
if (projectSchema.shapes[shapeName]) {
|
|
18
19
|
const shapeId = projectSchema.shapes[shapeName].id;
|
|
19
20
|
result.push({
|
|
20
|
-
propName: camelCase(refItem.typeName),
|
|
21
|
+
propName: getOutputShapeName(camelCase(refItem.typeName)),
|
|
21
22
|
propSchema: child,
|
|
22
23
|
shapeName,
|
|
23
24
|
shapeId
|
package/es/util/detect-cycles.js
CHANGED
|
@@ -1,52 +1,60 @@
|
|
|
1
1
|
import { getRef, refItemToShape } from '../refs';
|
|
2
|
+
import { value } from '@takeshape/util';
|
|
2
3
|
|
|
3
4
|
function immutableSetAdd(set, str) {
|
|
4
5
|
const result = new Set(set);
|
|
5
6
|
result.add(str);
|
|
6
7
|
return result;
|
|
7
8
|
}
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Use a basic depth-first search to determine whether a schema contains a cycle
|
|
10
12
|
*/
|
|
13
|
+
export function hasCycle(projectSchema, schema, predicate = value(true)) {
|
|
14
|
+
const hasCycleHelper = (schema, shapesSeen = new Set(), shapeName, propName) => {
|
|
15
|
+
if (!predicate(projectSchema, schema, shapeName, propName)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
11
18
|
|
|
19
|
+
const refItem = getRef(projectSchema, schema);
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
if (refItem) {
|
|
22
|
+
const refShape = refItemToShape(projectSchema, refItem); // If we have a refItem, but no shape, this is a dangling ref and has no cycles
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
if (!refShape) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
if (shapesSeen.has(refShape.name)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
return true;
|
|
32
|
+
return hasCycleHelper(refShape.schema, immutableSetAdd(shapesSeen, refShape.name), refShape.name);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
}
|
|
35
|
+
const combo = schema.oneOf ?? schema.allOf ?? schema.extends;
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
if (combo) {
|
|
38
|
+
return combo.some(childSchema => hasCycleHelper(childSchema, shapesSeen, shapeName, propName));
|
|
39
|
+
}
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
if (schema.items) {
|
|
42
|
+
return hasCycleHelper(schema.items, shapesSeen, shapeName, propName);
|
|
43
|
+
}
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
const {
|
|
46
|
+
properties
|
|
47
|
+
} = schema;
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
if (properties) {
|
|
50
|
+
return Object.keys(properties).some(name => {
|
|
51
|
+
const propSchema = properties[name];
|
|
52
|
+
return hasCycleHelper(propSchema, shapesSeen, shapeName, name);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const propSchema = properties[name];
|
|
47
|
-
return hasCycle(projectSchema, propSchema, shapesSeen);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
50
58
|
|
|
51
|
-
return
|
|
59
|
+
return hasCycleHelper(schema);
|
|
52
60
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { deepClone } from '@takeshape/util';
|
|
2
|
+
import { getRef, refItemToShapeName } from '../refs';
|
|
3
|
+
import { createSchemaPropertyAccessor } from '../schema-util';
|
|
4
|
+
export function normalizeForms(projectSchema) {
|
|
5
|
+
const normalizedForms = {};
|
|
6
|
+
const {
|
|
7
|
+
forms = {}
|
|
8
|
+
} = projectSchema;
|
|
9
|
+
|
|
10
|
+
const storeSourceForm = (shapeName, formConfig, propName) => {
|
|
11
|
+
// Leave original forms intact for backwards compatibility
|
|
12
|
+
if (!forms[shapeName]) {
|
|
13
|
+
if (!normalizedForms[shapeName]) {
|
|
14
|
+
normalizedForms[shapeName] = {
|
|
15
|
+
default: deepClone(formConfig)
|
|
16
|
+
};
|
|
17
|
+
} // Remove prop now that it has been normalized
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
delete normalizedForms[shapeName].default.properties[propName];
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const normalizeNested = (shapeName, formConfig) => {
|
|
26
|
+
const shape = projectSchema.shapes[shapeName];
|
|
27
|
+
|
|
28
|
+
if (!shape || !formConfig.properties) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const propertyAccessor = createSchemaPropertyAccessor(projectSchema, shape);
|
|
33
|
+
|
|
34
|
+
for (const propName of Object.keys(formConfig.properties)) {
|
|
35
|
+
const propConfig = formConfig.properties[propName];
|
|
36
|
+
const propSchema = propertyAccessor.getValue(propName);
|
|
37
|
+
|
|
38
|
+
if (propSchema) {
|
|
39
|
+
const ref = getRef(projectSchema, propSchema);
|
|
40
|
+
|
|
41
|
+
if (ref && propConfig.properties) {
|
|
42
|
+
const nestedShapeName = refItemToShapeName(ref);
|
|
43
|
+
normalizedForms[nestedShapeName] = {
|
|
44
|
+
default: deepClone(propConfig)
|
|
45
|
+
};
|
|
46
|
+
storeSourceForm(shapeName, formConfig, propName);
|
|
47
|
+
normalizeNested(nestedShapeName, propConfig);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (forms) {
|
|
54
|
+
for (const shapeName of Object.keys(forms)) {
|
|
55
|
+
normalizeNested(shapeName, forms[shapeName].default); // copy forms with no changes
|
|
56
|
+
|
|
57
|
+
if (!normalizedForms[shapeName]) {
|
|
58
|
+
normalizedForms[shapeName] = forms[shapeName];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return normalizedForms;
|
|
64
|
+
}
|
|
65
|
+
const cache = new WeakMap();
|
|
66
|
+
/**
|
|
67
|
+
* Find the form config for a given shape name and the form key.
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
export function findShapeFormConfig(projectSchema, shapeName, formName) {
|
|
71
|
+
var _normalizedForms$shap;
|
|
72
|
+
|
|
73
|
+
if (!projectSchema.forms) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let normalizedForms = cache.get(projectSchema);
|
|
78
|
+
|
|
79
|
+
if (!normalizedForms) {
|
|
80
|
+
normalizedForms = normalizeForms(projectSchema);
|
|
81
|
+
cache.set(projectSchema, normalizedForms);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (_normalizedForms$shap = normalizedForms[shapeName]) === null || _normalizedForms$shap === void 0 ? void 0 : _normalizedForms$shap[formName];
|
|
85
|
+
}
|
package/es/util/index.js
CHANGED
package/es/validate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { projectSchemaImportOptionalProps } from './types/types';
|
|
2
|
+
import { JSONPath } from 'jsonpath-plus';
|
|
2
3
|
import { createAjv } from '@takeshape/json-schema';
|
|
3
4
|
import { SchemaValidationError } from '@takeshape/errors';
|
|
4
5
|
import coerce from 'semver/functions/coerce';
|
|
@@ -11,7 +12,7 @@ import { CURRENT_SCHEMA_VERSION, LEGACY_SCHEMA_VERSION, LEGACY_API_VERSION } fro
|
|
|
11
12
|
import { defaultWorkflow } from './workflows';
|
|
12
13
|
import { allProjectSchemas } from './schemas';
|
|
13
14
|
import authSchemas from './schemas/auth-schemas.json';
|
|
14
|
-
import { getAllNamespaceShapes,
|
|
15
|
+
import { getAllNamespaceShapes, getAllRefsInShapes } from './schema-util';
|
|
15
16
|
import { builtInShapes } from './builtin-schema';
|
|
16
17
|
import { isValidTemplate } from './template-shapes';
|
|
17
18
|
import { isBasicResolver, isComposeResolver } from './types/utils';
|
|
@@ -22,6 +23,7 @@ import { isEnumLikeSchema } from './enum';
|
|
|
22
23
|
import metaSchemaV3_9_0 from './schemas/project-schema/meta-schema-v3.9.0.json';
|
|
23
24
|
import { isIntegerLike } from '@takeshape/util';
|
|
24
25
|
import forOwn from 'lodash/forOwn';
|
|
26
|
+
import { getRelationship } from './relationships';
|
|
25
27
|
|
|
26
28
|
function findDuplicates(items) {
|
|
27
29
|
const seen = {};
|
|
@@ -172,6 +174,15 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
172
174
|
return [takeshapeResolver, utilResolver, graphqlResolver, restResolver, awsLambdaResolver].flatMap(resolver => resolver.properties.name.enum).includes(resolverName);
|
|
173
175
|
};
|
|
174
176
|
|
|
177
|
+
const isValidShapeName = shapeName => {
|
|
178
|
+
var _indexedShapes;
|
|
179
|
+
|
|
180
|
+
const modelShape = shapeName ? projectSchema.shapes[shapeName] : undefined;
|
|
181
|
+
return Boolean((modelShape === null || modelShape === void 0 ? void 0 : modelShape.model) || shapeName && ((_indexedShapes = projectSchema.indexedShapes) === null || _indexedShapes === void 0 ? void 0 : _indexedShapes[shapeName]));
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const isLessThanV3_9_0 = lt(coerce(projectSchema.schemaVersion) ?? LEGACY_SCHEMA_VERSION, '3.9.0');
|
|
185
|
+
|
|
175
186
|
const validateBasicResolver = resolver => {
|
|
176
187
|
if (isBasicResolver(resolver)) {
|
|
177
188
|
var _projectSchema$servic;
|
|
@@ -184,35 +195,23 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
184
195
|
});
|
|
185
196
|
}
|
|
186
197
|
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
194
|
-
}
|
|
198
|
+
if (isLessThanV3_9_0 && !isValidResolverNameV3_9_0(resolver.name)) {
|
|
199
|
+
errors.push({
|
|
200
|
+
type: 'notFound',
|
|
201
|
+
path: basePath.concat(['name']),
|
|
202
|
+
message: `Invalid resolver name "${resolver.name}"`
|
|
203
|
+
});
|
|
195
204
|
}
|
|
196
205
|
|
|
197
|
-
if (resolver.name.startsWith('takeshape')) {
|
|
206
|
+
if (isLessThanV3_9_0 && resolver.name.startsWith('takeshape')) {
|
|
198
207
|
var _options;
|
|
199
208
|
|
|
200
|
-
let shapeName;
|
|
201
|
-
|
|
202
209
|
if ((_options = resolver.options) !== null && _options !== void 0 && _options.model) {
|
|
203
210
|
var _options2;
|
|
204
211
|
|
|
205
|
-
shapeName = (_options2 = resolver.options) === null || _options2 === void 0 ? void 0 : _options2.model;
|
|
206
|
-
} else if (resolver.shapeName) {
|
|
207
|
-
shapeName = resolver.shapeName;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (shapeName) {
|
|
211
|
-
var _indexedShapes;
|
|
212
|
+
const shapeName = (_options2 = resolver.options) === null || _options2 === void 0 ? void 0 : _options2.model;
|
|
212
213
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (!(modelShape !== null && modelShape !== void 0 && modelShape.model) && !(shapeName && (_indexedShapes = projectSchema.indexedShapes) !== null && _indexedShapes !== void 0 && _indexedShapes[shapeName])) {
|
|
214
|
+
if (!isValidShapeName(shapeName)) {
|
|
216
215
|
errors.push({
|
|
217
216
|
type: 'notFound',
|
|
218
217
|
path: basePath.concat(['options', 'model']),
|
|
@@ -221,6 +220,20 @@ function validateResolver(projectSchema, basePath, resolver) {
|
|
|
221
220
|
}
|
|
222
221
|
}
|
|
223
222
|
}
|
|
223
|
+
|
|
224
|
+
if (resolver.shapeName) {
|
|
225
|
+
const {
|
|
226
|
+
shapeName
|
|
227
|
+
} = resolver;
|
|
228
|
+
|
|
229
|
+
if (!isValidShapeName(shapeName)) {
|
|
230
|
+
errors.push({
|
|
231
|
+
type: 'notFound',
|
|
232
|
+
path: basePath.concat(['shapeName']),
|
|
233
|
+
message: `Invalid Model Shape "${shapeName ?? ''}"`
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
224
237
|
} else {
|
|
225
238
|
errors.push({
|
|
226
239
|
type: 'notFound',
|
|
@@ -366,29 +379,43 @@ function validateRefs(projectSchema, additionalShapeNames = builtInShapeNames) {
|
|
|
366
379
|
return errors;
|
|
367
380
|
}
|
|
368
381
|
|
|
382
|
+
function visitShapePropertiesJson(projectSchema, callback) {
|
|
383
|
+
JSONPath('shapes.*.schema.properties.*', projectSchema, (property, _type, {
|
|
384
|
+
path: propertyPath
|
|
385
|
+
}) => {
|
|
386
|
+
callback(property, propertyPath.slice(3, -2).split(`']['`));
|
|
387
|
+
}, undefined);
|
|
388
|
+
}
|
|
389
|
+
|
|
369
390
|
function validateDirectives(projectSchema, additionalShapeIds = builtInModelShapeIds) {
|
|
370
391
|
const errors = [];
|
|
371
392
|
const projectShapeIds = new Set([...getModelShapeIds(projectSchema.shapes), ...additionalShapeIds]);
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
393
|
+
const isLessThanV3_18_0 = lt(coerce(projectSchema.schemaVersion) ?? LEGACY_SCHEMA_VERSION, '3.18.0');
|
|
394
|
+
visitShapePropertiesJson(projectSchema, (property, propertyPath) => {
|
|
395
|
+
if (isLessThanV3_18_0) {
|
|
396
|
+
const propSchema = property;
|
|
397
|
+
|
|
398
|
+
if (propSchema['@relationship']) {
|
|
399
|
+
var _propSchema$Relation;
|
|
400
|
+
|
|
401
|
+
const shapeIds = (_propSchema$Relation = propSchema['@relationship']) === null || _propSchema$Relation === void 0 ? void 0 : _propSchema$Relation.shapeIds;
|
|
402
|
+
shapeIds.forEach(shapeId => {
|
|
403
|
+
if (!projectShapeIds.has(shapeId)) {
|
|
404
|
+
const propPath = propertyPath.concat('@relationship');
|
|
405
|
+
errors.push({
|
|
406
|
+
type: 'notFound',
|
|
407
|
+
path: propPath,
|
|
408
|
+
message: `Invalid shapeId relationship "${shapeId}" for property "${propPath[4]}" in shape "${propPath[1]}"`
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
387
413
|
}
|
|
388
414
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
415
|
+
const resolver = property['@resolver'];
|
|
416
|
+
|
|
417
|
+
if (resolver) {
|
|
418
|
+
const propPath = propertyPath.concat('@resolver');
|
|
392
419
|
const resolverErrors = validateResolver(projectSchema, propPath, resolver);
|
|
393
420
|
|
|
394
421
|
if (resolverErrors) {
|
|
@@ -401,11 +428,12 @@ function validateDirectives(projectSchema, additionalShapeIds = builtInModelShap
|
|
|
401
428
|
|
|
402
429
|
function validateOneOfs(projectSchema) {
|
|
403
430
|
const errors = [];
|
|
404
|
-
|
|
405
|
-
const oneOfPath =
|
|
406
|
-
|
|
431
|
+
visitShapePropertiesJson(projectSchema, (property, propertyPath) => {
|
|
432
|
+
const oneOfPath = property.items ? ['items', 'oneOf'] : ['oneOf'];
|
|
433
|
+
const relationship = getRelationship(property);
|
|
434
|
+
const schema = property.items ? property.items : property;
|
|
407
435
|
|
|
408
|
-
if (schema.oneOf) {
|
|
436
|
+
if (schema.oneOf && !relationship) {
|
|
409
437
|
if (isUnionSchema(schema)) {
|
|
410
438
|
const modelsReferenced = enumerateOneOfKeys(projectSchema, schema.oneOf).map(child => child.shapeName).filter(shapeName => projectSchema.shapes[shapeName].model);
|
|
411
439
|
|
|
@@ -413,14 +441,14 @@ function validateOneOfs(projectSchema) {
|
|
|
413
441
|
const shapeNames = modelsReferenced.join(', ');
|
|
414
442
|
errors.push({
|
|
415
443
|
type: 'json',
|
|
416
|
-
path:
|
|
444
|
+
path: propertyPath.concat(oneOfPath),
|
|
417
445
|
message: `Invalid refs to ${shapeNames}. oneOf unions cannot reference model shapes. Use @relationship instead`
|
|
418
446
|
});
|
|
419
447
|
}
|
|
420
448
|
} else if (!isEnumLikeSchema(schema)) {
|
|
421
449
|
errors.push({
|
|
422
450
|
type: 'json',
|
|
423
|
-
path:
|
|
451
|
+
path: propertyPath.concat(oneOfPath),
|
|
424
452
|
message: `Invalid oneOf must contain only @ref or title + enum/const schemas`
|
|
425
453
|
});
|
|
426
454
|
}
|