@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,222 @@
|
|
|
1
|
+
// Worked example for Flow_GWT — a single-plugin order flow threaded through a
|
|
2
|
+
// command slice, an automation, a second command slice, a read-model view and
|
|
3
|
+
// an outbound effect. One declarative chain asserts that the slices compose as
|
|
4
|
+
// an Event Modeling lane would draw them.
|
|
5
|
+
//
|
|
6
|
+
// Also exercises DCB tag isolation: a second order is placed successfully even
|
|
7
|
+
// though a first order is already on the shared log, because each command step
|
|
8
|
+
// filters the log by its command's tags.
|
|
9
|
+
// See `docs/plans/done/gwt-flow-and-extension-test-kinds.md` Phase 2.
|
|
10
|
+
|
|
11
|
+
S.enableJson()
|
|
12
|
+
|
|
13
|
+
open Flow_GWT
|
|
14
|
+
|
|
15
|
+
// -- Slices ------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
module PlaceOrderSlice = {
|
|
18
|
+
let name = "PlaceOrder"
|
|
19
|
+
|
|
20
|
+
@schema
|
|
21
|
+
type consumedEvent = OrderPlaced({orderId: @s.matches(Reventless.DcbTag.string) string})
|
|
22
|
+
|
|
23
|
+
@schema
|
|
24
|
+
type command = PlaceOrder({orderId: @s.matches(Reventless.DcbTag.string) string, item: string})
|
|
25
|
+
|
|
26
|
+
@schema
|
|
27
|
+
type error = AlreadyPlaced
|
|
28
|
+
|
|
29
|
+
@schema
|
|
30
|
+
type event = OrderPlaced({orderId: @s.matches(Reventless.DcbTag.string) string, item: string})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module PlaceOrderBehavior = {
|
|
34
|
+
module Spec = PlaceOrderSlice
|
|
35
|
+
type state = {placed: bool}
|
|
36
|
+
let initialState = {placed: false}
|
|
37
|
+
|
|
38
|
+
let evolve = (_state, event: PlaceOrderSlice.consumedEvent) =>
|
|
39
|
+
switch event {
|
|
40
|
+
| OrderPlaced(_) => {placed: true}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let decide = (state, command: PlaceOrderSlice.command) =>
|
|
44
|
+
switch command {
|
|
45
|
+
| PlaceOrder({orderId, item}) =>
|
|
46
|
+
state.placed
|
|
47
|
+
? Error(PlaceOrderSlice.AlreadyPlaced)
|
|
48
|
+
: Ok([PlaceOrderSlice.OrderPlaced({orderId, item})])
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module AutoShipSlice = {
|
|
53
|
+
let name = "AutoShip"
|
|
54
|
+
|
|
55
|
+
@schema
|
|
56
|
+
type consumedEvent =
|
|
57
|
+
| OrderPlaced({orderId: string})
|
|
58
|
+
| OrderShipped({orderId: string})
|
|
59
|
+
|
|
60
|
+
@schema
|
|
61
|
+
type todoItem = {orderId: string}
|
|
62
|
+
|
|
63
|
+
@schema
|
|
64
|
+
type command = ShipOrder({orderId: string})
|
|
65
|
+
|
|
66
|
+
let collect = event =>
|
|
67
|
+
switch event {
|
|
68
|
+
| OrderPlaced({orderId}) => [(orderId, ({orderId: orderId}: todoItem))]
|
|
69
|
+
| OrderShipped(_) => []
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let resolve = event =>
|
|
73
|
+
switch event {
|
|
74
|
+
| OrderShipped({orderId}) => Some(orderId)
|
|
75
|
+
| OrderPlaced(_) => None
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let process = (id, _todo) => Some((id, ShipOrder({orderId: id})))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module ShipOrderSlice = {
|
|
82
|
+
let name = "ShipOrder"
|
|
83
|
+
|
|
84
|
+
@schema
|
|
85
|
+
type consumedEvent =
|
|
86
|
+
| OrderPlaced({orderId: @s.matches(Reventless.DcbTag.string) string})
|
|
87
|
+
| OrderShipped({orderId: @s.matches(Reventless.DcbTag.string) string})
|
|
88
|
+
|
|
89
|
+
@schema
|
|
90
|
+
type command = ShipOrder({orderId: @s.matches(Reventless.DcbTag.string) string})
|
|
91
|
+
|
|
92
|
+
@schema
|
|
93
|
+
type error = NotPlaced
|
|
94
|
+
|
|
95
|
+
@schema
|
|
96
|
+
type event = OrderShipped({orderId: @s.matches(Reventless.DcbTag.string) string})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module ShipOrderBehavior = {
|
|
100
|
+
module Spec = ShipOrderSlice
|
|
101
|
+
type state = {placed: bool, shipped: bool}
|
|
102
|
+
let initialState = {placed: false, shipped: false}
|
|
103
|
+
|
|
104
|
+
let evolve = (state, event: ShipOrderSlice.consumedEvent) =>
|
|
105
|
+
switch event {
|
|
106
|
+
| OrderPlaced(_) => {...state, placed: true}
|
|
107
|
+
| OrderShipped(_) => {...state, shipped: true}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let decide = (state, command: ShipOrderSlice.command) =>
|
|
111
|
+
switch command {
|
|
112
|
+
| ShipOrder({orderId}) =>
|
|
113
|
+
if !state.placed {
|
|
114
|
+
Error(ShipOrderSlice.NotPlaced)
|
|
115
|
+
} else if state.shipped {
|
|
116
|
+
Ok([])
|
|
117
|
+
} else {
|
|
118
|
+
Ok([ShipOrderSlice.OrderShipped({orderId: orderId})])
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module OrdersViewSlice = {
|
|
124
|
+
let name = "OrdersView"
|
|
125
|
+
|
|
126
|
+
@schema
|
|
127
|
+
type state = {orderId: string, item: string, status: string}
|
|
128
|
+
|
|
129
|
+
@schema
|
|
130
|
+
type consumedEvent =
|
|
131
|
+
| OrderPlaced({orderId: string, item: string})
|
|
132
|
+
| OrderShipped({orderId: string})
|
|
133
|
+
|
|
134
|
+
let subIdConfig = None
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module OrdersViewProjection = {
|
|
138
|
+
module Spec = OrdersViewSlice
|
|
139
|
+
open Reventless.Projection
|
|
140
|
+
|
|
141
|
+
let project = (event: OrdersViewSlice.consumedEvent) =>
|
|
142
|
+
switch event {
|
|
143
|
+
| OrderPlaced({orderId, item}) => [
|
|
144
|
+
Set(orderId, ({orderId, item, status: "Placed"}: OrdersViewSlice.state)),
|
|
145
|
+
]
|
|
146
|
+
| OrderShipped({orderId}) => [Update(orderId, state => {...state, status: "Shipped"})]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module ConfirmSlice = {
|
|
151
|
+
let name = "Confirm"
|
|
152
|
+
|
|
153
|
+
@schema
|
|
154
|
+
type consumedEvent = OrderPlaced({orderId: string})
|
|
155
|
+
|
|
156
|
+
@schema
|
|
157
|
+
type outboundItem = {orderId: string}
|
|
158
|
+
|
|
159
|
+
@schema
|
|
160
|
+
type inboundCommand = unit
|
|
161
|
+
|
|
162
|
+
let collect = event =>
|
|
163
|
+
switch event {
|
|
164
|
+
| OrderPlaced({orderId}) => [(orderId, ({orderId: orderId}: outboundItem))]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// -- Step modules ------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
module Place = CommandStep(PlaceOrderSlice, PlaceOrderBehavior)
|
|
171
|
+
module Auto = AutomationStep(AutoShipSlice)
|
|
172
|
+
module Ship = CommandStep(ShipOrderSlice, ShipOrderBehavior)
|
|
173
|
+
module View = ViewStep(OrdersViewSlice, OrdersViewProjection)
|
|
174
|
+
module Confirm = OutboundStep(ConfirmSlice)
|
|
175
|
+
|
|
176
|
+
// -- Flows -------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
describe("Order flow (single plugin)", () => {
|
|
179
|
+
test("place → auto-ship → ship → view Shipped → confirmation fired", () =>
|
|
180
|
+
start
|
|
181
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o1", item: "book"}))
|
|
182
|
+
->Place.thenEvents([PlaceOrderSlice.OrderPlaced({orderId: "o1", item: "book"})])
|
|
183
|
+
->Auto.whenReacts
|
|
184
|
+
->Auto.thenIssuesCommand(AutoShipSlice.ShipOrder({orderId: "o1"}))
|
|
185
|
+
->Ship.whenCommand(ShipOrderSlice.ShipOrder({orderId: "o1"}))
|
|
186
|
+
->Ship.thenEvents([ShipOrderSlice.OrderShipped({orderId: "o1"})])
|
|
187
|
+
->View.thenViewState("o1", {OrdersViewSlice.orderId: "o1", item: "book", status: "Shipped"})
|
|
188
|
+
->Confirm.thenOutbound([("o1", {ConfirmSlice.orderId: "o1"})])
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
test("automation does not re-issue ShipOrder once the order is shipped", () =>
|
|
192
|
+
start
|
|
193
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o1", item: "book"}))
|
|
194
|
+
->Ship.whenCommand(ShipOrderSlice.ShipOrder({orderId: "o1"}))
|
|
195
|
+
->Auto.whenReacts
|
|
196
|
+
->Auto.thenIssuesNoCommand
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
test("re-placing the same order is rejected", () =>
|
|
200
|
+
start
|
|
201
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o1", item: "book"}))
|
|
202
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o1", item: "book"}))
|
|
203
|
+
->Place.thenError(AlreadyPlaced)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
test("a second, different order is not blocked by the first (DCB tag isolation)", () =>
|
|
207
|
+
start
|
|
208
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o1", item: "book"}))
|
|
209
|
+
->Place.thenEvent(PlaceOrderSlice.OrderPlaced({orderId: "o1", item: "book"}))
|
|
210
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o2", item: "pen"}))
|
|
211
|
+
->Place.thenEvent(PlaceOrderSlice.OrderPlaced({orderId: "o2", item: "pen"}))
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
test("shipping one order leaves a sibling order Placed", () =>
|
|
215
|
+
start
|
|
216
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o1", item: "book"}))
|
|
217
|
+
->Place.whenCommand(PlaceOrderSlice.PlaceOrder({orderId: "o2", item: "pen"}))
|
|
218
|
+
->Ship.whenCommand(ShipOrderSlice.ShipOrder({orderId: "o1"}))
|
|
219
|
+
->View.thenViewState("o1", {OrdersViewSlice.orderId: "o1", item: "book", status: "Shipped"})
|
|
220
|
+
->View.thenViewState("o2", {OrdersViewSlice.orderId: "o2", item: "pen", status: "Placed"})
|
|
221
|
+
)
|
|
222
|
+
})
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
|
+
import * as Flow_GWT$ReventlessGwt from "../src/Flow_GWT.res.mjs";
|
|
6
|
+
|
|
7
|
+
S.enableJson();
|
|
8
|
+
|
|
9
|
+
let consumedEventSchema = S.schema(s => ({
|
|
10
|
+
TAG: "OrderPlaced",
|
|
11
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
let commandSchema = S.schema(s => ({
|
|
15
|
+
TAG: "PlaceOrder",
|
|
16
|
+
orderId: s.m(DcbTag$Reventless.string),
|
|
17
|
+
item: s.m(S.string)
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
let errorSchema = S.literal("AlreadyPlaced");
|
|
21
|
+
|
|
22
|
+
let eventSchema = S.schema(s => ({
|
|
23
|
+
TAG: "OrderPlaced",
|
|
24
|
+
orderId: s.m(DcbTag$Reventless.string),
|
|
25
|
+
item: s.m(S.string)
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
function commandAuthorization(param) {
|
|
29
|
+
return "AllowAuthenticated";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let PlaceOrderSlice = {
|
|
33
|
+
name: "PlaceOrder",
|
|
34
|
+
consumedEventSchema: consumedEventSchema,
|
|
35
|
+
commandSchema: commandSchema,
|
|
36
|
+
errorSchema: errorSchema,
|
|
37
|
+
eventSchema: eventSchema,
|
|
38
|
+
commandAuthorization: commandAuthorization,
|
|
39
|
+
readConsistency: "EscalateOnRetry"
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let initialState = {
|
|
43
|
+
placed: false
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function evolve(_state, event) {
|
|
47
|
+
return {
|
|
48
|
+
placed: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function decide(state, command) {
|
|
53
|
+
if (state.placed) {
|
|
54
|
+
return {
|
|
55
|
+
TAG: "Error",
|
|
56
|
+
_0: "AlreadyPlaced"
|
|
57
|
+
};
|
|
58
|
+
} else {
|
|
59
|
+
return {
|
|
60
|
+
TAG: "Ok",
|
|
61
|
+
_0: [{
|
|
62
|
+
TAG: "OrderPlaced",
|
|
63
|
+
orderId: command.orderId,
|
|
64
|
+
item: command.item
|
|
65
|
+
}]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let PlaceOrderBehavior = {
|
|
71
|
+
Spec: undefined,
|
|
72
|
+
initialState: initialState,
|
|
73
|
+
evolve: evolve,
|
|
74
|
+
decide: decide
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
let consumedEventSchema$1 = S.union([
|
|
78
|
+
S.schema(s => ({
|
|
79
|
+
TAG: "OrderPlaced",
|
|
80
|
+
orderId: s.m(S.string)
|
|
81
|
+
})),
|
|
82
|
+
S.schema(s => ({
|
|
83
|
+
TAG: "OrderShipped",
|
|
84
|
+
orderId: s.m(S.string)
|
|
85
|
+
}))
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
let todoItemSchema = S.schema(s => ({
|
|
89
|
+
orderId: s.m(S.string)
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
let commandSchema$1 = S.schema(s => ({
|
|
93
|
+
TAG: "ShipOrder",
|
|
94
|
+
orderId: s.m(S.string)
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
function collect(event) {
|
|
98
|
+
if (event.TAG !== "OrderPlaced") {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
let orderId = event.orderId;
|
|
102
|
+
return [[
|
|
103
|
+
orderId,
|
|
104
|
+
{
|
|
105
|
+
orderId: orderId
|
|
106
|
+
}
|
|
107
|
+
]];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function resolve(event) {
|
|
111
|
+
if (event.TAG === "OrderPlaced") {
|
|
112
|
+
return;
|
|
113
|
+
} else {
|
|
114
|
+
return event.orderId;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function process(id, _todo) {
|
|
119
|
+
return [
|
|
120
|
+
id,
|
|
121
|
+
{
|
|
122
|
+
TAG: "ShipOrder",
|
|
123
|
+
orderId: id
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function commandAuthorization$1(param) {
|
|
129
|
+
return "AllowAuthenticated";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let AutoShipSlice = {
|
|
133
|
+
name: "AutoShip",
|
|
134
|
+
consumedEventSchema: consumedEventSchema$1,
|
|
135
|
+
todoItemSchema: todoItemSchema,
|
|
136
|
+
commandSchema: commandSchema$1,
|
|
137
|
+
collect: collect,
|
|
138
|
+
resolve: resolve,
|
|
139
|
+
process: process,
|
|
140
|
+
commandAuthorization: commandAuthorization$1
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
let consumedEventSchema$2 = S.union([
|
|
144
|
+
S.schema(s => ({
|
|
145
|
+
TAG: "OrderPlaced",
|
|
146
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
147
|
+
})),
|
|
148
|
+
S.schema(s => ({
|
|
149
|
+
TAG: "OrderShipped",
|
|
150
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
151
|
+
}))
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
let commandSchema$2 = S.schema(s => ({
|
|
155
|
+
TAG: "ShipOrder",
|
|
156
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
157
|
+
}));
|
|
158
|
+
|
|
159
|
+
let errorSchema$1 = S.literal("NotPlaced");
|
|
160
|
+
|
|
161
|
+
let eventSchema$1 = S.schema(s => ({
|
|
162
|
+
TAG: "OrderShipped",
|
|
163
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
function commandAuthorization$2(param) {
|
|
167
|
+
return "AllowAuthenticated";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let ShipOrderSlice = {
|
|
171
|
+
name: "ShipOrder",
|
|
172
|
+
consumedEventSchema: consumedEventSchema$2,
|
|
173
|
+
commandSchema: commandSchema$2,
|
|
174
|
+
errorSchema: errorSchema$1,
|
|
175
|
+
eventSchema: eventSchema$1,
|
|
176
|
+
commandAuthorization: commandAuthorization$2,
|
|
177
|
+
readConsistency: "EscalateOnRetry"
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
let initialState$1 = {
|
|
181
|
+
placed: false,
|
|
182
|
+
shipped: false
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
function evolve$1(state, event) {
|
|
186
|
+
if (event.TAG === "OrderPlaced") {
|
|
187
|
+
return {
|
|
188
|
+
placed: true,
|
|
189
|
+
shipped: state.shipped
|
|
190
|
+
};
|
|
191
|
+
} else {
|
|
192
|
+
return {
|
|
193
|
+
placed: state.placed,
|
|
194
|
+
shipped: true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function decide$1(state, command) {
|
|
200
|
+
if (state.placed) {
|
|
201
|
+
if (state.shipped) {
|
|
202
|
+
return {
|
|
203
|
+
TAG: "Ok",
|
|
204
|
+
_0: []
|
|
205
|
+
};
|
|
206
|
+
} else {
|
|
207
|
+
return {
|
|
208
|
+
TAG: "Ok",
|
|
209
|
+
_0: [{
|
|
210
|
+
TAG: "OrderShipped",
|
|
211
|
+
orderId: command.orderId
|
|
212
|
+
}]
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
return {
|
|
217
|
+
TAG: "Error",
|
|
218
|
+
_0: "NotPlaced"
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let ShipOrderBehavior = {
|
|
224
|
+
Spec: undefined,
|
|
225
|
+
initialState: initialState$1,
|
|
226
|
+
evolve: evolve$1,
|
|
227
|
+
decide: decide$1
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
let stateSchema = S.schema(s => ({
|
|
231
|
+
orderId: s.m(S.string),
|
|
232
|
+
item: s.m(S.string),
|
|
233
|
+
status: s.m(S.string)
|
|
234
|
+
}));
|
|
235
|
+
|
|
236
|
+
let consumedEventSchema$3 = S.union([
|
|
237
|
+
S.schema(s => ({
|
|
238
|
+
TAG: "OrderPlaced",
|
|
239
|
+
orderId: s.m(S.string),
|
|
240
|
+
item: s.m(S.string)
|
|
241
|
+
})),
|
|
242
|
+
S.schema(s => ({
|
|
243
|
+
TAG: "OrderShipped",
|
|
244
|
+
orderId: s.m(S.string)
|
|
245
|
+
}))
|
|
246
|
+
]);
|
|
247
|
+
|
|
248
|
+
let OrdersViewSlice = {
|
|
249
|
+
name: "OrdersView",
|
|
250
|
+
stateSchema: stateSchema,
|
|
251
|
+
consumedEventSchema: consumedEventSchema$3,
|
|
252
|
+
subIdConfig: undefined,
|
|
253
|
+
authorization: "AllowAuthenticated",
|
|
254
|
+
visibility: "Public"
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
function project(event) {
|
|
258
|
+
if (event.TAG !== "OrderPlaced") {
|
|
259
|
+
return [{
|
|
260
|
+
TAG: "Update",
|
|
261
|
+
_0: event.orderId,
|
|
262
|
+
_1: state => ({
|
|
263
|
+
orderId: state.orderId,
|
|
264
|
+
item: state.item,
|
|
265
|
+
status: "Shipped"
|
|
266
|
+
})
|
|
267
|
+
}];
|
|
268
|
+
}
|
|
269
|
+
let orderId = event.orderId;
|
|
270
|
+
return [{
|
|
271
|
+
TAG: "Set",
|
|
272
|
+
_0: orderId,
|
|
273
|
+
_1: {
|
|
274
|
+
orderId: orderId,
|
|
275
|
+
item: event.item,
|
|
276
|
+
status: "Placed"
|
|
277
|
+
}
|
|
278
|
+
}];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let OrdersViewProjection = {
|
|
282
|
+
Spec: undefined,
|
|
283
|
+
project: project
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
let consumedEventSchema$4 = S.schema(s => ({
|
|
287
|
+
TAG: "OrderPlaced",
|
|
288
|
+
orderId: s.m(S.string)
|
|
289
|
+
}));
|
|
290
|
+
|
|
291
|
+
let outboundItemSchema = S.schema(s => ({
|
|
292
|
+
orderId: s.m(S.string)
|
|
293
|
+
}));
|
|
294
|
+
|
|
295
|
+
function collect$1(event) {
|
|
296
|
+
let orderId = event.orderId;
|
|
297
|
+
return [[
|
|
298
|
+
orderId,
|
|
299
|
+
{
|
|
300
|
+
orderId: orderId
|
|
301
|
+
}
|
|
302
|
+
]];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let ConfirmSlice = {
|
|
306
|
+
name: "Confirm",
|
|
307
|
+
consumedEventSchema: consumedEventSchema$4,
|
|
308
|
+
outboundItemSchema: outboundItemSchema,
|
|
309
|
+
inboundCommandSchema: S.unit,
|
|
310
|
+
collect: collect$1
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
let Place = Flow_GWT$ReventlessGwt.CommandStep(PlaceOrderSlice)({
|
|
314
|
+
initialState: initialState,
|
|
315
|
+
evolve: evolve,
|
|
316
|
+
decide: decide
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
let Auto = Flow_GWT$ReventlessGwt.AutomationStep(AutoShipSlice);
|
|
320
|
+
|
|
321
|
+
let Ship = Flow_GWT$ReventlessGwt.CommandStep(ShipOrderSlice)({
|
|
322
|
+
initialState: initialState$1,
|
|
323
|
+
evolve: evolve$1,
|
|
324
|
+
decide: decide$1
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
let View = Flow_GWT$ReventlessGwt.ViewStep(OrdersViewSlice)({
|
|
328
|
+
project: project
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
let Confirm = Flow_GWT$ReventlessGwt.OutboundStep(ConfirmSlice);
|
|
332
|
+
|
|
333
|
+
Flow_GWT$ReventlessGwt.describe("Order flow (single plugin)", () => {
|
|
334
|
+
Flow_GWT$ReventlessGwt.test("place → auto-ship → ship → view Shipped → confirmation fired", undefined, () => Confirm.thenOutbound(View.thenViewState(Ship.thenEvents(Ship.whenCommand(Auto.thenIssuesCommand(Auto.whenReacts(Place.thenEvents(Place.whenCommand(Flow_GWT$ReventlessGwt.start, {
|
|
335
|
+
TAG: "PlaceOrder",
|
|
336
|
+
orderId: "o1",
|
|
337
|
+
item: "book"
|
|
338
|
+
}), [{
|
|
339
|
+
TAG: "OrderPlaced",
|
|
340
|
+
orderId: "o1",
|
|
341
|
+
item: "book"
|
|
342
|
+
}])), {
|
|
343
|
+
TAG: "ShipOrder",
|
|
344
|
+
orderId: "o1"
|
|
345
|
+
}), {
|
|
346
|
+
TAG: "ShipOrder",
|
|
347
|
+
orderId: "o1"
|
|
348
|
+
}), [{
|
|
349
|
+
TAG: "OrderShipped",
|
|
350
|
+
orderId: "o1"
|
|
351
|
+
}]), "o1", {
|
|
352
|
+
orderId: "o1",
|
|
353
|
+
item: "book",
|
|
354
|
+
status: "Shipped"
|
|
355
|
+
}), [[
|
|
356
|
+
"o1",
|
|
357
|
+
{
|
|
358
|
+
orderId: "o1"
|
|
359
|
+
}
|
|
360
|
+
]]));
|
|
361
|
+
Flow_GWT$ReventlessGwt.test("automation does not re-issue ShipOrder once the order is shipped", undefined, () => Auto.thenIssuesNoCommand(Auto.whenReacts(Ship.whenCommand(Place.whenCommand(Flow_GWT$ReventlessGwt.start, {
|
|
362
|
+
TAG: "PlaceOrder",
|
|
363
|
+
orderId: "o1",
|
|
364
|
+
item: "book"
|
|
365
|
+
}), {
|
|
366
|
+
TAG: "ShipOrder",
|
|
367
|
+
orderId: "o1"
|
|
368
|
+
}))));
|
|
369
|
+
Flow_GWT$ReventlessGwt.test("re-placing the same order is rejected", undefined, () => Place.thenError(Place.whenCommand(Place.whenCommand(Flow_GWT$ReventlessGwt.start, {
|
|
370
|
+
TAG: "PlaceOrder",
|
|
371
|
+
orderId: "o1",
|
|
372
|
+
item: "book"
|
|
373
|
+
}), {
|
|
374
|
+
TAG: "PlaceOrder",
|
|
375
|
+
orderId: "o1",
|
|
376
|
+
item: "book"
|
|
377
|
+
}), "AlreadyPlaced"));
|
|
378
|
+
Flow_GWT$ReventlessGwt.test("a second, different order is not blocked by the first (DCB tag isolation)", undefined, () => Place.thenEvent(Place.whenCommand(Place.thenEvent(Place.whenCommand(Flow_GWT$ReventlessGwt.start, {
|
|
379
|
+
TAG: "PlaceOrder",
|
|
380
|
+
orderId: "o1",
|
|
381
|
+
item: "book"
|
|
382
|
+
}), {
|
|
383
|
+
TAG: "OrderPlaced",
|
|
384
|
+
orderId: "o1",
|
|
385
|
+
item: "book"
|
|
386
|
+
}), {
|
|
387
|
+
TAG: "PlaceOrder",
|
|
388
|
+
orderId: "o2",
|
|
389
|
+
item: "pen"
|
|
390
|
+
}), {
|
|
391
|
+
TAG: "OrderPlaced",
|
|
392
|
+
orderId: "o2",
|
|
393
|
+
item: "pen"
|
|
394
|
+
}));
|
|
395
|
+
Flow_GWT$ReventlessGwt.test("shipping one order leaves a sibling order Placed", undefined, () => View.thenViewState(View.thenViewState(Ship.whenCommand(Place.whenCommand(Place.whenCommand(Flow_GWT$ReventlessGwt.start, {
|
|
396
|
+
TAG: "PlaceOrder",
|
|
397
|
+
orderId: "o1",
|
|
398
|
+
item: "book"
|
|
399
|
+
}), {
|
|
400
|
+
TAG: "PlaceOrder",
|
|
401
|
+
orderId: "o2",
|
|
402
|
+
item: "pen"
|
|
403
|
+
}), {
|
|
404
|
+
TAG: "ShipOrder",
|
|
405
|
+
orderId: "o1"
|
|
406
|
+
}), "o1", {
|
|
407
|
+
orderId: "o1",
|
|
408
|
+
item: "book",
|
|
409
|
+
status: "Shipped"
|
|
410
|
+
}), "o2", {
|
|
411
|
+
orderId: "o2",
|
|
412
|
+
item: "pen",
|
|
413
|
+
status: "Placed"
|
|
414
|
+
}));
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
export {
|
|
418
|
+
PlaceOrderSlice,
|
|
419
|
+
PlaceOrderBehavior,
|
|
420
|
+
AutoShipSlice,
|
|
421
|
+
ShipOrderSlice,
|
|
422
|
+
ShipOrderBehavior,
|
|
423
|
+
OrdersViewSlice,
|
|
424
|
+
OrdersViewProjection,
|
|
425
|
+
ConfirmSlice,
|
|
426
|
+
Place,
|
|
427
|
+
Auto,
|
|
428
|
+
Ship,
|
|
429
|
+
View,
|
|
430
|
+
Confirm,
|
|
431
|
+
}
|
|
432
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Unit test for FormatterVsCode.messagePayload — the failure payload the
|
|
2
|
+
// VS Code extension renders. Asserts the `kind` discriminator (the mismatch
|
|
3
|
+
// family) is present so a client can gate the apply-expected quick-fix.
|
|
4
|
+
|
|
5
|
+
open JestGlobals
|
|
6
|
+
|
|
7
|
+
let failingResult = (~mismatch): RunnerTypes.testResult => {
|
|
8
|
+
id: "t1",
|
|
9
|
+
name: "rejects when category already exists",
|
|
10
|
+
describePath: [],
|
|
11
|
+
status: RunnerTypes.Fail,
|
|
12
|
+
durationMs: 1.0,
|
|
13
|
+
location: None,
|
|
14
|
+
slice: None,
|
|
15
|
+
mismatch,
|
|
16
|
+
skipReason: None,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("FormatterVsCode.messagePayload", () => {
|
|
20
|
+
testPromise("includes the mismatch kind for an EventsMismatch", async () => {
|
|
21
|
+
let m = FormatterVsCode.messagePayload(
|
|
22
|
+
failingResult(~mismatch=Some(Outcome.EventsMismatch({expected: [], actual: []}))),
|
|
23
|
+
)
|
|
24
|
+
expect(m.kind)->toEqual(Some("EventsMismatch"))
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
testPromise("includes the mismatch kind for an ErrorMismatch", async () => {
|
|
28
|
+
let m = FormatterVsCode.messagePayload(
|
|
29
|
+
failingResult(
|
|
30
|
+
~mismatch=Some(
|
|
31
|
+
Outcome.ErrorMismatch({
|
|
32
|
+
expected: JSON.String("CategoryAlreadyExists"),
|
|
33
|
+
actual: None,
|
|
34
|
+
actualEvents: [],
|
|
35
|
+
}),
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
expect(m.kind)->toEqual(Some("ErrorMismatch"))
|
|
40
|
+
})
|
|
41
|
+
})
|