@reventlessdev/online-shop-aggregates-catalog 3.0.0-alpha.166 → 3.0.0-alpha.167

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.
Files changed (138) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/lib/bs/.compiler.log +2 -2
  3. package/lib/bs/compiler-info.json +1 -1
  4. package/lib/bs/src/Extension/Orders_Extension-CatalogPlugin.cmi +0 -0
  5. package/lib/bs/src/Extension/Orders_Extension-CatalogPlugin.cmt +0 -0
  6. package/lib/bs/src/ExtensionPoint/Products_ExtensionPointMapping-CatalogPlugin.cmi +0 -0
  7. package/lib/bs/src/ExtensionPoint/Products_ExtensionPointMapping-CatalogPlugin.cmt +0 -0
  8. package/lib/bs/src/ExtensionPoint/Products_ExtensionPointMapping.res.mjs +2 -3
  9. package/lib/bs/src/Plugin-CatalogPlugin.cmi +0 -0
  10. package/lib/bs/src/Plugin-CatalogPlugin.cmt +0 -0
  11. package/lib/bs/src/Product/Aggregate/Product-CatalogPlugin.cmi +0 -0
  12. package/lib/bs/src/Product/Aggregate/Product-CatalogPlugin.cmt +0 -0
  13. package/lib/bs/src/Product/Aggregate/Product.ast +0 -0
  14. package/lib/bs/src/Product/Aggregate/Product.res +16 -3
  15. package/lib/bs/src/Product/Aggregate/Product.res.mjs +13 -2
  16. package/lib/bs/src/Product/Aggregate/Product_Behavior-CatalogPlugin.cmi +0 -0
  17. package/lib/bs/src/Product/Aggregate/Product_Behavior-CatalogPlugin.cmt +0 -0
  18. package/lib/bs/src/Product/Aggregate/Product_Behavior.ast +0 -0
  19. package/lib/bs/src/Product/Aggregate/Product_Behavior.res +11 -5
  20. package/lib/bs/src/Product/Aggregate/Product_Behavior.res.mjs +38 -7
  21. package/lib/bs/src/Product/ReadModel/Products-CatalogPlugin.cmi +0 -0
  22. package/lib/bs/src/Product/ReadModel/Products-CatalogPlugin.cmt +0 -0
  23. package/lib/bs/src/Product/ReadModel/Products.ast +0 -0
  24. package/lib/bs/src/Product/ReadModel/Products.res +1 -0
  25. package/lib/bs/src/Product/ReadModel/Products.res.mjs +3 -1
  26. package/lib/bs/src/Product/ReadModel/Products_Projections-CatalogPlugin.cmi +0 -0
  27. package/lib/bs/src/Product/ReadModel/Products_Projections-CatalogPlugin.cmt +0 -0
  28. package/lib/bs/src/Product/ReadModel/Products_Projections.ast +0 -0
  29. package/lib/bs/src/Product/ReadModel/Products_Projections.res +3 -2
  30. package/lib/bs/src/Product/ReadModel/Products_Projections.res.mjs +20 -4
  31. package/lib/bs/src/ProductDemand/Aggregate/ProductDemand-CatalogPlugin.cmi +0 -0
  32. package/lib/bs/src/ProductDemand/Aggregate/ProductDemand-CatalogPlugin.cmt +0 -0
  33. package/lib/bs/src/ProductDemand/Aggregate/ProductDemand_Behavior-CatalogPlugin.cmi +0 -0
  34. package/lib/bs/src/ProductDemand/Aggregate/ProductDemand_Behavior-CatalogPlugin.cmt +0 -0
  35. package/lib/bs/src/ProductDemand/ReadModel/ProductDemands_Projections-CatalogPlugin.cmi +0 -0
  36. package/lib/bs/src/ProductDemand/ReadModel/ProductDemands_Projections-CatalogPlugin.cmt +0 -0
  37. package/lib/bs/src/Task/ImportProducts-CatalogPlugin.cmi +0 -0
  38. package/lib/bs/src/Task/ImportProducts-CatalogPlugin.cmt +0 -0
  39. package/lib/bs/src/Task/ImportProducts.ast +0 -0
  40. package/lib/bs/src/Task/ImportProducts.res +3 -0
  41. package/lib/bs/src/Task/ImportProducts.res.mjs +2 -1
  42. package/lib/bs/tests/Extension/Orders_Extension_GWT-CatalogPlugin.cmi +0 -0
  43. package/lib/bs/tests/Extension/Orders_Extension_GWT-CatalogPlugin.cmt +0 -0
  44. package/lib/bs/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT-CatalogPlugin.cmi +0 -0
  45. package/lib/bs/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT-CatalogPlugin.cmt +0 -0
  46. package/lib/bs/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT.ast +0 -0
  47. package/lib/bs/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT.res +1 -1
  48. package/lib/bs/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT.res.mjs +2 -1
  49. package/lib/bs/tests/Product/Aggregate/Product_GWT-CatalogPlugin.cmi +0 -0
  50. package/lib/bs/tests/Product/Aggregate/Product_GWT-CatalogPlugin.cmt +0 -0
  51. package/lib/bs/tests/Product/Aggregate/Product_GWT.ast +0 -0
  52. package/lib/bs/tests/Product/Aggregate/Product_GWT.res +49 -18
  53. package/lib/bs/tests/Product/Aggregate/Product_GWT.res.mjs +36 -50
  54. package/lib/bs/tests/Product/ReadModel/Products_GWT-CatalogPlugin.cmi +0 -0
  55. package/lib/bs/tests/Product/ReadModel/Products_GWT-CatalogPlugin.cmt +0 -0
  56. package/lib/bs/tests/Product/ReadModel/Products_GWT.ast +0 -0
  57. package/lib/bs/tests/Product/ReadModel/Products_GWT.res +46 -8
  58. package/lib/bs/tests/Product/ReadModel/Products_GWT.res.mjs +30 -28
  59. package/lib/bs/tests/ProductDemand/Aggregate/ProductDemand_GWT-CatalogPlugin.cmi +0 -0
  60. package/lib/bs/tests/ProductDemand/Aggregate/ProductDemand_GWT-CatalogPlugin.cmt +0 -0
  61. package/lib/bs/tests/ProductDemand/ReadModel/ProductDemands_GWT-CatalogPlugin.cmi +0 -0
  62. package/lib/bs/tests/ProductDemand/ReadModel/ProductDemands_GWT-CatalogPlugin.cmt +0 -0
  63. package/lib/bs/tests/ProductDemand/ReadModel/ProductDemands_GWT.ast +0 -0
  64. package/lib/bs/tests/ProductDemand/ReadModel/ProductDemands_GWT.res +4 -4
  65. package/lib/bs/tests/ProductDemand/ReadModel/ProductDemands_GWT.res.mjs +8 -4
  66. package/lib/ocaml/.compiler.log +2 -2
  67. package/lib/ocaml/ImportProducts-CatalogPlugin.cmi +0 -0
  68. package/lib/ocaml/ImportProducts-CatalogPlugin.cmt +0 -0
  69. package/lib/ocaml/ImportProducts.ast +0 -0
  70. package/lib/ocaml/ImportProducts.res +3 -0
  71. package/lib/ocaml/Orders_Extension-CatalogPlugin.cmi +0 -0
  72. package/lib/ocaml/Orders_Extension-CatalogPlugin.cmt +0 -0
  73. package/lib/ocaml/Orders_Extension_GWT-CatalogPlugin.cmi +0 -0
  74. package/lib/ocaml/Orders_Extension_GWT-CatalogPlugin.cmt +0 -0
  75. package/lib/ocaml/Plugin-CatalogPlugin.cmi +0 -0
  76. package/lib/ocaml/Plugin-CatalogPlugin.cmt +0 -0
  77. package/lib/ocaml/Product-CatalogPlugin.cmi +0 -0
  78. package/lib/ocaml/Product-CatalogPlugin.cmt +0 -0
  79. package/lib/ocaml/Product.ast +0 -0
  80. package/lib/ocaml/Product.res +16 -3
  81. package/lib/ocaml/ProductDemand-CatalogPlugin.cmi +0 -0
  82. package/lib/ocaml/ProductDemand-CatalogPlugin.cmt +0 -0
  83. package/lib/ocaml/ProductDemand_Behavior-CatalogPlugin.cmi +0 -0
  84. package/lib/ocaml/ProductDemand_Behavior-CatalogPlugin.cmt +0 -0
  85. package/lib/ocaml/ProductDemand_GWT-CatalogPlugin.cmi +0 -0
  86. package/lib/ocaml/ProductDemand_GWT-CatalogPlugin.cmt +0 -0
  87. package/lib/ocaml/ProductDemands_GWT-CatalogPlugin.cmi +0 -0
  88. package/lib/ocaml/ProductDemands_GWT-CatalogPlugin.cmt +0 -0
  89. package/lib/ocaml/ProductDemands_GWT.ast +0 -0
  90. package/lib/ocaml/ProductDemands_GWT.res +4 -4
  91. package/lib/ocaml/ProductDemands_Projections-CatalogPlugin.cmi +0 -0
  92. package/lib/ocaml/ProductDemands_Projections-CatalogPlugin.cmt +0 -0
  93. package/lib/ocaml/Product_Behavior-CatalogPlugin.cmi +0 -0
  94. package/lib/ocaml/Product_Behavior-CatalogPlugin.cmt +0 -0
  95. package/lib/ocaml/Product_Behavior.ast +0 -0
  96. package/lib/ocaml/Product_Behavior.res +11 -5
  97. package/lib/ocaml/Product_GWT-CatalogPlugin.cmi +0 -0
  98. package/lib/ocaml/Product_GWT-CatalogPlugin.cmt +0 -0
  99. package/lib/ocaml/Product_GWT.ast +0 -0
  100. package/lib/ocaml/Product_GWT.res +49 -18
  101. package/lib/ocaml/Products-CatalogPlugin.cmi +0 -0
  102. package/lib/ocaml/Products-CatalogPlugin.cmt +0 -0
  103. package/lib/ocaml/Products.ast +0 -0
  104. package/lib/ocaml/Products.res +1 -0
  105. package/lib/ocaml/Products_ExtensionPointMapping-CatalogPlugin.cmi +0 -0
  106. package/lib/ocaml/Products_ExtensionPointMapping-CatalogPlugin.cmt +0 -0
  107. package/lib/ocaml/Products_ExtensionPointMapping_GWT-CatalogPlugin.cmi +0 -0
  108. package/lib/ocaml/Products_ExtensionPointMapping_GWT-CatalogPlugin.cmt +0 -0
  109. package/lib/ocaml/Products_ExtensionPointMapping_GWT.ast +0 -0
  110. package/lib/ocaml/Products_ExtensionPointMapping_GWT.res +1 -1
  111. package/lib/ocaml/Products_GWT-CatalogPlugin.cmi +0 -0
  112. package/lib/ocaml/Products_GWT-CatalogPlugin.cmt +0 -0
  113. package/lib/ocaml/Products_GWT.ast +0 -0
  114. package/lib/ocaml/Products_GWT.res +46 -8
  115. package/lib/ocaml/Products_Projections-CatalogPlugin.cmi +0 -0
  116. package/lib/ocaml/Products_Projections-CatalogPlugin.cmt +0 -0
  117. package/lib/ocaml/Products_Projections.ast +0 -0
  118. package/lib/ocaml/Products_Projections.res +3 -2
  119. package/package.json +7 -7
  120. package/src/ExtensionPoint/Products_ExtensionPointMapping.res.mjs +2 -3
  121. package/src/Product/Aggregate/Product.res +16 -3
  122. package/src/Product/Aggregate/Product.res.mjs +13 -2
  123. package/src/Product/Aggregate/Product_Behavior.res +11 -5
  124. package/src/Product/Aggregate/Product_Behavior.res.mjs +38 -7
  125. package/src/Product/ReadModel/Products.res +1 -0
  126. package/src/Product/ReadModel/Products.res.mjs +3 -1
  127. package/src/Product/ReadModel/Products_Projections.res +3 -2
  128. package/src/Product/ReadModel/Products_Projections.res.mjs +20 -4
  129. package/src/Task/ImportProducts.res +3 -0
  130. package/src/Task/ImportProducts.res.mjs +2 -1
  131. package/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT.res +1 -1
  132. package/tests/ExtensionPoint/Products_ExtensionPointMapping_GWT.res.mjs +2 -1
  133. package/tests/Product/Aggregate/Product_GWT.res +49 -18
  134. package/tests/Product/Aggregate/Product_GWT.res.mjs +36 -50
  135. package/tests/Product/ReadModel/Products_GWT.res +46 -8
  136. package/tests/Product/ReadModel/Products_GWT.res.mjs +30 -28
  137. package/tests/ProductDemand/ReadModel/ProductDemands_GWT.res +4 -4
  138. package/tests/ProductDemand/ReadModel/ProductDemands_GWT.res.mjs +8 -4
package/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.167 (2026-07-29)
7
+
8
+ ### Features
9
+
10
+ * **example:** serve declared object stores without a host shell ([27794df](https://github.com/ReventlessDev/reventless-core/commit/27794df37a177a29419e82e4fbe698a1489a1bd9))
11
+
12
+
6
13
  # 3.0.0-alpha.166 (2026-07-28)
7
14
 
8
15
  **Note:** Version bump only for package @reventlessdev/online-shop-aggregates-catalog
@@ -1,2 +1,2 @@
1
- #Start(1785258655386)
2
- #Done(1785258660835)
1
+ #Start(1785350544294)
2
+ #Done(1785350549499)
@@ -4,5 +4,5 @@
4
4
  "bsc_hash": "75e3a59c95cc953608fd3dd6ea6ed68141bba4414a20a52f114261b8a48385fa",
5
5
  "rescript_config_hash": "50d5358a2cbe328e82ae9b442e4615b86dbfe6b2ce05e9eca04a0c78da0e4708",
6
6
  "runtime_path": "/home/runner/work/reventless-core/reventless-core/node_modules/@rescript/runtime",
7
- "generated_at": "1785258660842"
7
+ "generated_at": "1785350549512"
8
8
  }
@@ -18,9 +18,6 @@ let mapOutgoingEvent = (id, event, _meta, _queryEngine) => {
18
18
  price: event.price
19
19
  }
20
20
  }];
21
- case "NameUpdated" :
22
- case "DescriptionUpdated" :
23
- return [];
24
21
  case "PriceUpdated" :
25
22
  return [{
26
23
  TAG: "PublishEvent",
@@ -31,6 +28,8 @@ let mapOutgoingEvent = (id, event, _meta, _queryEngine) => {
31
28
  price: event.price
32
29
  }
33
30
  }];
31
+ default:
32
+ return [];
34
33
  }
35
34
  };
36
35
 
Binary file
Binary file
@@ -1,21 +1,34 @@
1
1
  // Product aggregate specification.
2
- // A product listing with name, description, and price.
2
+ // A product listing with name, description, price and an image.
3
+ //
4
+ // `@storageRef("productImages")` on the command inputs is what makes `imageUrl`
5
+ // an upload rather than a free-text field: the store name is unqualified, so it
6
+ // resolves to this plugin's own `Catalog.productImages`, and the platform must
7
+ // provision it. Events carry the resulting ref as a plain string — the
8
+ // annotation describes an input, not a stored value.
3
9
 
4
10
  @@reventless.spec
5
11
 
6
12
  @schema
7
13
  type command =
8
- | Add({name: string, description: string, price: float})
14
+ | Add({
15
+ name: string,
16
+ description: string,
17
+ price: float,
18
+ @storageRef("productImages") imageUrl: string,
19
+ })
9
20
  | UpdateName({name: string})
10
21
  | UpdateDescription({description: string})
11
22
  | UpdatePrice({price: float})
23
+ | UpdateImage({@storageRef("productImages") imageUrl: string})
12
24
 
13
25
  @schema
14
26
  type event =
15
- | Added({name: string, description: string, price: float})
27
+ | Added({name: string, description: string, price: float, imageUrl: string})
16
28
  | NameUpdated({name: string})
17
29
  | DescriptionUpdated({description: string})
18
30
  | PriceUpdated({price: float})
31
+ | ImageUpdated({imageUrl: string})
19
32
 
20
33
  @schema
21
34
  type error =
@@ -1,13 +1,15 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as S from "sury/src/S.res.mjs";
4
+ import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
4
5
 
5
6
  let commandSchema = S.union([
6
7
  S.schema(s => ({
7
8
  TAG: "Add",
8
9
  name: s.m(S.string),
9
10
  description: s.m(S.string),
10
- price: s.m(S.float)
11
+ price: s.m(S.float),
12
+ imageUrl: s.m(StorageRef$Reventless.forStore(undefined, "productImages"))
11
13
  })),
12
14
  S.schema(s => ({
13
15
  TAG: "UpdateName",
@@ -20,6 +22,10 @@ let commandSchema = S.union([
20
22
  S.schema(s => ({
21
23
  TAG: "UpdatePrice",
22
24
  price: s.m(S.float)
25
+ })),
26
+ S.schema(s => ({
27
+ TAG: "UpdateImage",
28
+ imageUrl: s.m(StorageRef$Reventless.forStore(undefined, "productImages"))
23
29
  }))
24
30
  ]);
25
31
 
@@ -28,7 +34,8 @@ let eventSchema = S.union([
28
34
  TAG: "Added",
29
35
  name: s.m(S.string),
30
36
  description: s.m(S.string),
31
- price: s.m(S.float)
37
+ price: s.m(S.float),
38
+ imageUrl: s.m(S.string)
32
39
  })),
33
40
  S.schema(s => ({
34
41
  TAG: "NameUpdated",
@@ -41,6 +48,10 @@ let eventSchema = S.union([
41
48
  S.schema(s => ({
42
49
  TAG: "PriceUpdated",
43
50
  price: s.m(S.float)
51
+ })),
52
+ S.schema(s => ({
53
+ TAG: "ImageUpdated",
54
+ imageUrl: s.m(S.string)
44
55
  }))
45
56
  ]);
46
57
 
@@ -12,27 +12,31 @@
12
12
  @schema
13
13
  type state =
14
14
  | NotCreated
15
- | Created({name: string, description: string, price: float})
15
+ | Created({name: string, description: string, price: float, imageUrl: string})
16
16
 
17
17
  let initialState = NotCreated
18
18
 
19
19
  let evolve = (state, event) =>
20
20
  switch (state, event) {
21
- | (NotCreated, Added({name, description, price})) => Created({name, description, price})
22
- | (Created(_), Added({name, description, price})) => Created({name, description, price})
21
+ | (NotCreated, Added({name, description, price, imageUrl})) =>
22
+ Created({name, description, price, imageUrl})
23
+ | (Created(_), Added({name, description, price, imageUrl})) =>
24
+ Created({name, description, price, imageUrl})
23
25
  | (Created(s), NameUpdated({name})) => Created({...s, name})
24
26
  | (Created(s), DescriptionUpdated({description})) => Created({...s, description})
25
27
  | (Created(s), PriceUpdated({price})) => Created({...s, price})
28
+ | (Created(s), ImageUpdated({imageUrl})) => Created({...s, imageUrl})
26
29
  | (NotCreated, _) => state
27
30
  }
28
31
 
29
32
  let decide = (state, command) =>
30
33
  switch (state, command) {
31
- | (NotCreated, Add({name, description, price})) =>
32
- Ok([Added({name, description, price})])
34
+ | (NotCreated, Add({name, description, price, imageUrl})) =>
35
+ Ok([Added({name, description, price, imageUrl})])
33
36
  | (NotCreated, UpdateName(_)) => Error(ProductNotFound)
34
37
  | (NotCreated, UpdateDescription(_)) => Error(ProductNotFound)
35
38
  | (NotCreated, UpdatePrice(_)) => Error(ProductNotFound)
39
+ | (NotCreated, UpdateImage(_)) => Error(ProductNotFound)
36
40
  | (Created(_), Add(_)) => Error(ProductAlreadyExists)
37
41
  | (Created(s), UpdateName({name})) if name == s.name => Ok([])
38
42
  | (Created(_), UpdateName({name})) => Ok([NameUpdated({name: name})])
@@ -41,4 +45,6 @@ let decide = (state, command) =>
41
45
  Ok([DescriptionUpdated({description: description})])
42
46
  | (Created(s), UpdatePrice({price})) if price == s.price => Ok([])
43
47
  | (Created(_), UpdatePrice({price})) => Ok([PriceUpdated({price: price})])
48
+ | (Created(s), UpdateImage({imageUrl})) if imageUrl == s.imageUrl => Ok([])
49
+ | (Created(_), UpdateImage({imageUrl})) => Ok([ImageUpdated({imageUrl: imageUrl})])
44
50
  }
@@ -8,7 +8,8 @@ let stateSchema = S.union([
8
8
  TAG: "Created",
9
9
  name: s.m(S.string),
10
10
  description: s.m(S.string),
11
- price: s.m(S.float)
11
+ price: s.m(S.float),
12
+ imageUrl: s.m(S.string)
12
13
  }))
13
14
  ]);
14
15
 
@@ -19,7 +20,8 @@ function evolve(state, event) {
19
20
  TAG: "Created",
20
21
  name: event.name,
21
22
  description: event.description,
22
- price: event.price
23
+ price: event.price,
24
+ imageUrl: event.imageUrl
23
25
  };
24
26
  } else {
25
27
  return state;
@@ -31,28 +33,40 @@ function evolve(state, event) {
31
33
  TAG: "Created",
32
34
  name: event.name,
33
35
  description: event.description,
34
- price: event.price
36
+ price: event.price,
37
+ imageUrl: event.imageUrl
35
38
  };
36
39
  case "NameUpdated" :
37
40
  return {
38
41
  TAG: "Created",
39
42
  name: event.name,
40
43
  description: state.description,
41
- price: state.price
44
+ price: state.price,
45
+ imageUrl: state.imageUrl
42
46
  };
43
47
  case "DescriptionUpdated" :
44
48
  return {
45
49
  TAG: "Created",
46
50
  name: state.name,
47
51
  description: event.description,
48
- price: state.price
52
+ price: state.price,
53
+ imageUrl: state.imageUrl
49
54
  };
50
55
  case "PriceUpdated" :
51
56
  return {
52
57
  TAG: "Created",
53
58
  name: state.name,
54
59
  description: state.description,
55
- price: event.price
60
+ price: event.price,
61
+ imageUrl: state.imageUrl
62
+ };
63
+ case "ImageUpdated" :
64
+ return {
65
+ TAG: "Created",
66
+ name: state.name,
67
+ description: state.description,
68
+ price: state.price,
69
+ imageUrl: event.imageUrl
56
70
  };
57
71
  }
58
72
  }
@@ -66,7 +80,8 @@ function decide(state, command) {
66
80
  TAG: "Added",
67
81
  name: command.name,
68
82
  description: command.description,
69
- price: command.price
83
+ price: command.price,
84
+ imageUrl: command.imageUrl
70
85
  }]
71
86
  };
72
87
  } else {
@@ -130,6 +145,22 @@ function decide(state, command) {
130
145
  }]
131
146
  };
132
147
  }
148
+ case "UpdateImage" :
149
+ let imageUrl = command.imageUrl;
150
+ if (imageUrl === state.imageUrl) {
151
+ return {
152
+ TAG: "Ok",
153
+ _0: []
154
+ };
155
+ } else {
156
+ return {
157
+ TAG: "Ok",
158
+ _0: [{
159
+ TAG: "ImageUpdated",
160
+ imageUrl: imageUrl
161
+ }]
162
+ };
163
+ }
133
164
  }
134
165
  }
135
166
 
@@ -8,5 +8,6 @@ type state = {
8
8
  name: string,
9
9
  description: string,
10
10
  price: float,
11
+ @storageRef("productImages") imageUrl: string,
11
12
  }
12
13
 
@@ -2,11 +2,13 @@
2
2
 
3
3
  import * as S from "sury/src/S.res.mjs";
4
4
  import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
5
+ import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
5
6
 
6
7
  let stateSchema = S.schema(s => ({
7
8
  name: s.m(S.string),
8
9
  description: s.m(S.string),
9
- price: s.m(S.float)
10
+ price: s.m(S.float),
11
+ imageUrl: s.m(StorageRef$Reventless.forStore(undefined, "productImages"))
10
12
  }));
11
13
 
12
14
  let config = ReadModel$Reventless.config(undefined, undefined, undefined);
@@ -9,12 +9,13 @@ module ProductMapping = Mapping.Make(
9
9
  open Product
10
10
  let project = ({event, id, _}) =>
11
11
  switch event {
12
- | Added({name, description, price}) =>
13
- Set(id, {Products.name: name, description, price})
12
+ | Added({name, description, price, imageUrl}) =>
13
+ Set(id, {Products.name: name, description, price, imageUrl})
14
14
  | NameUpdated({name}) => Update(id, state => {...state, name})
15
15
  | DescriptionUpdated({description}) =>
16
16
  Update(id, state => {...state, description})
17
17
  | PriceUpdated({price}) => Update(id, state => {...state, price})
18
+ | ImageUpdated({imageUrl}) => Update(id, state => {...state, imageUrl})
18
19
  }
19
20
  },
20
21
  )
@@ -23,7 +23,8 @@ function project(param) {
23
23
  _1: {
24
24
  name: event.name,
25
25
  description: event.description,
26
- price: event.price
26
+ price: event.price,
27
+ imageUrl: event.imageUrl
27
28
  }
28
29
  };
29
30
  case "NameUpdated" :
@@ -34,7 +35,8 @@ function project(param) {
34
35
  _1: state => ({
35
36
  name: name,
36
37
  description: state.description,
37
- price: state.price
38
+ price: state.price,
39
+ imageUrl: state.imageUrl
38
40
  })
39
41
  };
40
42
  case "DescriptionUpdated" :
@@ -45,7 +47,8 @@ function project(param) {
45
47
  _1: state => ({
46
48
  name: state.name,
47
49
  description: description,
48
- price: state.price
50
+ price: state.price,
51
+ imageUrl: state.imageUrl
49
52
  })
50
53
  };
51
54
  case "PriceUpdated" :
@@ -56,7 +59,20 @@ function project(param) {
56
59
  _1: state => ({
57
60
  name: state.name,
58
61
  description: state.description,
59
- price: price
62
+ price: price,
63
+ imageUrl: state.imageUrl
64
+ })
65
+ };
66
+ case "ImageUpdated" :
67
+ let imageUrl = event.imageUrl;
68
+ return {
69
+ TAG: "Update",
70
+ _0: id,
71
+ _1: state => ({
72
+ name: state.name,
73
+ description: state.description,
74
+ price: state.price,
75
+ imageUrl: imageUrl
60
76
  })
61
77
  };
62
78
  }
Binary file
@@ -27,6 +27,9 @@ let importCallback = (~eventName, ~key) => {
27
27
  name: "Imported Product",
28
28
  description: "Imported from " ++ key,
29
29
  price: 9.99,
30
+ // A CSV row carries no upload, so the ref is empty until someone
31
+ // issues UpdateImage against the presigned store.
32
+ imageUrl: "",
30
33
  })->Message.encode(Product.commandSchema),
31
34
  },
32
35
  ],
@@ -29,7 +29,8 @@ function importCallback(eventName, key) {
29
29
  TAG: "Add",
30
30
  name: "Imported Product",
31
31
  description: "Imported from " + key,
32
- price: 9.99
32
+ price: 9.99,
33
+ imageUrl: ""
33
34
  }, Product$CatalogPlugin.commandSchema)
34
35
  }]
35
36
  }]);
@@ -5,7 +5,7 @@
5
5
  describe("Products ExtensionPoint mapping", () => {
6
6
  test("Added publishes ProductBecameAvailable", () =>
7
7
  whenDelegateEvent(
8
- Delegate.Added({name: "Book", description: "A good book", price: 9.99}),
8
+ Delegate.Added({name: "Book", description: "A good book", price: 9.99, imageUrl: "/productImages/book.jpg"}),
9
9
  )->thenPublishesEvent(
10
10
  "gwt-id",
11
11
  ExtensionPoint.ProductBecameAvailable({productId: "gwt-id", name: "Book", price: 9.99}),
@@ -43,7 +43,8 @@ describe("Products ExtensionPoint mapping", () => {
43
43
  TAG: "Added",
44
44
  name: "Book",
45
45
  description: "A good book",
46
- price: 9.99
46
+ price: 9.99,
47
+ imageUrl: "/productImages/book.jpg"
47
48
  }), "gwt-id", {
48
49
  TAG: "ProductBecameAvailable",
49
50
  productId: "gwt-id",
@@ -1,15 +1,36 @@
1
1
  @@reventless.gwt
2
2
 
3
+ let added = Added({
4
+ name: "Laptop",
5
+ description: "A laptop",
6
+ price: 999.99,
7
+ imageUrl: "/productImages/laptop.jpg",
8
+ })
9
+
3
10
  describe("Product Behavior", () => {
4
11
  test("Add on new aggregate produces Added", () =>
5
12
  givenEvents([])
6
- ->whenCmd(Add({name: "Laptop", description: "A laptop", price: 999.99}))
7
- ->thenEvent(Added({name: "Laptop", description: "A laptop", price: 999.99}))
13
+ ->whenCmd(
14
+ Add({
15
+ name: "Laptop",
16
+ description: "A laptop",
17
+ price: 999.99,
18
+ imageUrl: "/productImages/laptop.jpg",
19
+ }),
20
+ )
21
+ ->thenEvent(added)
8
22
  )
9
23
 
10
24
  test("Add on existing aggregate returns ProductAlreadyExists", () =>
11
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
12
- ->whenCmd(Add({name: "Laptop 2", description: "Another", price: 1.0}))
25
+ givenEvents([added])
26
+ ->whenCmd(
27
+ Add({
28
+ name: "Laptop 2",
29
+ description: "Another",
30
+ price: 1.0,
31
+ imageUrl: "/productImages/laptop-2.jpg",
32
+ }),
33
+ )
13
34
  ->thenError(ProductAlreadyExists)
14
35
  )
15
36
 
@@ -20,15 +41,13 @@ describe("Product Behavior", () => {
20
41
  )
21
42
 
22
43
  test("UpdateName on existing product produces NameUpdated", () =>
23
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
44
+ givenEvents([added])
24
45
  ->whenCmd(UpdateName({name: "Gaming Laptop"}))
25
46
  ->thenEvent(NameUpdated({name: "Gaming Laptop"}))
26
47
  )
27
48
 
28
49
  test("UpdateName to same name produces no events (idempotent)", () =>
29
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
30
- ->whenCmd(UpdateName({name: "Laptop"}))
31
- ->thenNoEvent
50
+ givenEvents([added])->whenCmd(UpdateName({name: "Laptop"}))->thenNoEvent
32
51
  )
33
52
 
34
53
  test("UpdateDescription on non-existent aggregate returns ProductNotFound", () =>
@@ -38,32 +57,44 @@ describe("Product Behavior", () => {
38
57
  )
39
58
 
40
59
  test("UpdateDescription on existing product produces DescriptionUpdated", () =>
41
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
60
+ givenEvents([added])
42
61
  ->whenCmd(UpdateDescription({description: "A high-end laptop"}))
43
62
  ->thenEvent(DescriptionUpdated({description: "A high-end laptop"}))
44
63
  )
45
64
 
46
65
  test("UpdateDescription to same description produces no events (idempotent)", () =>
47
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
48
- ->whenCmd(UpdateDescription({description: "A laptop"}))
49
- ->thenNoEvent
66
+ givenEvents([added])->whenCmd(UpdateDescription({description: "A laptop"}))->thenNoEvent
50
67
  )
51
68
 
52
69
  test("UpdatePrice on non-existent aggregate returns ProductNotFound", () =>
53
- givenEvents([])
54
- ->whenCmd(UpdatePrice({price: 1.0}))
55
- ->thenError(ProductNotFound)
70
+ givenEvents([])->whenCmd(UpdatePrice({price: 1.0}))->thenError(ProductNotFound)
56
71
  )
57
72
 
58
73
  test("UpdatePrice on existing product produces PriceUpdated", () =>
59
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
74
+ givenEvents([added])
60
75
  ->whenCmd(UpdatePrice({price: 899.99}))
61
76
  ->thenEvent(PriceUpdated({price: 899.99}))
62
77
  )
63
78
 
64
79
  test("UpdatePrice to same price produces no events (idempotent)", () =>
65
- givenEvents([Added({name: "Laptop", description: "A laptop", price: 999.99})])
66
- ->whenCmd(UpdatePrice({price: 999.99}))
80
+ givenEvents([added])->whenCmd(UpdatePrice({price: 999.99}))->thenNoEvent
81
+ )
82
+
83
+ test("UpdateImage on non-existent aggregate returns ProductNotFound", () =>
84
+ givenEvents([])
85
+ ->whenCmd(UpdateImage({imageUrl: "/productImages/laptop.jpg"}))
86
+ ->thenError(ProductNotFound)
87
+ )
88
+
89
+ test("UpdateImage on existing product produces ImageUpdated", () =>
90
+ givenEvents([added])
91
+ ->whenCmd(UpdateImage({imageUrl: "/productImages/laptop-v2.jpg"}))
92
+ ->thenEvent(ImageUpdated({imageUrl: "/productImages/laptop-v2.jpg"}))
93
+ )
94
+
95
+ test("UpdateImage to the same ref produces no events (idempotent)", () =>
96
+ givenEvents([added])
97
+ ->whenCmd(UpdateImage({imageUrl: "/productImages/laptop.jpg"}))
67
98
  ->thenNoEvent
68
99
  )
69
100
  })