@forestadmin/agent-bff 1.16.0 → 1.18.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/README.md +15 -1
- package/dist/action/action-form-mapper.d.ts +2 -1
- package/dist/action/action-form-mapper.js +1 -1
- package/dist/action/agent-action-client.d.ts +2 -1
- package/dist/action/agent-action-client.js +1 -1
- package/dist/cli-core.d.ts +3 -0
- package/dist/cli-core.js +52 -10
- package/dist/cli-dispatch.d.ts +8 -2
- package/dist/cli-dispatch.js +90 -9
- package/dist/data/data-routes-middleware.js +3 -12
- package/dist/data/pack-id.d.ts +1 -0
- package/dist/data/pack-id.js +4 -3
- package/dist/openapi/collect-unfolding.d.ts +23 -0
- package/dist/openapi/collect-unfolding.js +144 -0
- package/dist/openapi/component-pool.d.ts +18 -0
- package/dist/openapi/component-pool.js +28 -0
- package/dist/openapi/field-schemas.d.ts +9 -0
- package/dist/openapi/field-schemas.js +43 -0
- package/dist/openapi/names.d.ts +16 -0
- package/dist/openapi/names.js +28 -0
- package/dist/openapi/openapi-document.d.ts +2 -1
- package/dist/openapi/openapi-document.js +112 -66
- package/dist/openapi/openapi-routes.d.ts +4 -1
- package/dist/openapi/openapi-routes.js +46 -9
- package/dist/openapi/schemas.d.ts +14 -1
- package/dist/openapi/schemas.js +11 -10
- package/dist/openapi/unfolded-document.d.ts +28 -0
- package/dist/openapi/unfolded-document.js +52 -0
- package/dist/openapi/unfolded-paths.d.ts +20 -0
- package/dist/openapi/unfolded-paths.js +408 -0
- package/dist/openapi/unfolding.d.ts +66 -0
- package/dist/openapi/unfolding.js +17 -0
- package/dist/read-model/agent-capabilities-fetcher.d.ts +8 -3
- package/dist/read-model/agent-capabilities-fetcher.js +15 -7
- package/dist/read-model/capabilities-cache.d.ts +11 -1
- package/dist/read-model/capabilities-cache.js +1 -1
- package/dist/read-model/read-model.d.ts +14 -0
- package/dist/read-model/read-model.js +36 -2
- package/dist/validation/operator-normalizer.d.ts +6 -0
- package/dist/validation/operator-normalizer.js +11 -1
- package/package.json +4 -4
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = registerUnfoldedPaths;
|
|
7
|
+
const field_schemas_1 = __importDefault(require("./field-schemas"));
|
|
8
|
+
const names_1 = __importDefault(require("./names"));
|
|
9
|
+
const schemas_1 = require("./schemas");
|
|
10
|
+
const pack_id_1 = require("../data/pack-id");
|
|
11
|
+
const AGGREGATORS = ['And', 'Or'];
|
|
12
|
+
const DEGRADED_NOTE = {
|
|
13
|
+
capabilities_unavailable: 'The field names are NOT enumerated here: the agent could not be asked for this ' +
|
|
14
|
+
"collection's capabilities when the document was built. Any field the collection really " +
|
|
15
|
+
'exposes is still accepted.',
|
|
16
|
+
no_fields: 'The field names are NOT enumerated here: the agent reports no filterable or projectable ' +
|
|
17
|
+
'field on this collection. Any field sent in `projection`, `sort` or `filter` is therefore ' +
|
|
18
|
+
'rejected with 422 unknown_field — call this path without them.',
|
|
19
|
+
};
|
|
20
|
+
function quoted(name) {
|
|
21
|
+
return JSON.stringify(name);
|
|
22
|
+
}
|
|
23
|
+
// A name reaches the runtime through decodeURIComponent, so the document must carry it encoded —
|
|
24
|
+
// including the separators that would otherwise change the route, such as a slash inside an action
|
|
25
|
+
// name (`a/b` is reachable only as `a%2Fb`).
|
|
26
|
+
function segment(name) {
|
|
27
|
+
return encodeURIComponent(name);
|
|
28
|
+
}
|
|
29
|
+
function fieldsEnum(pool, name, fields, description) {
|
|
30
|
+
// An empty enum would forbid every value, so a collection with no known field set keeps a plain
|
|
31
|
+
// string — the runtime is the one that rejects an unknown field, and it says which.
|
|
32
|
+
if (fields.length === 0)
|
|
33
|
+
return { type: 'string' };
|
|
34
|
+
return pool.add(name, { type: 'string', enum: fields, description });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Groups the filterable fields by the operator set they share. One leaf per field would be as exact
|
|
38
|
+
* but would multiply the document by the field count; the number of distinct operator sets is bounded
|
|
39
|
+
* by the column types a collection really uses, and the cross product of a group is precisely the set
|
|
40
|
+
* of valid `(field, operator)` pairs. The grouping is derived from what capabilities reported — two
|
|
41
|
+
* fields of DIFFERENT types sharing an operator set land in one group, so nothing here is a type
|
|
42
|
+
* table.
|
|
43
|
+
*/
|
|
44
|
+
function groupByOperators(filterable) {
|
|
45
|
+
const groups = new Map();
|
|
46
|
+
filterable.forEach(field => {
|
|
47
|
+
// An empty set would emit `enum: []`, which forbids every value and makes the leaf unsatisfiable —
|
|
48
|
+
// the trap `fieldsEnum` guards above. `collectFilterableFields` cannot produce one, but `Unfolding`
|
|
49
|
+
// is hand-constructible plain data, so the generator holds the invariant rather than assume it.
|
|
50
|
+
if (field.operators.length === 0)
|
|
51
|
+
return;
|
|
52
|
+
// Safe as a key because the operators are normalized to one canonical order at collect time.
|
|
53
|
+
const signature = field.operators.join(',');
|
|
54
|
+
const group = groups.get(signature);
|
|
55
|
+
if (group)
|
|
56
|
+
group.fields.push(field.name);
|
|
57
|
+
else
|
|
58
|
+
groups.set(signature, { fields: [field.name], operators: [...field.operators] });
|
|
59
|
+
});
|
|
60
|
+
return [...groups.values()];
|
|
61
|
+
}
|
|
62
|
+
// A node readable as BOTH a leaf and a branch is rejected with 400 (`agent-query.ts`
|
|
63
|
+
// `assertNoNodeReadableAsBothLeafAndBranch`), so each alternative excludes the other's discriminator.
|
|
64
|
+
// The exclusion carries the TYPE the runtime looks for — `isBranch` needs an ARRAY `conditions` and
|
|
65
|
+
// `isLeaf` a STRING `field` — because `{field, operator, conditions: "x"}` is a plain leaf the runtime
|
|
66
|
+
// accepts. Expressed as `not: { required }` rather than `additionalProperties: false`, which would
|
|
67
|
+
// also forbid an unknown extra key the runtime strips.
|
|
68
|
+
function leafShape(field, operators, description) {
|
|
69
|
+
return {
|
|
70
|
+
type: 'object',
|
|
71
|
+
description,
|
|
72
|
+
properties: {
|
|
73
|
+
field,
|
|
74
|
+
operator: { type: 'string', enum: operators },
|
|
75
|
+
value: {},
|
|
76
|
+
},
|
|
77
|
+
required: ['field', 'operator'],
|
|
78
|
+
not: { properties: { conditions: { type: 'array' } }, required: ['conditions'] },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One leaf alternative per operator set. A field absent from `filterable` is in none of them, which is
|
|
83
|
+
* the only honest way to document it: a `ManyToOne` answers 422 field_not_filterable, and a field the
|
|
84
|
+
* collector dropped for an unmappable operator answers 500 mapping_error on every filter.
|
|
85
|
+
*/
|
|
86
|
+
function filterLeaves(pool, plan) {
|
|
87
|
+
const { name, fields } = plan.collection;
|
|
88
|
+
const groups = groupByOperators(fields.filterable);
|
|
89
|
+
// No known filterable field: the field stays free-form and every operator stays allowed, because a
|
|
90
|
+
// collection whose capabilities could not be read still accepts whatever it really exposes.
|
|
91
|
+
if (groups.length === 0) {
|
|
92
|
+
return [
|
|
93
|
+
pool.add(`FilterLeaf_${plan.key}`, leafShape({ type: 'string' }, schemas_1.OPERATORS, `A single condition on ${quoted(name)}.`)),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
// `-` rather than `_` before the index, and the difference is load-bearing: `sanitizeIdentifier` maps
|
|
97
|
+
// every character outside `[A-Za-z0-9_]` to `_`, so a collection key can end in `_<digit>` — either
|
|
98
|
+
// because the customer named a collection `Invoice 1`, or because `createNamer` suffixed a name that
|
|
99
|
+
// collapsed. With `_`, collection `Invoice 1` (no group, so the bare name below) and collection
|
|
100
|
+
// `Invoice` (first group) both claim `FilterLeaf_Invoice_1`, and `ComponentPool.add` settles it
|
|
101
|
+
// last-wins in silence — one collection would document the other's fields and operators. Sanitizing
|
|
102
|
+
// can never produce a `-`, so this separator makes the two namespaces disjoint by construction.
|
|
103
|
+
return groups.map((group, index) => pool.add(`FilterLeaf_${plan.key}-${index + 1}`, leafShape({ type: 'string', enum: group.fields }, group.operators, `A single condition on ${quoted(name)}. These fields share one operator set, as the ` +
|
|
104
|
+
"collection's capabilities report it.")));
|
|
105
|
+
}
|
|
106
|
+
function filterSchema({ pool }, plan) {
|
|
107
|
+
const { name, fields } = plan.collection;
|
|
108
|
+
const treeName = `Filter_${plan.key}`;
|
|
109
|
+
const treeRef = { $ref: `#/components/schemas/${treeName}` };
|
|
110
|
+
const leaves = filterLeaves(pool, plan);
|
|
111
|
+
const pairing = fields.filterable.length > 0
|
|
112
|
+
? ' There is one leaf alternative per operator set: a field accepts only the operators of ' +
|
|
113
|
+
'the alternative listing it, and an operator it does not support answers 400 ' +
|
|
114
|
+
'invalid_filter_operator.'
|
|
115
|
+
: '';
|
|
116
|
+
return pool.add(treeName, {
|
|
117
|
+
description: `A filter on ${quoted(name)}: either a leaf condition or a branch nesting more ` +
|
|
118
|
+
'conditions. A branch must carry its aggregator. A node carrying both `field` and ' +
|
|
119
|
+
`\`conditions\` is rejected with 400, so the two shapes are mutually exclusive.${pairing}`,
|
|
120
|
+
anyOf: [
|
|
121
|
+
...leaves,
|
|
122
|
+
{
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
aggregator: { type: 'string', enum: AGGREGATORS },
|
|
126
|
+
conditions: { type: 'array', items: treeRef },
|
|
127
|
+
},
|
|
128
|
+
required: ['aggregator', 'conditions'],
|
|
129
|
+
not: { properties: { field: { type: 'string' } }, required: ['field'] },
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function fieldRefs(deps, plan) {
|
|
135
|
+
const { pool } = deps;
|
|
136
|
+
const { name, fields } = plan.collection;
|
|
137
|
+
const projectable = fieldsEnum(pool, `Fields_${plan.key}`, fields.projectable, `A field of ${quoted(name)}.`);
|
|
138
|
+
// Filterable is a strict subset of projectable: the agent reports a ManyToOne without operators, so
|
|
139
|
+
// projecting or sorting on it works while filtering on it answers 422 field_not_filterable. Which is
|
|
140
|
+
// why the filterable fields are not enumerated here but inside the filter leaves, where each one
|
|
141
|
+
// sits next to the operators it actually supports.
|
|
142
|
+
return {
|
|
143
|
+
projectable,
|
|
144
|
+
filter: filterSchema(deps, plan),
|
|
145
|
+
sort: fields.projectable.length === 0
|
|
146
|
+
? pool.reuse('SortClause', schemas_1.SortClauseSchema)
|
|
147
|
+
: pool.add(`SortClause_${plan.key}`, {
|
|
148
|
+
type: 'object',
|
|
149
|
+
description: 'Omitting `direction` sorts ascending.',
|
|
150
|
+
properties: {
|
|
151
|
+
field: projectable,
|
|
152
|
+
direction: { type: 'string', enum: ['asc', 'desc'] },
|
|
153
|
+
},
|
|
154
|
+
required: ['field'],
|
|
155
|
+
}),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function requestDescription(collection, subject) {
|
|
159
|
+
const note = collection.fields.degraded ? ` ${DEGRADED_NOTE[collection.fields.degraded]}` : '';
|
|
160
|
+
return `${subject}${note}`;
|
|
161
|
+
}
|
|
162
|
+
function registerRequests(deps, plan) {
|
|
163
|
+
const { pool } = deps;
|
|
164
|
+
const { collection, key } = plan;
|
|
165
|
+
const refs = fieldRefs(deps, plan);
|
|
166
|
+
const timezone = pool.reuse('Timezone', schemas_1.TimezoneSchema);
|
|
167
|
+
return {
|
|
168
|
+
list: pool.add(`ListRequest_${key}`, {
|
|
169
|
+
type: 'object',
|
|
170
|
+
description: requestDescription(collection, `Filter, sort and project records of ${quoted(collection.name)}.`),
|
|
171
|
+
properties: {
|
|
172
|
+
filter: refs.filter,
|
|
173
|
+
projection: { type: 'array', items: refs.projectable },
|
|
174
|
+
sort: { type: 'array', items: refs.sort },
|
|
175
|
+
page: pool.reuse('Page', schemas_1.PageSchema),
|
|
176
|
+
timezone,
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
count: pool.add(`CountRequest_${key}`, {
|
|
180
|
+
type: 'object',
|
|
181
|
+
description: requestDescription(collection, `Count records of ${quoted(collection.name)} matching a filter.`),
|
|
182
|
+
properties: { filter: refs.filter, timezone },
|
|
183
|
+
}),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const PARENT_ID_SHAPE = {
|
|
187
|
+
anyOf: [{ type: 'string', pattern: '\\S' }, { type: 'number' }],
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* `parentId` is opaque on the wire: `parseParentId` takes a non-blank string or a finite number
|
|
191
|
+
* whatever the key's type is, and forwards it to the agent as a string. So the SHAPE stays that
|
|
192
|
+
* union — narrowing a numeric key to `number` would make a generated client reject `"123"`, which
|
|
193
|
+
* the endpoint accepts. What the read-model knows goes in the description: which column the id
|
|
194
|
+
* belongs to, and, for a composite key, the `|`-joined order `unpackPrimaryKey` expects.
|
|
195
|
+
*/
|
|
196
|
+
function parentIdSchema(pool, parent, primaryKeys) {
|
|
197
|
+
if (primaryKeys.length === 0)
|
|
198
|
+
return pool.reuse('ParentId', schemas_1.ParentIdSchema);
|
|
199
|
+
if (primaryKeys.length > 1) {
|
|
200
|
+
return {
|
|
201
|
+
// A composite id only works as its packed string: a number could never carry the separator.
|
|
202
|
+
type: 'string',
|
|
203
|
+
pattern: '\\S',
|
|
204
|
+
description: `The composite id of the parent ${quoted(parent)} record: the values of ` +
|
|
205
|
+
`${primaryKeys.map(key => key.name).join(', ')} joined by ` +
|
|
206
|
+
`${quoted(pack_id_1.PACKED_ID_SEPARATOR)}, in that order.`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const [key] = primaryKeys;
|
|
210
|
+
return {
|
|
211
|
+
...PARENT_ID_SHAPE,
|
|
212
|
+
description: `The parent ${quoted(parent)} record id (${key.name}, ${key.type}). A number or its ` +
|
|
213
|
+
'string form are both accepted; the BFF forwards it to the agent as a string.',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function registerRelationRequests(deps, plan, relation, relationKey, foreign) {
|
|
217
|
+
const { pool } = deps;
|
|
218
|
+
const parentId = parentIdSchema(pool, plan.collection.name, plan.collection.primaryKeys);
|
|
219
|
+
const parentProperties = {
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: { parentId },
|
|
222
|
+
required: ['parentId'],
|
|
223
|
+
};
|
|
224
|
+
const description = `Filter, sort and projection apply to ${quoted(foreign.collection.name)}, the foreign ` +
|
|
225
|
+
`collection of ${quoted(plan.collection.name)}.${quoted(relation.name)}; the parent only ` +
|
|
226
|
+
'resolves which records are related.';
|
|
227
|
+
return {
|
|
228
|
+
list: pool.add(`RelationListRequest_${relationKey}`, {
|
|
229
|
+
description,
|
|
230
|
+
allOf: [foreign.requests.list, parentProperties],
|
|
231
|
+
}),
|
|
232
|
+
count: pool.add(`RelationCountRequest_${relationKey}`, {
|
|
233
|
+
description,
|
|
234
|
+
allOf: [foreign.requests.count, parentProperties],
|
|
235
|
+
}),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function actionFieldSchema(field) {
|
|
239
|
+
const base = field.enums
|
|
240
|
+
? { type: 'string', enum: field.enums }
|
|
241
|
+
: (0, field_schemas_1.default)(field.type);
|
|
242
|
+
return field.isRequired
|
|
243
|
+
? { ...base, description: 'The agent declares this field required on the static form.' }
|
|
244
|
+
: base;
|
|
245
|
+
}
|
|
246
|
+
function actionValuesSchema(action) {
|
|
247
|
+
return {
|
|
248
|
+
type: 'object',
|
|
249
|
+
// No property is required and no additional one is forbidden on purpose: a load or change hook
|
|
250
|
+
// rebuilds the form at call time, so the schema's static fields are an indication, not the
|
|
251
|
+
// contract — and the read-model cannot tell a hookless action from a legacy schema that simply
|
|
252
|
+
// omitted its hooks, so "static" is never certain.
|
|
253
|
+
description: 'The submitted action fields. These are the fields the schema declares statically; a load ' +
|
|
254
|
+
'or change hook can add, drop or require others at call time.',
|
|
255
|
+
properties: Object.fromEntries(action.fields.map(field => [field.name, actionFieldSchema(field)])),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function registerActionRequest(deps, plan, action, actionKey) {
|
|
259
|
+
const { pool } = deps;
|
|
260
|
+
return pool.add(`ActionRequest_${actionKey}`, {
|
|
261
|
+
type: 'object',
|
|
262
|
+
description: `The body of action ${quoted(action.name)} on ${quoted(plan.collection.name)}. ` +
|
|
263
|
+
'`recordIds` is required even on a global action: send an empty array when the action ' +
|
|
264
|
+
'targets no record.',
|
|
265
|
+
properties: {
|
|
266
|
+
recordIds: { type: 'array', items: { anyOf: [{ type: 'string' }, { type: 'number' }] } },
|
|
267
|
+
values: actionValuesSchema(action),
|
|
268
|
+
timezone: pool.reuse('Timezone', schemas_1.TimezoneSchema),
|
|
269
|
+
},
|
|
270
|
+
required: ['recordIds'],
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function registerOperation(deps, options) {
|
|
274
|
+
deps.registry.registerPath({
|
|
275
|
+
method: 'post',
|
|
276
|
+
path: `${deps.prefix}/${options.path}`,
|
|
277
|
+
operationId: options.operationId,
|
|
278
|
+
summary: options.summary,
|
|
279
|
+
description: options.description,
|
|
280
|
+
security: deps.security,
|
|
281
|
+
request: {
|
|
282
|
+
headers: deps.timezoneHeader,
|
|
283
|
+
body: {
|
|
284
|
+
required: options.bodyRequired,
|
|
285
|
+
content: { 'application/json': { schema: options.request } },
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
responses: {
|
|
289
|
+
200: {
|
|
290
|
+
description: options.responseDescription,
|
|
291
|
+
content: { 'application/json': { schema: options.response } },
|
|
292
|
+
},
|
|
293
|
+
...deps.errorResponses(options.executeResults === true),
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
function registerCollectionOperations(deps, plan) {
|
|
298
|
+
const { pool } = deps;
|
|
299
|
+
const { name } = plan.collection;
|
|
300
|
+
const listResponse = pool.reuse('ListResponse', schemas_1.ListResponseSchema);
|
|
301
|
+
const countResponse = pool.reuse('CountResponse', schemas_1.CountResponseSchema);
|
|
302
|
+
registerOperation(deps, {
|
|
303
|
+
path: `${segment(name)}/list`,
|
|
304
|
+
operationId: `listRecords_${plan.key}`,
|
|
305
|
+
summary: `List records of ${name}`,
|
|
306
|
+
description: `Lists records of the ${quoted(name)} collection.`,
|
|
307
|
+
request: plan.requests.list,
|
|
308
|
+
response: listResponse,
|
|
309
|
+
responseDescription: `A page of ${quoted(name)} records`,
|
|
310
|
+
bodyRequired: false,
|
|
311
|
+
});
|
|
312
|
+
registerOperation(deps, {
|
|
313
|
+
path: `${segment(name)}/count`,
|
|
314
|
+
operationId: `countRecords_${plan.key}`,
|
|
315
|
+
summary: `Count records of ${name}`,
|
|
316
|
+
description: `Counts records of the ${quoted(name)} collection.`,
|
|
317
|
+
request: plan.requests.count,
|
|
318
|
+
response: countResponse,
|
|
319
|
+
responseDescription: `The matching ${quoted(name)} record count`,
|
|
320
|
+
bodyRequired: false,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function registerRelationOperations(deps, plan, plansByName, namer) {
|
|
324
|
+
const { pool } = deps;
|
|
325
|
+
plan.collection.relations.forEach(relation => {
|
|
326
|
+
const foreign = plansByName.get(relation.foreignCollection);
|
|
327
|
+
if (!foreign)
|
|
328
|
+
return;
|
|
329
|
+
const relationKey = namer(`${plan.key}_${relation.name}`);
|
|
330
|
+
const requests = registerRelationRequests(deps, plan, relation, relationKey, foreign);
|
|
331
|
+
const parent = `${quoted(plan.collection.name)}.${quoted(relation.name)}`;
|
|
332
|
+
registerOperation(deps, {
|
|
333
|
+
path: `${segment(plan.collection.name)}/relations/${segment(relation.name)}/list`,
|
|
334
|
+
operationId: `listRelatedRecords_${relationKey}`,
|
|
335
|
+
summary: `List ${relation.name} of ${plan.collection.name}`,
|
|
336
|
+
description: `Lists the ${quoted(foreign.collection.name)} records related to a ${quoted(plan.collection.name)} record through ${parent}.`,
|
|
337
|
+
request: requests.list,
|
|
338
|
+
response: pool.reuse('ListResponse', schemas_1.ListResponseSchema),
|
|
339
|
+
responseDescription: `A page of related ${quoted(foreign.collection.name)} records`,
|
|
340
|
+
bodyRequired: true,
|
|
341
|
+
});
|
|
342
|
+
registerOperation(deps, {
|
|
343
|
+
path: `${segment(plan.collection.name)}/relations/${segment(relation.name)}/count`,
|
|
344
|
+
operationId: `countRelatedRecords_${relationKey}`,
|
|
345
|
+
summary: `Count ${relation.name} of ${plan.collection.name}`,
|
|
346
|
+
description: `Counts the ${quoted(foreign.collection.name)} records related through ${parent}.`,
|
|
347
|
+
request: requests.count,
|
|
348
|
+
response: pool.reuse('CountResponse', schemas_1.CountResponseSchema),
|
|
349
|
+
responseDescription: `The matching related ${quoted(foreign.collection.name)} record count`,
|
|
350
|
+
bodyRequired: true,
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
function registerActionOperations(deps, plan, namer) {
|
|
355
|
+
plan.collection.actions.forEach(action => {
|
|
356
|
+
const actionKey = namer(`${plan.key}_${action.name}`);
|
|
357
|
+
const request = registerActionRequest(deps, plan, action, actionKey);
|
|
358
|
+
// The path segment is the exact schema action name, URL-encoded — that is what the middleware
|
|
359
|
+
// decodes and looks up. The operationId is sanitized, so the exact name lives in prose.
|
|
360
|
+
const identity = `The action name is exactly ${quoted(action.name)}.`;
|
|
361
|
+
const base = `${segment(plan.collection.name)}/actions/${segment(action.name)}`;
|
|
362
|
+
registerOperation(deps, {
|
|
363
|
+
path: `${base}/form`,
|
|
364
|
+
operationId: `getActionForm_${actionKey}`,
|
|
365
|
+
summary: `Load the form of ${action.name} on ${plan.collection.name}`,
|
|
366
|
+
description: `Loads the form of the custom action. ${identity} An unknown submitted field is skipped here, not rejected.`,
|
|
367
|
+
request,
|
|
368
|
+
response: {},
|
|
369
|
+
responseDescription: 'The action form fields',
|
|
370
|
+
bodyRequired: true,
|
|
371
|
+
});
|
|
372
|
+
registerOperation(deps, {
|
|
373
|
+
path: `${base}/execute`,
|
|
374
|
+
operationId: `executeAction_${actionKey}`,
|
|
375
|
+
summary: `Execute ${action.name} on ${plan.collection.name}`,
|
|
376
|
+
description: `Executes the custom action. ${identity} A submitted field the loaded form does not carry is rejected with 400.`,
|
|
377
|
+
request,
|
|
378
|
+
response: {},
|
|
379
|
+
responseDescription: 'The normalized action result',
|
|
380
|
+
bodyRequired: true,
|
|
381
|
+
executeResults: true,
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Emits one path per exposed collection, to-many relation and action. The runtime routes stay the
|
|
387
|
+
* generic regexes — only the document unfolds.
|
|
388
|
+
*/
|
|
389
|
+
function registerUnfoldedPaths(deps, unfolding) {
|
|
390
|
+
// One namer per identifier family, fed the WHOLE composed name. Deduplicating each segment on its
|
|
391
|
+
// own would not be enough: sanitizing produces `_`, which is also the separator, so collection
|
|
392
|
+
// `A_B` with action `C` and collection `A` with action `B_C` both compose `A_B_C` — one action
|
|
393
|
+
// would then get the other's request schema.
|
|
394
|
+
const collections = (0, names_1.default)();
|
|
395
|
+
const relations = (0, names_1.default)();
|
|
396
|
+
const actions = (0, names_1.default)();
|
|
397
|
+
const plans = unfolding.collections.map(collection => {
|
|
398
|
+
const key = collections(collection.name);
|
|
399
|
+
return { collection, key, requests: registerRequests(deps, { collection, key }) };
|
|
400
|
+
});
|
|
401
|
+
const plansByName = new Map(plans.map(plan => [plan.collection.name, plan]));
|
|
402
|
+
plans.forEach(plan => {
|
|
403
|
+
registerCollectionOperations(deps, plan);
|
|
404
|
+
registerRelationOperations(deps, plan, plansByName, relations);
|
|
405
|
+
registerActionOperations(deps, plan, actions);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5mb2xkZWQtcGF0aHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS91bmZvbGRlZC1wYXRocy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQWtqQkEsd0NBcUJDO0FBeGpCRCxvRUFBNEM7QUFDNUMsb0RBQWtDO0FBQ2xDLHVDQVFtQjtBQUNuQiw2Q0FBc0Q7QUFFdEQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFFbEMsTUFBTSxhQUFhLEdBQThEO0lBQy9FLHdCQUF3QixFQUN0QixpRkFBaUY7UUFDakYseUZBQXlGO1FBQ3pGLDRCQUE0QjtJQUM5QixTQUFTLEVBQ1AsMEZBQTBGO1FBQzFGLDRGQUE0RjtRQUM1RixnRUFBZ0U7Q0FDbkUsQ0FBQztBQXNCRixTQUFTLE1BQU0sQ0FBQyxJQUFZO0lBQzFCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUM5QixDQUFDO0FBRUQsaUdBQWlHO0FBQ2pHLG1HQUFtRztBQUNuRyw2Q0FBNkM7QUFDN0MsU0FBUyxPQUFPLENBQUMsSUFBWTtJQUMzQixPQUFPLGtCQUFrQixDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ2xDLENBQUM7QUFFRCxTQUFTLFVBQVUsQ0FDakIsSUFBbUIsRUFDbkIsSUFBWSxFQUNaLE1BQWdCLEVBQ2hCLFdBQW1CO0lBRW5CLGdHQUFnRztJQUNoRyxvRkFBb0Y7SUFDcEYsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBRW5ELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLENBQUMsQ0FBQztBQUN2RSxDQUFDO0FBT0Q7Ozs7Ozs7R0FPRztBQUNILFNBQVMsZ0JBQWdCLENBQUMsVUFBNkI7SUFDckQsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQXlCLENBQUM7SUFFaEQsVUFBVSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtRQUN6QixtR0FBbUc7UUFDbkcsb0dBQW9HO1FBQ3BHLGdHQUFnRztRQUNoRyxJQUFJLEtBQUssQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPO1FBRXpDLDZGQUE2RjtRQUM3RixNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM1QyxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRXBDLElBQUksS0FBSztZQUFFLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQzs7WUFDcEMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsU0FBUyxFQUFFLENBQUMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3hGLENBQUMsQ0FBQyxDQUFDO0lBRUgsT0FBTyxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7QUFDOUIsQ0FBQztBQUVELHFGQUFxRjtBQUNyRixzR0FBc0c7QUFDdEcsb0dBQW9HO0FBQ3BHLHVHQUF1RztBQUN2RyxtR0FBbUc7QUFDbkcsdURBQXVEO0FBQ3ZELFNBQVMsU0FBUyxDQUNoQixLQUFxQyxFQUNyQyxTQUFtQixFQUNuQixXQUFtQjtJQUVuQixPQUFPO1FBQ0wsSUFBSSxFQUFFLFFBQVE7UUFDZCxXQUFXO1FBQ1gsVUFBVSxFQUFFO1lBQ1YsS0FBSztZQUNMLFFBQVEsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRTtZQUM3QyxLQUFLLEVBQUUsRUFBRTtTQUNWO1FBQ0QsUUFBUSxFQUFFLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQztRQUMvQixHQUFHLEVBQUUsRUFBRSxVQUFVLEVBQUUsRUFBRSxVQUFVLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsQ0FBQyxZQUFZLENBQUMsRUFBRTtLQUNqRixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLFlBQVksQ0FBQyxJQUFtQixFQUFFLElBQWdEO0lBQ3pGLE1BQU0sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QyxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7SUFFbkQsbUdBQW1HO0lBQ25HLDRGQUE0RjtJQUM1RixJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDeEIsT0FBTztZQUNMLElBQUksQ0FBQyxHQUFHLENBQ04sY0FBYyxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQ3hCLFNBQVMsQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsRUFBRSxtQkFBUyxFQUFFLHlCQUF5QixNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUNuRjtTQUNGLENBQUM7SUFDSixDQUFDO0lBRUQsc0dBQXNHO0lBQ3RHLG9HQUFvRztJQUNwRyxxR0FBcUc7SUFDckcsZ0dBQWdHO0lBQ2hHLGdHQUFnRztJQUNoRyxvR0FBb0c7SUFDcEcsZ0dBQWdHO0lBQ2hHLE9BQU8sTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUNqQyxJQUFJLENBQUMsR0FBRyxDQUNOLGNBQWMsSUFBSSxDQUFDLEdBQUcsSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEVBQ3JDLFNBQVMsQ0FDUCxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsRUFDdEMsS0FBSyxDQUFDLFNBQVMsRUFDZix5QkFBeUIsTUFBTSxDQUFDLElBQUksQ0FBQyxnREFBZ0Q7UUFDbkYsc0NBQXNDLENBQ3pDLENBQ0YsQ0FDRixDQUFDO0FBQ0osQ0FBQztBQUVELFNBQVMsWUFBWSxDQUNuQixFQUFFLElBQUksRUFBUSxFQUNkLElBQWdEO0lBRWhELE1BQU0sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxVQUFVLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN0QyxNQUFNLE9BQU8sR0FBRyxFQUFFLElBQUksRUFBRSx3QkFBd0IsUUFBUSxFQUFFLEVBQUUsQ0FBQztJQUM3RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3hDLE1BQU0sT0FBTyxHQUNYLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxHQUFHLENBQUM7UUFDMUIsQ0FBQyxDQUFDLHlGQUF5RjtZQUN6Riw4RUFBOEU7WUFDOUUsMEJBQTBCO1FBQzVCLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFFVCxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFO1FBQ3hCLFdBQVcsRUFDVCxlQUFlLE1BQU0sQ0FBQyxJQUFJLENBQUMscURBQXFEO1lBQ2hGLG1GQUFtRjtZQUNuRixpRkFBaUYsT0FBTyxFQUFFO1FBQzVGLEtBQUssRUFBRTtZQUNMLEdBQUcsTUFBTTtZQUNUO2dCQUNFLElBQUksRUFBRSxRQUFRO2dCQUNkLFVBQVUsRUFBRTtvQkFDVixVQUFVLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUU7b0JBQ2pELFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRTtpQkFDOUM7Z0JBQ0QsUUFBUSxFQUFFLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQztnQkFDdEMsR0FBRyxFQUFFLEVBQUUsVUFBVSxFQUFFLEVBQUUsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLENBQUMsT0FBTyxDQUFDLEVBQUU7YUFDeEU7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUM7QUFRRCxTQUFTLFNBQVMsQ0FBQyxJQUFVLEVBQUUsSUFBZ0Q7SUFDN0UsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQztJQUN0QixNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFFekMsTUFBTSxXQUFXLEdBQUcsVUFBVSxDQUM1QixJQUFJLEVBQ0osVUFBVSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQ3BCLE1BQU0sQ0FBQyxXQUFXLEVBQ2xCLGNBQWMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQzlCLENBQUM7SUFFRixvR0FBb0c7SUFDcEcscUdBQXFHO0lBQ3JHLGlHQUFpRztJQUNqRyxtREFBbUQ7SUFDbkQsT0FBTztRQUNMLFdBQVc7UUFDWCxNQUFNLEVBQUUsWUFBWSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUM7UUFDaEMsSUFBSSxFQUNGLE1BQU0sQ0FBQyxXQUFXLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDN0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLDBCQUFnQixDQUFDO1lBQzVDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLGNBQWMsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUNqQyxJQUFJLEVBQUUsUUFBUTtnQkFDZCxXQUFXLEVBQUUsdUNBQXVDO2dCQUNwRCxVQUFVLEVBQUU7b0JBQ1YsS0FBSyxFQUFFLFdBQVc7b0JBQ2xCLFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxFQUFFO2lCQUNyRDtnQkFDRCxRQUFRLEVBQUUsQ0FBQyxPQUFPLENBQUM7YUFDcEIsQ0FBQztLQUNULENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxrQkFBa0IsQ0FBQyxVQUE4QixFQUFFLE9BQWU7SUFDekUsTUFBTSxJQUFJLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksYUFBYSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBRS9GLE9BQU8sR0FBRyxPQUFPLEdBQUcsSUFBSSxFQUFFLENBQUM7QUFDN0IsQ0FBQztBQUVELFNBQVMsZ0JBQWdCLENBQUMsSUFBVSxFQUFFLElBQXNDO0lBQzFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFDdEIsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFDakMsTUFBTSxJQUFJLEdBQUcsU0FBUyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNuQyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSx3QkFBYyxDQUFDLENBQUM7SUFFeEQsT0FBTztRQUNMLElBQUksRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLGVBQWUsR0FBRyxFQUFFLEVBQUU7WUFDbkMsSUFBSSxFQUFFLFFBQVE7WUFDZCxXQUFXLEVBQUUsa0JBQWtCLENBQzdCLFVBQVUsRUFDVix1Q0FBdUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUNsRTtZQUNELFVBQVUsRUFBRTtnQkFDVixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07Z0JBQ25CLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxXQUFXLEVBQUU7Z0JBQ3RELElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUU7Z0JBQ3pDLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxvQkFBVSxDQUFDO2dCQUNwQyxRQUFRO2FBQ1Q7U0FDRixDQUFDO1FBQ0YsS0FBSyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEdBQUcsRUFBRSxFQUFFO1lBQ3JDLElBQUksRUFBRSxRQUFRO1lBQ2QsV0FBVyxFQUFFLGtCQUFrQixDQUM3QixVQUFVLEVBQ1Ysb0JBQW9CLE1BQU0sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUNqRTtZQUNELFVBQVUsRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRTtTQUM5QyxDQUFDO0tBQ0gsQ0FBQztBQUNKLENBQUM7QUFFRCxNQUFNLGVBQWUsR0FBRztJQUN0QixLQUFLLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFpQixFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFpQixFQUFFLENBQUM7Q0FDbEYsQ0FBQztBQUVGOzs7Ozs7R0FNRztBQUNILFNBQVMsY0FBYyxDQUNyQixJQUFtQixFQUNuQixNQUFjLEVBQ2QsV0FBOEI7SUFFOUIsSUFBSSxXQUFXLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLHdCQUFjLENBQUMsQ0FBQztJQUU1RSxJQUFJLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDM0IsT0FBTztZQUNMLDRGQUE0RjtZQUM1RixJQUFJLEVBQUUsUUFBUTtZQUNkLE9BQU8sRUFBRSxLQUFLO1lBQ2QsV0FBVyxFQUNULGtDQUFrQyxNQUFNLENBQUMsTUFBTSxDQUFDLHlCQUF5QjtnQkFDekUsR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYTtnQkFDM0QsR0FBRyxNQUFNLENBQUMsNkJBQW1CLENBQUMsa0JBQWtCO1NBQ25ELENBQUM7SUFDSixDQUFDO0lBRUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLFdBQVcsQ0FBQztJQUUxQixPQUFPO1FBQ0wsR0FBRyxlQUFlO1FBQ2xCLFdBQVcsRUFDVCxjQUFjLE1BQU0sQ0FBQyxNQUFNLENBQUMsZUFBZSxHQUFHLENBQUMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxJQUFJLHFCQUFxQjtZQUNyRiw4RUFBOEU7S0FDakYsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFTLHdCQUF3QixDQUMvQixJQUFVLEVBQ1YsSUFBb0IsRUFDcEIsUUFBMEIsRUFDMUIsV0FBbUIsRUFDbkIsT0FBdUI7SUFFdkIsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQztJQUN0QixNQUFNLFFBQVEsR0FBRyxjQUFjLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDekYsTUFBTSxnQkFBZ0IsR0FBRztRQUN2QixJQUFJLEVBQUUsUUFBaUI7UUFDdkIsVUFBVSxFQUFFLEVBQUUsUUFBUSxFQUFFO1FBQ3hCLFFBQVEsRUFBRSxDQUFDLFVBQVUsQ0FBQztLQUN2QixDQUFDO0lBQ0YsTUFBTSxXQUFXLEdBQ2Ysd0NBQXdDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxnQkFBZ0I7UUFDdkYsaUJBQWlCLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLG9CQUFvQjtRQUMxRixxQ0FBcUMsQ0FBQztJQUV4QyxPQUFPO1FBQ0wsSUFBSSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsdUJBQXVCLFdBQVcsRUFBRSxFQUFFO1lBQ25ELFdBQVc7WUFDWCxLQUFLLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxnQkFBZ0IsQ0FBQztTQUNqRCxDQUFDO1FBQ0YsS0FBSyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsd0JBQXdCLFdBQVcsRUFBRSxFQUFFO1lBQ3JELFdBQVc7WUFDWCxLQUFLLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsQ0FBQztTQUNsRCxDQUFDO0tBQ0gsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFTLGlCQUFpQixDQUFDLEtBQXVDO0lBQ2hFLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxLQUFLO1FBQ3RCLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFpQixFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsS0FBSyxFQUFFO1FBQ2hELENBQUMsQ0FBQyxJQUFBLHVCQUFhLEVBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRTlCLE9BQU8sS0FBSyxDQUFDLFVBQVU7UUFDckIsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsV0FBVyxFQUFFLDREQUE0RCxFQUFFO1FBQ3hGLENBQUMsQ0FBQyxJQUFJLENBQUM7QUFDWCxDQUFDO0FBRUQsU0FBUyxrQkFBa0IsQ0FBQyxNQUFzQjtJQUNoRCxPQUFPO1FBQ0wsSUFBSSxFQUFFLFFBQVE7UUFDZCwrRkFBK0Y7UUFDL0YsMkZBQTJGO1FBQzNGLCtGQUErRjtRQUMvRixtREFBbUQ7UUFDbkQsV0FBVyxFQUNULDJGQUEyRjtZQUMzRiw4REFBOEQ7UUFDaEUsVUFBVSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQzVCLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FDbkU7S0FDRixDQUFDO0FBQ0osQ0FBQztBQUVELFNBQVMscUJBQXFCLENBQzVCLElBQVUsRUFDVixJQUFvQixFQUNwQixNQUFzQixFQUN0QixTQUFpQjtJQUVqQixNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDO0lBRXRCLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsU0FBUyxFQUFFLEVBQUU7UUFDNUMsSUFBSSxFQUFFLFFBQVE7UUFDZCxXQUFXLEVBQ1Qsc0JBQXNCLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUk7WUFDaEYsdUZBQXVGO1lBQ3ZGLG9CQUFvQjtRQUN0QixVQUFVLEVBQUU7WUFDVixTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUN4RixNQUFNLEVBQUUsa0JBQWtCLENBQUMsTUFBTSxDQUFDO1lBQ2xDLFFBQVEsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSx3QkFBYyxDQUFDO1NBQ2pEO1FBQ0QsUUFBUSxFQUFFLENBQUMsV0FBVyxDQUFDO0tBQ3hCLENBQUMsQ0FBQztBQUNMLENBQUM7QUFjRCxTQUFTLGlCQUFpQixDQUFDLElBQVUsRUFBRSxPQUF5QjtJQUM5RCxJQUFJLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUN6QixNQUFNLEVBQUUsTUFBTTtRQUNkLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLElBQUksT0FBTyxDQUFDLElBQUksRUFBRTtRQUN0QyxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVc7UUFDaEMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxPQUFPO1FBQ3hCLFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVztRQUNoQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQVE7UUFDdkIsT0FBTyxFQUFFO1lBQ1AsT0FBTyxFQUFFLElBQUksQ0FBQyxjQUFjO1lBQzVCLElBQUksRUFBRTtnQkFDSixRQUFRLEVBQUUsT0FBTyxDQUFDLFlBQVk7Z0JBQzlCLE9BQU8sRUFBRSxFQUFFLGtCQUFrQixFQUFFLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRTthQUM3RDtTQUNGO1FBQ0QsU0FBUyxFQUFFO1lBQ1QsR0FBRyxFQUFFO2dCQUNILFdBQVcsRUFBRSxPQUFPLENBQUMsbUJBQW1CO2dCQUN4QyxPQUFPLEVBQUUsRUFBRSxrQkFBa0IsRUFBRSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUU7YUFDOUQ7WUFDRCxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLGNBQWMsS0FBSyxJQUFJLENBQUM7U0FDeEQ7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyw0QkFBNEIsQ0FBQyxJQUFVLEVBQUUsSUFBb0I7SUFDcEUsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQztJQUN0QixNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUNqQyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGNBQWMsRUFBRSw0QkFBa0IsQ0FBQyxDQUFDO0lBQ3BFLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsZUFBZSxFQUFFLDZCQUFtQixDQUFDLENBQUM7SUFFdkUsaUJBQWlCLENBQUMsSUFBSSxFQUFFO1FBQ3RCLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTztRQUM3QixXQUFXLEVBQUUsZUFBZSxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3RDLE9BQU8sRUFBRSxtQkFBbUIsSUFBSSxFQUFFO1FBQ2xDLFdBQVcsRUFBRSx3QkFBd0IsTUFBTSxDQUFDLElBQUksQ0FBQyxjQUFjO1FBQy9ELE9BQU8sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUk7UUFDM0IsUUFBUSxFQUFFLFlBQVk7UUFDdEIsbUJBQW1CLEVBQUUsYUFBYSxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVU7UUFDeEQsWUFBWSxFQUFFLEtBQUs7S0FDcEIsQ0FBQyxDQUFDO0lBRUgsaUJBQWlCLENBQUMsSUFBSSxFQUFFO1FBQ3RCLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUTtRQUM5QixXQUFXLEVBQUUsZ0JBQWdCLElBQUksQ0FBQyxHQUFHLEVBQUU7UUFDdkMsT0FBTyxFQUFFLG9CQUFvQixJQUFJLEVBQUU7UUFDbkMsV0FBVyxFQUFFLHlCQUF5QixNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWM7UUFDaEUsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSztRQUM1QixRQUFRLEVBQUUsYUFBYTtRQUN2QixtQkFBbUIsRUFBRSxnQkFBZ0IsTUFBTSxDQUFDLElBQUksQ0FBQyxlQUFlO1FBQ2hFLFlBQVksRUFBRSxLQUFLO0tBQ3BCLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFTLDBCQUEwQixDQUNqQyxJQUFVLEVBQ1YsSUFBb0IsRUFDcEIsV0FBd0MsRUFDeEMsS0FBWTtJQUVaLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFFdEIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFO1FBQzNDLE1BQU0sT0FBTyxHQUFHLFdBQVcsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDNUQsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBRXJCLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLElBQUksUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDMUQsTUFBTSxRQUFRLEdBQUcsd0JBQXdCLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3RGLE1BQU0sTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBRTFFLGlCQUFpQixDQUFDLElBQUksRUFBRTtZQUN0QixJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsY0FBYyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQ2pGLFdBQVcsRUFBRSxzQkFBc0IsV0FBVyxFQUFFO1lBQ2hELE9BQU8sRUFBRSxRQUFRLFFBQVEsQ0FBQyxJQUFJLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUU7WUFDM0QsV0FBVyxFQUFFLGFBQWEsTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLHlCQUF5QixNQUFNLENBQ3RGLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUNyQixtQkFBbUIsTUFBTSxHQUFHO1lBQzdCLE9BQU8sRUFBRSxRQUFRLENBQUMsSUFBSTtZQUN0QixRQUFRLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLEVBQUUsNEJBQWtCLENBQUM7WUFDeEQsbUJBQW1CLEVBQUUscUJBQXFCLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxVQUFVO1lBQ25GLFlBQVksRUFBRSxJQUFJO1NBQ25CLENBQUMsQ0FBQztRQUVILGlCQUFpQixDQUFDLElBQUksRUFBRTtZQUN0QixJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsY0FBYyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRO1lBQ2xGLFdBQVcsRUFBRSx1QkFBdUIsV0FBVyxFQUFFO1lBQ2pELE9BQU8sRUFBRSxTQUFTLFFBQVEsQ0FBQyxJQUFJLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUU7WUFDNUQsV0FBVyxFQUFFLGNBQWMsTUFBTSxDQUMvQixPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FDeEIsNEJBQTRCLE1BQU0sR0FBRztZQUN0QyxPQUFPLEVBQUUsUUFBUSxDQUFDLEtBQUs7WUFDdkIsUUFBUSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsZUFBZSxFQUFFLDZCQUFtQixDQUFDO1lBQzFELG1CQUFtQixFQUFFLHdCQUF3QixNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsZUFBZTtZQUMzRixZQUFZLEVBQUUsSUFBSTtTQUNuQixDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFTLHdCQUF3QixDQUFDLElBQVUsRUFBRSxJQUFvQixFQUFFLEtBQVk7SUFDOUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQ3ZDLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLElBQUksTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDdEQsTUFBTSxPQUFPLEdBQUcscUJBQXFCLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDckUsOEZBQThGO1FBQzlGLHdGQUF3RjtRQUN4RixNQUFNLFFBQVEsR0FBRyw4QkFBOEIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQ3RFLE1BQU0sSUFBSSxHQUFHLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFlBQVksT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBRWhGLGlCQUFpQixDQUFDLElBQUksRUFBRTtZQUN0QixJQUFJLEVBQUUsR0FBRyxJQUFJLE9BQU87WUFDcEIsV0FBVyxFQUFFLGlCQUFpQixTQUFTLEVBQUU7WUFDekMsT0FBTyxFQUFFLG9CQUFvQixNQUFNLENBQUMsSUFBSSxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFO1lBQ3JFLFdBQVcsRUFBRSx3Q0FBd0MsUUFBUSw0REFBNEQ7WUFDekgsT0FBTztZQUNQLFFBQVEsRUFBRSxFQUFFO1lBQ1osbUJBQW1CLEVBQUUsd0JBQXdCO1lBQzdDLFlBQVksRUFBRSxJQUFJO1NBQ25CLENBQUMsQ0FBQztRQUVILGlCQUFpQixDQUFDLElBQUksRUFBRTtZQUN0QixJQUFJLEVBQUUsR0FBRyxJQUFJLFVBQVU7WUFDdkIsV0FBVyxFQUFFLGlCQUFpQixTQUFTLEVBQUU7WUFDekMsT0FBTyxFQUFFLFdBQVcsTUFBTSxDQUFDLElBQUksT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRTtZQUM1RCxXQUFXLEVBQUUsK0JBQStCLFFBQVEseUVBQXlFO1lBQzdILE9BQU87WUFDUCxRQUFRLEVBQUUsRUFBRTtZQUNaLG1CQUFtQixFQUFFLDhCQUE4QjtZQUNuRCxZQUFZLEVBQUUsSUFBSTtZQUNsQixjQUFjLEVBQUUsSUFBSTtTQUNyQixDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUF3QixxQkFBcUIsQ0FBQyxJQUFVLEVBQUUsU0FBb0I7SUFDNUUsa0dBQWtHO0lBQ2xHLCtGQUErRjtJQUMvRiwrRkFBK0Y7SUFDL0YsNkNBQTZDO0lBQzdDLE1BQU0sV0FBVyxHQUFHLElBQUEsZUFBVyxHQUFFLENBQUM7SUFDbEMsTUFBTSxTQUFTLEdBQUcsSUFBQSxlQUFXLEdBQUUsQ0FBQztJQUNoQyxNQUFNLE9BQU8sR0FBRyxJQUFBLGVBQVcsR0FBRSxDQUFDO0lBRTlCLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxFQUFFO1FBQ25ELE1BQU0sR0FBRyxHQUFHLFdBQVcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFekMsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLGdCQUFnQixDQUFDLElBQUksRUFBRSxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDcEYsQ0FBQyxDQUFDLENBQUM7SUFDSCxNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFN0UsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRTtRQUNuQiw0QkFBNEIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDekMsMEJBQTBCLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDL0Qsd0JBQXdCLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNoRCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMifQ==
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { FieldType } from '../read-model/capabilities-cache';
|
|
2
|
+
import type { PrimaryKeyField } from '../read-model/read-model';
|
|
3
|
+
import type { Operator } from '@forestadmin/datasource-toolkit';
|
|
4
|
+
/**
|
|
5
|
+
* The snapshot the OpenAPI generator unfolds. It is plain data on purpose: collecting it hits the
|
|
6
|
+
* network (schema + capabilities per collection), generating from it does not, so the route and the
|
|
7
|
+
* CLI can be proven to emit the same document by feeding them one snapshot.
|
|
8
|
+
*/
|
|
9
|
+
export interface Unfolding {
|
|
10
|
+
collections: UnfoldedCollection[];
|
|
11
|
+
}
|
|
12
|
+
export interface UnfoldedCollection {
|
|
13
|
+
name: string;
|
|
14
|
+
fields: CollectionFields;
|
|
15
|
+
primaryKeys: PrimaryKeyField[];
|
|
16
|
+
/** To-many relations whose foreign collection is exposed too — the only ones with a route. */
|
|
17
|
+
relations: UnfoldedRelation[];
|
|
18
|
+
actions: UnfoldedAction[];
|
|
19
|
+
}
|
|
20
|
+
export interface UnfoldedRelation {
|
|
21
|
+
name: string;
|
|
22
|
+
foreignCollection: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `projectable` is every capability field; `filterable` is the subset carrying operators. They
|
|
26
|
+
* differ: a `ManyToOne` is reported by the agent without operators, so projecting or sorting on it
|
|
27
|
+
* works while filtering on it answers 422 field_not_filterable.
|
|
28
|
+
*
|
|
29
|
+
* `degraded` is set when the field set could not be established. The collection keeps its paths, but
|
|
30
|
+
* their field schemas stay free-form strings — an empty enum would forbid every valid call.
|
|
31
|
+
*/
|
|
32
|
+
export interface CollectionFields {
|
|
33
|
+
projectable: string[];
|
|
34
|
+
filterable: FilterableField[];
|
|
35
|
+
degraded: DegradedReason | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A field the agent reports at least one operator for, with that operator set — the very set the
|
|
39
|
+
* runtime validates a filter leaf against. Normalized to canonical PascalCase and to the
|
|
40
|
+
* `allOperators` order at collect time, so the same capabilities answer always yields the same
|
|
41
|
+
* document whatever order the agent serialized the operators in.
|
|
42
|
+
*/
|
|
43
|
+
export interface FilterableField {
|
|
44
|
+
name: string;
|
|
45
|
+
operators: Operator[];
|
|
46
|
+
}
|
|
47
|
+
export type DegradedReason = 'capabilities_unavailable' | 'no_fields';
|
|
48
|
+
/** True when at least one collection went out without its field set. */
|
|
49
|
+
export declare function hasDegradedCollection(unfolding: Unfolding): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* True when NO collection got its field set — the signature of an unreachable agent rather than of
|
|
52
|
+
* one odd collection. An empty schema is not degraded: there was nothing to enumerate.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isFullyDegraded(unfolding: Unfolding): boolean;
|
|
55
|
+
export interface UnfoldedAction {
|
|
56
|
+
name: string;
|
|
57
|
+
fields: UnfoldedActionField[];
|
|
58
|
+
}
|
|
59
|
+
export interface UnfoldedActionField {
|
|
60
|
+
name: string;
|
|
61
|
+
type: FieldType;
|
|
62
|
+
isRequired: boolean;
|
|
63
|
+
enums: string[] | null;
|
|
64
|
+
}
|
|
65
|
+
export type { FieldType };
|
|
66
|
+
//# sourceMappingURL=unfolding.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasDegradedCollection = hasDegradedCollection;
|
|
4
|
+
exports.isFullyDegraded = isFullyDegraded;
|
|
5
|
+
/** True when at least one collection went out without its field set. */
|
|
6
|
+
function hasDegradedCollection(unfolding) {
|
|
7
|
+
return unfolding.collections.some(collection => collection.fields.degraded !== null);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* True when NO collection got its field set — the signature of an unreachable agent rather than of
|
|
11
|
+
* one odd collection. An empty schema is not degraded: there was nothing to enumerate.
|
|
12
|
+
*/
|
|
13
|
+
function isFullyDegraded(unfolding) {
|
|
14
|
+
return (unfolding.collections.length > 0 &&
|
|
15
|
+
unfolding.collections.every(collection => collection.fields.degraded !== null));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5mb2xkaW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29wZW5hcGkvdW5mb2xkaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBdURBLHNEQUVDO0FBTUQsMENBS0M7QUFkRCx3RUFBd0U7QUFDeEUsU0FBZ0IscUJBQXFCLENBQUMsU0FBb0I7SUFDeEQsT0FBTyxTQUFTLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsUUFBUSxLQUFLLElBQUksQ0FBQyxDQUFDO0FBQ3ZGLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQixlQUFlLENBQUMsU0FBb0I7SUFDbEQsT0FBTyxDQUNMLFNBQVMsQ0FBQyxXQUFXLENBQUMsTUFBTSxHQUFHLENBQUM7UUFDaEMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLFFBQVEsS0FBSyxJQUFJLENBQUMsQ0FDL0UsQ0FBQztBQUNKLENBQUMifQ==
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { CapabilitiesFetcher } from './capabilities-cache';
|
|
2
2
|
export interface AgentCapabilitiesFetcherOptions {
|
|
3
3
|
agentUrl: string;
|
|
4
|
-
token
|
|
4
|
+
/** A borrowed request token, or a factory when the caller can mint one per fetch. */
|
|
5
|
+
token: string | (() => string);
|
|
5
6
|
timeoutMs?: number;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
|
-
* Builds a capabilities fetcher bound to
|
|
9
|
-
*
|
|
9
|
+
* Builds a capabilities fetcher bound to an agent token. The cache calls it only on a miss, so the
|
|
10
|
+
* token of whichever request first populates a collection is the one used.
|
|
11
|
+
*
|
|
12
|
+
* A factory exists for the token because an agent token lives 5 minutes: a caller fanning out over
|
|
13
|
+
* hundreds of collections can outlive a single one, and the tail of that fan-out would fail on an
|
|
14
|
+
* expired token. A request can only borrow its caller's, so it passes the string.
|
|
10
15
|
*/
|
|
11
16
|
export default function createAgentCapabilitiesFetcher({ agentUrl, token, timeoutMs, }: AgentCapabilitiesFetcherOptions): CapabilitiesFetcher;
|
|
12
17
|
//# sourceMappingURL=agent-capabilities-fetcher.d.ts.map
|
|
@@ -7,15 +7,23 @@ exports.default = createAgentCapabilitiesFetcher;
|
|
|
7
7
|
const agent_client_1 = require("@forestadmin/agent-client");
|
|
8
8
|
const create_agent_http_requester_1 = __importDefault(require("../agent/create-agent-http-requester"));
|
|
9
9
|
/**
|
|
10
|
-
* Builds a capabilities fetcher bound to
|
|
11
|
-
*
|
|
10
|
+
* Builds a capabilities fetcher bound to an agent token. The cache calls it only on a miss, so the
|
|
11
|
+
* token of whichever request first populates a collection is the one used.
|
|
12
|
+
*
|
|
13
|
+
* A factory exists for the token because an agent token lives 5 minutes: a caller fanning out over
|
|
14
|
+
* hundreds of collections can outlive a single one, and the tail of that fan-out would fail on an
|
|
15
|
+
* expired token. A request can only borrow its caller's, so it passes the string.
|
|
12
16
|
*/
|
|
13
17
|
function createAgentCapabilitiesFetcher({ agentUrl, token, timeoutMs, }) {
|
|
14
|
-
const
|
|
18
|
+
const clientFor = (bearer) => (0, agent_client_1.createRemoteAgentClient)({
|
|
15
19
|
url: agentUrl,
|
|
16
|
-
token,
|
|
17
|
-
httpRequester: (0, create_agent_http_requester_1.default)(
|
|
20
|
+
token: bearer,
|
|
21
|
+
httpRequester: (0, create_agent_http_requester_1.default)(bearer, agentUrl, timeoutMs),
|
|
18
22
|
});
|
|
19
|
-
|
|
23
|
+
if (typeof token === 'string') {
|
|
24
|
+
const client = clientFor(token);
|
|
25
|
+
return collection => client.collection(collection).capabilities();
|
|
26
|
+
}
|
|
27
|
+
return collection => clientFor(token()).collection(collection).capabilities();
|
|
20
28
|
}
|
|
21
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtY2FwYWJpbGl0aWVzLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9hZ2VudC1jYXBhYmlsaXRpZXMtZmV0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQXFCQSxpREFtQkM7QUF0Q0QsNERBQW9FO0FBRXBFLHVHQUE0RTtBQVM1RTs7Ozs7OztHQU9HO0FBQ0gsU0FBd0IsOEJBQThCLENBQUMsRUFDckQsUUFBUSxFQUNSLEtBQUssRUFDTCxTQUFTLEdBQ3VCO0lBQ2hDLE1BQU0sU0FBUyxHQUFHLENBQUMsTUFBYyxFQUFFLEVBQUUsQ0FDbkMsSUFBQSxzQ0FBdUIsRUFBQztRQUN0QixHQUFHLEVBQUUsUUFBUTtRQUNiLEtBQUssRUFBRSxNQUFNO1FBQ2IsYUFBYSxFQUFFLElBQUEscUNBQXdCLEVBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7S0FDckUsQ0FBQyxDQUFDO0lBRUwsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM5QixNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFaEMsT0FBTyxVQUFVLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDcEUsQ0FBQztJQUVELE9BQU8sVUFBVSxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsWUFBWSxFQUFFLENBQUM7QUFDaEYsQ0FBQyJ9
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Forest column type: a primitive name, a one-element array wrapping another type, a composite
|
|
3
|
+
* `{ fields }`, or a relation marker like `ManyToOne`.
|
|
4
|
+
*/
|
|
5
|
+
export type FieldType = string | FieldType[] | {
|
|
6
|
+
fields: {
|
|
7
|
+
field: string;
|
|
8
|
+
type: FieldType;
|
|
9
|
+
}[];
|
|
10
|
+
};
|
|
1
11
|
export interface CapabilitiesResult {
|
|
2
12
|
fields: {
|
|
3
13
|
name: string;
|
|
4
|
-
type:
|
|
14
|
+
type: FieldType;
|
|
5
15
|
operators?: string[];
|
|
6
16
|
}[];
|
|
7
17
|
}
|
|
@@ -44,4 +44,4 @@ class CapabilitiesCache {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
exports.default = CapabilitiesCache;
|
|
47
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
47
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwYWJpbGl0aWVzLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvY2FwYWJpbGl0aWVzLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsaURBQTRDO0FBMEI1Qzs7O0dBR0c7QUFDSCxNQUFxQixpQkFBaUI7SUFRcEMsWUFBWSxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEtBQUssR0FBRyx5QkFBVSxLQUErQixFQUFFO1FBSmhFLFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztRQUN4QyxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVDLENBQUM7UUFDbkUsZUFBVSxHQUFHLENBQUMsQ0FBQztRQUdyQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQWtCLEVBQUUsT0FBNEI7UUFDeEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0MsSUFBSSxLQUFLLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFNUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDOUMsSUFBSSxPQUFPO1lBQUUsT0FBTyxPQUFPLENBQUM7UUFFNUIsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQztRQUM1QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNiLHlGQUF5RjtZQUN6RixxRkFBcUY7WUFDckYsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbEUsQ0FBQztZQUVELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsQ0FBQzthQUNELE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDWixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEtBQUs7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEYsQ0FBQyxDQUFDLENBQUM7UUFFTCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFckMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0NBQ0Y7QUE3Q0Qsb0NBNkNDIn0=
|