@reventlessdev/online-shop-hybrid-ordering 1.0.0-alpha.100
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 +904 -0
- package/LICENSE +202 -0
- package/__mocks__/emptyModule.js +1 -0
- package/package.json +57 -0
- package/rescript.json +37 -0
- package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res +29 -0
- package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res.mjs +71 -0
- package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res +27 -0
- package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res.mjs +73 -0
- package/src/CatalogProduct/StateViewSliceStream/AvailableProducts.res +15 -0
- package/src/CatalogProduct/StateViewSliceStream/AvailableProducts.res.mjs +69 -0
- package/src/CatalogProduct/StateViewSliceStream/AvailableProducts_Projection.res +8 -0
- package/src/CatalogProduct/StateViewSliceStream/AvailableProducts_Projection.res.mjs +38 -0
- package/src/Customer/Aggregate/Customer.res +24 -0
- package/src/Customer/Aggregate/Customer.res.mjs +64 -0
- package/src/Customer/Aggregate/Customer_Behavior.res +41 -0
- package/src/Customer/Aggregate/Customer_Behavior.res.mjs +143 -0
- package/src/Customer/ReadModelStream/Customers.res +15 -0
- package/src/Customer/ReadModelStream/Customers.res.mjs +44 -0
- package/src/Customer/ReadModelStream/Customers_Projections.res +78 -0
- package/src/Customer/ReadModelStream/Customers_Projections.res.mjs +153 -0
- package/src/Extension/Products_Extension.res +23 -0
- package/src/Extension/Products_Extension.res.mjs +43 -0
- package/src/ExtensionPoint/Orders_ExtensionPointMapping.res +33 -0
- package/src/ExtensionPoint/Orders_ExtensionPointMapping.res.mjs +82 -0
- package/src/Order/AutomationSlice/AutoShipOrder.res +19 -0
- package/src/Order/AutomationSlice/AutoShipOrder.res.mjs +42 -0
- package/src/Order/AutomationSlice/AutoShipOrder_Automation.res +37 -0
- package/src/Order/AutomationSlice/AutoShipOrder_Automation.res.mjs +101 -0
- package/src/Order/OutboundTranslationSlice/SendOrderConfirmation.res +22 -0
- package/src/Order/OutboundTranslationSlice/SendOrderConfirmation.res.mjs +45 -0
- package/src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res +24 -0
- package/src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res.mjs +46 -0
- package/src/Order/StateChangeSlice/CancelOrder.res +29 -0
- package/src/Order/StateChangeSlice/CancelOrder.res.mjs +75 -0
- package/src/Order/StateChangeSlice/CancelOrder_Behavior.res +35 -0
- package/src/Order/StateChangeSlice/CancelOrder_Behavior.res.mjs +110 -0
- package/src/Order/StateChangeSlice/PlaceOrder.res +28 -0
- package/src/Order/StateChangeSlice/PlaceOrder.res.mjs +63 -0
- package/src/Order/StateChangeSlice/PlaceOrder_Behavior.res +39 -0
- package/src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs +70 -0
- package/src/Order/StateChangeSlice/RefundOrder.res +27 -0
- package/src/Order/StateChangeSlice/RefundOrder.res.mjs +59 -0
- package/src/Order/StateChangeSlice/RefundOrder_Behavior.res +26 -0
- package/src/Order/StateChangeSlice/RefundOrder_Behavior.res.mjs +77 -0
- package/src/Order/StateChangeSlice/ShipOrder.res +23 -0
- package/src/Order/StateChangeSlice/ShipOrder.res.mjs +56 -0
- package/src/Order/StateChangeSlice/ShipOrder_Behavior.res +26 -0
- package/src/Order/StateChangeSlice/ShipOrder_Behavior.res.mjs +73 -0
- package/src/Order/StateViewSliceStream/Orders.res +24 -0
- package/src/Order/StateViewSliceStream/Orders.res.mjs +80 -0
- package/src/Order/StateViewSliceStream/Orders_Projection.res +10 -0
- package/src/Order/StateViewSliceStream/Orders_Projection.res.mjs +52 -0
- package/src/Plugin.res +74 -0
- package/src/Plugin.res.mjs +304 -0
- package/src/Service/EmailService.res +3 -0
- package/src/Service/EmailService.res.mjs +11 -0
- package/src/plugin.json +1 -0
- package/tests/CatalogProduct/StateChangeSlice/SyncCatalogProduct_GWT.res +27 -0
- package/tests/CatalogProduct/StateChangeSlice/SyncCatalogProduct_GWT.res.mjs +109 -0
- package/tests/CatalogProduct/StateViewSliceStream/AvailableProducts_GWT.res +15 -0
- package/tests/CatalogProduct/StateViewSliceStream/AvailableProducts_GWT.res.mjs +93 -0
- package/tests/Customer/Aggregate/Customer_GWT.res +63 -0
- package/tests/Customer/Aggregate/Customer_GWT.res.mjs +153 -0
- package/tests/Customer/ReadModelStream/Customers_GWT.res +84 -0
- package/tests/Customer/ReadModelStream/Customers_GWT.res.mjs +90 -0
- package/tests/Extension/Products_Extension_GWT.res +23 -0
- package/tests/Extension/Products_Extension_GWT.res.mjs +108 -0
- package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res +23 -0
- package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res.mjs +139 -0
- package/tests/Flow/OrderingFlow_GWT.res +69 -0
- package/tests/Flow/OrderingFlow_GWT.res.mjs +184 -0
- package/tests/Order/AutomationSlice/AutoShipOrder_GWT.res +55 -0
- package/tests/Order/AutomationSlice/AutoShipOrder_GWT.res.mjs +133 -0
- package/tests/Order/OutboundTranslationSlice/SendOrderConfirmation_GWT.res +30 -0
- package/tests/Order/OutboundTranslationSlice/SendOrderConfirmation_GWT.res.mjs +102 -0
- package/tests/Order/StateChangeSlice/CancelOrder_GWT.res +45 -0
- package/tests/Order/StateChangeSlice/CancelOrder_GWT.res.mjs +131 -0
- package/tests/Order/StateChangeSlice/PlaceOrder_GWT.res +33 -0
- package/tests/Order/StateChangeSlice/PlaceOrder_GWT.res.mjs +139 -0
- package/tests/Order/StateChangeSlice/RefundOrder_GWT.res +31 -0
- package/tests/Order/StateChangeSlice/RefundOrder_GWT.res.mjs +100 -0
- package/tests/Order/StateChangeSlice/ShipOrder_GWT.res +27 -0
- package/tests/Order/StateChangeSlice/ShipOrder_GWT.res.mjs +91 -0
- package/tests/Order/StateViewSliceStream/Orders_GWT.res +36 -0
- package/tests/Order/StateViewSliceStream/Orders_GWT.res.mjs +114 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
4
|
+
import * as Delegate_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Delegate_GWT.res.mjs";
|
|
5
|
+
import * as Orders_ExtensionPoint$OrderingSpec from "@reventlessdev/online-shop-hybrid-ordering-spec/src/Orders_ExtensionPoint.res.mjs";
|
|
6
|
+
import * as Orders_ExtensionPointMapping$OrderingPlugin from "../../src/ExtensionPoint/Orders_ExtensionPointMapping.res.mjs";
|
|
7
|
+
|
|
8
|
+
let include = Delegate_GWT$ReventlessGwt.FromExtensionPoint({
|
|
9
|
+
ExtensionPoint: {
|
|
10
|
+
name: Orders_ExtensionPoint$OrderingSpec.name,
|
|
11
|
+
moduleUrl: Orders_ExtensionPoint$OrderingSpec.moduleUrl,
|
|
12
|
+
commandSchema: Orders_ExtensionPoint$OrderingSpec.commandSchema,
|
|
13
|
+
eventSchema: Orders_ExtensionPoint$OrderingSpec.eventSchema,
|
|
14
|
+
directiveSchema: Orders_ExtensionPoint$OrderingSpec.directiveSchema
|
|
15
|
+
},
|
|
16
|
+
Delegate: {
|
|
17
|
+
Id: Id$Reventless.$$String,
|
|
18
|
+
name: Orders_ExtensionPointMapping$OrderingPlugin.Delegate.name,
|
|
19
|
+
eventSchema: Orders_ExtensionPointMapping$OrderingPlugin.Delegate.eventSchema,
|
|
20
|
+
errorSchema: Orders_ExtensionPointMapping$OrderingPlugin.Delegate.errorSchema,
|
|
21
|
+
commandSchema: Orders_ExtensionPointMapping$OrderingPlugin.Delegate.commandSchema,
|
|
22
|
+
moduleUrl: Orders_ExtensionPointMapping$OrderingPlugin.Delegate.moduleUrl,
|
|
23
|
+
commandAuthorization: Orders_ExtensionPointMapping$OrderingPlugin.Delegate.commandAuthorization
|
|
24
|
+
},
|
|
25
|
+
moduleUrl: Orders_ExtensionPointMapping$OrderingPlugin.moduleUrl,
|
|
26
|
+
mapIncomingCommand: Orders_ExtensionPointMapping$OrderingPlugin.mapIncomingCommand,
|
|
27
|
+
mapOutgoingEvent: Orders_ExtensionPointMapping$OrderingPlugin.mapOutgoingEvent
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
let describe = include.describe;
|
|
31
|
+
|
|
32
|
+
let test = include.test;
|
|
33
|
+
|
|
34
|
+
let whenDelegateEvent = include.whenDelegateEvent;
|
|
35
|
+
|
|
36
|
+
let thenPublishesEvents = include.thenPublishesEvents;
|
|
37
|
+
|
|
38
|
+
describe("Orders ExtensionPoint mapping — per-product fan-out", () => {
|
|
39
|
+
test("OrderPlaced fans out to one ItemOrdered per product", undefined, () => thenPublishesEvents(whenDelegateEvent({
|
|
40
|
+
TAG: "OrderPlaced",
|
|
41
|
+
orderId: "o1",
|
|
42
|
+
customerId: "c1",
|
|
43
|
+
productIds: [
|
|
44
|
+
"p1",
|
|
45
|
+
"p2"
|
|
46
|
+
]
|
|
47
|
+
}), [
|
|
48
|
+
[
|
|
49
|
+
"p1",
|
|
50
|
+
{
|
|
51
|
+
TAG: "ItemOrdered",
|
|
52
|
+
productId: "p1",
|
|
53
|
+
orderId: "o1",
|
|
54
|
+
customerId: "c1"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"p2",
|
|
59
|
+
{
|
|
60
|
+
TAG: "ItemOrdered",
|
|
61
|
+
productId: "p2",
|
|
62
|
+
orderId: "o1",
|
|
63
|
+
customerId: "c1"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
]));
|
|
67
|
+
test("OrderCancelled fans out to one ItemOrderCancelled per product", undefined, () => thenPublishesEvents(whenDelegateEvent({
|
|
68
|
+
TAG: "OrderCancelled",
|
|
69
|
+
orderId: "o1",
|
|
70
|
+
productIds: [
|
|
71
|
+
"p1",
|
|
72
|
+
"p2"
|
|
73
|
+
]
|
|
74
|
+
}), [
|
|
75
|
+
[
|
|
76
|
+
"p1",
|
|
77
|
+
{
|
|
78
|
+
TAG: "ItemOrderCancelled",
|
|
79
|
+
productId: "p1",
|
|
80
|
+
orderId: "o1"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"p2",
|
|
85
|
+
{
|
|
86
|
+
TAG: "ItemOrderCancelled",
|
|
87
|
+
productId: "p2",
|
|
88
|
+
orderId: "o1"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
]));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
let encEvent = include.encEvent;
|
|
95
|
+
|
|
96
|
+
let encCommand = include.encCommand;
|
|
97
|
+
|
|
98
|
+
let encDirective = include.encDirective;
|
|
99
|
+
|
|
100
|
+
let EvCore = include.EvCore;
|
|
101
|
+
|
|
102
|
+
let CmdCore = include.CmdCore;
|
|
103
|
+
|
|
104
|
+
let thenPublishesNothing = include.thenPublishesNothing;
|
|
105
|
+
|
|
106
|
+
let thenPublishesEvent = include.thenPublishesEvent;
|
|
107
|
+
|
|
108
|
+
let whenIncomingCommand = include.whenIncomingCommand;
|
|
109
|
+
|
|
110
|
+
let thenPublishesCommand = include.thenPublishesCommand;
|
|
111
|
+
|
|
112
|
+
let thenPublishesCommands = include.thenPublishesCommands;
|
|
113
|
+
|
|
114
|
+
let thenHandlesDirective = include.thenHandlesDirective;
|
|
115
|
+
|
|
116
|
+
let thenHandlesDirectives = include.thenHandlesDirectives;
|
|
117
|
+
|
|
118
|
+
let thenHandlesNoDirective = include.thenHandlesNoDirective;
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
encEvent,
|
|
122
|
+
encCommand,
|
|
123
|
+
encDirective,
|
|
124
|
+
EvCore,
|
|
125
|
+
CmdCore,
|
|
126
|
+
describe,
|
|
127
|
+
test,
|
|
128
|
+
thenPublishesNothing,
|
|
129
|
+
whenDelegateEvent,
|
|
130
|
+
thenPublishesEvent,
|
|
131
|
+
thenPublishesEvents,
|
|
132
|
+
whenIncomingCommand,
|
|
133
|
+
thenPublishesCommand,
|
|
134
|
+
thenPublishesCommands,
|
|
135
|
+
thenHandlesDirective,
|
|
136
|
+
thenHandlesDirectives,
|
|
137
|
+
thenHandlesNoDirective,
|
|
138
|
+
}
|
|
139
|
+
/* include Not a pure module */
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Cross-slice flow GWT for the Ordering plugin: one declarative chain threads a
|
|
2
|
+
// single order through every tile of its Event Modeling lane —
|
|
3
|
+
//
|
|
4
|
+
// PlaceOrder ─→ OrderPlaced
|
|
5
|
+
// ├─ AutoShipOrder reacts → ShipOrder ─→ OrderShipped
|
|
6
|
+
// ├─ Orders (view) : status Placed → Shipped
|
|
7
|
+
// └─ SendOrderConfirmation : confirmation effect fired
|
|
8
|
+
//
|
|
9
|
+
// PlaceOrder only succeeds because the referenced product was synced first, so
|
|
10
|
+
// the flow seeds a CatalogProductSynced event before placing the order.
|
|
11
|
+
@@reventless.gwt
|
|
12
|
+
|
|
13
|
+
// Compose the automation onto a flat slice — the production split keeps
|
|
14
|
+
// collect/resolve inside the per-source mapping; the GWT needs them together.
|
|
15
|
+
let testContext: Reventless.AutomationSlice.context = {
|
|
16
|
+
environment: "test",
|
|
17
|
+
platformName: "test",
|
|
18
|
+
pluginName: "ordering",
|
|
19
|
+
sliceName: "AutoShipOrder",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module AutoShipOrderSlice = {
|
|
23
|
+
include AutoShipOrder
|
|
24
|
+
type consumedEvent = AutoShipOrder_Automation.FromOrderingDcb.sourceEvent
|
|
25
|
+
let consumedEventSchema = AutoShipOrder_Automation.FromOrderingDcb.sourceEventSchema
|
|
26
|
+
let collect = e => AutoShipOrder_Automation.FromOrderingDcb.collect(e, testContext)
|
|
27
|
+
let resolve = AutoShipOrder_Automation.FromOrderingDcb.resolve
|
|
28
|
+
let process = AutoShipOrder_Automation.process
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module ConfirmSlice = {
|
|
32
|
+
include SendOrderConfirmation
|
|
33
|
+
let collect = SendOrderConfirmation_Translation.collect
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module Sync = CommandStep(SyncCatalogProduct, SyncCatalogProduct_Behavior)
|
|
37
|
+
module Place = CommandStep(PlaceOrder, PlaceOrder_Behavior)
|
|
38
|
+
module Auto = AutomationStep(AutoShipOrderSlice)
|
|
39
|
+
module Ship = CommandStep(ShipOrder, ShipOrder_Behavior)
|
|
40
|
+
module OrdersView = ViewStep(Orders, Orders_Projection)
|
|
41
|
+
module Confirm = OutboundStep(ConfirmSlice)
|
|
42
|
+
|
|
43
|
+
describe("Ordering flow — place → auto-ship → confirm", () => {
|
|
44
|
+
test("an order is placed, auto-shipped, projected as Shipped, and confirmed", () =>
|
|
45
|
+
start
|
|
46
|
+
->Sync.givenEvents([
|
|
47
|
+
SyncCatalogProduct.CatalogProductSynced({productId: "p1", name: "Book", price: 9.99}),
|
|
48
|
+
])
|
|
49
|
+
->Place.whenCommand(PlaceOrder.PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
|
|
50
|
+
->Place.thenEvent(
|
|
51
|
+
PlaceOrder.OrderPlaced({orderId: "o1", customerId: "c1", productIds: ["p1"]}),
|
|
52
|
+
)
|
|
53
|
+
->Auto.whenReacts
|
|
54
|
+
->Auto.thenIssuesCommand(AutoShipOrder.ShipOrder({orderId: "o1"}))
|
|
55
|
+
->Ship.whenCommand(ShipOrder.ShipOrder({orderId: "o1"}))
|
|
56
|
+
->Ship.thenEvent(ShipOrder.OrderShipped({orderId: "o1"}))
|
|
57
|
+
->OrdersView.thenViewState(
|
|
58
|
+
"o1",
|
|
59
|
+
{Orders.orderId: "o1", customerId: "c1", productIds: ["p1"], status: Shipped},
|
|
60
|
+
)
|
|
61
|
+
->Confirm.thenOutbound([("o1", {SendOrderConfirmation.orderId: "o1", customerId: "c1"})])
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
test("placing an order for an unsynced product is rejected", () =>
|
|
65
|
+
start
|
|
66
|
+
->Place.whenCommand(PlaceOrder.PlaceOrder({orderId: "o1", customerId: "c1", productIds: ["p1"]}))
|
|
67
|
+
->Place.thenError(ProductsNotAvailable({missing: ["p1"]}))
|
|
68
|
+
)
|
|
69
|
+
})
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Orders$OrderingPlugin from "../../src/Order/StateViewSliceStream/Orders.res.mjs";
|
|
4
|
+
import * as Flow_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Flow_GWT.res.mjs";
|
|
5
|
+
import * as ShipOrder$OrderingPlugin from "../../src/Order/StateChangeSlice/ShipOrder.res.mjs";
|
|
6
|
+
import * as PlaceOrder$OrderingPlugin from "../../src/Order/StateChangeSlice/PlaceOrder.res.mjs";
|
|
7
|
+
import * as AutoShipOrder$OrderingPlugin from "../../src/Order/AutomationSlice/AutoShipOrder.res.mjs";
|
|
8
|
+
import * as Orders_Projection$OrderingPlugin from "../../src/Order/StateViewSliceStream/Orders_Projection.res.mjs";
|
|
9
|
+
import * as ShipOrder_Behavior$OrderingPlugin from "../../src/Order/StateChangeSlice/ShipOrder_Behavior.res.mjs";
|
|
10
|
+
import * as SyncCatalogProduct$OrderingPlugin from "../../src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res.mjs";
|
|
11
|
+
import * as PlaceOrder_Behavior$OrderingPlugin from "../../src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs";
|
|
12
|
+
import * as SendOrderConfirmation$OrderingPlugin from "../../src/Order/OutboundTranslationSlice/SendOrderConfirmation.res.mjs";
|
|
13
|
+
import * as AutoShipOrder_Automation$OrderingPlugin from "../../src/Order/AutomationSlice/AutoShipOrder_Automation.res.mjs";
|
|
14
|
+
import * as SyncCatalogProduct_Behavior$OrderingPlugin from "../../src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res.mjs";
|
|
15
|
+
import * as SendOrderConfirmation_Translation$OrderingPlugin from "../../src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res.mjs";
|
|
16
|
+
|
|
17
|
+
let testContext = {
|
|
18
|
+
environment: "test",
|
|
19
|
+
platformName: "test",
|
|
20
|
+
pluginName: "ordering",
|
|
21
|
+
sliceName: "AutoShipOrder"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let consumedEventSchema = AutoShipOrder_Automation$OrderingPlugin.FromOrderingDcb.sourceEventSchema;
|
|
25
|
+
|
|
26
|
+
function collect(e) {
|
|
27
|
+
return AutoShipOrder_Automation$OrderingPlugin.FromOrderingDcb.collect(e, testContext);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let resolve = AutoShipOrder_Automation$OrderingPlugin.FromOrderingDcb.resolve;
|
|
31
|
+
|
|
32
|
+
let AutoShipOrderSlice = {
|
|
33
|
+
name: AutoShipOrder$OrderingPlugin.name,
|
|
34
|
+
Id: AutoShipOrder$OrderingPlugin.Id,
|
|
35
|
+
todoItemSchema: AutoShipOrder$OrderingPlugin.todoItemSchema,
|
|
36
|
+
commandSchema: AutoShipOrder$OrderingPlugin.commandSchema,
|
|
37
|
+
maxRetries: AutoShipOrder$OrderingPlugin.maxRetries,
|
|
38
|
+
heartbeatInterval: AutoShipOrder$OrderingPlugin.heartbeatInterval,
|
|
39
|
+
targetName: AutoShipOrder$OrderingPlugin.targetName,
|
|
40
|
+
moduleUrl: AutoShipOrder$OrderingPlugin.moduleUrl,
|
|
41
|
+
commandAuthorization: AutoShipOrder$OrderingPlugin.commandAuthorization,
|
|
42
|
+
consumedEventSchema: consumedEventSchema,
|
|
43
|
+
collect: collect,
|
|
44
|
+
resolve: resolve,
|
|
45
|
+
process: AutoShipOrder_Automation$OrderingPlugin.process
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let ConfirmSlice = {
|
|
49
|
+
name: SendOrderConfirmation$OrderingPlugin.name,
|
|
50
|
+
Id: SendOrderConfirmation$OrderingPlugin.Id,
|
|
51
|
+
consumedEventSchema: SendOrderConfirmation$OrderingPlugin.consumedEventSchema,
|
|
52
|
+
outboundItemSchema: SendOrderConfirmation$OrderingPlugin.outboundItemSchema,
|
|
53
|
+
inboundCommandSchema: SendOrderConfirmation$OrderingPlugin.inboundCommandSchema,
|
|
54
|
+
maxRetries: SendOrderConfirmation$OrderingPlugin.maxRetries,
|
|
55
|
+
heartbeatInterval: SendOrderConfirmation$OrderingPlugin.heartbeatInterval,
|
|
56
|
+
targetName: undefined,
|
|
57
|
+
externalSystem: SendOrderConfirmation$OrderingPlugin.externalSystem,
|
|
58
|
+
moduleUrl: SendOrderConfirmation$OrderingPlugin.moduleUrl,
|
|
59
|
+
collect: SendOrderConfirmation_Translation$OrderingPlugin.collect
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let Sync = Flow_GWT$ReventlessGwt.CommandStep({
|
|
63
|
+
name: SyncCatalogProduct$OrderingPlugin.name,
|
|
64
|
+
consumedEventSchema: SyncCatalogProduct$OrderingPlugin.consumedEventSchema,
|
|
65
|
+
commandSchema: SyncCatalogProduct$OrderingPlugin.commandSchema,
|
|
66
|
+
errorSchema: SyncCatalogProduct$OrderingPlugin.errorSchema,
|
|
67
|
+
eventSchema: SyncCatalogProduct$OrderingPlugin.eventSchema
|
|
68
|
+
})({
|
|
69
|
+
initialState: SyncCatalogProduct_Behavior$OrderingPlugin.initialState,
|
|
70
|
+
evolve: SyncCatalogProduct_Behavior$OrderingPlugin.evolve,
|
|
71
|
+
decide: SyncCatalogProduct_Behavior$OrderingPlugin.decide
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let Place = Flow_GWT$ReventlessGwt.CommandStep({
|
|
75
|
+
name: PlaceOrder$OrderingPlugin.name,
|
|
76
|
+
consumedEventSchema: PlaceOrder$OrderingPlugin.consumedEventSchema,
|
|
77
|
+
commandSchema: PlaceOrder$OrderingPlugin.commandSchema,
|
|
78
|
+
errorSchema: PlaceOrder$OrderingPlugin.errorSchema,
|
|
79
|
+
eventSchema: PlaceOrder$OrderingPlugin.eventSchema
|
|
80
|
+
})({
|
|
81
|
+
initialState: PlaceOrder_Behavior$OrderingPlugin.initialState,
|
|
82
|
+
evolve: PlaceOrder_Behavior$OrderingPlugin.evolve,
|
|
83
|
+
decide: PlaceOrder_Behavior$OrderingPlugin.decide
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let Auto = Flow_GWT$ReventlessGwt.AutomationStep({
|
|
87
|
+
name: AutoShipOrder$OrderingPlugin.name,
|
|
88
|
+
consumedEventSchema: consumedEventSchema,
|
|
89
|
+
todoItemSchema: AutoShipOrder$OrderingPlugin.todoItemSchema,
|
|
90
|
+
commandSchema: AutoShipOrder$OrderingPlugin.commandSchema,
|
|
91
|
+
collect: collect,
|
|
92
|
+
resolve: resolve,
|
|
93
|
+
process: AutoShipOrder_Automation$OrderingPlugin.process
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
let Ship = Flow_GWT$ReventlessGwt.CommandStep({
|
|
97
|
+
name: ShipOrder$OrderingPlugin.name,
|
|
98
|
+
consumedEventSchema: ShipOrder$OrderingPlugin.consumedEventSchema,
|
|
99
|
+
commandSchema: ShipOrder$OrderingPlugin.commandSchema,
|
|
100
|
+
errorSchema: ShipOrder$OrderingPlugin.errorSchema,
|
|
101
|
+
eventSchema: ShipOrder$OrderingPlugin.eventSchema
|
|
102
|
+
})({
|
|
103
|
+
initialState: ShipOrder_Behavior$OrderingPlugin.initialState,
|
|
104
|
+
evolve: ShipOrder_Behavior$OrderingPlugin.evolve,
|
|
105
|
+
decide: ShipOrder_Behavior$OrderingPlugin.decide
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
let OrdersView = Flow_GWT$ReventlessGwt.ViewStep({
|
|
109
|
+
name: Orders$OrderingPlugin.name,
|
|
110
|
+
stateSchema: Orders$OrderingPlugin.stateSchema,
|
|
111
|
+
consumedEventSchema: Orders$OrderingPlugin.consumedEventSchema,
|
|
112
|
+
subIdConfig: undefined
|
|
113
|
+
})({
|
|
114
|
+
project: Orders_Projection$OrderingPlugin.project
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
let Confirm = Flow_GWT$ReventlessGwt.OutboundStep({
|
|
118
|
+
name: SendOrderConfirmation$OrderingPlugin.name,
|
|
119
|
+
consumedEventSchema: SendOrderConfirmation$OrderingPlugin.consumedEventSchema,
|
|
120
|
+
outboundItemSchema: SendOrderConfirmation$OrderingPlugin.outboundItemSchema,
|
|
121
|
+
inboundCommandSchema: SendOrderConfirmation$OrderingPlugin.inboundCommandSchema,
|
|
122
|
+
collect: SendOrderConfirmation_Translation$OrderingPlugin.collect
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
Flow_GWT$ReventlessGwt.describe("Ordering flow — place → auto-ship → confirm", () => {
|
|
126
|
+
Flow_GWT$ReventlessGwt.test("an order is placed, auto-shipped, projected as Shipped, and confirmed", undefined, () => Confirm.thenOutbound(OrdersView.thenViewState(Ship.thenEvent(Ship.whenCommand(Auto.thenIssuesCommand(Auto.whenReacts(Place.thenEvent(Place.whenCommand(Sync.givenEvents(Flow_GWT$ReventlessGwt.start, [{
|
|
127
|
+
TAG: "CatalogProductSynced",
|
|
128
|
+
productId: "p1",
|
|
129
|
+
name: "Book",
|
|
130
|
+
price: 9.99
|
|
131
|
+
}]), {
|
|
132
|
+
TAG: "PlaceOrder",
|
|
133
|
+
orderId: "o1",
|
|
134
|
+
customerId: "c1",
|
|
135
|
+
productIds: ["p1"]
|
|
136
|
+
}), {
|
|
137
|
+
TAG: "OrderPlaced",
|
|
138
|
+
orderId: "o1",
|
|
139
|
+
customerId: "c1",
|
|
140
|
+
productIds: ["p1"]
|
|
141
|
+
})), {
|
|
142
|
+
TAG: "ShipOrder",
|
|
143
|
+
orderId: "o1"
|
|
144
|
+
}), {
|
|
145
|
+
TAG: "ShipOrder",
|
|
146
|
+
orderId: "o1"
|
|
147
|
+
}), {
|
|
148
|
+
TAG: "OrderShipped",
|
|
149
|
+
orderId: "o1"
|
|
150
|
+
}), "o1", {
|
|
151
|
+
orderId: "o1",
|
|
152
|
+
customerId: "c1",
|
|
153
|
+
productIds: ["p1"],
|
|
154
|
+
status: "Shipped"
|
|
155
|
+
}), [[
|
|
156
|
+
"o1",
|
|
157
|
+
{
|
|
158
|
+
orderId: "o1",
|
|
159
|
+
customerId: "c1"
|
|
160
|
+
}
|
|
161
|
+
]]));
|
|
162
|
+
Flow_GWT$ReventlessGwt.test("placing an order for an unsynced product is rejected", undefined, () => Place.thenError(Place.whenCommand(Flow_GWT$ReventlessGwt.start, {
|
|
163
|
+
TAG: "PlaceOrder",
|
|
164
|
+
orderId: "o1",
|
|
165
|
+
customerId: "c1",
|
|
166
|
+
productIds: ["p1"]
|
|
167
|
+
}), {
|
|
168
|
+
TAG: "ProductsNotAvailable",
|
|
169
|
+
missing: ["p1"]
|
|
170
|
+
}));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export {
|
|
174
|
+
testContext,
|
|
175
|
+
AutoShipOrderSlice,
|
|
176
|
+
ConfirmSlice,
|
|
177
|
+
Sync,
|
|
178
|
+
Place,
|
|
179
|
+
Auto,
|
|
180
|
+
Ship,
|
|
181
|
+
OrdersView,
|
|
182
|
+
Confirm,
|
|
183
|
+
}
|
|
184
|
+
/* Sync Not a pure module */
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// `Automation_GWT.Make` expects a single SliceSpec with `collect`, `resolve`
|
|
2
|
+
// and `process` as top-level bindings. Compose them onto the spec module
|
|
3
|
+
// locally — the production split form keeps `collect`/`resolve` inside the
|
|
4
|
+
// per-source mapping.
|
|
5
|
+
|
|
6
|
+
let testContext: Reventless.AutomationSlice.context = {
|
|
7
|
+
environment: "test",
|
|
8
|
+
platformName: "test",
|
|
9
|
+
pluginName: "ordering",
|
|
10
|
+
sliceName: "AutoShipOrder",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module AutoShipOrderSlice = {
|
|
14
|
+
include AutoShipOrder
|
|
15
|
+
type consumedEvent = AutoShipOrder_Automation.FromOrderingDcb.sourceEvent
|
|
16
|
+
let consumedEventSchema = AutoShipOrder_Automation.FromOrderingDcb.sourceEventSchema
|
|
17
|
+
|
|
18
|
+
let collect = e => AutoShipOrder_Automation.FromOrderingDcb.collect(e, testContext)
|
|
19
|
+
let resolve = AutoShipOrder_Automation.FromOrderingDcb.resolve
|
|
20
|
+
let process = AutoShipOrder_Automation.process
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@@reventless.gwt
|
|
24
|
+
|
|
25
|
+
describe("AutoShipOrder AutomationSlice", () => {
|
|
26
|
+
test("collect: OrderPlaced creates a pending TODO", () =>
|
|
27
|
+
givenEvent(OrderPlaced({orderId: "o1"}))
|
|
28
|
+
->whenCollect
|
|
29
|
+
->thenTodos([("o1", {orderId: "o1"})])
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
test("collect: OrderShipped is ignored (no TODO)", () =>
|
|
33
|
+
givenEvent(OrderShipped({orderId: "o1"}))
|
|
34
|
+
->whenCollect
|
|
35
|
+
->thenTodos([])
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
test("resolve: OrderShipped marks the TODO done", () =>
|
|
39
|
+
givenEvent(OrderShipped({orderId: "o1"}))
|
|
40
|
+
->whenResolve
|
|
41
|
+
->thenResolved(Some("o1"))
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
test("resolve: OrderPlaced does not mark anything done", () =>
|
|
45
|
+
givenEvent(OrderPlaced({orderId: "o1"}))
|
|
46
|
+
->whenResolve
|
|
47
|
+
->thenResolved(None)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
test("process: pending TODO emits ShipOrder for the same id", () =>
|
|
51
|
+
givenTodo("o1", {orderId: "o1"})
|
|
52
|
+
->whenProcess
|
|
53
|
+
->thenCommand("o1", ShipOrder({orderId: "o1"}))
|
|
54
|
+
)
|
|
55
|
+
})
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as AutoShipOrder$OrderingPlugin from "../../../src/Order/AutomationSlice/AutoShipOrder.res.mjs";
|
|
4
|
+
import * as Automation_GWT$ReventlessGwt from "@reventlessdev/reventless-gwt/src/Automation_GWT.res.mjs";
|
|
5
|
+
import * as AutoShipOrder_Automation$OrderingPlugin from "../../../src/Order/AutomationSlice/AutoShipOrder_Automation.res.mjs";
|
|
6
|
+
|
|
7
|
+
let testContext = {
|
|
8
|
+
environment: "test",
|
|
9
|
+
platformName: "test",
|
|
10
|
+
pluginName: "ordering",
|
|
11
|
+
sliceName: "AutoShipOrder"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let consumedEventSchema = AutoShipOrder_Automation$OrderingPlugin.FromOrderingDcb.sourceEventSchema;
|
|
15
|
+
|
|
16
|
+
function collect(e) {
|
|
17
|
+
return AutoShipOrder_Automation$OrderingPlugin.FromOrderingDcb.collect(e, testContext);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let resolve = AutoShipOrder_Automation$OrderingPlugin.FromOrderingDcb.resolve;
|
|
21
|
+
|
|
22
|
+
let AutoShipOrderSlice = {
|
|
23
|
+
name: AutoShipOrder$OrderingPlugin.name,
|
|
24
|
+
Id: AutoShipOrder$OrderingPlugin.Id,
|
|
25
|
+
todoItemSchema: AutoShipOrder$OrderingPlugin.todoItemSchema,
|
|
26
|
+
commandSchema: AutoShipOrder$OrderingPlugin.commandSchema,
|
|
27
|
+
maxRetries: AutoShipOrder$OrderingPlugin.maxRetries,
|
|
28
|
+
heartbeatInterval: AutoShipOrder$OrderingPlugin.heartbeatInterval,
|
|
29
|
+
targetName: AutoShipOrder$OrderingPlugin.targetName,
|
|
30
|
+
moduleUrl: AutoShipOrder$OrderingPlugin.moduleUrl,
|
|
31
|
+
commandAuthorization: AutoShipOrder$OrderingPlugin.commandAuthorization,
|
|
32
|
+
consumedEventSchema: consumedEventSchema,
|
|
33
|
+
collect: collect,
|
|
34
|
+
resolve: resolve,
|
|
35
|
+
process: AutoShipOrder_Automation$OrderingPlugin.process
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let include = Automation_GWT$ReventlessGwt.Make({
|
|
39
|
+
name: AutoShipOrder$OrderingPlugin.name,
|
|
40
|
+
consumedEventSchema: consumedEventSchema,
|
|
41
|
+
todoItemSchema: AutoShipOrder$OrderingPlugin.todoItemSchema,
|
|
42
|
+
commandSchema: AutoShipOrder$OrderingPlugin.commandSchema,
|
|
43
|
+
collect: collect,
|
|
44
|
+
resolve: resolve,
|
|
45
|
+
process: AutoShipOrder_Automation$OrderingPlugin.process
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
let describe = include.describe;
|
|
49
|
+
|
|
50
|
+
let test = include.test;
|
|
51
|
+
|
|
52
|
+
let givenEvent = include.givenEvent;
|
|
53
|
+
|
|
54
|
+
let whenCollect = include.whenCollect;
|
|
55
|
+
|
|
56
|
+
let thenTodos = include.thenTodos;
|
|
57
|
+
|
|
58
|
+
let whenResolve = include.whenResolve;
|
|
59
|
+
|
|
60
|
+
let thenResolved = include.thenResolved;
|
|
61
|
+
|
|
62
|
+
let givenTodo = include.givenTodo;
|
|
63
|
+
|
|
64
|
+
let whenProcess = include.whenProcess;
|
|
65
|
+
|
|
66
|
+
let thenCommand = include.thenCommand;
|
|
67
|
+
|
|
68
|
+
describe("AutoShipOrder AutomationSlice", () => {
|
|
69
|
+
test("collect: OrderPlaced creates a pending TODO", () => thenTodos(whenCollect(givenEvent({
|
|
70
|
+
TAG: "OrderPlaced",
|
|
71
|
+
orderId: "o1"
|
|
72
|
+
})), [[
|
|
73
|
+
"o1",
|
|
74
|
+
{
|
|
75
|
+
orderId: "o1"
|
|
76
|
+
}
|
|
77
|
+
]]));
|
|
78
|
+
test("collect: OrderShipped is ignored (no TODO)", () => thenTodos(whenCollect(givenEvent({
|
|
79
|
+
TAG: "OrderShipped",
|
|
80
|
+
orderId: "o1"
|
|
81
|
+
})), []));
|
|
82
|
+
test("resolve: OrderShipped marks the TODO done", () => thenResolved(whenResolve(givenEvent({
|
|
83
|
+
TAG: "OrderShipped",
|
|
84
|
+
orderId: "o1"
|
|
85
|
+
})), "o1"));
|
|
86
|
+
test("resolve: OrderPlaced does not mark anything done", () => thenResolved(whenResolve(givenEvent({
|
|
87
|
+
TAG: "OrderPlaced",
|
|
88
|
+
orderId: "o1"
|
|
89
|
+
})), undefined));
|
|
90
|
+
test("process: pending TODO emits ShipOrder for the same id", () => thenCommand(whenProcess(givenTodo("o1", {
|
|
91
|
+
orderId: "o1"
|
|
92
|
+
})), "o1", {
|
|
93
|
+
TAG: "ShipOrder",
|
|
94
|
+
orderId: "o1"
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let Spec = include.Spec;
|
|
99
|
+
|
|
100
|
+
let thenNoCommand = include.thenNoCommand;
|
|
101
|
+
|
|
102
|
+
let givenEvents = include.givenEvents;
|
|
103
|
+
|
|
104
|
+
let whenSweep = include.whenSweep;
|
|
105
|
+
|
|
106
|
+
let thenCommands = include.thenCommands;
|
|
107
|
+
|
|
108
|
+
let andThenEvents = include.andThenEvents;
|
|
109
|
+
|
|
110
|
+
let thenScenarioTodos = include.thenScenarioTodos;
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
testContext,
|
|
114
|
+
AutoShipOrderSlice,
|
|
115
|
+
Spec,
|
|
116
|
+
describe,
|
|
117
|
+
test,
|
|
118
|
+
givenEvent,
|
|
119
|
+
whenCollect,
|
|
120
|
+
thenTodos,
|
|
121
|
+
whenResolve,
|
|
122
|
+
thenResolved,
|
|
123
|
+
givenTodo,
|
|
124
|
+
whenProcess,
|
|
125
|
+
thenCommand,
|
|
126
|
+
thenNoCommand,
|
|
127
|
+
givenEvents,
|
|
128
|
+
whenSweep,
|
|
129
|
+
thenCommands,
|
|
130
|
+
andThenEvents,
|
|
131
|
+
thenScenarioTodos,
|
|
132
|
+
}
|
|
133
|
+
/* include Not a pure module */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// `OutboundTranslation_GWT.Make` expects a single SliceSpec with `collect` at
|
|
2
|
+
// the top level. Compose `collect` onto the spec module locally — the real
|
|
3
|
+
// `translate` is mocked at test time via `whenTranslateMocked`.
|
|
4
|
+
|
|
5
|
+
module SendOrderConfirmationSlice = {
|
|
6
|
+
include SendOrderConfirmation
|
|
7
|
+
let collect = SendOrderConfirmation_Translation.collect
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@@reventless.gwt
|
|
11
|
+
|
|
12
|
+
describe("SendOrderConfirmation OutboundTranslationSlice", () => {
|
|
13
|
+
testSync("collect: OrderPlaced queues an outbound TODO", () =>
|
|
14
|
+
givenEvent(OrderPlaced({orderId: "o1", customerId: "c1"}))
|
|
15
|
+
->whenCollect
|
|
16
|
+
->thenTodos([("o1", {orderId: "o1", customerId: "c1"})])
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
test("translate success marks the TODO Completed", () =>
|
|
20
|
+
givenTodo("o1", {orderId: "o1", customerId: "c1"})
|
|
21
|
+
->whenTranslateMocked((_id, _item) => Promise.resolve(Ok(None)))
|
|
22
|
+
->thenTodoStatus("o1", #Completed)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
test("translate failure leaves the TODO Pending for retry", () =>
|
|
26
|
+
givenTodo("o1", {orderId: "o1", customerId: "c1"})
|
|
27
|
+
->whenTranslateMocked((_id, _item) => Promise.resolve(Error("smtp down")))
|
|
28
|
+
->thenTodoStatus("o1", #Pending)
|
|
29
|
+
)
|
|
30
|
+
})
|