@reventlessdev/reventless-core 3.0.0-alpha.250 → 3.0.0-alpha.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/package.json +7 -7
- package/src/admin/Platform_Admin_Structure.res +4 -1
- package/src/admin/Platform_Admin_Structure.res.mjs +4 -2
- package/src/admin/Platform_BakedManifest.res.mjs +3 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res +31 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +29 -1
- package/src/admin/Platform_PluginStructuresApi.res +80 -8
- package/src/admin/Platform_PluginStructuresApi.res.mjs +76 -2
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs +8 -0
- package/src/components/Api/ApiNoApiHelpers.res +37 -3
- package/src/components/Api/ApiNoApiHelpers.res.mjs +29 -2
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +4 -4
- package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +3 -3
- package/src/plugin/api/PluginBaseFragment.res +3 -3
- package/src/plugin/component/Plugin_Structure.res +787 -384
- package/src/plugin/component/Plugin_Structure.res.mjs +430 -20
- package/src/plugin/connect/PluginConnectExtension_Mapping.res +3 -6
- package/src/plugin/connect/PluginConnectExtension_Mapping.res.mjs +14 -2
- package/src/plugin/connect/PluginExtensionPoint_Plugin.res +0 -1
- package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +63 -3
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res.mjs +22 -3
- package/src/plugin/lifecycle/PluginBehavior.res +11 -11
- package/src/plugin/lifecycle/PluginBehavior.res.mjs +11 -11
- package/src/plugin/lifecycle/PluginSpec.res +11 -3
- package/src/plugin/lifecycle/PluginSpec.res.mjs +11 -3
- package/tests/admin/AdminApiSchemaDriftTest.res +486 -0
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +619 -0
- package/tests/admin/Platform_Admin_StructureTest.res +8 -3
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +8 -2
- package/tests/admin/Platform_BakedManifestTest.res +2 -0
- package/tests/admin/Platform_BakedManifestTest.res.mjs +3 -1
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +10 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +30 -8
- package/tests/admin/Platform_PluginStructuresApiTest.res +28 -1
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +27 -5
- package/tests/aggregate/AggregateCacheTest.res.mjs +15 -3
- package/tests/aggregate/AggregateConflictTest.res.mjs +15 -3
- package/tests/aggregate/AggregateFixtures.res.mjs +15 -3
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +21 -5
- package/tests/api/ApiNoApiTest.res +46 -0
- package/tests/api/ApiNoApiTest.res.mjs +49 -1
- package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +27 -3
- package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +12 -2
- package/tests/commandtopic/CommandOutcomeHookTest.res.mjs +2 -0
- package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +12 -2
- package/tests/dcb/DcbFixtures.res.mjs +8 -0
- package/tests/dcb/DcbStateChangeSliceTest.res.mjs +18 -0
- package/tests/eventmapper/EventMapperFixtures.res.mjs +15 -3
- package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +9 -1
- package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +9 -1
- package/tests/message/MessageTest.res +2 -0
- package/tests/message/MessageTest.res.mjs +3 -1
- package/tests/plugin/PluginStructureAccessTest.res.mjs +2 -0
- package/tests/plugin/PluginStructureTest.res +294 -1
- package/tests/plugin/PluginStructureTest.res.mjs +373 -3
- package/tests/plugin/StateChangeSlice/PsAttachInvoice.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsChangePhoto.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsGatedCommands.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsReserveStock.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsTransitionSwitch.res +59 -0
- package/tests/plugin/StateChangeSlice/PsTransitionSwitch.res.mjs +115 -0
- package/tests/plugin/StateChangeSlice/PsTypoStore.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsUploadAvatar.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsUploadImages.res.mjs +8 -0
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +26 -0
- package/tests/util/CommandPublisherTest.res.mjs +9 -1
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
// Structural guard for the three Platform_* admin queries. Every field of the
|
|
2
|
+
// record an entry is built from must reach both the SDL and the encoder, or be
|
|
3
|
+
// named below as a deliberate divergence. The tests beside this one pin values;
|
|
4
|
+
// this one pins the field SET — the thing that drifted silently when `chapter`
|
|
5
|
+
// and `externalSystem` were encoded for months with no SDL line to select them.
|
|
6
|
+
|
|
7
|
+
open JestGlobals
|
|
8
|
+
open Reventless.Plugin
|
|
9
|
+
|
|
10
|
+
// ── Field-name extraction ───────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
// The SDL blocks are one string per type, `type X {\n field: T\n}`. A line with
|
|
13
|
+
// no colon is the header or the brace.
|
|
14
|
+
let sdlFieldNames = (~typeName: string, sdlTypes: array<string>): array<string> =>
|
|
15
|
+
sdlTypes
|
|
16
|
+
->Array.find(block => block->String.startsWith(`type ${typeName} {`))
|
|
17
|
+
->Option.mapOr([], block =>
|
|
18
|
+
block
|
|
19
|
+
->String.split("\n")
|
|
20
|
+
->Array.filterMap(line => {
|
|
21
|
+
let trimmed = line->String.trim
|
|
22
|
+
switch trimmed->String.indexOf(":") {
|
|
23
|
+
| -1 => None
|
|
24
|
+
| i => Some(trimmed->String.slice(~start=0, ~end=i))
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
let jsonKeys = (j: JSON.t): array<string> =>
|
|
30
|
+
switch j {
|
|
31
|
+
| JSON.Object(d) => d->Dict.keysToArray
|
|
32
|
+
| _ => []
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let schemaKeys = (schema: S.t<'a>): array<string> =>
|
|
36
|
+
SchemaType.fromSuryObject(~typeName="", schema->S.castToUnknown)->Option.mapOr(
|
|
37
|
+
[],
|
|
38
|
+
Dict.keysToArray,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
// Nested types (`handledEvents`, `publishedEvents`) are encoded inline by their
|
|
42
|
+
// parent, so their wire keys are read off the first element rather than from an
|
|
43
|
+
// encoder of their own.
|
|
44
|
+
let firstOf = (j: JSON.t, key: string): JSON.t =>
|
|
45
|
+
switch j {
|
|
46
|
+
| JSON.Object(d) =>
|
|
47
|
+
switch d->Dict.get(key) {
|
|
48
|
+
| Some(JSON.Array(items)) => items->Array.get(0)->Option.getOr(JSON.Encode.null)
|
|
49
|
+
| _ => JSON.Encode.null
|
|
50
|
+
}
|
|
51
|
+
| _ => JSON.Encode.null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let missing = (names: array<string>, from: array<string>): array<string> =>
|
|
55
|
+
names->Array.filter(n => !(from->Array.includes(n)))
|
|
56
|
+
|
|
57
|
+
// ── Fixtures: every optional field populated, so no key can go missing ───────
|
|
58
|
+
|
|
59
|
+
let fieldRef: fieldReference = {
|
|
60
|
+
fieldName: "categoryId",
|
|
61
|
+
entity: "Category",
|
|
62
|
+
plugin: Some("Catalog"),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let cmd: commandDef = {
|
|
66
|
+
name: "Add",
|
|
67
|
+
schema: "{}",
|
|
68
|
+
level: Collection,
|
|
69
|
+
aggregateIdField: Some("productId"),
|
|
70
|
+
mutationField: "Catalog_Product_Add",
|
|
71
|
+
references: [fieldRef],
|
|
72
|
+
allowedStates: Some(["Draft"]),
|
|
73
|
+
targetState: Some("Active"),
|
|
74
|
+
apiExposed: Some(true),
|
|
75
|
+
requiredAccess: Some(["catalog.write"]),
|
|
76
|
+
ownerField: Some("ownerId"),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let evt: eventDef = {name: "ProductAdded", schema: "{}", references: [fieldRef]}
|
|
80
|
+
let err: errorDef = {name: "ProductAlreadyExists", schema: "{}", references: []}
|
|
81
|
+
|
|
82
|
+
let writable: writableDef = {
|
|
83
|
+
name: "Product",
|
|
84
|
+
commands: [cmd],
|
|
85
|
+
producedEventTypes: ["ProductAdded"],
|
|
86
|
+
consumedEventTypes: [],
|
|
87
|
+
linkedViews: ["Products"],
|
|
88
|
+
consistencyRead: Some("Products"),
|
|
89
|
+
events: [evt],
|
|
90
|
+
errors: [err],
|
|
91
|
+
chapter: Some("Catalogue"),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let queryable: queryableDef = {
|
|
95
|
+
name: "Products",
|
|
96
|
+
queryField: "Catalog_Products",
|
|
97
|
+
schema: "{}",
|
|
98
|
+
consumedEventTypes: ["ProductAdded"],
|
|
99
|
+
linkedWriteSide: ["Product"],
|
|
100
|
+
labelField: "displayName",
|
|
101
|
+
searchableFields: ["displayName"],
|
|
102
|
+
labelFieldSource: Some("annotation"),
|
|
103
|
+
lifecycleField: Some("lifecycle"),
|
|
104
|
+
ownerField: Some("ownerId"),
|
|
105
|
+
retiredField: Some("archived"),
|
|
106
|
+
retiredValues: Some(["Archived"]),
|
|
107
|
+
namedWhenRetired: Some(true),
|
|
108
|
+
visibility: None,
|
|
109
|
+
chapter: Some("Catalogue"),
|
|
110
|
+
singleQueryField: Some("Catalog_Product"),
|
|
111
|
+
idField: Some("productId"),
|
|
112
|
+
idFieldSource: Some("convention"),
|
|
113
|
+
requiredAccess: Some(["catalog.read"]),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let automation: automationSliceDef = {
|
|
117
|
+
name: "Restocker",
|
|
118
|
+
consumedEventTypes: ["StockLow"],
|
|
119
|
+
producedCommandTypes: ["Restock"],
|
|
120
|
+
targetName: "Product",
|
|
121
|
+
chapter: Some("Replenishment"),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let outbound: outboundTranslationSliceDef = {
|
|
125
|
+
name: "ToShipper",
|
|
126
|
+
consumedEventTypes: ["OrderPlaced"],
|
|
127
|
+
inboundCommandTypes: ["Ship"],
|
|
128
|
+
targetName: Some("Shipment"),
|
|
129
|
+
externalSystem: Some("Shipper"),
|
|
130
|
+
chapter: Some("Fulfilment"),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let inbound: inboundTranslationSliceDef = {
|
|
134
|
+
name: "FromPsp",
|
|
135
|
+
commandTypes: ["RecordPayment"],
|
|
136
|
+
targetName: "Order",
|
|
137
|
+
externalSystem: Some("Psp"),
|
|
138
|
+
chapter: Some("Payments"),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let extension: extensionDef = {
|
|
142
|
+
name: "Catalog.Products",
|
|
143
|
+
delegateNames: ["Product"],
|
|
144
|
+
eventTypes: ["ProductAdded"],
|
|
145
|
+
commandTypes: ["Add"],
|
|
146
|
+
handledEvents: Some([{name: "ProductAdded", toCommandTypes: ["Ordering.SyncNewProduct"]}]),
|
|
147
|
+
issuedCommands: Some([{name: "Catalog.Products.Ack", fromEventTypes: ["Ordering.Synced"]}]),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let extensionPoint: extensionPointDef = {
|
|
151
|
+
name: "Catalog.Products",
|
|
152
|
+
delegateNames: ["Product"],
|
|
153
|
+
sourceEventTypes: ["Catalog.ProductAdded"],
|
|
154
|
+
commandTypes: Some(["Add"]),
|
|
155
|
+
publishedEvents: Some([
|
|
156
|
+
{name: "Catalog.Products.ProductAdded", fromEventTypes: ["Catalog.ProductAdded"]},
|
|
157
|
+
]),
|
|
158
|
+
acceptedCommands: Some([{name: "Catalog.Products.Add", toCommandTypes: ["Catalog.Add"]}]),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let storeDecl: requiredStoreDeclaration = {
|
|
162
|
+
store: "catalog.images",
|
|
163
|
+
component: "Product",
|
|
164
|
+
field: "image",
|
|
165
|
+
annotation: Some("images"),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let capabilityDecl: requiredCapabilityDeclaration = {
|
|
169
|
+
capability: "Geocoding",
|
|
170
|
+
component: "GeocodeAddress",
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let structure: pluginStructure = {
|
|
174
|
+
readModels: [queryable],
|
|
175
|
+
stateViewSlices: [],
|
|
176
|
+
stateChangeSlices: [],
|
|
177
|
+
aggregates: [writable],
|
|
178
|
+
automationSlices: [automation],
|
|
179
|
+
outboundTranslationSlices: [outbound],
|
|
180
|
+
inboundTranslationSlices: [inbound],
|
|
181
|
+
extensions: [extension],
|
|
182
|
+
extensionPoints: Some([extensionPoint]),
|
|
183
|
+
requiredStores: Some(["catalog.images"]),
|
|
184
|
+
requiredStoreDeclarations: Some([storeDecl]),
|
|
185
|
+
requiredCapabilities: Some([capabilityDecl]),
|
|
186
|
+
traitDeclarations: None,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let panel: panelManifestEntry = {
|
|
190
|
+
fragmentId: "Catalog.Products.list",
|
|
191
|
+
title: "Products",
|
|
192
|
+
description: "",
|
|
193
|
+
positions: ["platform-summary"],
|
|
194
|
+
requiredAccess: Some("admin"),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let menu: menuEntry = {
|
|
198
|
+
label: "Products",
|
|
199
|
+
icon: Some("box"),
|
|
200
|
+
group: Some("Catalog"),
|
|
201
|
+
sortOrder: 0,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let page: pageManifestEntry = {
|
|
205
|
+
fragmentId: "Catalog.Products.list",
|
|
206
|
+
title: "Products",
|
|
207
|
+
menuEntry: menu,
|
|
208
|
+
requiredAccess: Some("admin"),
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let uiState: UiFragments.state = {
|
|
212
|
+
pluginId: "Catalog",
|
|
213
|
+
remoteEntryUrl: "https://cdn.example.test/catalog@1.0/remoteEntry.js",
|
|
214
|
+
panels: [panel],
|
|
215
|
+
pages: [page],
|
|
216
|
+
registeredAt: "2026-01-01T00:00:00Z",
|
|
217
|
+
updatedAt: "2026-01-01T00:00:00Z",
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── Guards ──────────────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
type guard = {
|
|
223
|
+
gqlType: string,
|
|
224
|
+
sdlTypes: array<string>,
|
|
225
|
+
recordFields: array<string>,
|
|
226
|
+
wireFields: array<string>,
|
|
227
|
+
/** Wire fields the encoder computes, with no counterpart on the record. */
|
|
228
|
+
wireOnly: array<string>,
|
|
229
|
+
/** Record fields deliberately kept off this query's wire. */
|
|
230
|
+
recordOnly: array<string>,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let componentSdl = Platform_ComponentDefinitionsApi.sdlTypes
|
|
234
|
+
let structuresSdl = Platform_PluginStructuresApi.sdlTypes
|
|
235
|
+
let fragmentsSdl = Platform_UIFragmentsApi.sdlTypes
|
|
236
|
+
|
|
237
|
+
let encodedExtension = Platform_ComponentDefinitionsApi.encodeExtensionDef(extension)
|
|
238
|
+
let encodedExtensionPoint = Platform_PluginStructuresApi.encodeExtensionPointDef(extensionPoint)
|
|
239
|
+
|
|
240
|
+
let guards: array<guard> = [
|
|
241
|
+
{
|
|
242
|
+
gqlType: "Platform_FieldReference",
|
|
243
|
+
sdlTypes: componentSdl,
|
|
244
|
+
recordFields: schemaKeys(fieldReferenceSchema),
|
|
245
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeFieldReference(fieldRef)),
|
|
246
|
+
wireOnly: [],
|
|
247
|
+
recordOnly: [],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
gqlType: "Platform_CommandDef",
|
|
251
|
+
sdlTypes: componentSdl,
|
|
252
|
+
recordFields: schemaKeys(commandDefSchema),
|
|
253
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeCommandDef(cmd)),
|
|
254
|
+
wireOnly: [],
|
|
255
|
+
recordOnly: [],
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
gqlType: "Platform_EventDef",
|
|
259
|
+
sdlTypes: componentSdl,
|
|
260
|
+
recordFields: schemaKeys(eventDefSchema),
|
|
261
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeEventDef(evt)),
|
|
262
|
+
wireOnly: [],
|
|
263
|
+
recordOnly: [],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
gqlType: "Platform_ErrorDef",
|
|
267
|
+
sdlTypes: componentSdl,
|
|
268
|
+
recordFields: schemaKeys(errorDefSchema),
|
|
269
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeErrorDef(err)),
|
|
270
|
+
wireOnly: [],
|
|
271
|
+
recordOnly: [],
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
gqlType: "Platform_WriteSideDef",
|
|
275
|
+
sdlTypes: componentSdl,
|
|
276
|
+
recordFields: schemaKeys(writableDefSchema),
|
|
277
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeWritableDef(writable)),
|
|
278
|
+
wireOnly: [],
|
|
279
|
+
recordOnly: [],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
gqlType: "Platform_ReadSideDef",
|
|
283
|
+
sdlTypes: componentSdl,
|
|
284
|
+
recordFields: schemaKeys(queryableDefSchema),
|
|
285
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeQueryableDef(queryable)),
|
|
286
|
+
wireOnly: [],
|
|
287
|
+
recordOnly: [],
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
gqlType: "Platform_AutomationSliceDef",
|
|
291
|
+
sdlTypes: componentSdl,
|
|
292
|
+
recordFields: schemaKeys(automationSliceDefSchema),
|
|
293
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeAutomationSliceDef(automation)),
|
|
294
|
+
wireOnly: [],
|
|
295
|
+
recordOnly: [],
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
gqlType: "Platform_OutboundTranslationSliceDef",
|
|
299
|
+
sdlTypes: componentSdl,
|
|
300
|
+
recordFields: schemaKeys(outboundTranslationSliceDefSchema),
|
|
301
|
+
wireFields: jsonKeys(
|
|
302
|
+
Platform_ComponentDefinitionsApi.encodeOutboundTranslationSliceDef(outbound),
|
|
303
|
+
),
|
|
304
|
+
wireOnly: [],
|
|
305
|
+
recordOnly: [],
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
gqlType: "Platform_InboundTranslationSliceDef",
|
|
309
|
+
sdlTypes: componentSdl,
|
|
310
|
+
recordFields: schemaKeys(inboundTranslationSliceDefSchema),
|
|
311
|
+
wireFields: jsonKeys(
|
|
312
|
+
Platform_ComponentDefinitionsApi.encodeInboundTranslationSliceDef(inbound),
|
|
313
|
+
),
|
|
314
|
+
wireOnly: [],
|
|
315
|
+
recordOnly: [],
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
gqlType: "Platform_ExtensionDef",
|
|
319
|
+
sdlTypes: componentSdl,
|
|
320
|
+
recordFields: schemaKeys(extensionDefSchema),
|
|
321
|
+
wireFields: jsonKeys(encodedExtension),
|
|
322
|
+
wireOnly: [],
|
|
323
|
+
recordOnly: [],
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
gqlType: "Platform_HandledEventDef",
|
|
327
|
+
sdlTypes: componentSdl,
|
|
328
|
+
recordFields: schemaKeys(handledEventDefSchema),
|
|
329
|
+
wireFields: jsonKeys(encodedExtension->firstOf("handledEvents")),
|
|
330
|
+
wireOnly: [],
|
|
331
|
+
recordOnly: [],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
gqlType: "Platform_ComponentDefinitionEntry",
|
|
335
|
+
sdlTypes: componentSdl,
|
|
336
|
+
recordFields: schemaKeys(pluginStructureSchema),
|
|
337
|
+
wireFields: jsonKeys(
|
|
338
|
+
structure->Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
339
|
+
~pluginId="Catalog",
|
|
340
|
+
_,
|
|
341
|
+
),
|
|
342
|
+
),
|
|
343
|
+
// `pluginId` names the entry; `internalQueryables` is the complement of the
|
|
344
|
+
// Internal filter, computed from the two lists beside it.
|
|
345
|
+
wireOnly: ["pluginId", "internalQueryables"],
|
|
346
|
+
// Producer-side and store fields belong to Platform_PluginStructures — the
|
|
347
|
+
// deployed AutoUI has no use for either.
|
|
348
|
+
recordOnly: [
|
|
349
|
+
"extensionPoints",
|
|
350
|
+
"requiredStores",
|
|
351
|
+
"requiredStoreDeclarations",
|
|
352
|
+
"requiredCapabilities",
|
|
353
|
+
// Structure-level provenance, like the three above: AutoUI renders
|
|
354
|
+
// components, and where one came from is the map's question rather than a
|
|
355
|
+
// generated page's. It reaches the wire on `Platform_PluginStructures`.
|
|
356
|
+
"traitDeclarations",
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
gqlType: "Platform_PublishedEventDef",
|
|
361
|
+
sdlTypes: structuresSdl,
|
|
362
|
+
recordFields: schemaKeys(publishedEventDefSchema),
|
|
363
|
+
wireFields: jsonKeys(encodedExtensionPoint->firstOf("publishedEvents")),
|
|
364
|
+
wireOnly: [],
|
|
365
|
+
recordOnly: [],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
gqlType: "Platform_ExtensionPointDef",
|
|
369
|
+
sdlTypes: structuresSdl,
|
|
370
|
+
recordFields: schemaKeys(extensionPointDefSchema),
|
|
371
|
+
wireFields: jsonKeys(encodedExtensionPoint),
|
|
372
|
+
wireOnly: [],
|
|
373
|
+
recordOnly: [],
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
gqlType: "Platform_RequiredStoreDeclaration",
|
|
377
|
+
sdlTypes: structuresSdl,
|
|
378
|
+
recordFields: schemaKeys(requiredStoreDeclarationSchema),
|
|
379
|
+
wireFields: jsonKeys(Platform_PluginStructuresApi.encodeRequiredStoreDeclaration(storeDecl)),
|
|
380
|
+
wireOnly: [],
|
|
381
|
+
recordOnly: [],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
gqlType: "Platform_RequiredCapabilityDeclaration",
|
|
385
|
+
sdlTypes: structuresSdl,
|
|
386
|
+
recordFields: schemaKeys(requiredCapabilityDeclarationSchema),
|
|
387
|
+
wireFields: jsonKeys(
|
|
388
|
+
Platform_PluginStructuresApi.encodeRequiredCapabilityDeclaration(capabilityDecl),
|
|
389
|
+
),
|
|
390
|
+
wireOnly: [],
|
|
391
|
+
recordOnly: [],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
gqlType: "Platform_PluginStructureEntry",
|
|
395
|
+
sdlTypes: structuresSdl,
|
|
396
|
+
recordFields: schemaKeys(pluginStructureSchema),
|
|
397
|
+
wireFields: jsonKeys(
|
|
398
|
+
Platform_PluginStructuresApi.encodePluginStructureEntry(~pluginId="Catalog", structure),
|
|
399
|
+
),
|
|
400
|
+
wireOnly: ["pluginId"],
|
|
401
|
+
// Nothing: this query is the unfiltered structure, by contract.
|
|
402
|
+
recordOnly: [],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
gqlType: "Platform_UIPanel",
|
|
406
|
+
sdlTypes: fragmentsSdl,
|
|
407
|
+
recordFields: schemaKeys(panelManifestEntrySchema),
|
|
408
|
+
wireFields: jsonKeys(Platform_UIFragmentsApi.encodePanel(panel)),
|
|
409
|
+
wireOnly: [],
|
|
410
|
+
recordOnly: [],
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
gqlType: "Platform_UIMenuEntry",
|
|
414
|
+
sdlTypes: fragmentsSdl,
|
|
415
|
+
recordFields: schemaKeys(menuEntrySchema),
|
|
416
|
+
wireFields: jsonKeys(Platform_UIFragmentsApi.encodeMenuEntry(menu)),
|
|
417
|
+
wireOnly: [],
|
|
418
|
+
recordOnly: [],
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
gqlType: "Platform_UIPage",
|
|
422
|
+
sdlTypes: fragmentsSdl,
|
|
423
|
+
recordFields: schemaKeys(pageManifestEntrySchema),
|
|
424
|
+
wireFields: jsonKeys(Platform_UIFragmentsApi.encodePage(page)),
|
|
425
|
+
wireOnly: [],
|
|
426
|
+
recordOnly: [],
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
gqlType: "Platform_UIFragmentEntry",
|
|
430
|
+
sdlTypes: fragmentsSdl,
|
|
431
|
+
recordFields: schemaKeys(UiFragments.stateSchema),
|
|
432
|
+
wireFields: jsonKeys(Platform_UIFragmentsApi.encodeUIFragmentEntry(uiState)),
|
|
433
|
+
wireOnly: [],
|
|
434
|
+
recordOnly: [],
|
|
435
|
+
},
|
|
436
|
+
]
|
|
437
|
+
|
|
438
|
+
guards->Array.forEach(g => {
|
|
439
|
+
let sdlFields = sdlFieldNames(~typeName=g.gqlType, g.sdlTypes)
|
|
440
|
+
describe(g.gqlType, () => {
|
|
441
|
+
// Without this, a renamed GraphQL type or an unreadable schema would make
|
|
442
|
+
// every assertion below pass over two empty lists.
|
|
443
|
+
testSync("the SDL block and the record schema are both readable", () => {
|
|
444
|
+
expect(sdlFields->Array.length > 0)->toEqual(true)
|
|
445
|
+
expect(g.recordFields->Array.length > 0)->toEqual(true)
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
testSync("every encoded field is selectable", () =>
|
|
449
|
+
expect(missing(g.wireFields, sdlFields))->toEqual([])
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
testSync("every declared field is encoded", () =>
|
|
453
|
+
expect(missing(sdlFields, g.wireFields))->toEqual([])
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
testSync("every record field reaches the wire", () =>
|
|
457
|
+
expect(missing(g.recordFields, Array.concat(sdlFields, g.recordOnly)))->toEqual([])
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
testSync("the wire carries nothing the record and the divergences do not name", () =>
|
|
461
|
+
expect(missing(sdlFields, Array.concat(g.recordFields, g.wireOnly)))->toEqual([])
|
|
462
|
+
)
|
|
463
|
+
})
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
// `encodePluginStructureEntry` serves two consumers: the query resolvers, which
|
|
467
|
+
// pass no `~derived`, and the baked manifest, which does. `derivedPages` is
|
|
468
|
+
// therefore absent from the SDL by design, and the guard above compares the
|
|
469
|
+
// resolver's call.
|
|
470
|
+
describe("derivedPages", () => {
|
|
471
|
+
testSync("is emitted only for the baked manifest, and is not selectable", () => {
|
|
472
|
+
let baked =
|
|
473
|
+
structure->Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
474
|
+
~pluginId="Catalog",
|
|
475
|
+
~derived=["dashboard"],
|
|
476
|
+
_,
|
|
477
|
+
)
|
|
478
|
+
expect(jsonKeys(baked)->Array.includes("derivedPages"))->toEqual(true)
|
|
479
|
+
expect(
|
|
480
|
+
sdlFieldNames(
|
|
481
|
+
~typeName="Platform_ComponentDefinitionEntry",
|
|
482
|
+
componentSdl,
|
|
483
|
+
)->Array.includes("derivedPages"),
|
|
484
|
+
)->toEqual(false)
|
|
485
|
+
})
|
|
486
|
+
})
|