@reventlessdev/reventless-core 3.0.0-alpha.211 → 3.0.0-alpha.212
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 +10 -0
- package/package.json +3 -3
- package/src/admin/Platform_AdminApi.res +2 -0
- package/src/admin/Platform_AdminApi.res.mjs +3 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res +26 -5
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +21 -5
- package/src/admin/Platform_PluginStructuresApi.res +132 -0
- package/src/admin/Platform_PluginStructuresApi.res.mjs +117 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +12 -7
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +2 -2
- package/tests/admin/Platform_PluginStructuresApiTest.res +144 -0
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +197 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.212 (2026-08-04)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **admin:** declare the component fields the resolver already returned ([879fdf3](https://github.com/ReventlessDev/reventless-core/commit/879fdf3e7ac68be23f915db6bdd0fdc68294de5d))
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **admin:** serve the complete plugin structure to developer tooling ([e99cda0](https://github.com/ReventlessDev/reventless-core/commit/e99cda02a2f862b36bcfdb018a0fb34bb7426a2b))
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
# 3.0.0-alpha.211 (2026-08-04)
|
|
7
17
|
|
|
8
18
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.212",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
31
|
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.1",
|
|
32
32
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
33
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
34
33
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
|
|
35
34
|
"@reventlessdev/rescript-node": "2.0.0-alpha.1",
|
|
35
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
37
|
-
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
38
37
|
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.1",
|
|
38
|
+
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
39
39
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.126",
|
|
40
40
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.30",
|
|
41
41
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.100"
|
|
@@ -136,11 +136,13 @@ let baseFragment = (~cloner: bool) => {
|
|
|
136
136
|
->Array.concat(uiFragmentSubscriptionTypes)
|
|
137
137
|
->Array.concat(pluginStatusSubscriptionTypes)
|
|
138
138
|
->Array.concat(Platform_ComponentDefinitionsApi.sdlTypes)
|
|
139
|
+
->Array.concat(Platform_PluginStructuresApi.sdlTypes)
|
|
139
140
|
->Array.concat(Platform_UIFragmentsApi.sdlTypes),
|
|
140
141
|
queries: Array.concat(
|
|
141
142
|
parts.queries,
|
|
142
143
|
[
|
|
143
144
|
Platform_ComponentDefinitionsApi.sdlQueryField,
|
|
145
|
+
Platform_PluginStructuresApi.sdlQueryField,
|
|
144
146
|
Platform_UIFragmentsApi.sdlQueryField,
|
|
145
147
|
],
|
|
146
148
|
),
|
|
@@ -7,6 +7,7 @@ import * as PluginBaseFragment$ReventlessCore from "../plugin/api/PluginBaseFrag
|
|
|
7
7
|
import * as Platform_UIFragmentsApi$ReventlessCore from "./Platform_UIFragmentsApi.res.mjs";
|
|
8
8
|
import * as GraphQL_FragmentGenerator$ReventlessCore from "../components/Api/GraphQL_FragmentGenerator.res.mjs";
|
|
9
9
|
import * as Plugin_SubscriptionSchema$ReventlessCore from "../plugin/component/Plugin_SubscriptionSchema.res.mjs";
|
|
10
|
+
import * as Platform_PluginStructuresApi$ReventlessCore from "./Platform_PluginStructuresApi.res.mjs";
|
|
10
11
|
import * as Platform_ComponentDefinitionsApi$ReventlessCore from "./Platform_ComponentDefinitionsApi.res.mjs";
|
|
11
12
|
|
|
12
13
|
let cloneArgsSchema = S.schema(s => ({
|
|
@@ -90,10 +91,11 @@ function baseFragment(cloner) {
|
|
|
90
91
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(base);
|
|
91
92
|
let match = Plugin_SubscriptionSchema$ReventlessCore.sourceCFields(PluginBaseFragment$ReventlessCore.pluginAggregateMutationEntries);
|
|
92
93
|
return GraphQL_Stitcher$ReventlessCore.encode({
|
|
93
|
-
types: parts.types.concat(uiFragmentSubscriptionTypes).concat(pluginStatusSubscriptionTypes).concat(Platform_ComponentDefinitionsApi$ReventlessCore.sdlTypes).concat(Platform_UIFragmentsApi$ReventlessCore.sdlTypes),
|
|
94
|
+
types: parts.types.concat(uiFragmentSubscriptionTypes).concat(pluginStatusSubscriptionTypes).concat(Platform_ComponentDefinitionsApi$ReventlessCore.sdlTypes).concat(Platform_PluginStructuresApi$ReventlessCore.sdlTypes).concat(Platform_UIFragmentsApi$ReventlessCore.sdlTypes),
|
|
94
95
|
mutations: parts.mutations.concat(uiFragmentMutationFields).concat(pluginStatusMutationFields),
|
|
95
96
|
queries: parts.queries.concat([
|
|
96
97
|
Platform_ComponentDefinitionsApi$ReventlessCore.sdlQueryField,
|
|
98
|
+
Platform_PluginStructuresApi$ReventlessCore.sdlQueryField,
|
|
97
99
|
Platform_UIFragmentsApi$ReventlessCore.sdlQueryField
|
|
98
100
|
]),
|
|
99
101
|
subscriptions: [
|
|
@@ -3,18 +3,28 @@
|
|
|
3
3
|
// and the AWS adapter (which resolves from the Plugin read model). Keeping both
|
|
4
4
|
// adapters byte-identical guarantees a single client query string works against
|
|
5
5
|
// both.
|
|
6
|
+
//
|
|
7
|
+
// The component-level types below are shared with `Platform_PluginStructuresApi`,
|
|
8
|
+
// which serves the same components unfiltered to developer tooling. They are
|
|
9
|
+
// declared here because this is where their encoders live; the two queries differ
|
|
10
|
+
// in WHICH components they return and what the entry carries around them, never in
|
|
11
|
+
// how one component is shaped.
|
|
6
12
|
|
|
7
13
|
open Reventless.Plugin
|
|
8
14
|
|
|
15
|
+
// Component-level SDL. Every field a leaf encoder below emits is declared here:
|
|
16
|
+
// a field the resolver returns but the SDL omits is unreachable (GraphQL rejects
|
|
17
|
+
// selecting it), which is how `chapter` and `externalSystem` stayed invisible to
|
|
18
|
+
// consumers long after they were being encoded.
|
|
9
19
|
let sdlTypes: array<string> = [
|
|
10
20
|
`type Platform_FieldReference {\n fieldName: String!\n entity: String!\n plugin: String\n}`,
|
|
11
21
|
`type Platform_CommandDef {\n name: String!\n schema: String!\n level: String!\n aggregateIdField: String\n mutationField: String!\n references: [Platform_FieldReference!]!\n allowedStates: [String!]\n targetState: String\n apiExposed: Boolean\n}`,
|
|
12
22
|
`type Platform_EventDef {\n name: String!\n schema: String!\n references: [Platform_FieldReference!]!\n}`,
|
|
13
|
-
`type Platform_WriteSideDef {\n name: String!\n commands: [Platform_CommandDef!]!\n linkedViews: [String!]!\n consistencyRead: String\n producedEventTypes: [String!]!\n consumedEventTypes: [String!]!\n events: [Platform_EventDef!]!\n}`,
|
|
14
|
-
`type Platform_ReadSideDef {\n name: String!\n queryField: String!\n schema: String!\n consumedEventTypes: [String!]!\n linkedWriteSide: [String!]!\n labelField: String!\n searchableFields: [String!]!\n labelFieldSource: String\n statusField: String\n}`,
|
|
15
|
-
`type Platform_AutomationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n producedCommandTypes: [String!]!\n}`,
|
|
16
|
-
`type Platform_OutboundTranslationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n inboundCommandTypes: [String!]!\n}`,
|
|
17
|
-
`type Platform_InboundTranslationSliceDef {\n name: String!\n commandTypes: [String!]!\n}`,
|
|
23
|
+
`type Platform_WriteSideDef {\n name: String!\n commands: [Platform_CommandDef!]!\n linkedViews: [String!]!\n consistencyRead: String\n producedEventTypes: [String!]!\n consumedEventTypes: [String!]!\n events: [Platform_EventDef!]!\n chapter: String\n}`,
|
|
24
|
+
`type Platform_ReadSideDef {\n name: String!\n queryField: String!\n schema: String!\n consumedEventTypes: [String!]!\n linkedWriteSide: [String!]!\n labelField: String!\n searchableFields: [String!]!\n labelFieldSource: String\n statusField: String\n visibility: String\n chapter: String\n}`,
|
|
25
|
+
`type Platform_AutomationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n producedCommandTypes: [String!]!\n targetName: String\n chapter: String\n}`,
|
|
26
|
+
`type Platform_OutboundTranslationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n inboundCommandTypes: [String!]!\n targetName: String\n externalSystem: String\n chapter: String\n}`,
|
|
27
|
+
`type Platform_InboundTranslationSliceDef {\n name: String!\n commandTypes: [String!]!\n targetName: String\n externalSystem: String\n chapter: String\n}`,
|
|
18
28
|
`type Platform_ExtensionDef {\n name: String!\n delegateNames: [String!]!\n eventTypes: [String!]!\n commandTypes: [String!]!\n}`,
|
|
19
29
|
`type Platform_ComponentDefinitionEntry {\n pluginId: String!\n readModels: [Platform_ReadSideDef!]!\n stateViewSlices: [Platform_ReadSideDef!]!\n stateChangeSlices: [Platform_WriteSideDef!]!\n aggregates: [Platform_WriteSideDef!]!\n automationSlices: [Platform_AutomationSliceDef!]!\n outboundTranslationSlices: [Platform_OutboundTranslationSliceDef!]!\n inboundTranslationSlices: [Platform_InboundTranslationSliceDef!]!\n extensions: [Platform_ExtensionDef!]!\n}`,
|
|
20
30
|
]
|
|
@@ -77,6 +87,11 @@ let encodeQueryableDef = (r: queryableDef): JSON.t =>
|
|
|
77
87
|
r.labelFieldSource->Option.mapOr(JSON.Encode.null, JSON.Encode.string),
|
|
78
88
|
),
|
|
79
89
|
("statusField", r.statusField->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
90
|
+
// Carried rather than only filtered on: a tooling consumer reading the
|
|
91
|
+
// unfiltered structure needs to know WHICH components are Internal, and a
|
|
92
|
+
// consumer of the filtered query reads null here because nothing Internal
|
|
93
|
+
// survives the filter anyway.
|
|
94
|
+
("visibility", r.visibility->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
80
95
|
("chapter", r.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
81
96
|
])->JSON.Encode.object
|
|
82
97
|
|
|
@@ -108,6 +123,10 @@ let encodeAutomationSliceDef = (a: automationSliceDef): JSON.t =>
|
|
|
108
123
|
("name", JSON.Encode.string(a.name)),
|
|
109
124
|
("consumedEventTypes", encodeStrings(a.consumedEventTypes)),
|
|
110
125
|
("producedCommandTypes", encodeStrings(a.producedCommandTypes)),
|
|
126
|
+
// Routing target — the plugin this slice's commands are dispatched to. It is
|
|
127
|
+
// what turns a slice into a cross-plugin edge, so a graph consumer needs it
|
|
128
|
+
// from the structure rather than from the deploy-time inspector sync.
|
|
129
|
+
("targetName", JSON.Encode.string(a.targetName)),
|
|
111
130
|
("chapter", a.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
112
131
|
])->JSON.Encode.object
|
|
113
132
|
|
|
@@ -116,6 +135,7 @@ let encodeOutboundTranslationSliceDef = (o: outboundTranslationSliceDef): JSON.t
|
|
|
116
135
|
("name", JSON.Encode.string(o.name)),
|
|
117
136
|
("consumedEventTypes", encodeStrings(o.consumedEventTypes)),
|
|
118
137
|
("inboundCommandTypes", encodeStrings(o.inboundCommandTypes)),
|
|
138
|
+
("targetName", o.targetName->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
119
139
|
// Foreign system this slice publishes to — lets a deployed-graph consumer draw
|
|
120
140
|
// the external-system boundary box without workspace access. None → null.
|
|
121
141
|
("externalSystem", o.externalSystem->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
@@ -126,6 +146,7 @@ let encodeInboundTranslationSliceDef = (i: inboundTranslationSliceDef): JSON.t =
|
|
|
126
146
|
Dict.fromArray([
|
|
127
147
|
("name", JSON.Encode.string(i.name)),
|
|
128
148
|
("commandTypes", encodeStrings(i.commandTypes)),
|
|
149
|
+
("targetName", JSON.Encode.string(i.targetName)),
|
|
129
150
|
// Foreign system this slice receives from — lets a deployed-graph consumer draw
|
|
130
151
|
// the external-system boundary box without workspace access. None → null.
|
|
131
152
|
("externalSystem", i.externalSystem->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
@@ -8,11 +8,11 @@ let sdlTypes = [
|
|
|
8
8
|
`type Platform_FieldReference {\n fieldName: String!\n entity: String!\n plugin: String\n}`,
|
|
9
9
|
`type Platform_CommandDef {\n name: String!\n schema: String!\n level: String!\n aggregateIdField: String\n mutationField: String!\n references: [Platform_FieldReference!]!\n allowedStates: [String!]\n targetState: String\n apiExposed: Boolean\n}`,
|
|
10
10
|
`type Platform_EventDef {\n name: String!\n schema: String!\n references: [Platform_FieldReference!]!\n}`,
|
|
11
|
-
`type Platform_WriteSideDef {\n name: String!\n commands: [Platform_CommandDef!]!\n linkedViews: [String!]!\n consistencyRead: String\n producedEventTypes: [String!]!\n consumedEventTypes: [String!]!\n events: [Platform_EventDef!]!\n}`,
|
|
12
|
-
`type Platform_ReadSideDef {\n name: String!\n queryField: String!\n schema: String!\n consumedEventTypes: [String!]!\n linkedWriteSide: [String!]!\n labelField: String!\n searchableFields: [String!]!\n labelFieldSource: String\n statusField: String\n}`,
|
|
13
|
-
`type Platform_AutomationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n producedCommandTypes: [String!]!\n}`,
|
|
14
|
-
`type Platform_OutboundTranslationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n inboundCommandTypes: [String!]!\n}`,
|
|
15
|
-
`type Platform_InboundTranslationSliceDef {\n name: String!\n commandTypes: [String!]!\n}`,
|
|
11
|
+
`type Platform_WriteSideDef {\n name: String!\n commands: [Platform_CommandDef!]!\n linkedViews: [String!]!\n consistencyRead: String\n producedEventTypes: [String!]!\n consumedEventTypes: [String!]!\n events: [Platform_EventDef!]!\n chapter: String\n}`,
|
|
12
|
+
`type Platform_ReadSideDef {\n name: String!\n queryField: String!\n schema: String!\n consumedEventTypes: [String!]!\n linkedWriteSide: [String!]!\n labelField: String!\n searchableFields: [String!]!\n labelFieldSource: String\n statusField: String\n visibility: String\n chapter: String\n}`,
|
|
13
|
+
`type Platform_AutomationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n producedCommandTypes: [String!]!\n targetName: String\n chapter: String\n}`,
|
|
14
|
+
`type Platform_OutboundTranslationSliceDef {\n name: String!\n consumedEventTypes: [String!]!\n inboundCommandTypes: [String!]!\n targetName: String\n externalSystem: String\n chapter: String\n}`,
|
|
15
|
+
`type Platform_InboundTranslationSliceDef {\n name: String!\n commandTypes: [String!]!\n targetName: String\n externalSystem: String\n chapter: String\n}`,
|
|
16
16
|
`type Platform_ExtensionDef {\n name: String!\n delegateNames: [String!]!\n eventTypes: [String!]!\n commandTypes: [String!]!\n}`,
|
|
17
17
|
`type Platform_ComponentDefinitionEntry {\n pluginId: String!\n readModels: [Platform_ReadSideDef!]!\n stateViewSlices: [Platform_ReadSideDef!]!\n stateChangeSlices: [Platform_WriteSideDef!]!\n aggregates: [Platform_WriteSideDef!]!\n automationSlices: [Platform_AutomationSliceDef!]!\n outboundTranslationSlices: [Platform_OutboundTranslationSliceDef!]!\n inboundTranslationSlices: [Platform_InboundTranslationSliceDef!]!\n extensions: [Platform_ExtensionDef!]!\n}`
|
|
18
18
|
];
|
|
@@ -124,6 +124,10 @@ function encodeQueryableDef(r) {
|
|
|
124
124
|
"statusField",
|
|
125
125
|
Stdlib_Option.mapOr(r.statusField, null, prim => prim)
|
|
126
126
|
],
|
|
127
|
+
[
|
|
128
|
+
"visibility",
|
|
129
|
+
Stdlib_Option.mapOr(r.visibility, null, prim => prim)
|
|
130
|
+
],
|
|
127
131
|
[
|
|
128
132
|
"chapter",
|
|
129
133
|
Stdlib_Option.mapOr(r.chapter, null, prim => prim)
|
|
@@ -199,6 +203,10 @@ function encodeAutomationSliceDef(a) {
|
|
|
199
203
|
"producedCommandTypes",
|
|
200
204
|
a.producedCommandTypes.map(prim => prim)
|
|
201
205
|
],
|
|
206
|
+
[
|
|
207
|
+
"targetName",
|
|
208
|
+
a.targetName
|
|
209
|
+
],
|
|
202
210
|
[
|
|
203
211
|
"chapter",
|
|
204
212
|
Stdlib_Option.mapOr(a.chapter, null, prim => prim)
|
|
@@ -220,6 +228,10 @@ function encodeOutboundTranslationSliceDef(o) {
|
|
|
220
228
|
"inboundCommandTypes",
|
|
221
229
|
o.inboundCommandTypes.map(prim => prim)
|
|
222
230
|
],
|
|
231
|
+
[
|
|
232
|
+
"targetName",
|
|
233
|
+
Stdlib_Option.mapOr(o.targetName, null, prim => prim)
|
|
234
|
+
],
|
|
223
235
|
[
|
|
224
236
|
"externalSystem",
|
|
225
237
|
Stdlib_Option.mapOr(o.externalSystem, null, prim => prim)
|
|
@@ -241,6 +253,10 @@ function encodeInboundTranslationSliceDef(i) {
|
|
|
241
253
|
"commandTypes",
|
|
242
254
|
i.commandTypes.map(prim => prim)
|
|
243
255
|
],
|
|
256
|
+
[
|
|
257
|
+
"targetName",
|
|
258
|
+
i.targetName
|
|
259
|
+
],
|
|
244
260
|
[
|
|
245
261
|
"externalSystem",
|
|
246
262
|
Stdlib_Option.mapOr(i.externalSystem, null, prim => prim)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// SDL types and JSON encoder for the `Platform_PluginStructures` GraphQL query —
|
|
2
|
+
// the COMPLETE plugin structure, for developer tooling.
|
|
3
|
+
//
|
|
4
|
+
// Why this is a separate query rather than more fields on
|
|
5
|
+
// `Platform_ComponentDefinitions`: the two have different contracts, and the
|
|
6
|
+
// difference is not cosmetic.
|
|
7
|
+
//
|
|
8
|
+
// Platform_ComponentDefinitions the deployed AutoUI's menu, drill-down pages and
|
|
9
|
+
// queryable defs. Internal ReadModels /
|
|
10
|
+
// StateViewSlices are filtered OUT — showing them
|
|
11
|
+
// would put components in an end user's menu that
|
|
12
|
+
// the author marked as not-for-display.
|
|
13
|
+
// Platform_PluginStructures what the plugin actually IS. Nothing is filtered,
|
|
14
|
+
// and the producer-side `extensionPoints` are
|
|
15
|
+
// carried, because a tool drawing the domain graph
|
|
16
|
+
// has to see every component and every cross-plugin
|
|
17
|
+
// bridge or the graph it draws is a lie.
|
|
18
|
+
//
|
|
19
|
+
// Folding both into one field would mean either leaking Internal components into
|
|
20
|
+
// AutoUI or hiding them from tooling; there is no filter setting that is right for
|
|
21
|
+
// both. The component-level types are shared with `Platform_ComponentDefinitionsApi`
|
|
22
|
+
// (one component has one shape), so only the entry type and the two structure-level
|
|
23
|
+
// collections live here.
|
|
24
|
+
//
|
|
25
|
+
// Both adapters resolve this from the same persisted `pluginStructure`, so a single
|
|
26
|
+
// client query string works against a deployed platform and an in-memory one.
|
|
27
|
+
|
|
28
|
+
open Reventless.Plugin
|
|
29
|
+
|
|
30
|
+
let sdlTypes: array<string> = [
|
|
31
|
+
// Producer side of an extension point. `commandTypes` is null for a
|
|
32
|
+
// notification-only (`command = unit`) extension point — see `extensionPointDef`.
|
|
33
|
+
`type Platform_ExtensionPointDef {\n name: String!\n delegateNames: [String!]!\n sourceEventTypes: [String!]!\n commandTypes: [String!]\n}`,
|
|
34
|
+
`type Platform_RequiredStoreDeclaration {\n store: String!\n component: String!\n field: String!\n annotation: String\n}`,
|
|
35
|
+
// `extensionPoints` / `requiredStores` / `requiredStoreDeclarations` are nullable
|
|
36
|
+
// lists, not `[T!]!`: all three are optional on `pluginStructure` so that plugin
|
|
37
|
+
// definitions persisted before the field existed still decode. A structure written
|
|
38
|
+
// by an older deploy sends null, and null is the honest answer — an empty list
|
|
39
|
+
// would claim the plugin has no extension points when the truth is that the
|
|
40
|
+
// deployment cannot say.
|
|
41
|
+
`type Platform_PluginStructureEntry {\n pluginId: String!\n readModels: [Platform_ReadSideDef!]!\n stateViewSlices: [Platform_ReadSideDef!]!\n stateChangeSlices: [Platform_WriteSideDef!]!\n aggregates: [Platform_WriteSideDef!]!\n automationSlices: [Platform_AutomationSliceDef!]!\n outboundTranslationSlices: [Platform_OutboundTranslationSliceDef!]!\n inboundTranslationSlices: [Platform_InboundTranslationSliceDef!]!\n extensions: [Platform_ExtensionDef!]!\n extensionPoints: [Platform_ExtensionPointDef!]\n requiredStores: [String!]\n requiredStoreDeclarations: [Platform_RequiredStoreDeclaration!]\n}`,
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
let sdlQueryField: string = ` Platform_PluginStructures: [Platform_PluginStructureEntry!]!`
|
|
45
|
+
|
|
46
|
+
let encodeExtensionPointDef = (e: extensionPointDef): JSON.t =>
|
|
47
|
+
Dict.fromArray([
|
|
48
|
+
("name", JSON.Encode.string(e.name)),
|
|
49
|
+
("delegateNames", Platform_ComponentDefinitionsApi.encodeStrings(e.delegateNames)),
|
|
50
|
+
("sourceEventTypes", Platform_ComponentDefinitionsApi.encodeStrings(e.sourceEventTypes)),
|
|
51
|
+
(
|
|
52
|
+
"commandTypes",
|
|
53
|
+
e.commandTypes->Option.mapOr(JSON.Encode.null, Platform_ComponentDefinitionsApi.encodeStrings),
|
|
54
|
+
),
|
|
55
|
+
])->JSON.Encode.object
|
|
56
|
+
|
|
57
|
+
let encodeRequiredStoreDeclaration = (d: requiredStoreDeclaration): JSON.t =>
|
|
58
|
+
Dict.fromArray([
|
|
59
|
+
("store", JSON.Encode.string(d.store)),
|
|
60
|
+
("component", JSON.Encode.string(d.component)),
|
|
61
|
+
("field", JSON.Encode.string(d.field)),
|
|
62
|
+
("annotation", d.annotation->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
63
|
+
])->JSON.Encode.object
|
|
64
|
+
|
|
65
|
+
// Deliberately NOT filtered by `isPublicQueryable`: that filter is the AutoUI
|
|
66
|
+
// contract, and applying it here would silently drop Internal components from the
|
|
67
|
+
// graph a developer tool draws.
|
|
68
|
+
let encodePluginStructureEntry = (~pluginId: string, def: pluginStructure): JSON.t =>
|
|
69
|
+
Dict.fromArray([
|
|
70
|
+
("pluginId", JSON.Encode.string(Plugin.name(pluginId))),
|
|
71
|
+
(
|
|
72
|
+
"readModels",
|
|
73
|
+
def.readModels->Array.map(Platform_ComponentDefinitionsApi.encodeQueryableDef)->JSON.Encode.array,
|
|
74
|
+
),
|
|
75
|
+
(
|
|
76
|
+
"stateViewSlices",
|
|
77
|
+
def.stateViewSlices
|
|
78
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeQueryableDef)
|
|
79
|
+
->JSON.Encode.array,
|
|
80
|
+
),
|
|
81
|
+
(
|
|
82
|
+
"stateChangeSlices",
|
|
83
|
+
def.stateChangeSlices
|
|
84
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeWritableDef)
|
|
85
|
+
->JSON.Encode.array,
|
|
86
|
+
),
|
|
87
|
+
(
|
|
88
|
+
"aggregates",
|
|
89
|
+
def.aggregates->Array.map(Platform_ComponentDefinitionsApi.encodeWritableDef)->JSON.Encode.array,
|
|
90
|
+
),
|
|
91
|
+
(
|
|
92
|
+
"automationSlices",
|
|
93
|
+
def.automationSlices
|
|
94
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeAutomationSliceDef)
|
|
95
|
+
->JSON.Encode.array,
|
|
96
|
+
),
|
|
97
|
+
(
|
|
98
|
+
"outboundTranslationSlices",
|
|
99
|
+
def.outboundTranslationSlices
|
|
100
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeOutboundTranslationSliceDef)
|
|
101
|
+
->JSON.Encode.array,
|
|
102
|
+
),
|
|
103
|
+
(
|
|
104
|
+
"inboundTranslationSlices",
|
|
105
|
+
def.inboundTranslationSlices
|
|
106
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeInboundTranslationSliceDef)
|
|
107
|
+
->JSON.Encode.array,
|
|
108
|
+
),
|
|
109
|
+
(
|
|
110
|
+
"extensions",
|
|
111
|
+
def.extensions->Array.map(Platform_ComponentDefinitionsApi.encodeExtensionDef)->JSON.Encode.array,
|
|
112
|
+
),
|
|
113
|
+
(
|
|
114
|
+
"extensionPoints",
|
|
115
|
+
def.extensionPoints->Option.mapOr(JSON.Encode.null, eps =>
|
|
116
|
+
eps->Array.map(encodeExtensionPointDef)->JSON.Encode.array
|
|
117
|
+
),
|
|
118
|
+
),
|
|
119
|
+
(
|
|
120
|
+
"requiredStores",
|
|
121
|
+
def.requiredStores->Option.mapOr(
|
|
122
|
+
JSON.Encode.null,
|
|
123
|
+
Platform_ComponentDefinitionsApi.encodeStrings,
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
(
|
|
127
|
+
"requiredStoreDeclarations",
|
|
128
|
+
def.requiredStoreDeclarations->Option.mapOr(JSON.Encode.null, ds =>
|
|
129
|
+
ds->Array.map(encodeRequiredStoreDeclaration)->JSON.Encode.array
|
|
130
|
+
),
|
|
131
|
+
),
|
|
132
|
+
])->JSON.Encode.object
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Plugin$ReventlessCore from "../plugin/component/Plugin.res.mjs";
|
|
5
|
+
import * as Platform_ComponentDefinitionsApi$ReventlessCore from "./Platform_ComponentDefinitionsApi.res.mjs";
|
|
6
|
+
|
|
7
|
+
let sdlTypes = [
|
|
8
|
+
`type Platform_ExtensionPointDef {\n name: String!\n delegateNames: [String!]!\n sourceEventTypes: [String!]!\n commandTypes: [String!]\n}`,
|
|
9
|
+
`type Platform_RequiredStoreDeclaration {\n store: String!\n component: String!\n field: String!\n annotation: String\n}`,
|
|
10
|
+
`type Platform_PluginStructureEntry {\n pluginId: String!\n readModels: [Platform_ReadSideDef!]!\n stateViewSlices: [Platform_ReadSideDef!]!\n stateChangeSlices: [Platform_WriteSideDef!]!\n aggregates: [Platform_WriteSideDef!]!\n automationSlices: [Platform_AutomationSliceDef!]!\n outboundTranslationSlices: [Platform_OutboundTranslationSliceDef!]!\n inboundTranslationSlices: [Platform_InboundTranslationSliceDef!]!\n extensions: [Platform_ExtensionDef!]!\n extensionPoints: [Platform_ExtensionPointDef!]\n requiredStores: [String!]\n requiredStoreDeclarations: [Platform_RequiredStoreDeclaration!]\n}`
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function encodeExtensionPointDef(e) {
|
|
14
|
+
return Object.fromEntries([
|
|
15
|
+
[
|
|
16
|
+
"name",
|
|
17
|
+
e.name
|
|
18
|
+
],
|
|
19
|
+
[
|
|
20
|
+
"delegateNames",
|
|
21
|
+
Platform_ComponentDefinitionsApi$ReventlessCore.encodeStrings(e.delegateNames)
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"sourceEventTypes",
|
|
25
|
+
Platform_ComponentDefinitionsApi$ReventlessCore.encodeStrings(e.sourceEventTypes)
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"commandTypes",
|
|
29
|
+
Stdlib_Option.mapOr(e.commandTypes, null, Platform_ComponentDefinitionsApi$ReventlessCore.encodeStrings)
|
|
30
|
+
]
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function encodeRequiredStoreDeclaration(d) {
|
|
35
|
+
return Object.fromEntries([
|
|
36
|
+
[
|
|
37
|
+
"store",
|
|
38
|
+
d.store
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
"component",
|
|
42
|
+
d.component
|
|
43
|
+
],
|
|
44
|
+
[
|
|
45
|
+
"field",
|
|
46
|
+
d.field
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
"annotation",
|
|
50
|
+
Stdlib_Option.mapOr(d.annotation, null, prim => prim)
|
|
51
|
+
]
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function encodePluginStructureEntry(pluginId, def) {
|
|
56
|
+
return Object.fromEntries([
|
|
57
|
+
[
|
|
58
|
+
"pluginId",
|
|
59
|
+
Plugin$ReventlessCore.name(pluginId)
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"readModels",
|
|
63
|
+
def.readModels.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef)
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"stateViewSlices",
|
|
67
|
+
def.stateViewSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef)
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"stateChangeSlices",
|
|
71
|
+
def.stateChangeSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeWritableDef)
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"aggregates",
|
|
75
|
+
def.aggregates.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeWritableDef)
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
"automationSlices",
|
|
79
|
+
def.automationSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeAutomationSliceDef)
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"outboundTranslationSlices",
|
|
83
|
+
def.outboundTranslationSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeOutboundTranslationSliceDef)
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
"inboundTranslationSlices",
|
|
87
|
+
def.inboundTranslationSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeInboundTranslationSliceDef)
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"extensions",
|
|
91
|
+
def.extensions.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeExtensionDef)
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"extensionPoints",
|
|
95
|
+
Stdlib_Option.mapOr(def.extensionPoints, null, eps => eps.map(encodeExtensionPointDef))
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"requiredStores",
|
|
99
|
+
Stdlib_Option.mapOr(def.requiredStores, null, Platform_ComponentDefinitionsApi$ReventlessCore.encodeStrings)
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"requiredStoreDeclarations",
|
|
103
|
+
Stdlib_Option.mapOr(def.requiredStoreDeclarations, null, ds => ds.map(encodeRequiredStoreDeclaration))
|
|
104
|
+
]
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let sdlQueryField = ` Platform_PluginStructures: [Platform_PluginStructureEntry!]!`;
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
sdlTypes,
|
|
112
|
+
sdlQueryField,
|
|
113
|
+
encodeExtensionPointDef,
|
|
114
|
+
encodeRequiredStoreDeclaration,
|
|
115
|
+
encodePluginStructureEntry,
|
|
116
|
+
}
|
|
117
|
+
/* No side effect */
|
|
@@ -163,17 +163,22 @@ describe("encodePluginStructureEntry", () => {
|
|
|
163
163
|
)
|
|
164
164
|
|
|
165
165
|
// Translation-slice externalSystem: absent on the fixture above (both None), so it
|
|
166
|
-
// must serialize as null — the "no external box" case.
|
|
166
|
+
// must serialize as null — the "no external box" case. The leading field keeps the
|
|
167
|
+
// two directions apart; `targetName` sits between them and is pinned here too.
|
|
167
168
|
testSync("encodes None outbound externalSystem as null", () =>
|
|
168
|
-
expect(
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
expect(
|
|
170
|
+
json->String.includes(
|
|
171
|
+
"\"inboundCommandTypes\":[\"Ship\"],\"targetName\":null,\"externalSystem\":null",
|
|
172
|
+
),
|
|
173
|
+
)->toEqual(true)
|
|
171
174
|
)
|
|
172
175
|
|
|
173
176
|
testSync("encodes None inbound externalSystem as null", () =>
|
|
174
|
-
expect(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
expect(
|
|
178
|
+
json->String.includes(
|
|
179
|
+
"\"commandTypes\":[\"RecordPayment\"],\"targetName\":\"Order\",\"externalSystem\":null",
|
|
180
|
+
),
|
|
181
|
+
)->toEqual(true)
|
|
177
182
|
)
|
|
178
183
|
})
|
|
179
184
|
|
|
@@ -183,10 +183,10 @@ globalThis.describe("encodePluginStructureEntry", () => {
|
|
|
183
183
|
globalThis.expect(json.includes("\"events\":[{\"name\":\"ProductAdded\"")).toEqual(true);
|
|
184
184
|
});
|
|
185
185
|
globalThis.test("encodes None outbound externalSystem as null", () => {
|
|
186
|
-
globalThis.expect(json.includes("\"inboundCommandTypes\":[\"Ship\"],\"externalSystem\":null")).toEqual(true);
|
|
186
|
+
globalThis.expect(json.includes("\"inboundCommandTypes\":[\"Ship\"],\"targetName\":null,\"externalSystem\":null")).toEqual(true);
|
|
187
187
|
});
|
|
188
188
|
globalThis.test("encodes None inbound externalSystem as null", () => {
|
|
189
|
-
globalThis.expect(json.includes("\"commandTypes\":[\"RecordPayment\"],\"externalSystem\":null")).toEqual(true);
|
|
189
|
+
globalThis.expect(json.includes("\"commandTypes\":[\"RecordPayment\"],\"targetName\":\"Order\",\"externalSystem\":null")).toEqual(true);
|
|
190
190
|
});
|
|
191
191
|
});
|
|
192
192
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Asserts what makes `Platform_PluginStructures` different from
|
|
2
|
+
// `Platform_ComponentDefinitions`: nothing is filtered out, and the structure-level
|
|
3
|
+
// collections the AutoUI entry does not carry are present. A developer tool draws
|
|
4
|
+
// its graph from this response, so a component or a bridge missing here is a graph
|
|
5
|
+
// that silently disagrees with the source.
|
|
6
|
+
|
|
7
|
+
open JestGlobals
|
|
8
|
+
open Reventless.Plugin
|
|
9
|
+
|
|
10
|
+
let publicRm: queryableDef = {
|
|
11
|
+
name: "Products",
|
|
12
|
+
queryField: "Catalog_Products",
|
|
13
|
+
schema: "{}",
|
|
14
|
+
consumedEventTypes: ["ProductAdded"],
|
|
15
|
+
linkedWriteSide: ["Product"],
|
|
16
|
+
labelField: "displayName",
|
|
17
|
+
searchableFields: ["displayName"],
|
|
18
|
+
labelFieldSource: Some("annotation"),
|
|
19
|
+
statusField: None,
|
|
20
|
+
visibility: None,
|
|
21
|
+
chapter: Some("Inventory"),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The component `Platform_ComponentDefinitions` drops and this query must keep.
|
|
25
|
+
let internalRm: queryableDef = {
|
|
26
|
+
...publicRm,
|
|
27
|
+
name: "ProductAudit",
|
|
28
|
+
queryField: "Catalog_ProductAudit",
|
|
29
|
+
visibility: Some("Internal"),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let structure: pluginStructure = {
|
|
33
|
+
readModels: [publicRm, internalRm],
|
|
34
|
+
stateViewSlices: [internalRm],
|
|
35
|
+
stateChangeSlices: [],
|
|
36
|
+
aggregates: [],
|
|
37
|
+
automationSlices: [],
|
|
38
|
+
outboundTranslationSlices: [],
|
|
39
|
+
inboundTranslationSlices: [],
|
|
40
|
+
extensions: [],
|
|
41
|
+
extensionPoints: Some([
|
|
42
|
+
{
|
|
43
|
+
name: "Catalog.Products",
|
|
44
|
+
delegateNames: ["onAdded"],
|
|
45
|
+
sourceEventTypes: ["Catalog.ProductAdded"],
|
|
46
|
+
commandTypes: None,
|
|
47
|
+
},
|
|
48
|
+
]),
|
|
49
|
+
requiredStores: Some(["Catalog.images"]),
|
|
50
|
+
requiredStoreDeclarations: Some([
|
|
51
|
+
{store: "Catalog.images", component: "Product", field: "image", annotation: Some("images")},
|
|
52
|
+
]),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let encoded = Platform_PluginStructuresApi.encodePluginStructureEntry(
|
|
56
|
+
~pluginId="Catalog",
|
|
57
|
+
structure,
|
|
58
|
+
)
|
|
59
|
+
let json = encoded->JSON.stringify
|
|
60
|
+
|
|
61
|
+
let namesOf = (key): array<string> =>
|
|
62
|
+
encoded
|
|
63
|
+
->JSON.Decode.object
|
|
64
|
+
->Option.flatMap(d => d->Dict.get(key))
|
|
65
|
+
->Option.flatMap(JSON.Decode.array)
|
|
66
|
+
->Option.getOr([])
|
|
67
|
+
->Array.filterMap(v =>
|
|
68
|
+
v->JSON.Decode.object->Option.flatMap(o => o->Dict.get("name"))->Option.flatMap(JSON.Decode.string)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
describe("Platform_PluginStructures entry", () => {
|
|
72
|
+
testSync("carries the structure-level keys the AutoUI entry omits", () => {
|
|
73
|
+
let dict = encoded->JSON.Decode.object->Option.getOr(Dict.make())
|
|
74
|
+
let keys = dict->Dict.keysToArray->Array.toSorted(String.compare)
|
|
75
|
+
expect(keys)->toEqual([
|
|
76
|
+
"aggregates",
|
|
77
|
+
"automationSlices",
|
|
78
|
+
"extensionPoints",
|
|
79
|
+
"extensions",
|
|
80
|
+
"inboundTranslationSlices",
|
|
81
|
+
"outboundTranslationSlices",
|
|
82
|
+
"pluginId",
|
|
83
|
+
"readModels",
|
|
84
|
+
"requiredStoreDeclarations",
|
|
85
|
+
"requiredStores",
|
|
86
|
+
"stateChangeSlices",
|
|
87
|
+
"stateViewSlices",
|
|
88
|
+
])
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// The whole reason this query exists as a separate field.
|
|
92
|
+
testSync("keeps Internal read models", () =>
|
|
93
|
+
expect(namesOf("readModels"))->toEqual(["Products", "ProductAudit"])
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
testSync("keeps Internal state-view slices", () =>
|
|
97
|
+
expect(namesOf("stateViewSlices"))->toEqual(["ProductAudit"])
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
testSync("carries visibility so a consumer can tell which are Internal", () =>
|
|
101
|
+
expect(json->String.includes("\"visibility\":\"Internal\""))->toEqual(true)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
testSync("encodes the chapter band", () =>
|
|
105
|
+
expect(json->String.includes("\"chapter\":\"Inventory\""))->toEqual(true)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
testSync("encodes extension points with their source event types", () =>
|
|
109
|
+
expect(
|
|
110
|
+
json->String.includes(
|
|
111
|
+
"\"extensionPoints\":[{\"name\":\"Catalog.Products\",\"delegateNames\":[\"onAdded\"],\"sourceEventTypes\":[\"Catalog.ProductAdded\"],\"commandTypes\":null}]",
|
|
112
|
+
),
|
|
113
|
+
)->toEqual(true)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
testSync("encodes required stores with their provenance", () =>
|
|
117
|
+
expect(json->String.includes("\"requiredStores\":[\"Catalog.images\"]"))->toEqual(true)
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// A structure persisted before these fields existed decodes as None, and null is the
|
|
122
|
+
// honest wire value — an empty list would claim the plugin has no extension points
|
|
123
|
+
// when the truth is that the deployment cannot say.
|
|
124
|
+
describe("absent optional collections", () => {
|
|
125
|
+
let bare: pluginStructure = {
|
|
126
|
+
...structure,
|
|
127
|
+
extensionPoints: None,
|
|
128
|
+
requiredStores: None,
|
|
129
|
+
requiredStoreDeclarations: None,
|
|
130
|
+
}
|
|
131
|
+
let bareJson =
|
|
132
|
+
Platform_PluginStructuresApi.encodePluginStructureEntry(
|
|
133
|
+
~pluginId="Catalog",
|
|
134
|
+
bare,
|
|
135
|
+
)->JSON.stringify
|
|
136
|
+
|
|
137
|
+
testSync("encodes absent extensionPoints as null", () =>
|
|
138
|
+
expect(bareJson->String.includes("\"extensionPoints\":null"))->toEqual(true)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
testSync("encodes absent requiredStores as null", () =>
|
|
142
|
+
expect(bareJson->String.includes("\"requiredStores\":null"))->toEqual(true)
|
|
143
|
+
)
|
|
144
|
+
})
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
7
|
+
import * as Platform_PluginStructuresApi$ReventlessCore from "../../src/admin/Platform_PluginStructuresApi.res.mjs";
|
|
8
|
+
|
|
9
|
+
let publicRm_consumedEventTypes = ["ProductAdded"];
|
|
10
|
+
|
|
11
|
+
let publicRm_linkedWriteSide = ["Product"];
|
|
12
|
+
|
|
13
|
+
let publicRm_searchableFields = ["displayName"];
|
|
14
|
+
|
|
15
|
+
let publicRm_labelFieldSource = "annotation";
|
|
16
|
+
|
|
17
|
+
let publicRm_chapter = "Inventory";
|
|
18
|
+
|
|
19
|
+
let publicRm = {
|
|
20
|
+
name: "Products",
|
|
21
|
+
queryField: "Catalog_Products",
|
|
22
|
+
schema: "{}",
|
|
23
|
+
consumedEventTypes: publicRm_consumedEventTypes,
|
|
24
|
+
linkedWriteSide: publicRm_linkedWriteSide,
|
|
25
|
+
labelField: "displayName",
|
|
26
|
+
searchableFields: publicRm_searchableFields,
|
|
27
|
+
labelFieldSource: publicRm_labelFieldSource,
|
|
28
|
+
statusField: undefined,
|
|
29
|
+
visibility: undefined,
|
|
30
|
+
chapter: publicRm_chapter
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let internalRm_consumedEventTypes = publicRm_consumedEventTypes;
|
|
34
|
+
|
|
35
|
+
let internalRm_linkedWriteSide = publicRm_linkedWriteSide;
|
|
36
|
+
|
|
37
|
+
let internalRm_searchableFields = publicRm_searchableFields;
|
|
38
|
+
|
|
39
|
+
let internalRm_labelFieldSource = "annotation";
|
|
40
|
+
|
|
41
|
+
let internalRm_visibility = "Internal";
|
|
42
|
+
|
|
43
|
+
let internalRm_chapter = "Inventory";
|
|
44
|
+
|
|
45
|
+
let internalRm = {
|
|
46
|
+
name: "ProductAudit",
|
|
47
|
+
queryField: "Catalog_ProductAudit",
|
|
48
|
+
schema: "{}",
|
|
49
|
+
consumedEventTypes: internalRm_consumedEventTypes,
|
|
50
|
+
linkedWriteSide: internalRm_linkedWriteSide,
|
|
51
|
+
labelField: "displayName",
|
|
52
|
+
searchableFields: internalRm_searchableFields,
|
|
53
|
+
labelFieldSource: internalRm_labelFieldSource,
|
|
54
|
+
statusField: undefined,
|
|
55
|
+
visibility: internalRm_visibility,
|
|
56
|
+
chapter: internalRm_chapter
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
let structure_readModels = [
|
|
60
|
+
publicRm,
|
|
61
|
+
internalRm
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
let structure_stateViewSlices = [internalRm];
|
|
65
|
+
|
|
66
|
+
let structure_stateChangeSlices = [];
|
|
67
|
+
|
|
68
|
+
let structure_aggregates = [];
|
|
69
|
+
|
|
70
|
+
let structure_automationSlices = [];
|
|
71
|
+
|
|
72
|
+
let structure_outboundTranslationSlices = [];
|
|
73
|
+
|
|
74
|
+
let structure_inboundTranslationSlices = [];
|
|
75
|
+
|
|
76
|
+
let structure_extensions = [];
|
|
77
|
+
|
|
78
|
+
let structure_extensionPoints = [{
|
|
79
|
+
name: "Catalog.Products",
|
|
80
|
+
delegateNames: ["onAdded"],
|
|
81
|
+
sourceEventTypes: ["Catalog.ProductAdded"],
|
|
82
|
+
commandTypes: undefined
|
|
83
|
+
}];
|
|
84
|
+
|
|
85
|
+
let structure_requiredStores = ["Catalog.images"];
|
|
86
|
+
|
|
87
|
+
let structure_requiredStoreDeclarations = [{
|
|
88
|
+
store: "Catalog.images",
|
|
89
|
+
component: "Product",
|
|
90
|
+
field: "image",
|
|
91
|
+
annotation: "images"
|
|
92
|
+
}];
|
|
93
|
+
|
|
94
|
+
let structure = {
|
|
95
|
+
readModels: structure_readModels,
|
|
96
|
+
stateViewSlices: structure_stateViewSlices,
|
|
97
|
+
stateChangeSlices: structure_stateChangeSlices,
|
|
98
|
+
aggregates: structure_aggregates,
|
|
99
|
+
automationSlices: structure_automationSlices,
|
|
100
|
+
outboundTranslationSlices: structure_outboundTranslationSlices,
|
|
101
|
+
inboundTranslationSlices: structure_inboundTranslationSlices,
|
|
102
|
+
extensions: structure_extensions,
|
|
103
|
+
extensionPoints: structure_extensionPoints,
|
|
104
|
+
requiredStores: structure_requiredStores,
|
|
105
|
+
requiredStoreDeclarations: structure_requiredStoreDeclarations
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let encoded = Platform_PluginStructuresApi$ReventlessCore.encodePluginStructureEntry("Catalog", structure);
|
|
109
|
+
|
|
110
|
+
let json = JSON.stringify(encoded);
|
|
111
|
+
|
|
112
|
+
function namesOf(key) {
|
|
113
|
+
return Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(encoded), d => d[key]), Stdlib_JSON.Decode.array), []), v => Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(v), o => o["name"]), Stdlib_JSON.Decode.string));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
globalThis.describe("Platform_PluginStructures entry", () => {
|
|
117
|
+
globalThis.test("carries the structure-level keys the AutoUI entry omits", () => {
|
|
118
|
+
let dict = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(encoded), {});
|
|
119
|
+
let keys = Object.keys(dict).toSorted(Primitive_string.compare);
|
|
120
|
+
globalThis.expect(keys).toEqual([
|
|
121
|
+
"aggregates",
|
|
122
|
+
"automationSlices",
|
|
123
|
+
"extensionPoints",
|
|
124
|
+
"extensions",
|
|
125
|
+
"inboundTranslationSlices",
|
|
126
|
+
"outboundTranslationSlices",
|
|
127
|
+
"pluginId",
|
|
128
|
+
"readModels",
|
|
129
|
+
"requiredStoreDeclarations",
|
|
130
|
+
"requiredStores",
|
|
131
|
+
"stateChangeSlices",
|
|
132
|
+
"stateViewSlices"
|
|
133
|
+
]);
|
|
134
|
+
});
|
|
135
|
+
globalThis.test("keeps Internal read models", () => {
|
|
136
|
+
globalThis.expect(namesOf("readModels")).toEqual([
|
|
137
|
+
"Products",
|
|
138
|
+
"ProductAudit"
|
|
139
|
+
]);
|
|
140
|
+
});
|
|
141
|
+
globalThis.test("keeps Internal state-view slices", () => {
|
|
142
|
+
globalThis.expect(namesOf("stateViewSlices")).toEqual(["ProductAudit"]);
|
|
143
|
+
});
|
|
144
|
+
globalThis.test("carries visibility so a consumer can tell which are Internal", () => {
|
|
145
|
+
globalThis.expect(json.includes("\"visibility\":\"Internal\"")).toEqual(true);
|
|
146
|
+
});
|
|
147
|
+
globalThis.test("encodes the chapter band", () => {
|
|
148
|
+
globalThis.expect(json.includes("\"chapter\":\"Inventory\"")).toEqual(true);
|
|
149
|
+
});
|
|
150
|
+
globalThis.test("encodes extension points with their source event types", () => {
|
|
151
|
+
globalThis.expect(json.includes("\"extensionPoints\":[{\"name\":\"Catalog.Products\",\"delegateNames\":[\"onAdded\"],\"sourceEventTypes\":[\"Catalog.ProductAdded\"],\"commandTypes\":null}]")).toEqual(true);
|
|
152
|
+
});
|
|
153
|
+
globalThis.test("encodes required stores with their provenance", () => {
|
|
154
|
+
globalThis.expect(json.includes("\"requiredStores\":[\"Catalog.images\"]")).toEqual(true);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
globalThis.describe("absent optional collections", () => {
|
|
159
|
+
let bare_readModels = structure_readModels;
|
|
160
|
+
let bare_stateViewSlices = structure_stateViewSlices;
|
|
161
|
+
let bare_stateChangeSlices = structure_stateChangeSlices;
|
|
162
|
+
let bare_aggregates = structure_aggregates;
|
|
163
|
+
let bare_automationSlices = structure_automationSlices;
|
|
164
|
+
let bare_outboundTranslationSlices = structure_outboundTranslationSlices;
|
|
165
|
+
let bare_inboundTranslationSlices = structure_inboundTranslationSlices;
|
|
166
|
+
let bare_extensions = structure_extensions;
|
|
167
|
+
let bare = {
|
|
168
|
+
readModels: bare_readModels,
|
|
169
|
+
stateViewSlices: bare_stateViewSlices,
|
|
170
|
+
stateChangeSlices: bare_stateChangeSlices,
|
|
171
|
+
aggregates: bare_aggregates,
|
|
172
|
+
automationSlices: bare_automationSlices,
|
|
173
|
+
outboundTranslationSlices: bare_outboundTranslationSlices,
|
|
174
|
+
inboundTranslationSlices: bare_inboundTranslationSlices,
|
|
175
|
+
extensions: bare_extensions,
|
|
176
|
+
extensionPoints: undefined,
|
|
177
|
+
requiredStores: undefined,
|
|
178
|
+
requiredStoreDeclarations: undefined
|
|
179
|
+
};
|
|
180
|
+
let bareJson = JSON.stringify(Platform_PluginStructuresApi$ReventlessCore.encodePluginStructureEntry("Catalog", bare));
|
|
181
|
+
globalThis.test("encodes absent extensionPoints as null", () => {
|
|
182
|
+
globalThis.expect(bareJson.includes("\"extensionPoints\":null")).toEqual(true);
|
|
183
|
+
});
|
|
184
|
+
globalThis.test("encodes absent requiredStores as null", () => {
|
|
185
|
+
globalThis.expect(bareJson.includes("\"requiredStores\":null")).toEqual(true);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export {
|
|
190
|
+
publicRm,
|
|
191
|
+
internalRm,
|
|
192
|
+
structure,
|
|
193
|
+
encoded,
|
|
194
|
+
json,
|
|
195
|
+
namesOf,
|
|
196
|
+
}
|
|
197
|
+
/* encoded Not a pure module */
|