@reventlessdev/reventless-gwt 1.0.0-alpha.74
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 +698 -0
- package/LICENSE +202 -0
- package/bin/reventless-dev.mjs +21 -0
- package/package.json +54 -0
- package/rescript.json +36 -0
- package/src/.gwtignore +10 -0
- package/src/Automation_GWT.res +207 -0
- package/src/Automation_GWT.res.mjs +160 -0
- package/src/Behavior_GWT.res +603 -0
- package/src/Behavior_GWT.res.mjs +426 -0
- package/src/Bind.res +29 -0
- package/src/Bind.res.mjs +22 -0
- package/src/BuildClassifier.res +137 -0
- package/src/BuildClassifier.res.mjs +136 -0
- package/src/Cancellation.res +31 -0
- package/src/Cancellation.res.mjs +45 -0
- package/src/ChildProcess.res +84 -0
- package/src/ChildProcess.res.mjs +92 -0
- package/src/Cli.res +671 -0
- package/src/Cli.res.mjs +754 -0
- package/src/Collector.res +201 -0
- package/src/Collector.res.mjs +215 -0
- package/src/ComponentMeta.res +104 -0
- package/src/ComponentMeta.res.mjs +120 -0
- package/src/ComponentScan.res +103 -0
- package/src/ComponentScan.res.mjs +105 -0
- package/src/Delegate_GWT.res +303 -0
- package/src/Delegate_GWT.res.mjs +372 -0
- package/src/Diff.res +91 -0
- package/src/Diff.res.mjs +98 -0
- package/src/Discovery.res +105 -0
- package/src/Discovery.res.mjs +103 -0
- package/src/DomainDeadCode.res +93 -0
- package/src/DomainDeadCode.res.mjs +69 -0
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/EventMapping_GWT.res +29 -0
- package/src/EventMapping_GWT.res.mjs +44 -0
- package/src/Filter.res +36 -0
- package/src/Filter.res.mjs +40 -0
- package/src/Flow_GWT.res +664 -0
- package/src/Flow_GWT.res.mjs +604 -0
- package/src/FormatterHuman.res +138 -0
- package/src/FormatterHuman.res.mjs +122 -0
- package/src/FormatterJson.res +257 -0
- package/src/FormatterJson.res.mjs +320 -0
- package/src/FormatterJunit.res +93 -0
- package/src/FormatterJunit.res.mjs +90 -0
- package/src/FormatterTap.res +139 -0
- package/src/FormatterTap.res.mjs +149 -0
- package/src/FormatterVsCode.res +362 -0
- package/src/FormatterVsCode.res.mjs +564 -0
- package/src/Hint.res +119 -0
- package/src/Hint.res.mjs +124 -0
- package/src/InboundTranslation_GWT.res +83 -0
- package/src/InboundTranslation_GWT.res.mjs +94 -0
- package/src/JestBind.res +70 -0
- package/src/JestBind.res.mjs +63 -0
- package/src/Loader.res +24 -0
- package/src/Loader.res.mjs +23 -0
- package/src/LocalHost.res +135 -0
- package/src/LocalHost.res.mjs +127 -0
- package/src/Mapping_GWT.res +474 -0
- package/src/Mapping_GWT.res.mjs +322 -0
- package/src/MultiSourceProjection_GWT.res +316 -0
- package/src/MultiSourceProjection_GWT.res.mjs +354 -0
- package/src/OutboundTranslation_GWT.res +238 -0
- package/src/OutboundTranslation_GWT.res.mjs +186 -0
- package/src/Outcome.res +161 -0
- package/src/Outcome.res.mjs +140 -0
- package/src/PackageScan.res +80 -0
- package/src/PackageScan.res.mjs +81 -0
- package/src/PlatformRunner.res +160 -0
- package/src/PlatformRunner.res.mjs +155 -0
- package/src/PlatformScan.res +138 -0
- package/src/PlatformScan.res.mjs +131 -0
- package/src/ProcessManager.res +91 -0
- package/src/ProcessManager.res.mjs +80 -0
- package/src/Projection_GWT.res +312 -0
- package/src/Projection_GWT.res.mjs +332 -0
- package/src/Query_GWT.res +379 -0
- package/src/Query_GWT.res.mjs +367 -0
- package/src/RenderRescript.res +125 -0
- package/src/RenderRescript.res.mjs +141 -0
- package/src/RunnerTypes.res +68 -0
- package/src/RunnerTypes.res.mjs +60 -0
- package/src/SideEffect_GWT.res +157 -0
- package/src/SideEffect_GWT.res.mjs +113 -0
- package/src/StateChange/ExternalAddCategorySlice.res +25 -0
- package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
- package/src/StateChange/WithFixtures.res +22 -0
- package/src/StateChange/WithFixtures.res.mjs +31 -0
- package/src/StateChange/WithFixtures_Behavior.res +17 -0
- package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
- package/src/StateChange/WithManualOpen.res +23 -0
- package/src/StateChange/WithManualOpen.res.mjs +31 -0
- package/src/StateChange/WithManualOpen_Behavior.res +17 -0
- package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
- package/src/StubRuntime.res +53 -0
- package/src/StubRuntime.res.mjs +54 -0
- package/src/TestFixtures.res +16 -0
- package/src/TestFixtures.res.mjs +34 -0
- package/src/Watch.res +92 -0
- package/src/Watch.res.mjs +83 -0
- package/src/WatcherProbe.res +48 -0
- package/src/WatcherProbe.res.mjs +46 -0
- package/tests/AutomationGwtTest.res +71 -0
- package/tests/AutomationGwtTest.res.mjs +189 -0
- package/tests/BuildClassifierTest.res +94 -0
- package/tests/BuildClassifierTest.res.mjs +132 -0
- package/tests/ComponentMetaTest.res +52 -0
- package/tests/ComponentMetaTest.res.mjs +57 -0
- package/tests/DelegateGwtTest.res +487 -0
- package/tests/DelegateGwtTest.res.mjs +891 -0
- package/tests/DiscoveryTest.res +42 -0
- package/tests/DiscoveryTest.res.mjs +43 -0
- package/tests/DomainDeadCodeTest.res +116 -0
- package/tests/DomainDeadCodeTest.res.mjs +155 -0
- package/tests/DomainGraphTest.res +365 -0
- package/tests/DomainGraphTest.res.mjs +413 -0
- package/tests/FlowAggregateGwtTest.res +169 -0
- package/tests/FlowAggregateGwtTest.res.mjs +269 -0
- package/tests/FlowCrossPluginGwtTest.res +340 -0
- package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
- package/tests/FlowGwtTest.res +222 -0
- package/tests/FlowGwtTest.res.mjs +432 -0
- package/tests/FormatterVsCodeMessageTest.res +41 -0
- package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
- package/tests/InboundTranslationGwtTest.res +34 -0
- package/tests/InboundTranslationGwtTest.res.mjs +98 -0
- package/tests/LocalHostIntegration.res +70 -0
- package/tests/LocalHostIntegration.res.mjs +64 -0
- package/tests/LocalHostTest.res +71 -0
- package/tests/LocalHostTest.res.mjs +63 -0
- package/tests/MappingGwtTest.res +348 -0
- package/tests/MappingGwtTest.res.mjs +592 -0
- package/tests/OutboundTranslationGwtTest.res +59 -0
- package/tests/OutboundTranslationGwtTest.res.mjs +138 -0
- package/tests/PlatformRunnerTest.res +66 -0
- package/tests/PlatformRunnerTest.res.mjs +85 -0
- package/tests/PlatformScanTest.res +61 -0
- package/tests/PlatformScanTest.res.mjs +56 -0
- package/tests/QueryGwtTest.res +161 -0
- package/tests/QueryGwtTest.res.mjs +314 -0
- package/tests/QueryResolverGwtTest.res +71 -0
- package/tests/QueryResolverGwtTest.res.mjs +136 -0
- package/tests/RunnerUnitTest.res +219 -0
- package/tests/RunnerUnitTest.res.mjs +244 -0
- package/tests/SideEffect/FakeOrderNotification.res +23 -0
- package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
- package/tests/SideEffect/MockEmail.res +18 -0
- package/tests/SideEffect/MockEmail.res.mjs +47 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
- package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
- package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithFixtures_GWT.res +23 -0
- package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithManualOpen_GWT.res +17 -0
- package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
- package/tests/StateChangeSliceGwtTest.res +160 -0
- package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
- package/tests/StateViewSliceGwtTest.res +64 -0
- package/tests/StateViewSliceGwtTest.res.mjs +161 -0
- package/tests/WatchDebounceTest.res +78 -0
- package/tests/WatchDebounceTest.res.mjs +97 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
// Worked examples for Delegate_GWT — the ExtensionPoint / Extension boundary
|
|
2
|
+
// primitive. Mirrors the hybrid example's cross-plugin seam:
|
|
3
|
+
// * an ExtensionPoint mapping turns internal events into public EP events
|
|
4
|
+
// (including one-to-many fan-out), exercised via `FromExtensionPoint`;
|
|
5
|
+
// * an Extension delegate turns EP events into delegate commands, exercised
|
|
6
|
+
// via `FromExtension`.
|
|
7
|
+
// See `docs/plans/done/gwt-flow-and-extension-test-kinds.md` Phase 1.
|
|
8
|
+
|
|
9
|
+
S.enableJson()
|
|
10
|
+
|
|
11
|
+
module EPM = ReventlessInfra.ExtensionPointMapping
|
|
12
|
+
module EM = ReventlessInfra.ExtensionMapping
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Catalog Products extension point + its internal Product delegate.
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
module ProductsEpSpec = {
|
|
19
|
+
let name = "Catalog.Products"
|
|
20
|
+
let moduleUrl = ""
|
|
21
|
+
|
|
22
|
+
@schema
|
|
23
|
+
type command = NoCommand
|
|
24
|
+
|
|
25
|
+
@schema
|
|
26
|
+
type event =
|
|
27
|
+
| ProductBecameAvailable({productId: string, name: string, price: float})
|
|
28
|
+
| ProductPriceChanged({productId: string, price: float})
|
|
29
|
+
|
|
30
|
+
@schema
|
|
31
|
+
type directive = NoDirective
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module ProductDelegate = {
|
|
35
|
+
module Id = Reventless.Id.StringPure
|
|
36
|
+
let name = "Product"
|
|
37
|
+
|
|
38
|
+
@schema
|
|
39
|
+
type command = NoCommand
|
|
40
|
+
|
|
41
|
+
@schema
|
|
42
|
+
type event =
|
|
43
|
+
| ProductAdded({productId: string, name: string, price: float})
|
|
44
|
+
| ProductPriceChanged({productId: string, price: float})
|
|
45
|
+
|
|
46
|
+
@schema
|
|
47
|
+
type error = NoError
|
|
48
|
+
|
|
49
|
+
let moduleUrl = ""
|
|
50
|
+
let commandAuthorization = (_: command): Reventless.Authorization.permission => AllowAuthenticated
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module ProductsEpMapping = {
|
|
54
|
+
module ExtensionPoint = ProductsEpSpec
|
|
55
|
+
module Delegate = ProductDelegate
|
|
56
|
+
|
|
57
|
+
let moduleUrl = "test:DelegateGwtTest:ProductsEpMapping"
|
|
58
|
+
|
|
59
|
+
let mapIncomingCommand = (_id, _command, _meta) => []
|
|
60
|
+
|
|
61
|
+
let mapOutgoingEvent = Some(
|
|
62
|
+
(_id, event: ProductDelegate.event, _meta, _queryEngine) =>
|
|
63
|
+
switch event {
|
|
64
|
+
| ProductAdded({productId, name, price}) => [
|
|
65
|
+
EPM.PublishEvent(
|
|
66
|
+
productId,
|
|
67
|
+
ProductsEpSpec.ProductBecameAvailable({productId, name, price}),
|
|
68
|
+
),
|
|
69
|
+
]
|
|
70
|
+
| ProductPriceChanged({productId, price}) => [
|
|
71
|
+
EPM.PublishEvent(productId, ProductsEpSpec.ProductPriceChanged({productId, price})),
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module ProductsEpGwt = Delegate_GWT.FromExtensionPoint(ProductsEpMapping)
|
|
78
|
+
|
|
79
|
+
ProductsEpGwt.describe("Products ExtensionPoint mapping (FromExtensionPoint)", () => {
|
|
80
|
+
ProductsEpGwt.test("ProductAdded publishes ProductBecameAvailable", () =>
|
|
81
|
+
ProductsEpGwt.whenDelegateEvent(
|
|
82
|
+
ProductDelegate.ProductAdded({productId: "p1", name: "Book", price: 9.99}),
|
|
83
|
+
)->ProductsEpGwt.thenPublishesEvent(
|
|
84
|
+
"p1",
|
|
85
|
+
ProductsEpSpec.ProductBecameAvailable({productId: "p1", name: "Book", price: 9.99}),
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
ProductsEpGwt.test("ProductPriceChanged publishes ProductPriceChanged", () =>
|
|
90
|
+
ProductsEpGwt.whenDelegateEvent(
|
|
91
|
+
ProductDelegate.ProductPriceChanged({productId: "p1", price: 7.5}),
|
|
92
|
+
)->ProductsEpGwt.thenPublishesEvent(
|
|
93
|
+
"p1",
|
|
94
|
+
ProductsEpSpec.ProductPriceChanged({productId: "p1", price: 7.5}),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Ordering Orders extension point — array-decomposed (one-to-many) fan-out.
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
module OrdersEpSpec = {
|
|
104
|
+
let name = "Ordering.Orders"
|
|
105
|
+
let moduleUrl = ""
|
|
106
|
+
|
|
107
|
+
@schema
|
|
108
|
+
type command = NoCommand
|
|
109
|
+
|
|
110
|
+
@schema
|
|
111
|
+
type event =
|
|
112
|
+
| ItemOrdered({productId: string, orderId: string, customerId: string})
|
|
113
|
+
| ItemOrderCancelled({productId: string, orderId: string})
|
|
114
|
+
|
|
115
|
+
@schema
|
|
116
|
+
type directive = NoDirective
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module OrderDelegate = {
|
|
120
|
+
module Id = Reventless.Id.StringPure
|
|
121
|
+
let name = "Order"
|
|
122
|
+
|
|
123
|
+
@schema
|
|
124
|
+
type command = NoCommand
|
|
125
|
+
|
|
126
|
+
@schema
|
|
127
|
+
type event =
|
|
128
|
+
| OrderPlaced({orderId: string, customerId: string, productIds: array<string>})
|
|
129
|
+
| OrderCancelled({orderId: string, productIds: array<string>})
|
|
130
|
+
|
|
131
|
+
@schema
|
|
132
|
+
type error = NoError
|
|
133
|
+
|
|
134
|
+
let moduleUrl = ""
|
|
135
|
+
let commandAuthorization = (_: command): Reventless.Authorization.permission => AllowAuthenticated
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module OrdersEpMapping = {
|
|
139
|
+
module ExtensionPoint = OrdersEpSpec
|
|
140
|
+
module Delegate = OrderDelegate
|
|
141
|
+
|
|
142
|
+
let moduleUrl = "test:DelegateGwtTest:OrdersEpMapping"
|
|
143
|
+
|
|
144
|
+
let mapIncomingCommand = (_id, _command, _meta) => []
|
|
145
|
+
|
|
146
|
+
let mapOutgoingEvent = Some(
|
|
147
|
+
(_id, event: OrderDelegate.event, _meta, _queryEngine) =>
|
|
148
|
+
switch event {
|
|
149
|
+
| OrderPlaced({orderId, customerId, productIds}) =>
|
|
150
|
+
productIds->Array.map(pid => EPM.PublishEvent(
|
|
151
|
+
pid,
|
|
152
|
+
OrdersEpSpec.ItemOrdered({productId: pid, orderId, customerId}),
|
|
153
|
+
))
|
|
154
|
+
| OrderCancelled({orderId, productIds}) =>
|
|
155
|
+
productIds->Array.map(pid => EPM.PublishEvent(
|
|
156
|
+
pid,
|
|
157
|
+
OrdersEpSpec.ItemOrderCancelled({productId: pid, orderId}),
|
|
158
|
+
))
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module OrdersEpGwt = Delegate_GWT.FromExtensionPoint(OrdersEpMapping)
|
|
164
|
+
|
|
165
|
+
OrdersEpGwt.describe("Orders ExtensionPoint mapping — fan-out", () => {
|
|
166
|
+
OrdersEpGwt.test("OrderPlaced fans out to one ItemOrdered per product", () =>
|
|
167
|
+
OrdersEpGwt.whenDelegateEvent(
|
|
168
|
+
OrderDelegate.OrderPlaced({orderId: "o1", customerId: "c1", productIds: ["p1", "p2"]}),
|
|
169
|
+
)->OrdersEpGwt.thenPublishesEvents([
|
|
170
|
+
("p1", OrdersEpSpec.ItemOrdered({productId: "p1", orderId: "o1", customerId: "c1"})),
|
|
171
|
+
("p2", OrdersEpSpec.ItemOrdered({productId: "p2", orderId: "o1", customerId: "c1"})),
|
|
172
|
+
])
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
OrdersEpGwt.test("OrderPlaced with no products publishes nothing", () =>
|
|
176
|
+
OrdersEpGwt.whenDelegateEvent(
|
|
177
|
+
OrderDelegate.OrderPlaced({orderId: "o2", customerId: "c1", productIds: []}),
|
|
178
|
+
)->OrdersEpGwt.thenPublishesNothing
|
|
179
|
+
)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Ordering Products extension — delegates EP events to SyncCatalogProduct.
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
module SyncDelegate = {
|
|
187
|
+
module Id = Reventless.Id.StringPure
|
|
188
|
+
let name = "SyncCatalogProduct"
|
|
189
|
+
|
|
190
|
+
@schema
|
|
191
|
+
type command =
|
|
192
|
+
| SyncNewProduct({productId: string, name: string, price: float})
|
|
193
|
+
| ChangeSyncedPrice({productId: string, price: float})
|
|
194
|
+
|
|
195
|
+
@schema
|
|
196
|
+
type event = NoEvent
|
|
197
|
+
|
|
198
|
+
@schema
|
|
199
|
+
type error = NoError
|
|
200
|
+
|
|
201
|
+
let moduleUrl = ""
|
|
202
|
+
let commandAuthorization = (_: command): Reventless.Authorization.permission => AllowAuthenticated
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module ProductsExtMapping = {
|
|
206
|
+
module ExtensionPoint = ProductsEpSpec
|
|
207
|
+
module Delegate = SyncDelegate
|
|
208
|
+
|
|
209
|
+
let moduleUrl = ""
|
|
210
|
+
let delegateModuleUrl = ""
|
|
211
|
+
|
|
212
|
+
let mapIncomingEvent = (_id, event: ProductsEpSpec.event, _meta, _pluginDef, _queryEngine) =>
|
|
213
|
+
switch event {
|
|
214
|
+
| ProductBecameAvailable({productId, name, price}) => [
|
|
215
|
+
EM.PublishStateChangeSliceCommand(SyncDelegate.SyncNewProduct({productId, name, price})),
|
|
216
|
+
]
|
|
217
|
+
| ProductPriceChanged({productId, price}) => [
|
|
218
|
+
EM.PublishStateChangeSliceCommand(SyncDelegate.ChangeSyncedPrice({productId, price})),
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let mapOutgoingEvent = None
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module ProductsExtGwt = Delegate_GWT.FromExtension(ProductsExtMapping)
|
|
226
|
+
|
|
227
|
+
ProductsExtGwt.describe("Products Extension delegate (FromExtension)", () => {
|
|
228
|
+
ProductsExtGwt.test("ProductBecameAvailable issues SyncNewProduct", () =>
|
|
229
|
+
ProductsExtGwt.whenIncomingEvent(
|
|
230
|
+
ProductsEpSpec.ProductBecameAvailable({productId: "p1", name: "Book", price: 9.99}),
|
|
231
|
+
)->ProductsExtGwt.thenPublishesCommand(
|
|
232
|
+
SyncDelegate.SyncNewProduct({productId: "p1", name: "Book", price: 9.99}),
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
ProductsExtGwt.test("ProductPriceChanged issues ChangeSyncedPrice", () =>
|
|
237
|
+
ProductsExtGwt.whenIncomingEvent(
|
|
238
|
+
ProductsEpSpec.ProductPriceChanged({productId: "p1", price: 7.5}),
|
|
239
|
+
)->ProductsExtGwt.thenPublishesCommand(
|
|
240
|
+
SyncDelegate.ChangeSyncedPrice({productId: "p1", price: 7.5}),
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// EP `mapIncomingCommand` — an incoming protocol command routed to the wrapped
|
|
247
|
+
// aggregate (the other EP direction: `whenIncomingCommand`).
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
module InventoryEpSpec = {
|
|
251
|
+
let name = "Catalog.Inventory"
|
|
252
|
+
let moduleUrl = ""
|
|
253
|
+
|
|
254
|
+
@schema
|
|
255
|
+
type command = Restock({sku: string, qty: int})
|
|
256
|
+
|
|
257
|
+
@schema
|
|
258
|
+
type event = NoEvent
|
|
259
|
+
|
|
260
|
+
@schema
|
|
261
|
+
type directive = NoDirective
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module StockDelegate = {
|
|
265
|
+
module Id = Reventless.Id.StringPure
|
|
266
|
+
let name = "Stock"
|
|
267
|
+
|
|
268
|
+
@schema
|
|
269
|
+
type command = Restock({sku: string, qty: int})
|
|
270
|
+
|
|
271
|
+
@schema
|
|
272
|
+
type event = NoEvent
|
|
273
|
+
|
|
274
|
+
@schema
|
|
275
|
+
type error = NoError
|
|
276
|
+
|
|
277
|
+
let moduleUrl = ""
|
|
278
|
+
let commandAuthorization = (_: command): Reventless.Authorization.permission => AllowAuthenticated
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
module InventoryEpMapping = {
|
|
282
|
+
module ExtensionPoint = InventoryEpSpec
|
|
283
|
+
module Delegate = StockDelegate
|
|
284
|
+
|
|
285
|
+
let moduleUrl = "test:DelegateGwtTest:InventoryEpMapping"
|
|
286
|
+
|
|
287
|
+
let mapIncomingCommand = (id, command: InventoryEpSpec.command, _meta) =>
|
|
288
|
+
switch command {
|
|
289
|
+
| Restock({sku, qty}) => [EPM.PublishCommand(id, StockDelegate.Restock({sku, qty}))]
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let mapOutgoingEvent = None
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module InventoryEpGwt = Delegate_GWT.FromExtensionPoint(InventoryEpMapping)
|
|
296
|
+
|
|
297
|
+
InventoryEpGwt.describe("Inventory ExtensionPoint mapping — incoming command", () => {
|
|
298
|
+
InventoryEpGwt.test("Restock protocol command routes to the Stock delegate", () =>
|
|
299
|
+
InventoryEpGwt.whenIncomingCommand(
|
|
300
|
+
InventoryEpSpec.Restock({sku: "s1", qty: 5}),
|
|
301
|
+
)->InventoryEpGwt.thenPublishesCommand("gwt-id", StockDelegate.Restock({sku: "s1", qty: 5}))
|
|
302
|
+
)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
// Extension `mapOutgoingEvent` — an internal delegate event published back as a
|
|
307
|
+
// protocol command (the other Extension direction: `whenDelegateEvent`).
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
module FeedbackEpSpec = {
|
|
311
|
+
let name = "Ordering.Feedback"
|
|
312
|
+
let moduleUrl = ""
|
|
313
|
+
|
|
314
|
+
@schema
|
|
315
|
+
type command = AckProduct({productId: string})
|
|
316
|
+
|
|
317
|
+
@schema
|
|
318
|
+
type event = NoEvent
|
|
319
|
+
|
|
320
|
+
@schema
|
|
321
|
+
type directive = NoDirective
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
module SyncedDelegate = {
|
|
325
|
+
module Id = Reventless.Id.StringPure
|
|
326
|
+
let name = "SyncedProduct"
|
|
327
|
+
|
|
328
|
+
@schema
|
|
329
|
+
type command = NoCommand
|
|
330
|
+
|
|
331
|
+
@schema
|
|
332
|
+
type event = ProductSynced({productId: string})
|
|
333
|
+
|
|
334
|
+
@schema
|
|
335
|
+
type error = NoError
|
|
336
|
+
|
|
337
|
+
let moduleUrl = ""
|
|
338
|
+
let commandAuthorization = (_: command): Reventless.Authorization.permission => AllowAuthenticated
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
module FeedbackExtMapping = {
|
|
342
|
+
module ExtensionPoint = FeedbackEpSpec
|
|
343
|
+
module Delegate = SyncedDelegate
|
|
344
|
+
|
|
345
|
+
let moduleUrl = ""
|
|
346
|
+
let delegateModuleUrl = ""
|
|
347
|
+
|
|
348
|
+
let mapIncomingEvent = (_id, event: FeedbackEpSpec.event, _meta, _pluginDef, _queryEngine) =>
|
|
349
|
+
switch event {
|
|
350
|
+
| NoEvent => []
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let mapOutgoingEvent = Some(
|
|
354
|
+
(id, event: SyncedDelegate.event, _meta, _pluginDef) =>
|
|
355
|
+
switch event {
|
|
356
|
+
| ProductSynced({productId}) => [
|
|
357
|
+
EM.PublishExtensionPointCommand(id, FeedbackEpSpec.AckProduct({productId: productId})),
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
module FeedbackExtGwt = Delegate_GWT.FromExtension(FeedbackExtMapping)
|
|
364
|
+
|
|
365
|
+
FeedbackExtGwt.describe("Feedback Extension delegate — outgoing command", () => {
|
|
366
|
+
FeedbackExtGwt.test("ProductSynced publishes the AckProduct protocol command", () =>
|
|
367
|
+
FeedbackExtGwt.whenDelegateEvent(
|
|
368
|
+
SyncedDelegate.ProductSynced({productId: "p1"}),
|
|
369
|
+
)->FeedbackExtGwt.thenPublishesExtensionPointCommand(
|
|
370
|
+
"gwt-id",
|
|
371
|
+
FeedbackEpSpec.AckProduct({productId: "p1"}),
|
|
372
|
+
)
|
|
373
|
+
)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
// Directives — a `HandleDirective` action carries a typed directive on its own
|
|
378
|
+
// channel: asserted via `thenHandlesDirective`, disjoint from published events
|
|
379
|
+
// (an arm can do both), and `thenHandlesNoDirective` confirms an arm raises none.
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
|
|
382
|
+
module NotifyEpSpec = {
|
|
383
|
+
let name = "Catalog.Notify"
|
|
384
|
+
let moduleUrl = ""
|
|
385
|
+
|
|
386
|
+
@schema
|
|
387
|
+
type command = NoCommand
|
|
388
|
+
|
|
389
|
+
@schema
|
|
390
|
+
type event = Pinged({productId: string})
|
|
391
|
+
|
|
392
|
+
@schema
|
|
393
|
+
type directive =
|
|
394
|
+
| NotifyOps({channel: string})
|
|
395
|
+
| FlushCache
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
module NotifyEpMapping = {
|
|
399
|
+
module ExtensionPoint = NotifyEpSpec
|
|
400
|
+
module Delegate = ProductDelegate
|
|
401
|
+
|
|
402
|
+
let moduleUrl = "test:DelegateGwtTest:NotifyEpMapping"
|
|
403
|
+
|
|
404
|
+
let handleDirective: EPM.directiveHandler<NotifyEpSpec.directive> = (_c, _d, _q, _dir) =>
|
|
405
|
+
Promise.resolve()
|
|
406
|
+
|
|
407
|
+
let mapIncomingCommand = (_id, _command, _meta) => []
|
|
408
|
+
|
|
409
|
+
// ProductAdded both PUBLISHES an event and HANDLES a directive; ProductPriceChanged
|
|
410
|
+
// publishes only — so `thenHandlesNoDirective` holds for it.
|
|
411
|
+
let mapOutgoingEvent = Some(
|
|
412
|
+
(_id, event: ProductDelegate.event, _meta, _queryEngine) =>
|
|
413
|
+
switch event {
|
|
414
|
+
| ProductAdded({productId}) => [
|
|
415
|
+
EPM.PublishEvent(productId, NotifyEpSpec.Pinged({productId: productId})),
|
|
416
|
+
EPM.HandleDirective(handleDirective, NotifyEpSpec.NotifyOps({channel: "ops"})),
|
|
417
|
+
]
|
|
418
|
+
| ProductPriceChanged({productId}) => [
|
|
419
|
+
EPM.PublishEvent(productId, NotifyEpSpec.Pinged({productId: productId})),
|
|
420
|
+
]
|
|
421
|
+
},
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
module NotifyEpGwt = Delegate_GWT.FromExtensionPoint(NotifyEpMapping)
|
|
426
|
+
|
|
427
|
+
NotifyEpGwt.describe("Notify ExtensionPoint mapping — directives", () => {
|
|
428
|
+
NotifyEpGwt.test("ProductAdded handles the NotifyOps directive", () =>
|
|
429
|
+
NotifyEpGwt.whenDelegateEvent(
|
|
430
|
+
ProductDelegate.ProductAdded({productId: "p1", name: "Book", price: 9.99}),
|
|
431
|
+
)->NotifyEpGwt.thenHandlesDirective(NotifyEpSpec.NotifyOps({channel: "ops"}))
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
// The directive does NOT leak into the published-event channel.
|
|
435
|
+
NotifyEpGwt.test("ProductAdded still publishes only the Pinged event", () =>
|
|
436
|
+
NotifyEpGwt.whenDelegateEvent(
|
|
437
|
+
ProductDelegate.ProductAdded({productId: "p1", name: "Book", price: 9.99}),
|
|
438
|
+
)->NotifyEpGwt.thenPublishesEvent("p1", NotifyEpSpec.Pinged({productId: "p1"}))
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
NotifyEpGwt.test("ProductPriceChanged raises no directive", () =>
|
|
442
|
+
NotifyEpGwt.whenDelegateEvent(
|
|
443
|
+
ProductDelegate.ProductPriceChanged({productId: "p1", price: 7.5}),
|
|
444
|
+
)->NotifyEpGwt.thenHandlesNoDirective
|
|
445
|
+
)
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
module NotifyExtMapping = {
|
|
449
|
+
module ExtensionPoint = NotifyEpSpec
|
|
450
|
+
module Delegate = SyncDelegate
|
|
451
|
+
|
|
452
|
+
let moduleUrl = ""
|
|
453
|
+
let delegateModuleUrl = ""
|
|
454
|
+
|
|
455
|
+
// An Extension directive handler is a bare `Reventless.Handler.handler` (no
|
|
456
|
+
// Schedule/QueryEngine args — extensions are user code).
|
|
457
|
+
let handleDirective: Reventless.Handler.handler<NotifyEpSpec.directive> = _dir =>
|
|
458
|
+
Promise.resolve()
|
|
459
|
+
|
|
460
|
+
let mapIncomingEvent = (_id, event: NotifyEpSpec.event, _meta, _pluginDef, _queryEngine) =>
|
|
461
|
+
switch event {
|
|
462
|
+
| Pinged({productId}) => [
|
|
463
|
+
EM.PublishStateChangeSliceCommand(SyncDelegate.ChangeSyncedPrice({productId, price: 0.0})),
|
|
464
|
+
EM.HandleDirective(handleDirective, NotifyEpSpec.FlushCache),
|
|
465
|
+
]
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
let mapOutgoingEvent = None
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
module NotifyExtGwt = Delegate_GWT.FromExtension(NotifyExtMapping)
|
|
472
|
+
|
|
473
|
+
NotifyExtGwt.describe("Notify Extension delegate — directives", () => {
|
|
474
|
+
NotifyExtGwt.test("Pinged handles the FlushCache directive", () =>
|
|
475
|
+
NotifyExtGwt.whenIncomingEvent(
|
|
476
|
+
NotifyEpSpec.Pinged({productId: "p1"}),
|
|
477
|
+
)->NotifyExtGwt.thenHandlesDirective(NotifyEpSpec.FlushCache)
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
NotifyExtGwt.test("Pinged still publishes only the ChangeSyncedPrice command", () =>
|
|
481
|
+
NotifyExtGwt.whenIncomingEvent(
|
|
482
|
+
NotifyEpSpec.Pinged({productId: "p1"}),
|
|
483
|
+
)->NotifyExtGwt.thenPublishesCommand(
|
|
484
|
+
SyncDelegate.ChangeSyncedPrice({productId: "p1", price: 0.0}),
|
|
485
|
+
)
|
|
486
|
+
)
|
|
487
|
+
})
|