@reventlessdev/reventless-core 3.0.0-alpha.251 → 3.0.0-alpha.253
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 +23 -0
- package/package.json +8 -8
- 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_PluginStructuresApi.res +43 -7
- package/src/admin/Platform_PluginStructuresApi.res.mjs +47 -1
- 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 +158 -25
- package/src/plugin/component/Plugin_Structure.res.mjs +82 -18
- package/src/plugin/connect/PluginConnectExtension_Mapping.res.mjs +3 -1
- package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +3 -1
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res.mjs +3 -1
- package/src/plugin/lifecycle/PluginBehavior.res +11 -11
- package/src/plugin/lifecycle/PluginBehavior.res.mjs +11 -11
- package/src/plugin/lifecycle/PluginSpec.res +39 -16
- package/src/plugin/lifecycle/PluginSpec.res.mjs +54 -56
- package/tests/admin/AdminApiSchemaDriftTest.res +27 -1
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +22 -2
- 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 +4 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +21 -7
- package/tests/admin/Platform_PluginStructuresApiTest.res +16 -0
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +17 -3
- 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 +191 -12
- package/tests/plugin/PluginStructureTest.res.mjs +246 -6
- package/tests/plugin/StateChangeSlice/PsAttachInvoice.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsChangePhoto.res.mjs +8 -0
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res +17 -7
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs +12 -11
- 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 +17 -13
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +17 -11
- package/tests/plugin/StateChangeSlice/PsTransitionSwitch.res +54 -0
- package/tests/plugin/StateChangeSlice/PsTransitionSwitch.res.mjs +104 -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/StateViewSlice/PsShipmentsView.res +1 -1
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +18 -0
- package/tests/util/CommandPublisherTest.res.mjs +9 -1
- package/src/components/Api/ApiAllowedStatesHelpers.res +0 -18
- package/src/components/Api/ApiAllowedStatesHelpers.res.mjs +0 -16
- package/src/components/Api/ApiTargetStateHelpers.res +0 -21
- package/src/components/Api/ApiTargetStateHelpers.res.mjs +0 -16
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
open Reventless.Plugin
|
|
4
4
|
|
|
5
|
-
// Alias, not `open`: the
|
|
6
|
-
//
|
|
5
|
+
// Alias, not `open`: the lifecycle states are named qualified in
|
|
6
|
+
// `commandTransition` below, and an `open` would pull the whole view's namespace
|
|
7
|
+
// in for four constructors.
|
|
7
8
|
module Plugins = PluginsReadModelSpec
|
|
8
9
|
|
|
9
10
|
// The Plugin aggregate is keyed by plugin **name** (not name@version). One
|
|
@@ -15,10 +16,7 @@ module Plugins = PluginsReadModelSpec
|
|
|
15
16
|
// more same-shaped ones, so the `pluginDefinition` pair must lead (DZakh/sury#392).
|
|
16
17
|
@schema
|
|
17
18
|
type command =
|
|
18
|
-
|
|
19
|
-
// — `decide` accepts it against an unknown version and against every status
|
|
20
|
-
// but `Connected`, where it is a definition refresh rather than a move.
|
|
21
|
-
| @noApi @transition(() => Plugins.Connected) Connect(pluginDefinition)
|
|
19
|
+
| @noApi Connect(pluginDefinition)
|
|
22
20
|
// Records a protocol-version incompatibility without changing connection state.
|
|
23
21
|
| @noApi ReportIncompatibility(pluginDefinition)
|
|
24
22
|
| @noApi Heartbeat(version)
|
|
@@ -26,20 +24,20 @@ type command =
|
|
|
26
24
|
// re-run for an already-connected version so its definition is refreshed on the
|
|
27
25
|
// row — unlike Heartbeat, which no-ops a connected keep-alive.
|
|
28
26
|
| @noApi Redetect(version)
|
|
29
|
-
|
|
30
|
-
// `decide` moves the row for exactly one status — anything else is a stray
|
|
31
|
-
// disconnect it tolerates without emitting.
|
|
32
|
-
| @noApi @transition(([Plugins.Connected]) => Plugins.Disconnected) Disconnect(version)
|
|
27
|
+
| @noApi Disconnect(version)
|
|
33
28
|
// Admin lifecycle commands — API-exposed (auto-derived admin mutations,
|
|
34
29
|
// Cognito group-gated). `version` selects which known version to act on.
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
//
|
|
31
|
+
// Inline records, unlike the `@noApi` commands above, and the difference is the
|
|
32
|
+
// API. A positional payload publishes its argument as `_0`, which names nothing:
|
|
33
|
+
// a caller cannot match it against a field it holds, and a coercion failure says
|
|
34
|
+
// `Variable '_0'` rather than what was missing. These three are the ones a person
|
|
35
|
+
// calls, so their argument carries its own name.
|
|
36
|
+
| Activate({version: version})
|
|
37
|
+
| Deactivate({version: version})
|
|
39
38
|
// Manual admin retirement of a specific version (archive). Distinct from the
|
|
40
39
|
// automatic `Superseded` transition (which is decided, not commanded).
|
|
41
|
-
|
|
|
42
|
-
Retire(version)
|
|
40
|
+
| Retire({version: version})
|
|
43
41
|
|
|
44
42
|
// Carries both the superseded and the superseding version's full definition —
|
|
45
43
|
// a deterministic trigger for version-to-version schema/data migrations
|
|
@@ -83,3 +81,28 @@ type error =
|
|
|
83
81
|
| AlreadyConnected
|
|
84
82
|
| IsDisconnected
|
|
85
83
|
| IsInactive
|
|
84
|
+
|
|
85
|
+
// The edges must agree with `decide`; `Platform_Admin_Structure` reads them off
|
|
86
|
+
// the assembled structure rather than restating them.
|
|
87
|
+
type lifecycleState = Plugins.status
|
|
88
|
+
|
|
89
|
+
let commandTransition = (command: command): Reventless.Transition.t<lifecycleState> => {
|
|
90
|
+
open Reventless.Transition
|
|
91
|
+
switch command {
|
|
92
|
+
// The handshake brings the version's row into being, so it runs from no state
|
|
93
|
+
// — `decide` accepts it against an unknown version and against every status
|
|
94
|
+
// but `Connected`, where it is a definition refresh rather than a move.
|
|
95
|
+
| Connect(_) => Creates(Plugins.Connected)
|
|
96
|
+
// Neither records a move: an incompatibility is noted beside the row, and a
|
|
97
|
+
// heartbeat or a re-detect refreshes one that is already where it belongs.
|
|
98
|
+
| ReportIncompatibility(_) | Heartbeat(_) | Redetect(_) => Unrestricted
|
|
99
|
+
// A heartbeat timing out is the only way a row reaches `Disconnected`, and
|
|
100
|
+
// `decide` moves the row for exactly one status — anything else is a stray
|
|
101
|
+
// disconnect it tolerates without emitting.
|
|
102
|
+
| Disconnect(_) => Moves([Plugins.Connected], Plugins.Disconnected)
|
|
103
|
+
| Activate(_) => Moves([Plugins.Inactive, Plugins.Retired], Plugins.Connected)
|
|
104
|
+
| Deactivate(_) => Moves([Plugins.Connected, Plugins.Disconnected], Plugins.Inactive)
|
|
105
|
+
| Retire(_) =>
|
|
106
|
+
Moves([Plugins.Connected, Plugins.Disconnected, Plugins.Inactive], Plugins.Retired)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -27,15 +27,15 @@ let commandSchema = Sury.union([
|
|
|
27
27
|
})),
|
|
28
28
|
Sury.$schema(s => ({
|
|
29
29
|
TAG: "Activate",
|
|
30
|
-
|
|
30
|
+
version: s.m(Plugin$Reventless.versionSchema)
|
|
31
31
|
})),
|
|
32
32
|
Sury.$schema(s => ({
|
|
33
33
|
TAG: "Deactivate",
|
|
34
|
-
|
|
34
|
+
version: s.m(Plugin$Reventless.versionSchema)
|
|
35
35
|
})),
|
|
36
36
|
Sury.$schema(s => ({
|
|
37
37
|
TAG: "Retire",
|
|
38
|
-
|
|
38
|
+
version: s.m(Plugin$Reventless.versionSchema)
|
|
39
39
|
}))
|
|
40
40
|
]);
|
|
41
41
|
|
|
@@ -92,6 +92,52 @@ let errorSchema = Sury.union([
|
|
|
92
92
|
Sury.literal("IsInactive")
|
|
93
93
|
]);
|
|
94
94
|
|
|
95
|
+
function commandTransition(command) {
|
|
96
|
+
switch (command.TAG) {
|
|
97
|
+
case "Connect" :
|
|
98
|
+
return {
|
|
99
|
+
TAG: "Creates",
|
|
100
|
+
_0: "Connected"
|
|
101
|
+
};
|
|
102
|
+
case "Disconnect" :
|
|
103
|
+
return {
|
|
104
|
+
TAG: "Moves",
|
|
105
|
+
_0: ["Connected"],
|
|
106
|
+
_1: "Disconnected"
|
|
107
|
+
};
|
|
108
|
+
case "Activate" :
|
|
109
|
+
return {
|
|
110
|
+
TAG: "Moves",
|
|
111
|
+
_0: [
|
|
112
|
+
"Inactive",
|
|
113
|
+
"Retired"
|
|
114
|
+
],
|
|
115
|
+
_1: "Connected"
|
|
116
|
+
};
|
|
117
|
+
case "Deactivate" :
|
|
118
|
+
return {
|
|
119
|
+
TAG: "Moves",
|
|
120
|
+
_0: [
|
|
121
|
+
"Connected",
|
|
122
|
+
"Disconnected"
|
|
123
|
+
],
|
|
124
|
+
_1: "Inactive"
|
|
125
|
+
};
|
|
126
|
+
case "Retire" :
|
|
127
|
+
return {
|
|
128
|
+
TAG: "Moves",
|
|
129
|
+
_0: [
|
|
130
|
+
"Connected",
|
|
131
|
+
"Disconnected",
|
|
132
|
+
"Inactive"
|
|
133
|
+
],
|
|
134
|
+
_1: "Retired"
|
|
135
|
+
};
|
|
136
|
+
default:
|
|
137
|
+
return "Unrestricted";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
95
141
|
let commandSchema$1 = Api$ReventlessInfra.markNoApiVariants(commandSchema, [
|
|
96
142
|
"Connect",
|
|
97
143
|
"ReportIncompatibility",
|
|
@@ -100,62 +146,12 @@ let commandSchema$1 = Api$ReventlessInfra.markNoApiVariants(commandSchema, [
|
|
|
100
146
|
"Disconnect"
|
|
101
147
|
]);
|
|
102
148
|
|
|
103
|
-
let commandSchema$2 = Api$ReventlessInfra.markAllowedStates(commandSchema$1, [
|
|
104
|
-
[
|
|
105
|
-
"Disconnect",
|
|
106
|
-
["Connected"]
|
|
107
|
-
],
|
|
108
|
-
[
|
|
109
|
-
"Activate",
|
|
110
|
-
[
|
|
111
|
-
"Inactive",
|
|
112
|
-
"Retired"
|
|
113
|
-
]
|
|
114
|
-
],
|
|
115
|
-
[
|
|
116
|
-
"Deactivate",
|
|
117
|
-
[
|
|
118
|
-
"Connected",
|
|
119
|
-
"Disconnected"
|
|
120
|
-
]
|
|
121
|
-
],
|
|
122
|
-
[
|
|
123
|
-
"Retire",
|
|
124
|
-
[
|
|
125
|
-
"Connected",
|
|
126
|
-
"Disconnected",
|
|
127
|
-
"Inactive"
|
|
128
|
-
]
|
|
129
|
-
]
|
|
130
|
-
]);
|
|
131
|
-
|
|
132
|
-
let commandSchema$3 = Api$ReventlessInfra.markTargetState(commandSchema$2, [
|
|
133
|
-
[
|
|
134
|
-
"Connect",
|
|
135
|
-
"Connected"
|
|
136
|
-
],
|
|
137
|
-
[
|
|
138
|
-
"Disconnect",
|
|
139
|
-
"Disconnected"
|
|
140
|
-
],
|
|
141
|
-
[
|
|
142
|
-
"Activate",
|
|
143
|
-
"Connected"
|
|
144
|
-
],
|
|
145
|
-
[
|
|
146
|
-
"Deactivate",
|
|
147
|
-
"Inactive"
|
|
148
|
-
],
|
|
149
|
-
[
|
|
150
|
-
"Retire",
|
|
151
|
-
"Retired"
|
|
152
|
-
]
|
|
153
|
-
]);
|
|
154
|
-
|
|
155
149
|
function commandAuthorization(param) {
|
|
156
150
|
return "AllowAuthenticated";
|
|
157
151
|
}
|
|
158
152
|
|
|
153
|
+
let traits = [];
|
|
154
|
+
|
|
159
155
|
let name = "Plugin";
|
|
160
156
|
|
|
161
157
|
let Id;
|
|
@@ -171,8 +167,10 @@ export {
|
|
|
171
167
|
versionSupersededDataSchema,
|
|
172
168
|
eventSchema,
|
|
173
169
|
errorSchema,
|
|
174
|
-
|
|
170
|
+
commandTransition,
|
|
171
|
+
commandSchema$1 as commandSchema,
|
|
175
172
|
moduleUrl,
|
|
176
173
|
commandAuthorization,
|
|
174
|
+
traits,
|
|
177
175
|
}
|
|
178
176
|
/* commandSchema Not a pure module */
|
|
@@ -165,6 +165,11 @@ let storeDecl: requiredStoreDeclaration = {
|
|
|
165
165
|
annotation: Some("images"),
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
let capabilityDecl: requiredCapabilityDeclaration = {
|
|
169
|
+
capability: "Geocoding",
|
|
170
|
+
component: "GeocodeAddress",
|
|
171
|
+
}
|
|
172
|
+
|
|
168
173
|
let structure: pluginStructure = {
|
|
169
174
|
readModels: [queryable],
|
|
170
175
|
stateViewSlices: [],
|
|
@@ -177,6 +182,8 @@ let structure: pluginStructure = {
|
|
|
177
182
|
extensionPoints: Some([extensionPoint]),
|
|
178
183
|
requiredStores: Some(["catalog.images"]),
|
|
179
184
|
requiredStoreDeclarations: Some([storeDecl]),
|
|
185
|
+
requiredCapabilities: Some([capabilityDecl]),
|
|
186
|
+
traitDeclarations: None,
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
let panel: panelManifestEntry = {
|
|
@@ -338,7 +345,16 @@ let guards: array<guard> = [
|
|
|
338
345
|
wireOnly: ["pluginId", "internalQueryables"],
|
|
339
346
|
// Producer-side and store fields belong to Platform_PluginStructures — the
|
|
340
347
|
// deployed AutoUI has no use for either.
|
|
341
|
-
recordOnly: [
|
|
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
|
+
],
|
|
342
358
|
},
|
|
343
359
|
{
|
|
344
360
|
gqlType: "Platform_PublishedEventDef",
|
|
@@ -364,6 +380,16 @@ let guards: array<guard> = [
|
|
|
364
380
|
wireOnly: [],
|
|
365
381
|
recordOnly: [],
|
|
366
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
|
+
},
|
|
367
393
|
{
|
|
368
394
|
gqlType: "Platform_PluginStructureEntry",
|
|
369
395
|
sdlTypes: structuresSdl,
|
|
@@ -279,6 +279,11 @@ let storeDecl = {
|
|
|
279
279
|
annotation: "images"
|
|
280
280
|
};
|
|
281
281
|
|
|
282
|
+
let capabilityDecl = {
|
|
283
|
+
capability: "Geocoding",
|
|
284
|
+
component: "GeocodeAddress"
|
|
285
|
+
};
|
|
286
|
+
|
|
282
287
|
let structure_readModels = [queryable];
|
|
283
288
|
|
|
284
289
|
let structure_stateViewSlices = [];
|
|
@@ -301,6 +306,8 @@ let structure_requiredStores = ["catalog.images"];
|
|
|
301
306
|
|
|
302
307
|
let structure_requiredStoreDeclarations = [storeDecl];
|
|
303
308
|
|
|
309
|
+
let structure_requiredCapabilities = [capabilityDecl];
|
|
310
|
+
|
|
304
311
|
let structure = {
|
|
305
312
|
readModels: structure_readModels,
|
|
306
313
|
stateViewSlices: structure_stateViewSlices,
|
|
@@ -312,7 +319,9 @@ let structure = {
|
|
|
312
319
|
extensions: structure_extensions,
|
|
313
320
|
extensionPoints: structure_extensionPoints,
|
|
314
321
|
requiredStores: structure_requiredStores,
|
|
315
|
-
requiredStoreDeclarations: structure_requiredStoreDeclarations
|
|
322
|
+
requiredStoreDeclarations: structure_requiredStoreDeclarations,
|
|
323
|
+
requiredCapabilities: structure_requiredCapabilities,
|
|
324
|
+
traitDeclarations: undefined
|
|
316
325
|
};
|
|
317
326
|
|
|
318
327
|
let panel_positions = ["platform-summary"];
|
|
@@ -461,7 +470,9 @@ let guards = [
|
|
|
461
470
|
recordOnly: [
|
|
462
471
|
"extensionPoints",
|
|
463
472
|
"requiredStores",
|
|
464
|
-
"requiredStoreDeclarations"
|
|
473
|
+
"requiredStoreDeclarations",
|
|
474
|
+
"requiredCapabilities",
|
|
475
|
+
"traitDeclarations"
|
|
465
476
|
]
|
|
466
477
|
},
|
|
467
478
|
{
|
|
@@ -488,6 +499,14 @@ let guards = [
|
|
|
488
499
|
wireOnly: [],
|
|
489
500
|
recordOnly: []
|
|
490
501
|
},
|
|
502
|
+
{
|
|
503
|
+
gqlType: "Platform_RequiredCapabilityDeclaration",
|
|
504
|
+
sdlTypes: Platform_PluginStructuresApi$ReventlessCore.sdlTypes,
|
|
505
|
+
recordFields: schemaKeys(Plugin$Reventless.requiredCapabilityDeclarationSchema),
|
|
506
|
+
wireFields: jsonKeys(Platform_PluginStructuresApi$ReventlessCore.encodeRequiredCapabilityDeclaration(capabilityDecl)),
|
|
507
|
+
wireOnly: [],
|
|
508
|
+
recordOnly: []
|
|
509
|
+
},
|
|
491
510
|
{
|
|
492
511
|
gqlType: "Platform_PluginStructureEntry",
|
|
493
512
|
sdlTypes: Platform_PluginStructuresApi$ReventlessCore.sdlTypes,
|
|
@@ -584,6 +603,7 @@ export {
|
|
|
584
603
|
extension,
|
|
585
604
|
extensionPoint,
|
|
586
605
|
storeDecl,
|
|
606
|
+
capabilityDecl,
|
|
587
607
|
structure,
|
|
588
608
|
panel,
|
|
589
609
|
menu,
|
|
@@ -111,13 +111,18 @@ describe("the admin structure's declared transitions are checked", () => {
|
|
|
111
111
|
|
|
112
112
|
// The drift that made deriving these defs worth doing rather than tidy. The
|
|
113
113
|
// hand-written arg schema described only `id`, while the SDL generated off the
|
|
114
|
-
// same `commandSchema` declares `Platform_Plugin_Activate(
|
|
114
|
+
// same `commandSchema` declares `Platform_Plugin_Activate(id: ID!, version: String!)`.
|
|
115
115
|
// AutoUI builds its mutation document from the published schema, so it sent a
|
|
116
116
|
// document missing a required argument and every admin row action was rejected at
|
|
117
117
|
// GraphQL validation, before the aggregate saw it.
|
|
118
118
|
describe("the Plugin command defs carry every argument the SDL requires", () => {
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
// Named, not positional. A caller can match `version` against a field it holds;
|
|
120
|
+
// `_0` is a name only the encoder ever chose.
|
|
121
|
+
testSync("the version argument is published, under its own name", () =>
|
|
122
|
+
expect((
|
|
123
|
+
activate().schema->String.includes(`"version"`),
|
|
124
|
+
activate().schema->String.includes(`"_0"`),
|
|
125
|
+
))->toEqual((true, false))
|
|
121
126
|
)
|
|
122
127
|
|
|
123
128
|
testSync("and so is the aggregate id", () => {
|
|
@@ -101,8 +101,14 @@ globalThis.describe("the admin structure's declared transitions are checked", ()
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
globalThis.describe("the Plugin command defs carry every argument the SDL requires", () => {
|
|
104
|
-
globalThis.test("the version argument is published", () => {
|
|
105
|
-
globalThis.expect(
|
|
104
|
+
globalThis.test("the version argument is published, under its own name", () => {
|
|
105
|
+
globalThis.expect([
|
|
106
|
+
commandNamed("Activate").schema.includes(`"version"`),
|
|
107
|
+
commandNamed("Activate").schema.includes(`"_0"`)
|
|
108
|
+
]).toEqual([
|
|
109
|
+
true,
|
|
110
|
+
false
|
|
111
|
+
]);
|
|
106
112
|
});
|
|
107
113
|
globalThis.test("and so is the aggregate id", () => {
|
|
108
114
|
globalThis.expect(commandNamed("Activate").aggregateIdField).toEqual(undefined);
|
|
@@ -100,7 +100,9 @@ let structure = {
|
|
|
100
100
|
extensions: structure_extensions,
|
|
101
101
|
extensionPoints: undefined,
|
|
102
102
|
requiredStores: undefined,
|
|
103
|
-
requiredStoreDeclarations: undefined
|
|
103
|
+
requiredStoreDeclarations: undefined,
|
|
104
|
+
requiredCapabilities: undefined,
|
|
105
|
+
traitDeclarations: undefined
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
let structures = [[
|
|
@@ -105,6 +105,8 @@ let structure: pluginStructure = {
|
|
|
105
105
|
extensionPoints: None,
|
|
106
106
|
requiredStores: None,
|
|
107
107
|
requiredStoreDeclarations: None,
|
|
108
|
+
requiredCapabilities: None,
|
|
109
|
+
traitDeclarations: None,
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
let encoded = Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
@@ -402,6 +404,8 @@ describe("allowedStates + lifecycleField populated", () => {
|
|
|
402
404
|
extensionPoints: None,
|
|
403
405
|
requiredStores: None,
|
|
404
406
|
requiredStoreDeclarations: None,
|
|
407
|
+
requiredCapabilities: None,
|
|
408
|
+
traitDeclarations: None,
|
|
405
409
|
}
|
|
406
410
|
|
|
407
411
|
let json =
|
|
@@ -155,7 +155,9 @@ let structure = {
|
|
|
155
155
|
extensions: structure_extensions,
|
|
156
156
|
extensionPoints: undefined,
|
|
157
157
|
requiredStores: undefined,
|
|
158
|
-
requiredStoreDeclarations: undefined
|
|
158
|
+
requiredStoreDeclarations: undefined,
|
|
159
|
+
requiredCapabilities: undefined,
|
|
160
|
+
traitDeclarations: undefined
|
|
159
161
|
};
|
|
160
162
|
|
|
161
163
|
let encoded = Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Catalog", undefined, structure);
|
|
@@ -264,7 +266,9 @@ globalThis.describe("translation-slice externalSystem round-trip", () => {
|
|
|
264
266
|
extensions: externalStructure_extensions,
|
|
265
267
|
extensionPoints: undefined,
|
|
266
268
|
requiredStores: undefined,
|
|
267
|
-
requiredStoreDeclarations: undefined
|
|
269
|
+
requiredStoreDeclarations: undefined,
|
|
270
|
+
requiredCapabilities: undefined,
|
|
271
|
+
traitDeclarations: undefined
|
|
268
272
|
};
|
|
269
273
|
let externalJson = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Catalog", undefined, externalStructure));
|
|
270
274
|
globalThis.test("surfaces Some outbound externalSystem as its inner string", () => {
|
|
@@ -356,7 +360,9 @@ globalThis.describe("visibility filtering (deployed AutoUI hides Internal)", ()
|
|
|
356
360
|
extensions: mixed_extensions,
|
|
357
361
|
extensionPoints: undefined,
|
|
358
362
|
requiredStores: undefined,
|
|
359
|
-
requiredStoreDeclarations: undefined
|
|
363
|
+
requiredStoreDeclarations: undefined,
|
|
364
|
+
requiredCapabilities: undefined,
|
|
365
|
+
traitDeclarations: undefined
|
|
360
366
|
};
|
|
361
367
|
let mixedEntry = Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Ordering", undefined, mixed);
|
|
362
368
|
let namesAt = (entry, key) => Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(entry), o => o[key]), Stdlib_JSON.Decode.array), []), c => Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(c), o => o["name"]), Stdlib_JSON.Decode.string));
|
|
@@ -385,7 +391,9 @@ globalThis.describe("visibility filtering (deployed AutoUI hides Internal)", ()
|
|
|
385
391
|
extensions: structure_extensions,
|
|
386
392
|
extensionPoints: undefined,
|
|
387
393
|
requiredStores: undefined,
|
|
388
|
-
requiredStoreDeclarations: undefined
|
|
394
|
+
requiredStoreDeclarations: undefined,
|
|
395
|
+
requiredCapabilities: undefined,
|
|
396
|
+
traitDeclarations: undefined
|
|
389
397
|
});
|
|
390
398
|
globalThis.expect(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(entry), o => o["internalQueryables"])).toEqual([]);
|
|
391
399
|
});
|
|
@@ -477,7 +485,9 @@ globalThis.describe("allowedStates + lifecycleField populated", () => {
|
|
|
477
485
|
extensions: structureWithStates_extensions,
|
|
478
486
|
extensionPoints: undefined,
|
|
479
487
|
requiredStores: undefined,
|
|
480
|
-
requiredStoreDeclarations: undefined
|
|
488
|
+
requiredStoreDeclarations: undefined,
|
|
489
|
+
requiredCapabilities: undefined,
|
|
490
|
+
traitDeclarations: undefined
|
|
481
491
|
};
|
|
482
492
|
let json = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Platform", undefined, structureWithStates));
|
|
483
493
|
globalThis.test("encodes populated allowedStates as a JSON array", () => {
|
|
@@ -532,7 +542,9 @@ globalThis.describe("singleQueryField reaches the caller", () => {
|
|
|
532
542
|
extensions: structure_extensions,
|
|
533
543
|
extensionPoints: undefined,
|
|
534
544
|
requiredStores: undefined,
|
|
535
|
-
requiredStoreDeclarations: undefined
|
|
545
|
+
requiredStoreDeclarations: undefined,
|
|
546
|
+
requiredCapabilities: undefined,
|
|
547
|
+
traitDeclarations: undefined
|
|
536
548
|
}));
|
|
537
549
|
globalThis.expect(unstated.includes("\"singleQueryField\":null")).toEqual(true);
|
|
538
550
|
});
|
|
@@ -580,7 +592,9 @@ globalThis.describe("idField / idFieldSource reach the caller", () => {
|
|
|
580
592
|
extensions: structure_extensions,
|
|
581
593
|
extensionPoints: undefined,
|
|
582
594
|
requiredStores: undefined,
|
|
583
|
-
requiredStoreDeclarations: undefined
|
|
595
|
+
requiredStoreDeclarations: undefined,
|
|
596
|
+
requiredCapabilities: undefined,
|
|
597
|
+
traitDeclarations: undefined
|
|
584
598
|
}));
|
|
585
599
|
globalThis.expect(unresolved.includes("\"idField\":null")).toEqual(true);
|
|
586
600
|
globalThis.expect(unresolved.includes("\"idFieldSource\":null")).toEqual(true);
|
|
@@ -67,6 +67,8 @@ let structure: pluginStructure = {
|
|
|
67
67
|
requiredStoreDeclarations: Some([
|
|
68
68
|
{store: "Catalog.images", component: "Product", field: "image", annotation: Some("images")},
|
|
69
69
|
]),
|
|
70
|
+
requiredCapabilities: Some([{capability: "Geocoding", component: "GeocodeAddress"}]),
|
|
71
|
+
traitDeclarations: None,
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
let encoded = Platform_PluginStructuresApi.encodePluginStructureEntry(
|
|
@@ -98,10 +100,12 @@ describe("Platform_PluginStructures entry", () => {
|
|
|
98
100
|
"outboundTranslationSlices",
|
|
99
101
|
"pluginId",
|
|
100
102
|
"readModels",
|
|
103
|
+
"requiredCapabilities",
|
|
101
104
|
"requiredStoreDeclarations",
|
|
102
105
|
"requiredStores",
|
|
103
106
|
"stateChangeSlices",
|
|
104
107
|
"stateViewSlices",
|
|
108
|
+
"traitDeclarations",
|
|
105
109
|
])
|
|
106
110
|
})
|
|
107
111
|
|
|
@@ -142,6 +146,16 @@ describe("Platform_PluginStructures entry", () => {
|
|
|
142
146
|
testSync("encodes required stores with their provenance", () =>
|
|
143
147
|
expect(json->String.includes("\"requiredStores\":[\"Catalog.images\"]"))->toEqual(true)
|
|
144
148
|
)
|
|
149
|
+
|
|
150
|
+
// No `field`: a capability a slice declares is not a field's need, which is why
|
|
151
|
+
// it travels beside `requiredStores` rather than inside it.
|
|
152
|
+
testSync("encodes required capabilities with their declaring component", () =>
|
|
153
|
+
expect(
|
|
154
|
+
json->String.includes(
|
|
155
|
+
"\"requiredCapabilities\":[{\"capability\":\"Geocoding\",\"component\":\"GeocodeAddress\"}]",
|
|
156
|
+
),
|
|
157
|
+
)->toEqual(true)
|
|
158
|
+
)
|
|
145
159
|
})
|
|
146
160
|
|
|
147
161
|
// A structure persisted before these fields existed decodes as None, and null is the
|
|
@@ -153,6 +167,8 @@ describe("absent optional collections", () => {
|
|
|
153
167
|
extensionPoints: None,
|
|
154
168
|
requiredStores: None,
|
|
155
169
|
requiredStoreDeclarations: None,
|
|
170
|
+
requiredCapabilities: None,
|
|
171
|
+
traitDeclarations: None,
|
|
156
172
|
}
|
|
157
173
|
let bareJson =
|
|
158
174
|
Platform_PluginStructuresApi.encodePluginStructureEntry(
|
|
@@ -127,6 +127,11 @@ let structure_requiredStoreDeclarations = [{
|
|
|
127
127
|
annotation: "images"
|
|
128
128
|
}];
|
|
129
129
|
|
|
130
|
+
let structure_requiredCapabilities = [{
|
|
131
|
+
capability: "Geocoding",
|
|
132
|
+
component: "GeocodeAddress"
|
|
133
|
+
}];
|
|
134
|
+
|
|
130
135
|
let structure = {
|
|
131
136
|
readModels: structure_readModels,
|
|
132
137
|
stateViewSlices: structure_stateViewSlices,
|
|
@@ -138,7 +143,9 @@ let structure = {
|
|
|
138
143
|
extensions: structure_extensions,
|
|
139
144
|
extensionPoints: structure_extensionPoints,
|
|
140
145
|
requiredStores: structure_requiredStores,
|
|
141
|
-
requiredStoreDeclarations: structure_requiredStoreDeclarations
|
|
146
|
+
requiredStoreDeclarations: structure_requiredStoreDeclarations,
|
|
147
|
+
requiredCapabilities: structure_requiredCapabilities,
|
|
148
|
+
traitDeclarations: undefined
|
|
142
149
|
};
|
|
143
150
|
|
|
144
151
|
let encoded = Platform_PluginStructuresApi$ReventlessCore.encodePluginStructureEntry("Catalog", structure);
|
|
@@ -162,10 +169,12 @@ globalThis.describe("Platform_PluginStructures entry", () => {
|
|
|
162
169
|
"outboundTranslationSlices",
|
|
163
170
|
"pluginId",
|
|
164
171
|
"readModels",
|
|
172
|
+
"requiredCapabilities",
|
|
165
173
|
"requiredStoreDeclarations",
|
|
166
174
|
"requiredStores",
|
|
167
175
|
"stateChangeSlices",
|
|
168
|
-
"stateViewSlices"
|
|
176
|
+
"stateViewSlices",
|
|
177
|
+
"traitDeclarations"
|
|
169
178
|
]);
|
|
170
179
|
});
|
|
171
180
|
globalThis.test("keeps Internal read models", () => {
|
|
@@ -192,6 +201,9 @@ globalThis.describe("Platform_PluginStructures entry", () => {
|
|
|
192
201
|
globalThis.test("encodes required stores with their provenance", () => {
|
|
193
202
|
globalThis.expect(json.includes("\"requiredStores\":[\"Catalog.images\"]")).toEqual(true);
|
|
194
203
|
});
|
|
204
|
+
globalThis.test("encodes required capabilities with their declaring component", () => {
|
|
205
|
+
globalThis.expect(json.includes("\"requiredCapabilities\":[{\"capability\":\"Geocoding\",\"component\":\"GeocodeAddress\"}]")).toEqual(true);
|
|
206
|
+
});
|
|
195
207
|
});
|
|
196
208
|
|
|
197
209
|
globalThis.describe("absent optional collections", () => {
|
|
@@ -214,7 +226,9 @@ globalThis.describe("absent optional collections", () => {
|
|
|
214
226
|
extensions: bare_extensions,
|
|
215
227
|
extensionPoints: undefined,
|
|
216
228
|
requiredStores: undefined,
|
|
217
|
-
requiredStoreDeclarations: undefined
|
|
229
|
+
requiredStoreDeclarations: undefined,
|
|
230
|
+
requiredCapabilities: undefined,
|
|
231
|
+
traitDeclarations: undefined
|
|
218
232
|
};
|
|
219
233
|
let bareJson = JSON.stringify(Platform_PluginStructuresApi$ReventlessCore.encodePluginStructureEntry("Catalog", bare));
|
|
220
234
|
globalThis.test("encodes absent extensionPoints as null", () => {
|
|
@@ -37,6 +37,12 @@ function commandAuthorization(param) {
|
|
|
37
37
|
return "AllowAuthenticated";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function commandTransition(param) {
|
|
41
|
+
return "Unrestricted";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let traits = [];
|
|
45
|
+
|
|
40
46
|
let AggSpec = {
|
|
41
47
|
Id: undefined,
|
|
42
48
|
name: name,
|
|
@@ -44,7 +50,9 @@ let AggSpec = {
|
|
|
44
50
|
eventSchema: eventSchema,
|
|
45
51
|
errorSchema: errorSchema,
|
|
46
52
|
moduleUrl: moduleUrl,
|
|
47
|
-
commandAuthorization: commandAuthorization
|
|
53
|
+
commandAuthorization: commandAuthorization,
|
|
54
|
+
commandTransition: commandTransition,
|
|
55
|
+
traits: traits
|
|
48
56
|
};
|
|
49
57
|
|
|
50
58
|
let initialState = {
|
|
@@ -237,7 +245,9 @@ let TestHandler = Aggregate_Callback$ReventlessCore.Make({
|
|
|
237
245
|
errorSchema: errorSchema,
|
|
238
246
|
commandSchema: commandSchema,
|
|
239
247
|
moduleUrl: moduleUrl,
|
|
240
|
-
commandAuthorization: commandAuthorization
|
|
248
|
+
commandAuthorization: commandAuthorization,
|
|
249
|
+
commandTransition: commandTransition,
|
|
250
|
+
traits: traits
|
|
241
251
|
})({
|
|
242
252
|
initialState: initialState,
|
|
243
253
|
evolve: evolve,
|
|
@@ -258,7 +268,9 @@ let TestHandler = Aggregate_Callback$ReventlessCore.Make({
|
|
|
258
268
|
errorSchema: errorSchema,
|
|
259
269
|
commandSchema: commandSchema,
|
|
260
270
|
moduleUrl: moduleUrl,
|
|
261
|
-
commandAuthorization: commandAuthorization
|
|
271
|
+
commandAuthorization: commandAuthorization,
|
|
272
|
+
commandTransition: commandTransition,
|
|
273
|
+
traits: traits
|
|
262
274
|
},
|
|
263
275
|
EventLog: {
|
|
264
276
|
Spec: {
|