@takeshape/schema 8.37.0 → 8.38.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/es/flatten-templates.js +26 -0
- package/es/index.js +2 -1
- package/es/layers/layers.js +1 -1
- package/es/layers/refs.js +1 -1
- package/es/refs.js +74 -2
- package/es/schema-util.js +28 -17
- package/es/template-shapes/index.js +35 -0
- package/es/template-shapes/templates.js +379 -0
- package/es/template-shapes/types.js +1 -0
- package/es/template-shapes/where.js +501 -0
- package/es/validate.js +2 -2
- package/lib/flatten-templates.d.ts +3 -0
- package/lib/flatten-templates.d.ts.map +1 -0
- package/lib/flatten-templates.js +39 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +14 -0
- package/lib/layers/layers.js +3 -3
- package/lib/layers/refs.js +2 -2
- package/lib/refs.d.ts +26 -1
- package/lib/refs.d.ts.map +1 -1
- package/lib/refs.js +94 -11
- package/lib/schema-util.d.ts +7 -6
- package/lib/schema-util.d.ts.map +1 -1
- package/lib/schema-util.js +41 -29
- package/lib/template-shapes/index.d.ts +8 -0
- package/lib/template-shapes/index.d.ts.map +1 -0
- package/lib/template-shapes/index.js +46 -0
- package/lib/template-shapes/templates.d.ts +29 -0
- package/lib/template-shapes/templates.d.ts.map +1 -0
- package/lib/template-shapes/templates.js +427 -0
- package/lib/template-shapes/types.d.ts +10 -0
- package/lib/template-shapes/types.d.ts.map +1 -0
- package/lib/template-shapes/types.js +5 -0
- package/lib/template-shapes/where.d.ts +39 -0
- package/lib/template-shapes/where.d.ts.map +1 -0
- package/lib/template-shapes/where.js +534 -0
- package/lib/validate.js +1 -1
- package/package.json +4 -4
- package/es/template-shapes.js +0 -79
- package/lib/template-shapes.d.ts +0 -32
- package/lib/template-shapes.d.ts.map +0 -1
- package/lib/template-shapes.js +0 -101
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
2
|
+
|
|
3
|
+
import { pascalCase, isDefined } from '@takeshape/util';
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
import get from 'lodash/get';
|
|
6
|
+
import unset from 'lodash/unset';
|
|
7
|
+
import isFunction from 'lodash/isFunction';
|
|
8
|
+
import { createSchemaPropertyList, createShape, getAllRefsInShapes, getShapeById, isIndexedRemoteShape } from '../schema-util';
|
|
9
|
+
import { workflowsEnabled } from '../api-version';
|
|
10
|
+
import { followRef, getRefOrItemsRef, hasRefProperty, hasResolvableRef, refItemToShapeName } from '../refs';
|
|
11
|
+
import { enumerateOneOfKeys, isUnionSchema } from '../unions';
|
|
12
|
+
import { isObjectSchema } from '../types';
|
|
13
|
+
export const MAX_RELATIONSHIP_DEPTH = 1;
|
|
14
|
+
export const exceededRelationshipDepth = (depth = 0) => depth >= MAX_RELATIONSHIP_DEPTH;
|
|
15
|
+
|
|
16
|
+
class ShapeCache {
|
|
17
|
+
constructor() {
|
|
18
|
+
_defineProperty(this, "thunks", new Map());
|
|
19
|
+
|
|
20
|
+
_defineProperty(this, "unresolved", new Set());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
has(shapeName) {
|
|
24
|
+
return this.thunks.has(shapeName);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set(shapeName, thunk) {
|
|
28
|
+
this.thunks.set(shapeName, thunk);
|
|
29
|
+
|
|
30
|
+
if (isFunction(thunk)) {
|
|
31
|
+
this.unresolved.add(shapeName);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
resolveAll() {
|
|
36
|
+
while (this.unresolved.size) {
|
|
37
|
+
const shapeName = this.unresolved.values().next().value;
|
|
38
|
+
const thunk = this.thunks.get(shapeName);
|
|
39
|
+
|
|
40
|
+
if (isFunction(thunk)) {
|
|
41
|
+
this.thunks.set(shapeName, thunk());
|
|
42
|
+
this.unresolved.delete(shapeName);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const result = {};
|
|
47
|
+
|
|
48
|
+
for (const [shapeName, shape] of this.thunks) {
|
|
49
|
+
result[shapeName] = shape;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const fieldTypeComparison = {
|
|
58
|
+
id: {
|
|
59
|
+
comparators: ['eq', 'in'],
|
|
60
|
+
type: 'string'
|
|
61
|
+
},
|
|
62
|
+
boolean: {
|
|
63
|
+
comparators: ['eq'],
|
|
64
|
+
type: 'boolean'
|
|
65
|
+
},
|
|
66
|
+
string: {
|
|
67
|
+
comparators: ['eq', 'in', 'match', 'regexp'],
|
|
68
|
+
type: 'string'
|
|
69
|
+
},
|
|
70
|
+
date: {
|
|
71
|
+
comparators: ['eq', 'lt', 'lte', 'gt', 'gte'],
|
|
72
|
+
type: 'string'
|
|
73
|
+
},
|
|
74
|
+
number: {
|
|
75
|
+
comparators: ['eq', 'lt', 'lte', 'gt', 'gte', 'in'],
|
|
76
|
+
type: 'number'
|
|
77
|
+
},
|
|
78
|
+
integer: {
|
|
79
|
+
comparators: ['eq', 'lt', 'lte', 'gt', 'gte', 'in'],
|
|
80
|
+
type: 'integer'
|
|
81
|
+
},
|
|
82
|
+
workflow: {
|
|
83
|
+
comparators: ['eq', 'lt', 'lte', 'gt', 'gte', 'in'],
|
|
84
|
+
type: 'string'
|
|
85
|
+
},
|
|
86
|
+
draftjs: {
|
|
87
|
+
comparators: ['match'],
|
|
88
|
+
type: 'string'
|
|
89
|
+
},
|
|
90
|
+
mdx: {
|
|
91
|
+
comparators: ['match'],
|
|
92
|
+
type: 'string'
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
export const comparatorDescriptions = {
|
|
96
|
+
eq: 'Exact match',
|
|
97
|
+
gt: 'Greater than',
|
|
98
|
+
gte: 'Greater than or equal',
|
|
99
|
+
lt: 'Less than',
|
|
100
|
+
lte: 'Less than or equal',
|
|
101
|
+
in: 'Array of possible exact match values.',
|
|
102
|
+
match: 'Full text searching with fuzzy matching.',
|
|
103
|
+
regexp: 'Regular expression string matching. Use of * wildcards could degrade performance.'
|
|
104
|
+
};
|
|
105
|
+
export const booleanOperators = ['AND', 'OR', 'NOT'];
|
|
106
|
+
export const boolOpDescriptionMap = {
|
|
107
|
+
AND: 'AND takes an array of conditions that must appear in the matching results. Nested boolean operators can be used to create complex filters.',
|
|
108
|
+
OR: 'OR takes an array of conditions that should appear in the matching results. Nested boolean operators can be used to create complex filters.',
|
|
109
|
+
NOT: 'NOT takes a single condition that must not appear in the matching results.'
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const getConflictingProperties = (projectSchema, shapes) => {
|
|
113
|
+
let conflicts = [];
|
|
114
|
+
const allProps = {};
|
|
115
|
+
|
|
116
|
+
for (const shape of Object.values(shapes)) {
|
|
117
|
+
const shapeConflicts = createSchemaPropertyList(projectSchema, shape.schema).filterBy(([name, prop]) => name in allProps && (allProps[name].type !== prop.type || !isEqual(get(allProps, [name, '@relationship', 'shapeIds']), get(prop, ['@relationship', 'shapeIds'])))).getNames();
|
|
118
|
+
conflicts = conflicts.concat(shapeConflicts);
|
|
119
|
+
|
|
120
|
+
if (isObjectSchema(shape.schema)) {
|
|
121
|
+
Object.assign(allProps, shape.schema.properties);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return conflicts;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export function getFieldTypeComparison(fieldType) {
|
|
129
|
+
const comparison = fieldTypeComparison[fieldType];
|
|
130
|
+
|
|
131
|
+
if (!comparison) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const {
|
|
136
|
+
comparators,
|
|
137
|
+
type
|
|
138
|
+
} = comparison;
|
|
139
|
+
const result = {};
|
|
140
|
+
|
|
141
|
+
for (const op of comparators) {
|
|
142
|
+
const description = comparatorDescriptions[op];
|
|
143
|
+
result[op] = op === 'in' ? {
|
|
144
|
+
type: 'array',
|
|
145
|
+
items: {
|
|
146
|
+
type
|
|
147
|
+
},
|
|
148
|
+
description
|
|
149
|
+
} : {
|
|
150
|
+
description,
|
|
151
|
+
type
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isId(name) {
|
|
159
|
+
return name === '_id' || name === '_contentTypeId' || name === '_shapeId';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isNonRelationshipRef(prop) {
|
|
163
|
+
return Boolean(hasRefProperty(prop) && !prop['@relationship']);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getPropType(projectSchema, name, prop) {
|
|
167
|
+
if (prop['@workflow']) {
|
|
168
|
+
return 'workflow';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (prop['@tag'] === 'draftjs') {
|
|
172
|
+
return 'draftjs';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (prop['@tag'] === 'mdx') {
|
|
176
|
+
return 'mdx';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (prop.format === 'date-time') {
|
|
180
|
+
return 'date';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isId(name)) {
|
|
184
|
+
return 'id';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isNonRelationshipRef(prop)) {
|
|
188
|
+
const followed = followRef(projectSchema, prop);
|
|
189
|
+
|
|
190
|
+
if (followed.enum) {
|
|
191
|
+
return 'string';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return 'object';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return prop.type;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getRelatedShapeNames(prop, projectSchema) {
|
|
201
|
+
var _prop$Relationship;
|
|
202
|
+
|
|
203
|
+
const names = (_prop$Relationship = prop['@relationship']) === null || _prop$Relationship === void 0 ? void 0 : _prop$Relationship.shapeIds.map(id => {
|
|
204
|
+
const shape = getShapeById(projectSchema, id);
|
|
205
|
+
return pascalCase(shape ? shape.name : '');
|
|
206
|
+
}).sort().join('');
|
|
207
|
+
|
|
208
|
+
if (!names) {
|
|
209
|
+
throw new Error(`Relationship property ${prop.title} is missing related shapes`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return names;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function joinShapeNames(shapes) {
|
|
216
|
+
return shapes.sort().join('');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function getTypeName(name, prop, shapeName, context) {
|
|
220
|
+
const {
|
|
221
|
+
projectSchema,
|
|
222
|
+
relationshipDepth,
|
|
223
|
+
conflictingProperties
|
|
224
|
+
} = context;
|
|
225
|
+
let type = getPropType(projectSchema, name, prop);
|
|
226
|
+
|
|
227
|
+
if (isId(name)) {
|
|
228
|
+
return `TSWhereID`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (prop['@relationship']) {
|
|
232
|
+
const shapeNames = getRelatedShapeNames(prop, projectSchema);
|
|
233
|
+
return `TSWhere${shapeNames}Relationship`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (isUnionSchema(prop)) {
|
|
237
|
+
if (prop.oneOf.length > 1) {
|
|
238
|
+
const shapeNames = joinShapeNames(enumerateOneOfKeys(projectSchema, prop.oneOf).map(child => child.shapeName));
|
|
239
|
+
return `TSWhere${shapeNames}Union`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type = 'object';
|
|
243
|
+
prop = prop.oneOf[0];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (type === 'object' || type === 'array') {
|
|
247
|
+
const prefix = `TS${relationshipDepth >= MAX_RELATIONSHIP_DEPTH ? 'Shallow' : ''}Where`;
|
|
248
|
+
const fieldName = `${conflictingProperties.includes(name) ? '_' : ''}${pascalCase(name)}`;
|
|
249
|
+
const refItem = getRefOrItemsRef(projectSchema, prop);
|
|
250
|
+
const suffix = refItem ? refItemToShapeName(refItem) : `${shapeName}${fieldName}`;
|
|
251
|
+
return `${prefix}${suffix}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return `TSWhere${pascalCase(type)}`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function skipField(name, prop, projectSchema, exceededRelationshipDepth) {
|
|
258
|
+
return Boolean(prop['@user'] || prop['@sensitive'] || prop['@resolver'] || prop['@relationship'] && exceededRelationshipDepth || (name === '_enabled' || name === '_enabledAt') && workflowsEnabled(projectSchema.apiVersion));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function getFields(typeName, shapes, context) {
|
|
262
|
+
const {
|
|
263
|
+
shapeCache,
|
|
264
|
+
relationshipDepth,
|
|
265
|
+
booleanOperators,
|
|
266
|
+
projectSchema
|
|
267
|
+
} = context;
|
|
268
|
+
const result = {};
|
|
269
|
+
const tooDeep = exceededRelationshipDepth(relationshipDepth);
|
|
270
|
+
|
|
271
|
+
for (const shape of shapes) {
|
|
272
|
+
const nodes = createSchemaPropertyList(projectSchema, shape.schema).filterBy(([name, property]) => !skipField(name, property, projectSchema, tooDeep)).getNodes();
|
|
273
|
+
|
|
274
|
+
for (const [name, property] of nodes) {
|
|
275
|
+
Object.assign(result, getPropertyComparisonType(name, property, shape.name, context));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return tooDeep || booleanOperators === false ? result : Object.assign(result, getBooleanOperatorTypes(result, typeName, shapeCache));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function getBooleanOperatorTypes(fields, typeName, shapeCache) {
|
|
283
|
+
const result = {};
|
|
284
|
+
|
|
285
|
+
for (const op of booleanOperators) {
|
|
286
|
+
const name = `${typeName}${pascalCase(op)}Operator`;
|
|
287
|
+
|
|
288
|
+
if (!shapeCache.has(name)) {
|
|
289
|
+
shapeCache.set(name, () => createShape(name, {
|
|
290
|
+
type: 'object',
|
|
291
|
+
properties: { ...fields,
|
|
292
|
+
...(op !== 'NOT' && getBooleanOperatorTypes(fields, typeName, shapeCache))
|
|
293
|
+
}
|
|
294
|
+
}, {
|
|
295
|
+
description: boolOpDescriptionMap[op]
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const refSchema = {
|
|
300
|
+
'@ref': `local:${name}`
|
|
301
|
+
};
|
|
302
|
+
result[op] = op === 'NOT' ? refSchema : {
|
|
303
|
+
type: 'array',
|
|
304
|
+
items: refSchema
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function getPropertyComparisonType(name, prop, shapeName, context) {
|
|
312
|
+
const {
|
|
313
|
+
shapeCache,
|
|
314
|
+
projectSchema,
|
|
315
|
+
relationshipDepth,
|
|
316
|
+
conflictingProperties
|
|
317
|
+
} = context;
|
|
318
|
+
|
|
319
|
+
if (skipField(name, prop, projectSchema, exceededRelationshipDepth(relationshipDepth))) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const fieldType = getPropType(projectSchema, name, prop) ?? 'string';
|
|
324
|
+
const typeName = getTypeName(name, prop, shapeName, context);
|
|
325
|
+
|
|
326
|
+
if (hasRefProperty(prop.items ?? prop)) {
|
|
327
|
+
if (!hasResolvableRef(projectSchema, prop.items ?? prop)) {
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (prop.items) {
|
|
332
|
+
prop = { ...prop,
|
|
333
|
+
items: followRef(projectSchema, prop.items)
|
|
334
|
+
};
|
|
335
|
+
} else {
|
|
336
|
+
prop = followRef(projectSchema, prop);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!shapeCache.has(typeName)) {
|
|
341
|
+
shapeCache.set(typeName, () => {
|
|
342
|
+
var _prop$items;
|
|
343
|
+
|
|
344
|
+
let props;
|
|
345
|
+
const propOrItems = prop.items ?? prop;
|
|
346
|
+
|
|
347
|
+
if (isId(name)) {
|
|
348
|
+
props = getFieldTypeComparison('id');
|
|
349
|
+
} else if (prop['@relationship']) {
|
|
350
|
+
const shapes = prop['@relationship'].shapeIds.map(id => getShapeById(projectSchema, id)).filter(isDefined);
|
|
351
|
+
props = getFields(typeName, shapes, { ...context,
|
|
352
|
+
conflictingProperties: getConflictingProperties(context.projectSchema, shapes),
|
|
353
|
+
relationshipDepth: relationshipDepth + 1
|
|
354
|
+
});
|
|
355
|
+
} else if (isUnionSchema(propOrItems)) {
|
|
356
|
+
const shapes = enumerateOneOfKeys(projectSchema, propOrItems.oneOf).map(option => projectSchema.shapes[option.shapeName]);
|
|
357
|
+
props = getFields(typeName, shapes, { ...context,
|
|
358
|
+
conflictingProperties: getConflictingProperties(context.projectSchema, shapes),
|
|
359
|
+
relationshipDepth,
|
|
360
|
+
booleanOperators: false
|
|
361
|
+
});
|
|
362
|
+
} else if (fieldType === 'object' || fieldType === 'array' && ((_prop$items = prop.items) === null || _prop$items === void 0 ? void 0 : _prop$items.type) === 'object') {
|
|
363
|
+
var _prop$items2;
|
|
364
|
+
|
|
365
|
+
const properties = ((_prop$items2 = prop.items) === null || _prop$items2 === void 0 ? void 0 : _prop$items2.properties) ?? prop.properties;
|
|
366
|
+
|
|
367
|
+
if (properties) {
|
|
368
|
+
for (const propName of Object.keys(properties)) {
|
|
369
|
+
props = { ...props,
|
|
370
|
+
...getPropertyComparisonType(propName, properties[propName], shapeName, context)
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
var _prop$items3;
|
|
376
|
+
|
|
377
|
+
// Treat non-object arrays as their base type
|
|
378
|
+
let type;
|
|
379
|
+
|
|
380
|
+
if (typeof ((_prop$items3 = prop.items) === null || _prop$items3 === void 0 ? void 0 : _prop$items3.type) === 'string') {
|
|
381
|
+
type = prop.items.type;
|
|
382
|
+
} else {
|
|
383
|
+
type = fieldType;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
props = getFieldTypeComparison(type);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return createShape(typeName, {
|
|
390
|
+
type: 'object',
|
|
391
|
+
properties: props ?? {}
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (shapeCache.has(typeName)) {
|
|
397
|
+
const fieldName = `${conflictingProperties.includes(name) ? `${shapeName}_` : ''}${name}`;
|
|
398
|
+
return {
|
|
399
|
+
[fieldName]: {
|
|
400
|
+
'@ref': `local:${typeName}`
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function findEmpty(shapes) {
|
|
407
|
+
const empty = new Set();
|
|
408
|
+
|
|
409
|
+
for (const [shapeName, shape] of Object.entries(shapes)) {
|
|
410
|
+
if (isObjectSchema(shape.schema) && Object.keys(shape.schema.properties).length === 0) {
|
|
411
|
+
empty.add(shapeName);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return empty;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function getRefSummary(shapes) {
|
|
419
|
+
const result = {};
|
|
420
|
+
|
|
421
|
+
for (const ref of getAllRefsInShapes({
|
|
422
|
+
shapes,
|
|
423
|
+
services: {}
|
|
424
|
+
})) {
|
|
425
|
+
const path = ref.path.slice(1, ref.path.length - 1);
|
|
426
|
+
|
|
427
|
+
if (result[ref.typeName]) {
|
|
428
|
+
result[ref.typeName].push(path);
|
|
429
|
+
} else {
|
|
430
|
+
result[ref.typeName] = [path];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function pruneEmpty(shapes) {
|
|
438
|
+
let toPrune = findEmpty(shapes);
|
|
439
|
+
|
|
440
|
+
if (toPrune.size) {
|
|
441
|
+
const refSummary = getRefSummary(shapes);
|
|
442
|
+
|
|
443
|
+
while (toPrune.size) {
|
|
444
|
+
for (const shapeName of toPrune) {
|
|
445
|
+
unset(shapes, shapeName);
|
|
446
|
+
|
|
447
|
+
for (const path of refSummary[shapeName] ?? []) {
|
|
448
|
+
unset(shapes, path);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
toPrune = findEmpty(shapes);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return shapes;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export function getWhereShape(projectSchema, shapes) {
|
|
460
|
+
const isManyContentTypes = shapes.length > 1;
|
|
461
|
+
const shapeName = isManyContentTypes ? 'TSWhereInput' : `TSWhere${pascalCase(shapes[0].name)}Input`;
|
|
462
|
+
const shapeCache = new ShapeCache();
|
|
463
|
+
const conflictingProperties = getConflictingProperties(projectSchema, shapes);
|
|
464
|
+
const fields = getFields(shapeName, shapes, {
|
|
465
|
+
projectSchema,
|
|
466
|
+
shapeCache,
|
|
467
|
+
conflictingProperties,
|
|
468
|
+
relationshipDepth: 0
|
|
469
|
+
});
|
|
470
|
+
return {
|
|
471
|
+
shapeName,
|
|
472
|
+
dependencies: pruneEmpty({
|
|
473
|
+
[shapeName]: createShape(shapeName, {
|
|
474
|
+
type: 'object',
|
|
475
|
+
properties: { ...fields,
|
|
476
|
+
...(isManyContentTypes && getBooleanOperatorTypes(fields, shapeName, shapeCache))
|
|
477
|
+
}
|
|
478
|
+
}),
|
|
479
|
+
...shapeCache.resolveAll()
|
|
480
|
+
})
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
export function getWhereSearchArg(projectSchema, shapes) {
|
|
484
|
+
const {
|
|
485
|
+
shapeName,
|
|
486
|
+
dependencies
|
|
487
|
+
} = getWhereShape(projectSchema, shapes.filter(s => s.model !== undefined || isIndexedRemoteShape(projectSchema, s)));
|
|
488
|
+
const schema = {
|
|
489
|
+
type: 'object',
|
|
490
|
+
properties: {
|
|
491
|
+
where: {
|
|
492
|
+
'@ref': `local:${shapeName}`,
|
|
493
|
+
description: 'The where clause uses the boolean AND, OR, and NOT parameters to construct complex filters based on the values of your fields. It applies an implicit AND to all the top-level keys. To avoid this, use a single OR or NOT key as the only top-level key.'
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
schema,
|
|
499
|
+
dependencies
|
|
500
|
+
};
|
|
501
|
+
}
|
package/es/validate.js
CHANGED
|
@@ -11,9 +11,9 @@ import { allProjectSchemas } from './schemas';
|
|
|
11
11
|
import authSchemas from './schemas/auth-schemas.json';
|
|
12
12
|
import { getAllNamespaceShapes, visitShapeProperties, getAllRefsInShapes } from './schema-util';
|
|
13
13
|
import { builtInShapes } from './builtin-schema';
|
|
14
|
-
import { isValidTemplate
|
|
14
|
+
import { isValidTemplate } from './template-shapes';
|
|
15
15
|
import { isBasicResolver, isComposeResolver } from './types/utils';
|
|
16
|
-
import { refItemToAtRef, refItemToShapeName } from './refs';
|
|
16
|
+
import { refItemToAtRef, refItemToShapeName, parseReturnShape } from './refs';
|
|
17
17
|
import { scalars } from './scalars';
|
|
18
18
|
import { enumerateOneOfKeys, isUnionSchema } from './unions';
|
|
19
19
|
import { isEnumLikeSchema } from './enum';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flatten-templates.d.ts","sourceRoot":"","sources":["../../src/flatten-templates.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAK/C,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,CAgB5E"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.flattenTemplates = flattenTemplates;
|
|
7
|
+
|
|
8
|
+
var _templateShapes = require("./template-shapes");
|
|
9
|
+
|
|
10
|
+
var _schemaUtil = require("./schema-util");
|
|
11
|
+
|
|
12
|
+
var _set = _interopRequireDefault(require("lodash/set"));
|
|
13
|
+
|
|
14
|
+
var _util = require("@takeshape/util");
|
|
15
|
+
|
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
|
+
|
|
18
|
+
function flattenTemplates(projectSchema) {
|
|
19
|
+
const templateRefs = (0, _schemaUtil.getAllRefs)(projectSchema, ref => Boolean(ref.template));
|
|
20
|
+
|
|
21
|
+
if (!templateRefs.length) {
|
|
22
|
+
return projectSchema;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const newSchema = (0, _util.deepClone)(projectSchema);
|
|
26
|
+
|
|
27
|
+
for (const ref of templateRefs) {
|
|
28
|
+
if (ref.template && (0, _templateShapes.isValidTemplate)(ref.template)) {
|
|
29
|
+
const {
|
|
30
|
+
shapeName,
|
|
31
|
+
dependencies
|
|
32
|
+
} = (0, _templateShapes.resolveTemplate)(projectSchema, ref.template, ref.typeName);
|
|
33
|
+
(0, _set.default)(newSchema, ref.path, shapeName);
|
|
34
|
+
Object.assign(newSchema.shapes, dependencies);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return newSchema;
|
|
39
|
+
}
|
package/lib/index.d.ts
CHANGED
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAC,mBAAmB,EAAE,6BAA6B,EAAC,MAAM,mBAAmB,CAAC;AAE1F,YAAY,EAAC,IAAI,EAAC,MAAM,eAAe,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAC/C,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAC,mBAAmB,EAAE,6BAA6B,EAAC,MAAM,mBAAmB,CAAC;AAE1F,YAAY,EAAC,IAAI,EAAC,MAAM,eAAe,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAC/C,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -333,4 +333,18 @@ Object.keys(_utils).forEach(function (key) {
|
|
|
333
333
|
return _utils[key];
|
|
334
334
|
}
|
|
335
335
|
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
var _flattenTemplates = require("./flatten-templates");
|
|
339
|
+
|
|
340
|
+
Object.keys(_flattenTemplates).forEach(function (key) {
|
|
341
|
+
if (key === "default" || key === "__esModule") return;
|
|
342
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
343
|
+
if (key in exports && exports[key] === _flattenTemplates[key]) return;
|
|
344
|
+
Object.defineProperty(exports, key, {
|
|
345
|
+
enumerable: true,
|
|
346
|
+
get: function () {
|
|
347
|
+
return _flattenTemplates[key];
|
|
348
|
+
}
|
|
349
|
+
});
|
|
336
350
|
});
|
package/lib/layers/layers.js
CHANGED
|
@@ -25,7 +25,7 @@ var _visitor = require("./visitor");
|
|
|
25
25
|
|
|
26
26
|
var _set = _interopRequireDefault(require("lodash/set"));
|
|
27
27
|
|
|
28
|
-
var
|
|
28
|
+
var _refs2 = require("../refs");
|
|
29
29
|
|
|
30
30
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
31
31
|
|
|
@@ -281,7 +281,7 @@ function rewriteLayerId(localShapes, layerId, query) {
|
|
|
281
281
|
|
|
282
282
|
if (query.args) {
|
|
283
283
|
if (typeof query.args === 'string') {
|
|
284
|
-
const [argsLayerId, shapeName] = (0, _refs.splitAtRef)((0,
|
|
284
|
+
const [argsLayerId, shapeName] = (0, _refs.splitAtRef)((0, _refs2.parseTemplateShape)(query.args).shapeName);
|
|
285
285
|
|
|
286
286
|
if (argsLayerId === 'local' && !localShapes[shapeName]) {
|
|
287
287
|
newQuery.args = `${layerId}:${shapeName}`;
|
|
@@ -292,7 +292,7 @@ function rewriteLayerId(localShapes, layerId, query) {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
if (typeof query.shape === 'string') {
|
|
295
|
-
const [argsLayerId, shapeName] = (0, _refs.splitAtRef)((0,
|
|
295
|
+
const [argsLayerId, shapeName] = (0, _refs.splitAtRef)((0, _refs2.parseTemplateShape)(query.shape).shapeName);
|
|
296
296
|
|
|
297
297
|
if (argsLayerId === 'local' && !localShapes[shapeName]) {
|
|
298
298
|
newQuery.shape = `${layerId}:${shapeName}`;
|
package/lib/layers/refs.js
CHANGED
|
@@ -23,7 +23,7 @@ exports.splitAtRef = splitAtRef;
|
|
|
23
23
|
|
|
24
24
|
var _typeUtils = require("./type-utils");
|
|
25
25
|
|
|
26
|
-
var
|
|
26
|
+
var _refs = require("../refs");
|
|
27
27
|
|
|
28
28
|
var _assign = _interopRequireDefault(require("lodash/fp/assign"));
|
|
29
29
|
|
|
@@ -47,7 +47,7 @@ function refExpressionToRefItem(refExpression) {
|
|
|
47
47
|
const {
|
|
48
48
|
shapeName,
|
|
49
49
|
template
|
|
50
|
-
} = (0,
|
|
50
|
+
} = (0, _refs.parseTemplateShape)(refExpression);
|
|
51
51
|
return atRefToRefItem(shapeName, template);
|
|
52
52
|
}
|
|
53
53
|
/**
|
package/lib/refs.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/// <reference types="lodash" />
|
|
2
|
-
import type { SchemaPath, SchemaWithRef, ProjectSchemaV3X } from './types
|
|
2
|
+
import type { SchemaPath, SchemaWithRef, ProjectSchemaV3X } from './types';
|
|
3
3
|
import type { PropertySchema, Shape, ShapeSchema } from './project-schema';
|
|
4
4
|
import type { Maybe } from '@takeshape/util';
|
|
5
|
+
import { ReturnShape } from './project-schema';
|
|
5
6
|
export declare type ServicesContext = Pick<ProjectSchemaV3X, 'services'>;
|
|
6
7
|
export declare type ServicesShapesContext = Pick<ProjectSchemaV3X, 'services' | 'shapes'>;
|
|
7
8
|
/**
|
|
@@ -22,6 +23,30 @@ export interface RefItem {
|
|
|
22
23
|
export interface RefItemWithPath extends RefItem {
|
|
23
24
|
path: SchemaPath;
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse a template like `PaginatedList<Post>` and return both the template and the shape name.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseTemplateShape(shapeExpression: string): {
|
|
30
|
+
template: string | undefined;
|
|
31
|
+
shapeName: string;
|
|
32
|
+
};
|
|
33
|
+
export interface ParsedReturnShape {
|
|
34
|
+
isArray: boolean;
|
|
35
|
+
template?: string;
|
|
36
|
+
shapeName: string;
|
|
37
|
+
ref?: RefItem;
|
|
38
|
+
}
|
|
39
|
+
export declare function parseReturnShape(projectSchema: ServicesContext, shape: ReturnShape): ParsedReturnShape;
|
|
40
|
+
/**
|
|
41
|
+
* Create a template string like `PaginatedList<Post>` from a template and shape name.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createTemplateShapeName(template: string, shapeName: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* If the string is a template like `PaginatedList<Post>`, return the shape name,
|
|
46
|
+
* in this case `Post`. Otherwise return the input.
|
|
47
|
+
*/
|
|
48
|
+
export declare const untemplate: (input: string) => string;
|
|
49
|
+
export declare function getFlattenedTemplateShapeName(shapeName: string, template?: string): string;
|
|
25
50
|
export declare function $refToAtRef($ref: string, service: string, namespace?: string): string;
|
|
26
51
|
export declare function refSchemaToPath(context: ServicesContext, refSchema: SchemaWithRef): string[];
|
|
27
52
|
/**
|