@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,28 @@
|
|
|
1
|
+
// PlaceOrder StateChangeSlice.
|
|
2
|
+
// Rejects duplicate placement and validates that all referenced products are
|
|
3
|
+
// available (synced to the ordering event log).
|
|
4
|
+
|
|
5
|
+
@@reventless.spec
|
|
6
|
+
|
|
7
|
+
@schema
|
|
8
|
+
type consumedEvent =
|
|
9
|
+
| OrderPlaced({orderId: string})
|
|
10
|
+
| CatalogProductSynced({productId: string})
|
|
11
|
+
|
|
12
|
+
@schema
|
|
13
|
+
type command =
|
|
14
|
+
PlaceOrder({
|
|
15
|
+
@partitionTag orderId: string,
|
|
16
|
+
// customerId is payload, not a query key — @noDcbTag stops it auto-tagging.
|
|
17
|
+
@noDcbTag customerId: string,
|
|
18
|
+
@ref("AvailableProducts") productIds: array<string>,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
@schema
|
|
22
|
+
type error =
|
|
23
|
+
| OrderAlreadyPlaced
|
|
24
|
+
| ProductsNotAvailable({missing: array<string>})
|
|
25
|
+
|
|
26
|
+
@schema
|
|
27
|
+
type event =
|
|
28
|
+
OrderPlaced({@partitionTag orderId: string, customerId: string, productIds: array<string>})
|
|
@@ -0,0 +1,63 @@
|
|
|
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 Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.union([
|
|
8
|
+
S.schema(s => ({
|
|
9
|
+
TAG: "OrderPlaced",
|
|
10
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
11
|
+
})),
|
|
12
|
+
S.schema(s => ({
|
|
13
|
+
TAG: "CatalogProductSynced",
|
|
14
|
+
productId: s.m(DcbTag$Reventless.string)
|
|
15
|
+
}))
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
let commandSchema = S.schema(s => ({
|
|
19
|
+
TAG: "PlaceOrder",
|
|
20
|
+
orderId: s.m(DcbTag$Reventless.partition),
|
|
21
|
+
customerId: s.m(S.string),
|
|
22
|
+
productIds: s.m(S.array(Reference$Reventless.to_(undefined, "productId", "AvailableProducts")))
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
let errorSchema = S.union([
|
|
26
|
+
S.literal("OrderAlreadyPlaced"),
|
|
27
|
+
S.schema(s => ({
|
|
28
|
+
TAG: "ProductsNotAvailable",
|
|
29
|
+
missing: s.m(S.array(S.string))
|
|
30
|
+
}))
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
let eventSchema = S.schema(s => ({
|
|
34
|
+
TAG: "OrderPlaced",
|
|
35
|
+
orderId: s.m(DcbTag$Reventless.partition),
|
|
36
|
+
customerId: s.m(DcbTag$Reventless.string),
|
|
37
|
+
productIds: s.m(S.array(DcbTag$Reventless.stringForKey("productId")))
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
function commandAuthorization(param) {
|
|
41
|
+
return "AllowAuthenticated";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let name = "PlaceOrder";
|
|
45
|
+
|
|
46
|
+
let Id;
|
|
47
|
+
|
|
48
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/PlaceOrder.res.mjs";
|
|
49
|
+
|
|
50
|
+
let readConsistency = "EscalateOnRetry";
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
name,
|
|
54
|
+
Id,
|
|
55
|
+
consumedEventSchema,
|
|
56
|
+
commandSchema,
|
|
57
|
+
errorSchema,
|
|
58
|
+
eventSchema,
|
|
59
|
+
moduleUrl,
|
|
60
|
+
commandAuthorization,
|
|
61
|
+
readConsistency,
|
|
62
|
+
}
|
|
63
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@@reventless.behavior
|
|
2
|
+
|
|
3
|
+
// Stored as immutable arrays (rather than `Set.t`) so each fold yields a fresh
|
|
4
|
+
// state — `evolve` returns a new record instead of mutating a shared one. This
|
|
5
|
+
// keeps successive `decide` calls (and unit tests) hermetic.
|
|
6
|
+
type state = {placedOrderIds: array<string>, availableProductIds: array<string>}
|
|
7
|
+
|
|
8
|
+
let initialState = {placedOrderIds: [], availableProductIds: []}
|
|
9
|
+
|
|
10
|
+
let evolve = (state, event: consumedEvent) =>
|
|
11
|
+
switch event {
|
|
12
|
+
| OrderPlaced({orderId}) => {
|
|
13
|
+
...state,
|
|
14
|
+
placedOrderIds: state.placedOrderIds->Array.includes(orderId)
|
|
15
|
+
? state.placedOrderIds
|
|
16
|
+
: Array.concat(state.placedOrderIds, [orderId]),
|
|
17
|
+
}
|
|
18
|
+
| CatalogProductSynced({productId}) => {
|
|
19
|
+
...state,
|
|
20
|
+
availableProductIds: state.availableProductIds->Array.includes(productId)
|
|
21
|
+
? state.availableProductIds
|
|
22
|
+
: Array.concat(state.availableProductIds, [productId]),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let decide = (state, command) =>
|
|
27
|
+
switch command {
|
|
28
|
+
| PlaceOrder({orderId, customerId, productIds}) =>
|
|
29
|
+
if state.placedOrderIds->Array.includes(orderId) {
|
|
30
|
+
Error(OrderAlreadyPlaced)
|
|
31
|
+
} else {
|
|
32
|
+
let missing = productIds->Array.filter(pid => !(state.availableProductIds->Array.includes(pid)))
|
|
33
|
+
if missing->Array.length > 0 {
|
|
34
|
+
Error(ProductsNotAvailable({missing: missing}))
|
|
35
|
+
} else {
|
|
36
|
+
Ok([OrderPlaced({orderId, customerId, productIds})])
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
let initialState_placedOrderIds = [];
|
|
5
|
+
|
|
6
|
+
let initialState_availableProductIds = [];
|
|
7
|
+
|
|
8
|
+
let initialState = {
|
|
9
|
+
placedOrderIds: initialState_placedOrderIds,
|
|
10
|
+
availableProductIds: initialState_availableProductIds
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function evolve(state, event) {
|
|
14
|
+
if (event.TAG === "OrderPlaced") {
|
|
15
|
+
let orderId = event.orderId;
|
|
16
|
+
return {
|
|
17
|
+
placedOrderIds: state.placedOrderIds.includes(orderId) ? state.placedOrderIds : state.placedOrderIds.concat([orderId]),
|
|
18
|
+
availableProductIds: state.availableProductIds
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
let productId = event.productId;
|
|
22
|
+
return {
|
|
23
|
+
placedOrderIds: state.placedOrderIds,
|
|
24
|
+
availableProductIds: state.availableProductIds.includes(productId) ? state.availableProductIds : state.availableProductIds.concat([productId])
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function decide(state, command) {
|
|
29
|
+
let orderId = command.orderId;
|
|
30
|
+
if (state.placedOrderIds.includes(orderId)) {
|
|
31
|
+
return {
|
|
32
|
+
TAG: "Error",
|
|
33
|
+
_0: "OrderAlreadyPlaced"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
let productIds = command.productIds;
|
|
37
|
+
let missing = productIds.filter(pid => !state.availableProductIds.includes(pid));
|
|
38
|
+
if (missing.length !== 0) {
|
|
39
|
+
return {
|
|
40
|
+
TAG: "Error",
|
|
41
|
+
_0: {
|
|
42
|
+
TAG: "ProductsNotAvailable",
|
|
43
|
+
missing: missing
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
return {
|
|
48
|
+
TAG: "Ok",
|
|
49
|
+
_0: [{
|
|
50
|
+
TAG: "OrderPlaced",
|
|
51
|
+
orderId: orderId,
|
|
52
|
+
customerId: command.customerId,
|
|
53
|
+
productIds: productIds
|
|
54
|
+
}]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let Spec;
|
|
60
|
+
|
|
61
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs";
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
Spec,
|
|
65
|
+
initialState,
|
|
66
|
+
evolve,
|
|
67
|
+
decide,
|
|
68
|
+
moduleUrl,
|
|
69
|
+
}
|
|
70
|
+
/* No side effect */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// RefundOrder StateChangeSlice.
|
|
2
|
+
// Handles internal refund processing — entirely hidden from public API.
|
|
3
|
+
// This is an automation/admin-only workflow triggered after cancellation.
|
|
4
|
+
|
|
5
|
+
@@reventless.spec
|
|
6
|
+
|
|
7
|
+
@schema
|
|
8
|
+
type consumedEvent =
|
|
9
|
+
| OrderPlaced
|
|
10
|
+
| OrderCancelled
|
|
11
|
+
| RefundIssued({reason: string})
|
|
12
|
+
|
|
13
|
+
@schema @noApi
|
|
14
|
+
type command = IssueRefund({orderId: string, reason: string})
|
|
15
|
+
|
|
16
|
+
@schema
|
|
17
|
+
type error =
|
|
18
|
+
| OrderNotFound
|
|
19
|
+
| OrderNotCancelled
|
|
20
|
+
| RefundAlreadyIssued
|
|
21
|
+
|
|
22
|
+
@schema
|
|
23
|
+
type event =
|
|
24
|
+
| RefundIssued({
|
|
25
|
+
orderId: string,
|
|
26
|
+
reason: string,
|
|
27
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
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 Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.union([
|
|
8
|
+
S.literal("OrderPlaced"),
|
|
9
|
+
S.literal("OrderCancelled"),
|
|
10
|
+
S.schema(s => ({
|
|
11
|
+
TAG: "RefundIssued",
|
|
12
|
+
reason: s.m(S.string)
|
|
13
|
+
}))
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
let commandSchema = S.schema(s => ({
|
|
17
|
+
TAG: "IssueRefund",
|
|
18
|
+
orderId: s.m(DcbTag$Reventless.string),
|
|
19
|
+
reason: s.m(S.string)
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
let errorSchema = S.union([
|
|
23
|
+
S.literal("OrderNotFound"),
|
|
24
|
+
S.literal("OrderNotCancelled"),
|
|
25
|
+
S.literal("RefundAlreadyIssued")
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
let eventSchema = S.schema(s => ({
|
|
29
|
+
TAG: "RefundIssued",
|
|
30
|
+
orderId: s.m(DcbTag$Reventless.string),
|
|
31
|
+
reason: s.m(S.string)
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
let commandSchema$1 = Api$ReventlessInfra.markNoApi(commandSchema);
|
|
35
|
+
|
|
36
|
+
function commandAuthorization(param) {
|
|
37
|
+
return "AllowAuthenticated";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let name = "RefundOrder";
|
|
41
|
+
|
|
42
|
+
let Id;
|
|
43
|
+
|
|
44
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/RefundOrder.res.mjs";
|
|
45
|
+
|
|
46
|
+
let readConsistency = "EscalateOnRetry";
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
name,
|
|
50
|
+
Id,
|
|
51
|
+
consumedEventSchema,
|
|
52
|
+
errorSchema,
|
|
53
|
+
eventSchema,
|
|
54
|
+
commandSchema$1 as commandSchema,
|
|
55
|
+
moduleUrl,
|
|
56
|
+
commandAuthorization,
|
|
57
|
+
readConsistency,
|
|
58
|
+
}
|
|
59
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@@reventless.behavior
|
|
2
|
+
|
|
3
|
+
type state = {exists: bool, cancelled: bool, refunded: bool}
|
|
4
|
+
|
|
5
|
+
let initialState = {exists: false, cancelled: false, refunded: false}
|
|
6
|
+
|
|
7
|
+
let evolve = (state, event) =>
|
|
8
|
+
switch event {
|
|
9
|
+
| OrderPlaced => {exists: true, cancelled: false, refunded: false}
|
|
10
|
+
| OrderCancelled => {...state, cancelled: true}
|
|
11
|
+
| RefundIssued(_) => {...state, refunded: true}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let decide = (state, command) =>
|
|
15
|
+
switch command {
|
|
16
|
+
| IssueRefund({orderId, reason}) =>
|
|
17
|
+
if !state.exists {
|
|
18
|
+
Error(OrderNotFound)
|
|
19
|
+
} else if !state.cancelled {
|
|
20
|
+
Error(OrderNotCancelled)
|
|
21
|
+
} else if state.refunded {
|
|
22
|
+
Error(RefundAlreadyIssued)
|
|
23
|
+
} else {
|
|
24
|
+
Ok([RefundIssued({orderId, reason})])
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function evolve(state, event) {
|
|
5
|
+
if (typeof event !== "object") {
|
|
6
|
+
if (event === "OrderPlaced") {
|
|
7
|
+
return {
|
|
8
|
+
exists: true,
|
|
9
|
+
cancelled: false,
|
|
10
|
+
refunded: false
|
|
11
|
+
};
|
|
12
|
+
} else {
|
|
13
|
+
return {
|
|
14
|
+
exists: state.exists,
|
|
15
|
+
cancelled: true,
|
|
16
|
+
refunded: state.refunded
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
return {
|
|
21
|
+
exists: state.exists,
|
|
22
|
+
cancelled: state.cancelled,
|
|
23
|
+
refunded: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function decide(state, command) {
|
|
29
|
+
if (state.exists) {
|
|
30
|
+
if (state.cancelled) {
|
|
31
|
+
if (state.refunded) {
|
|
32
|
+
return {
|
|
33
|
+
TAG: "Error",
|
|
34
|
+
_0: "RefundAlreadyIssued"
|
|
35
|
+
};
|
|
36
|
+
} else {
|
|
37
|
+
return {
|
|
38
|
+
TAG: "Ok",
|
|
39
|
+
_0: [{
|
|
40
|
+
TAG: "RefundIssued",
|
|
41
|
+
orderId: command.orderId,
|
|
42
|
+
reason: command.reason
|
|
43
|
+
}]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
return {
|
|
48
|
+
TAG: "Error",
|
|
49
|
+
_0: "OrderNotCancelled"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
return {
|
|
54
|
+
TAG: "Error",
|
|
55
|
+
_0: "OrderNotFound"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let Spec;
|
|
61
|
+
|
|
62
|
+
let initialState = {
|
|
63
|
+
exists: false,
|
|
64
|
+
cancelled: false,
|
|
65
|
+
refunded: false
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/RefundOrder_Behavior.res.mjs";
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
Spec,
|
|
72
|
+
initialState,
|
|
73
|
+
evolve,
|
|
74
|
+
decide,
|
|
75
|
+
moduleUrl,
|
|
76
|
+
}
|
|
77
|
+
/* No side effect */
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// ShipOrder StateChangeSlice.
|
|
2
|
+
// Requires order to exist and not be cancelled; idempotent if already shipped.
|
|
3
|
+
|
|
4
|
+
@@reventless.spec
|
|
5
|
+
|
|
6
|
+
@schema
|
|
7
|
+
type consumedEvent =
|
|
8
|
+
| OrderPlaced
|
|
9
|
+
| OrderShipped
|
|
10
|
+
| OrderCancelled
|
|
11
|
+
|
|
12
|
+
@schema
|
|
13
|
+
type command =
|
|
14
|
+
| @allowedStates([Orders.Placed]) ShipOrder({orderId: string})
|
|
15
|
+
|
|
16
|
+
@schema
|
|
17
|
+
type error =
|
|
18
|
+
| OrderNotFound
|
|
19
|
+
| OrderAlreadyCancelled
|
|
20
|
+
|
|
21
|
+
@schema
|
|
22
|
+
type event =
|
|
23
|
+
| OrderShipped({orderId: string})
|
|
@@ -0,0 +1,56 @@
|
|
|
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 Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.union([
|
|
8
|
+
S.literal("OrderPlaced"),
|
|
9
|
+
S.literal("OrderShipped"),
|
|
10
|
+
S.literal("OrderCancelled")
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
let commandSchema = S.schema(s => ({
|
|
14
|
+
TAG: "ShipOrder",
|
|
15
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
let errorSchema = S.union([
|
|
19
|
+
S.literal("OrderNotFound"),
|
|
20
|
+
S.literal("OrderAlreadyCancelled")
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
let eventSchema = S.schema(s => ({
|
|
24
|
+
TAG: "OrderShipped",
|
|
25
|
+
orderId: s.m(DcbTag$Reventless.string)
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
let commandSchema$1 = Api$ReventlessInfra.markAllowedStates(commandSchema, [[
|
|
29
|
+
"ShipOrder",
|
|
30
|
+
["Placed"]
|
|
31
|
+
]]);
|
|
32
|
+
|
|
33
|
+
function commandAuthorization(param) {
|
|
34
|
+
return "AllowAuthenticated";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let name = "ShipOrder";
|
|
38
|
+
|
|
39
|
+
let Id;
|
|
40
|
+
|
|
41
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/ShipOrder.res.mjs";
|
|
42
|
+
|
|
43
|
+
let readConsistency = "EscalateOnRetry";
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
name,
|
|
47
|
+
Id,
|
|
48
|
+
consumedEventSchema,
|
|
49
|
+
errorSchema,
|
|
50
|
+
eventSchema,
|
|
51
|
+
commandSchema$1 as commandSchema,
|
|
52
|
+
moduleUrl,
|
|
53
|
+
commandAuthorization,
|
|
54
|
+
readConsistency,
|
|
55
|
+
}
|
|
56
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@@reventless.behavior
|
|
2
|
+
|
|
3
|
+
type state = {exists: bool, shipped: bool, cancelled: bool}
|
|
4
|
+
|
|
5
|
+
let initialState = {exists: false, shipped: false, cancelled: false}
|
|
6
|
+
|
|
7
|
+
let evolve = (state, event) =>
|
|
8
|
+
switch event {
|
|
9
|
+
| OrderPlaced => {exists: true, shipped: false, cancelled: false}
|
|
10
|
+
| OrderShipped => {...state, shipped: true}
|
|
11
|
+
| OrderCancelled => {...state, cancelled: true}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let decide = (state, command) =>
|
|
15
|
+
switch command {
|
|
16
|
+
| ShipOrder({orderId: theId}) =>
|
|
17
|
+
if !state.exists {
|
|
18
|
+
Error(OrderNotFound)
|
|
19
|
+
} else if state.cancelled {
|
|
20
|
+
Error(OrderAlreadyCancelled)
|
|
21
|
+
} else if state.shipped {
|
|
22
|
+
Ok([]) // idempotent — already shipped
|
|
23
|
+
} else {
|
|
24
|
+
Ok([OrderShipped({orderId: theId})])
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function evolve(state, event) {
|
|
5
|
+
switch (event) {
|
|
6
|
+
case "OrderPlaced" :
|
|
7
|
+
return {
|
|
8
|
+
exists: true,
|
|
9
|
+
shipped: false,
|
|
10
|
+
cancelled: false
|
|
11
|
+
};
|
|
12
|
+
case "OrderShipped" :
|
|
13
|
+
return {
|
|
14
|
+
exists: state.exists,
|
|
15
|
+
shipped: true,
|
|
16
|
+
cancelled: state.cancelled
|
|
17
|
+
};
|
|
18
|
+
case "OrderCancelled" :
|
|
19
|
+
return {
|
|
20
|
+
exists: state.exists,
|
|
21
|
+
shipped: state.shipped,
|
|
22
|
+
cancelled: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function decide(state, command) {
|
|
28
|
+
if (state.exists) {
|
|
29
|
+
if (state.cancelled) {
|
|
30
|
+
return {
|
|
31
|
+
TAG: "Error",
|
|
32
|
+
_0: "OrderAlreadyCancelled"
|
|
33
|
+
};
|
|
34
|
+
} else if (state.shipped) {
|
|
35
|
+
return {
|
|
36
|
+
TAG: "Ok",
|
|
37
|
+
_0: []
|
|
38
|
+
};
|
|
39
|
+
} else {
|
|
40
|
+
return {
|
|
41
|
+
TAG: "Ok",
|
|
42
|
+
_0: [{
|
|
43
|
+
TAG: "OrderShipped",
|
|
44
|
+
orderId: command.orderId
|
|
45
|
+
}]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
return {
|
|
50
|
+
TAG: "Error",
|
|
51
|
+
_0: "OrderNotFound"
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let Spec;
|
|
57
|
+
|
|
58
|
+
let initialState = {
|
|
59
|
+
exists: false,
|
|
60
|
+
shipped: false,
|
|
61
|
+
cancelled: false
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/ShipOrder_Behavior.res.mjs";
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
Spec,
|
|
68
|
+
initialState,
|
|
69
|
+
evolve,
|
|
70
|
+
decide,
|
|
71
|
+
moduleUrl,
|
|
72
|
+
}
|
|
73
|
+
/* No side effect */
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Orders StateViewSliceStream.
|
|
2
|
+
// Projects order events from the shared ordering event log into an Orders read model.
|
|
3
|
+
|
|
4
|
+
@@reventless.spec
|
|
5
|
+
|
|
6
|
+
@schema
|
|
7
|
+
type consumedEvent =
|
|
8
|
+
| OrderPlaced({orderId: string, customerId: string, productIds: array<string>})
|
|
9
|
+
| OrderShipped({orderId: string})
|
|
10
|
+
| OrderCancelled({orderId: string})
|
|
11
|
+
|
|
12
|
+
@schema
|
|
13
|
+
type status =
|
|
14
|
+
| Placed
|
|
15
|
+
| Shipped
|
|
16
|
+
| Cancelled
|
|
17
|
+
|
|
18
|
+
@schema
|
|
19
|
+
type state = {
|
|
20
|
+
orderId: string,
|
|
21
|
+
customerId: string,
|
|
22
|
+
productIds: array<string>,
|
|
23
|
+
@status status: status,
|
|
24
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
5
|
+
import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.union([
|
|
8
|
+
S.schema(s => ({
|
|
9
|
+
TAG: "OrderPlaced",
|
|
10
|
+
orderId: s.m(S.string),
|
|
11
|
+
customerId: s.m(S.string),
|
|
12
|
+
productIds: s.m(S.array(S.string))
|
|
13
|
+
})),
|
|
14
|
+
S.schema(s => ({
|
|
15
|
+
TAG: "OrderShipped",
|
|
16
|
+
orderId: s.m(S.string)
|
|
17
|
+
})),
|
|
18
|
+
S.schema(s => ({
|
|
19
|
+
TAG: "OrderCancelled",
|
|
20
|
+
orderId: s.m(S.string)
|
|
21
|
+
}))
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
let statusSchema = S.union([
|
|
25
|
+
S.literal("Placed"),
|
|
26
|
+
S.literal("Shipped"),
|
|
27
|
+
S.literal("Cancelled")
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
let stateSchema = S.schema(s => ({
|
|
31
|
+
orderId: s.m(S.string),
|
|
32
|
+
customerId: s.m(S.string),
|
|
33
|
+
productIds: s.m(S.array(S.string)),
|
|
34
|
+
status: s.m(statusSchema)
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
let config = ReadModel$Reventless.config(undefined, undefined, undefined);
|
|
38
|
+
|
|
39
|
+
let stateSchema$1 = S.Metadata.set(stateSchema, StateAnnotations$Reventless.stateAnnotationsId, {
|
|
40
|
+
ids: [],
|
|
41
|
+
compositeIds: [],
|
|
42
|
+
subIds: [],
|
|
43
|
+
compositeSubIds: [],
|
|
44
|
+
indexes: [],
|
|
45
|
+
hidden: [],
|
|
46
|
+
summary: [],
|
|
47
|
+
drillTargets: [],
|
|
48
|
+
drillTargetKeys: [],
|
|
49
|
+
collapsed: [],
|
|
50
|
+
scan: [],
|
|
51
|
+
scanSort: [],
|
|
52
|
+
status: "status",
|
|
53
|
+
visibility: undefined
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
let name = "Orders";
|
|
57
|
+
|
|
58
|
+
let Id;
|
|
59
|
+
|
|
60
|
+
let subIdConfig;
|
|
61
|
+
|
|
62
|
+
let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateViewSliceStream/Orders.res.mjs";
|
|
63
|
+
|
|
64
|
+
let authorization = "AllowAuthenticated";
|
|
65
|
+
|
|
66
|
+
let visibility = "Public";
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
name,
|
|
70
|
+
Id,
|
|
71
|
+
consumedEventSchema,
|
|
72
|
+
statusSchema,
|
|
73
|
+
config,
|
|
74
|
+
subIdConfig,
|
|
75
|
+
stateSchema$1 as stateSchema,
|
|
76
|
+
moduleUrl,
|
|
77
|
+
authorization,
|
|
78
|
+
visibility,
|
|
79
|
+
}
|
|
80
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@@reventless.projection
|
|
2
|
+
|
|
3
|
+
let project = event =>
|
|
4
|
+
switch event {
|
|
5
|
+
| OrderPlaced({orderId, customerId, productIds}) => [
|
|
6
|
+
Set(orderId, {orderId, customerId, productIds, status: Placed}),
|
|
7
|
+
]
|
|
8
|
+
| OrderShipped({orderId}) => [Update(orderId, state => {...state, status: Shipped})]
|
|
9
|
+
| OrderCancelled({orderId}) => [Update(orderId, state => {...state, status: Cancelled})]
|
|
10
|
+
}
|