@reventlessdev/reventless-gwt 1.0.0-alpha.238 → 1.0.0-alpha.240

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 (37) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +6 -6
  3. package/src/Automation_GWT.res +130 -0
  4. package/src/Automation_GWT.res.mjs +193 -0
  5. package/src/Behavior_GWT.res +6 -6
  6. package/src/Behavior_GWT.res.mjs +5 -5
  7. package/src/Capabilities_Fake.res +157 -0
  8. package/src/Capabilities_Fake.res.mjs +254 -0
  9. package/src/Flow_GWT.res +112 -1
  10. package/src/Flow_GWT.res.mjs +165 -11
  11. package/src/InboundTranslation_GWT.res +112 -0
  12. package/src/InboundTranslation_GWT.res.mjs +190 -1
  13. package/src/OutboundTranslation_GWT.res +207 -4
  14. package/src/OutboundTranslation_GWT.res.mjs +257 -1
  15. package/src/StateChange/ExternalAddCategorySlice.res +1 -1
  16. package/src/StateChange/ExternalAddCategorySlice.res.mjs +2 -2
  17. package/src/StateChange/WithFixtures.res +1 -1
  18. package/src/StateChange/WithFixtures.res.mjs +2 -2
  19. package/src/StateChange/WithManualOpen.res +1 -1
  20. package/src/StateChange/WithManualOpen.res.mjs +2 -2
  21. package/tests/AutomationFromSliceGwtTest.res +145 -0
  22. package/tests/AutomationFromSliceGwtTest.res.mjs +353 -0
  23. package/tests/AutomationGwtTest.res.mjs +2 -2
  24. package/tests/DelegateGwtTest.res +5 -5
  25. package/tests/DelegateGwtTest.res.mjs +27 -27
  26. package/tests/FlowAggregateGwtTest.res.mjs +4 -4
  27. package/tests/FlowCrossPluginGwtTest.res +4 -4
  28. package/tests/FlowCrossPluginGwtTest.res.mjs +22 -22
  29. package/tests/FlowGwtTest.res.mjs +6 -6
  30. package/tests/InboundFromSliceGwtTest.res +63 -0
  31. package/tests/InboundFromSliceGwtTest.res.mjs +164 -0
  32. package/tests/InboundTranslationGwtTest.res.mjs +2 -7
  33. package/tests/MappingGwtTest.res.mjs +12 -12
  34. package/tests/OutboundFromSliceGwtTest.res +124 -0
  35. package/tests/OutboundFromSliceGwtTest.res.mjs +268 -0
  36. package/tests/StateChangeSliceAuthorizeGwtTest.res.mjs +20 -16
  37. package/tests/StateChangeSliceGwtTest.res.mjs +6 -6
@@ -0,0 +1,254 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Messaging$Reventless from "@reventlessdev/reventless-spec/src/semantic/Messaging.res.mjs";
6
+ import * as IdentityProvider$Reventless from "@reventlessdev/reventless-spec/src/semantic/IdentityProvider.res.mjs";
7
+
8
+ function make(geocode, send, channelsOpt, pushServicesOpt, createPrincipal, token, hash, secrets, param) {
9
+ let channels = channelsOpt !== undefined ? channelsOpt : [
10
+ "Email",
11
+ "Sms"
12
+ ];
13
+ let pushServices = pushServicesOpt !== undefined ? pushServicesOpt : [];
14
+ let log = [];
15
+ let geocode$1 = async text => {
16
+ log.push({
17
+ TAG: "Geocoded",
18
+ text: text
19
+ });
20
+ if (geocode !== undefined) {
21
+ return await geocode(text);
22
+ } else {
23
+ return {
24
+ TAG: "Ok",
25
+ _0: []
26
+ };
27
+ }
28
+ };
29
+ let send$1 = async (recipient, message) => {
30
+ log.push({
31
+ TAG: "Sent",
32
+ recipient: recipient,
33
+ message: message
34
+ });
35
+ if (send !== undefined) {
36
+ return await send(recipient, message);
37
+ } else {
38
+ return {
39
+ TAG: "Ok",
40
+ _0: {
41
+ ref: `receipt-` + log.length.toString()
42
+ }
43
+ };
44
+ }
45
+ };
46
+ let createPrincipal$1 = async (contact, credential, groups) => {
47
+ log.push({
48
+ TAG: "PrincipalCreated",
49
+ contact: contact,
50
+ credential: credential,
51
+ groups: groups
52
+ });
53
+ if (createPrincipal !== undefined) {
54
+ return await createPrincipal(contact, credential, groups);
55
+ } else {
56
+ return {
57
+ TAG: "Ok",
58
+ _0: {
59
+ providerId: `principal-` + log.length.toString()
60
+ }
61
+ };
62
+ }
63
+ };
64
+ let capabilities_messaging = Messaging$Reventless.makeProvider(channels, pushServices, send$1);
65
+ let capabilities_identityProvider = IdentityProvider$Reventless.make(createPrincipal$1, async (principal, group) => {
66
+ log.push({
67
+ TAG: "AddedToGroup",
68
+ principal: principal,
69
+ group: group
70
+ });
71
+ return {
72
+ TAG: "Ok",
73
+ _0: undefined
74
+ };
75
+ }, async (principal, group) => {
76
+ log.push({
77
+ TAG: "RemovedFromGroup",
78
+ principal: principal,
79
+ group: group
80
+ });
81
+ return {
82
+ TAG: "Ok",
83
+ _0: undefined
84
+ };
85
+ }, async principal => {
86
+ log.push({
87
+ TAG: "PrincipalDeleted",
88
+ principal: principal
89
+ });
90
+ return {
91
+ TAG: "Ok",
92
+ _0: undefined
93
+ };
94
+ }, [
95
+ "CreatePrincipal",
96
+ "AddToGroup",
97
+ "RemoveFromGroup",
98
+ "DeletePrincipal"
99
+ ]);
100
+ let capabilities_secrets = {
101
+ randomToken: (length, alphabet) => {
102
+ log.push({
103
+ TAG: "TokenDrawn",
104
+ length: length,
105
+ alphabet: alphabet
106
+ });
107
+ if (secrets !== undefined) {
108
+ return secrets.randomToken(length, alphabet);
109
+ } else {
110
+ return {
111
+ TAG: "Ok",
112
+ _0: Stdlib_Option.getOr(token, "t".repeat(length))
113
+ };
114
+ }
115
+ },
116
+ hash: input => {
117
+ log.push({
118
+ TAG: "Hashed",
119
+ input: input
120
+ });
121
+ if (secrets !== undefined) {
122
+ return secrets.hash(input);
123
+ } else {
124
+ return {
125
+ TAG: "Ok",
126
+ _0: Stdlib_Option.mapOr(hash, `hash:` + input, f => f(input))
127
+ };
128
+ }
129
+ }
130
+ };
131
+ let capabilities = {
132
+ geocode: geocode$1,
133
+ messaging: capabilities_messaging,
134
+ identityProvider: capabilities_identityProvider,
135
+ secrets: capabilities_secrets
136
+ };
137
+ return {
138
+ capabilities: capabilities,
139
+ calls: () => log.slice()
140
+ };
141
+ }
142
+
143
+ function toJson(c) {
144
+ let recipientText = r => {
145
+ switch (r.TAG) {
146
+ case "ToEmail" :
147
+ return `email:` + r._0;
148
+ case "ToSms" :
149
+ return `sms:` + r._0;
150
+ case "ToPush" :
151
+ return `push:` + Messaging$Reventless.pushServiceToString(Messaging$Reventless.serviceOf(r._0));
152
+ }
153
+ };
154
+ let obj = (tag, fields) => Object.fromEntries(Belt_Array.concatMany([
155
+ [[
156
+ "TAG",
157
+ tag
158
+ ]],
159
+ fields
160
+ ]));
161
+ let str = prim => prim;
162
+ switch (c.TAG) {
163
+ case "Geocoded" :
164
+ return obj("Geocoded", [[
165
+ "text",
166
+ c.text
167
+ ]]);
168
+ case "Sent" :
169
+ let message = c.message;
170
+ return obj("Sent", [
171
+ [
172
+ "recipient",
173
+ recipientText(c.recipient)
174
+ ],
175
+ [
176
+ "subject",
177
+ Stdlib_Option.mapOr(message.subject, null, str)
178
+ ],
179
+ [
180
+ "body",
181
+ message.body
182
+ ]
183
+ ]);
184
+ case "PrincipalCreated" :
185
+ return obj("PrincipalCreated", [
186
+ [
187
+ "contact",
188
+ recipientText(c.contact)
189
+ ],
190
+ [
191
+ "groups",
192
+ c.groups.map(str)
193
+ ]
194
+ ]);
195
+ case "AddedToGroup" :
196
+ return obj("AddedToGroup", [
197
+ [
198
+ "principal",
199
+ c.principal.providerId
200
+ ],
201
+ [
202
+ "group",
203
+ c.group
204
+ ]
205
+ ]);
206
+ case "RemovedFromGroup" :
207
+ return obj("RemovedFromGroup", [
208
+ [
209
+ "principal",
210
+ c.principal.providerId
211
+ ],
212
+ [
213
+ "group",
214
+ c.group
215
+ ]
216
+ ]);
217
+ case "PrincipalDeleted" :
218
+ return obj("PrincipalDeleted", [[
219
+ "principal",
220
+ c.principal.providerId
221
+ ]]);
222
+ case "TokenDrawn" :
223
+ return obj("TokenDrawn", [[
224
+ "length",
225
+ c.length
226
+ ]]);
227
+ case "Hashed" :
228
+ return obj("Hashed", [[
229
+ "input",
230
+ c.input
231
+ ]]);
232
+ }
233
+ }
234
+
235
+ let Capabilities;
236
+
237
+ let Geocoding;
238
+
239
+ let Messaging;
240
+
241
+ let IdentityProvider;
242
+
243
+ let Secrets;
244
+
245
+ export {
246
+ Capabilities,
247
+ Geocoding,
248
+ Messaging,
249
+ IdentityProvider,
250
+ Secrets,
251
+ make,
252
+ toJson,
253
+ }
254
+ /* No side effect */
package/src/Flow_GWT.res CHANGED
@@ -20,7 +20,7 @@ open ReventlessCore
20
20
  // pipe-first so it reads top-to-bottom:
21
21
  //
22
22
  // module Place = Flow_GWT.CommandStep(PlaceOrder, PlaceOrder_Behavior)
23
- // module Auto = Flow_GWT.AutomationStep(AutoShipOrderSlice)
23
+ // module Auto = Flow_GWT.AutomationSlice(AutoShipOrder, AutoShipOrder_Automation)
24
24
  // module Ship = Flow_GWT.CommandStep(ShipOrder, ShipOrder_Behavior)
25
25
  // module View = Flow_GWT.ViewStep(Orders, Orders_Projection)
26
26
  //
@@ -77,6 +77,8 @@ type flowState = {
77
77
  // step falls back to the synthetic `gwt-id` so the existing DCB Flow tests
78
78
  // are unchanged.
79
79
  lastAggregateId: option<string>,
80
+ // Why the last inbound step produced no commands, when it was refused.
81
+ lastRefusal: option<string>,
80
82
  }
81
83
 
82
84
  type flow = promise<flowState>
@@ -89,6 +91,7 @@ let emptyState = {
89
91
  lastCommands: [],
90
92
  lastPublic: [],
91
93
  lastAggregateId: None,
94
+ lastRefusal: None,
92
95
  }
93
96
 
94
97
  // Entry point for a chain — an empty flow with no prior history.
@@ -489,6 +492,8 @@ module AggregateCommandStep = (
489
492
 
490
493
  // -- AutomationStep: a policy that reacts to log events and issues a command --
491
494
 
495
+ // Deprecated: takes an adapter. Use `AutomationSlice`; this goes one release
496
+ // after the examples have moved.
492
497
  module AutomationStep = (Spec: Automation_GWT.SliceSpec) => {
493
498
  let consumedDecoder = Reventless.DcbDecode.makeDecoder(Spec.consumedEventSchema)
494
499
 
@@ -558,6 +563,8 @@ module ViewStep = (
558
563
 
559
564
  // -- OutboundStep: an OutboundTranslationSlice's `collect` over the log -------
560
565
 
566
+ // Deprecated: takes an adapter. Use `OutboundSlice`; this goes one release
567
+ // after the examples have moved.
561
568
  module OutboundStep = (Spec: OutboundTranslation_GWT.SliceSpec) => {
562
569
  let consumedDecoder = Reventless.DcbDecode.makeDecoder(Spec.consumedEventSchema)
563
570
 
@@ -587,6 +594,110 @@ module OutboundStep = (Spec: OutboundTranslation_GWT.SliceSpec) => {
587
594
  let thenOutboundNothing = flowP => thenOutbound(flowP, [])
588
595
  }
589
596
 
597
+ // -- Slice-as-written steps --------------------------------------------------
598
+ //
599
+ // The same steps over a slice's `Spec` and body, with no adapter. The log keeps
600
+ // no source name, so every mapping is offered every entry; the source id is the
601
+ // producing aggregate's, or "" for a DCB event, which names its own subject.
602
+
603
+ let sourceIdOf = (entry: logEntry) => entry.aggregateId->Option.getOr("")
604
+
605
+ let encodedCommandsMatch = async (flowP: flow, expJson: array<JSON.t>) => {
606
+ let s = await flowP
607
+ let o = switch s.lastRefusal {
608
+ | Some(reason) => Outcome.fail(TranslateError({expected: "(commands)", actual: Some(reason)}))
609
+ | None =>
610
+ s.lastCommands == expJson
611
+ ? Outcome.pass
612
+ : Outcome.fail(EventsMismatch({expected: expJson, actual: s.lastCommands}))
613
+ }
614
+ s->recordOutcome(o)
615
+ }
616
+
617
+ module AutomationSlice = (
618
+ Spec: Reventless.AutomationSlice.Spec,
619
+ Automation: Reventless.AutomationSlice.Automation with module Spec := Spec,
620
+ ) => {
621
+ module A = Automation_GWT.FromSlice(Spec, Automation)
622
+
623
+ let whenReacts = async (flowP: flow) => {
624
+ let s = await flowP
625
+ let routed =
626
+ s.log->Array.flatMap(entry =>
627
+ A.Route.dispatches->Array.filterMap(d =>
628
+ d.handle(entry.json, ~sourceId=entry->sourceIdOf, A.testContext)
629
+ )
630
+ )
631
+ let {commands, _} = A.sweep(routed)
632
+ {
633
+ ...s,
634
+ lastCommands: commands->Array.map(((_id, cmd)) => cmd->Message.encode(Spec.commandSchema)),
635
+ lastRefusal: None,
636
+ }
637
+ }
638
+
639
+ let thenIssuesCommands = (flowP, expected: array<Spec.command>) =>
640
+ encodedCommandsMatch(flowP, expected->Array.map(c => c->Message.encode(Spec.commandSchema)))
641
+ let thenIssuesCommand = (flowP, command) => thenIssuesCommands(flowP, [command])
642
+ let thenIssuesNoCommand = flowP => thenIssuesCommands(flowP, [])
643
+ }
644
+
645
+ module OutboundSlice = (
646
+ Spec: Reventless.OutboundTranslationSlice.Spec,
647
+ Translation: Reventless.OutboundTranslationSlice.Translation with module Spec := Spec,
648
+ ) => {
649
+ let consumedDecoder = Reventless.DcbDecode.makeDecoder(Spec.consumedEventSchema)
650
+
651
+ let encItems = (arr: array<(string, Spec.outboundItem)>) =>
652
+ arr->Array.map(((id, item)) => (id, item->Message.encode(Spec.outboundItemSchema)))
653
+
654
+ // Asserts what `collect` queues; `translate` is the slice test's to run.
655
+ let thenOutbound = async (flowP: flow, expected: array<(string, Spec.outboundItem)>) => {
656
+ let s = await flowP
657
+ let collected =
658
+ s.log->Array.flatMap(entry =>
659
+ decodeMatching([entry], consumedDecoder, [])->Array.flatMap(e =>
660
+ Translation.collect(e, ~sourceId=entry->sourceIdOf)
661
+ )
662
+ )
663
+ let actual = encItems(collected)
664
+ let expectedJson = encItems(expected)
665
+ let o =
666
+ actual == expectedJson
667
+ ? Outcome.pass
668
+ : Outcome.fail(TodoMismatch({expected: expectedJson, actual}))
669
+ s->recordOutcome(o)
670
+ }
671
+
672
+ let thenOutboundNothing = flowP => thenOutbound(flowP, [])
673
+ }
674
+
675
+ module InboundSlice = (
676
+ Spec: Reventless.InboundTranslationSlice.Spec,
677
+ Translation: Reventless.InboundTranslationSlice.Translation with module Spec := Spec,
678
+ ) => {
679
+ module I = InboundTranslation_GWT.FromSlice(Spec, Translation)
680
+
681
+ // Received input becomes the flow's next commands, as an automation's do.
682
+ let whenReceived = async (flowP: flow, json: JSON.t) => {
683
+ let s = await flowP
684
+ switch I.whenReceived(json) {
685
+ | Translated(pairs) => {
686
+ ...s,
687
+ lastCommands: pairs->Array.map(((_id, cmd)) => cmd->Message.encode(Spec.commandSchema)),
688
+ lastRefusal: None,
689
+ }
690
+ | NotUnderstood(msg) => {...s, lastCommands: [], lastRefusal: Some(`not understood: ${msg}`)}
691
+ | InputRefused(msg) => {...s, lastCommands: [], lastRefusal: Some(`input refused: ${msg}`)}
692
+ }
693
+ }
694
+
695
+ let thenIssuesCommands = (flowP, expected: array<Spec.command>) =>
696
+ encodedCommandsMatch(flowP, expected->Array.map(c => c->Message.encode(Spec.commandSchema)))
697
+ let thenIssuesCommand = (flowP, command) => thenIssuesCommands(flowP, [command])
698
+ let thenIssuesNoCommand = flowP => thenIssuesCommands(flowP, [])
699
+ }
700
+
590
701
  // -- Cross-plugin boundary steps (Phase 3) -----------------------------------
591
702
 
592
703
  // Order-independent comparison for published actions — fan-out order is
@@ -14,8 +14,10 @@ import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
14
14
  import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
15
15
  import * as DcbValidation$Reventless from "@reventlessdev/reventless-spec/src/components/DcbValidation.res.mjs";
16
16
  import * as StubRuntime$ReventlessGwt from "./StubRuntime.res.mjs";
17
+ import * as Automation_GWT$ReventlessGwt from "./Automation_GWT.res.mjs";
17
18
  import * as DcbScopeInference$Reventless from "@reventlessdev/reventless-spec/src/components/DcbScopeInference.res.mjs";
18
19
  import * as Projection_GWT$ReventlessGwt from "./Projection_GWT.res.mjs";
20
+ import * as InboundTranslation_GWT$ReventlessGwt from "./InboundTranslation_GWT.res.mjs";
19
21
 
20
22
  let emptyState_log = [];
21
23
 
@@ -32,7 +34,8 @@ let emptyState = {
32
34
  lastError: undefined,
33
35
  lastCommands: emptyState_lastCommands,
34
36
  lastPublic: emptyState_lastPublic,
35
- lastAggregateId: undefined
37
+ lastAggregateId: undefined,
38
+ lastRefusal: undefined
36
39
  };
37
40
 
38
41
  let start = Promise.resolve(emptyState);
@@ -47,7 +50,8 @@ function recordOutcome(s, o) {
47
50
  lastError: s.lastError,
48
51
  lastCommands: s.lastCommands,
49
52
  lastPublic: s.lastPublic,
50
- lastAggregateId: s.lastAggregateId
53
+ lastAggregateId: s.lastAggregateId,
54
+ lastRefusal: s.lastRefusal
51
55
  };
52
56
  } else {
53
57
  return s;
@@ -179,7 +183,8 @@ function CommandStep(Spec) {
179
183
  lastError: s.lastError,
180
184
  lastCommands: s.lastCommands,
181
185
  lastPublic: s.lastPublic,
182
- lastAggregateId: s.lastAggregateId
186
+ lastAggregateId: s.lastAggregateId,
187
+ lastRefusal: s.lastRefusal
183
188
  };
184
189
  };
185
190
  let scopeShape = DcbTag$Reventless.sliceShapeFromSchemas("", Spec.commandSchema, Spec.consumedEventSchema, Spec.eventSchema, undefined);
@@ -212,7 +217,8 @@ function CommandStep(Spec) {
212
217
  lastError: Message$ReventlessCore.encode(events._0, Spec.errorSchema),
213
218
  lastCommands: s.lastCommands,
214
219
  lastPublic: s.lastPublic,
215
- lastAggregateId: undefined
220
+ lastAggregateId: undefined,
221
+ lastRefusal: s.lastRefusal
216
222
  };
217
223
  }
218
224
  let events$1 = events._0;
@@ -223,7 +229,8 @@ function CommandStep(Spec) {
223
229
  lastError: undefined,
224
230
  lastCommands: s.lastCommands,
225
231
  lastPublic: s.lastPublic,
226
- lastAggregateId: undefined
232
+ lastAggregateId: undefined,
233
+ lastRefusal: s.lastRefusal
227
234
  };
228
235
  };
229
236
  let thenEvents = async (flowP, expected) => {
@@ -292,7 +299,8 @@ function AggregateCommandStep(Spec) {
292
299
  lastError: s.lastError,
293
300
  lastCommands: s.lastCommands,
294
301
  lastPublic: s.lastPublic,
295
- lastAggregateId: s.lastAggregateId
302
+ lastAggregateId: s.lastAggregateId,
303
+ lastRefusal: s.lastRefusal
296
304
  };
297
305
  };
298
306
  let whenCommand = async (flowP, id, command) => {
@@ -308,7 +316,8 @@ function AggregateCommandStep(Spec) {
308
316
  lastError: Message$ReventlessCore.encode(events._0, Spec.errorSchema),
309
317
  lastCommands: s.lastCommands,
310
318
  lastPublic: s.lastPublic,
311
- lastAggregateId: id
319
+ lastAggregateId: id,
320
+ lastRefusal: s.lastRefusal
312
321
  };
313
322
  }
314
323
  let events$1 = events._0;
@@ -319,7 +328,8 @@ function AggregateCommandStep(Spec) {
319
328
  lastError: undefined,
320
329
  lastCommands: s.lastCommands,
321
330
  lastPublic: s.lastPublic,
322
- lastAggregateId: id
331
+ lastAggregateId: id,
332
+ lastRefusal: s.lastRefusal
323
333
  };
324
334
  };
325
335
  let thenEvents = async (flowP, expected) => {
@@ -404,7 +414,8 @@ function AutomationStep(Spec) {
404
414
  lastError: s.lastError,
405
415
  lastCommands: commands.map(param => Message$ReventlessCore.encode(param[1], Spec.commandSchema)),
406
416
  lastPublic: s.lastPublic,
407
- lastAggregateId: s.lastAggregateId
417
+ lastAggregateId: s.lastAggregateId,
418
+ lastRefusal: s.lastRefusal
408
419
  };
409
420
  };
410
421
  let thenIssuesCommands = async (flowP, expected) => {
@@ -486,6 +497,142 @@ function OutboundStep(Spec) {
486
497
  };
487
498
  }
488
499
 
500
+ function sourceIdOf(entry) {
501
+ return Stdlib_Option.getOr(entry.aggregateId, "");
502
+ }
503
+
504
+ async function encodedCommandsMatch(flowP, expJson) {
505
+ let s = await flowP;
506
+ let reason = s.lastRefusal;
507
+ let o = reason !== undefined ? Outcome$ReventlessGwt.fail({
508
+ TAG: "TranslateError",
509
+ expected: "(commands)",
510
+ actual: reason
511
+ }) : (
512
+ Primitive_object.equal(s.lastCommands, expJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
513
+ TAG: "EventsMismatch",
514
+ expected: expJson,
515
+ actual: s.lastCommands
516
+ })
517
+ );
518
+ return recordOutcome(s, o);
519
+ }
520
+
521
+ function AutomationSlice(Spec) {
522
+ return Automation => {
523
+ let A = Automation_GWT$ReventlessGwt.FromSlice(Spec)(Automation);
524
+ let whenReacts = async flowP => {
525
+ let s = await flowP;
526
+ let routed = s.log.flatMap(entry => Stdlib_Array.filterMap(A.Route.dispatches, d => d.handle(entry.json, Stdlib_Option.getOr(entry.aggregateId, ""), A.testContext)));
527
+ let match = A.sweep(routed);
528
+ return {
529
+ log: s.log,
530
+ outcome: s.outcome,
531
+ lastEvents: s.lastEvents,
532
+ lastError: s.lastError,
533
+ lastCommands: match.commands.map(param => Message$ReventlessCore.encode(param[1], Spec.commandSchema)),
534
+ lastPublic: s.lastPublic,
535
+ lastAggregateId: s.lastAggregateId,
536
+ lastRefusal: undefined
537
+ };
538
+ };
539
+ let thenIssuesCommands = (flowP, expected) => encodedCommandsMatch(flowP, expected.map(c => Message$ReventlessCore.encode(c, Spec.commandSchema)));
540
+ let thenIssuesCommand = (flowP, command) => thenIssuesCommands(flowP, [command]);
541
+ let thenIssuesNoCommand = flowP => thenIssuesCommands(flowP, []);
542
+ return {
543
+ A: A,
544
+ whenReacts: whenReacts,
545
+ thenIssuesCommands: thenIssuesCommands,
546
+ thenIssuesCommand: thenIssuesCommand,
547
+ thenIssuesNoCommand: thenIssuesNoCommand
548
+ };
549
+ };
550
+ }
551
+
552
+ function OutboundSlice(Spec) {
553
+ return Translation => {
554
+ let consumedDecoder = DcbDecode$Reventless.makeDecoder(Spec.consumedEventSchema);
555
+ let encItems = arr => arr.map(param => [
556
+ param[0],
557
+ Message$ReventlessCore.encode(param[1], Spec.outboundItemSchema)
558
+ ]);
559
+ let thenOutbound = async (flowP, expected) => {
560
+ let s = await flowP;
561
+ let collected = s.log.flatMap(entry => decodeMatching([entry], consumedDecoder, []).flatMap(e => Translation.collect(e, Stdlib_Option.getOr(entry.aggregateId, ""))));
562
+ let actual = encItems(collected);
563
+ let expectedJson = encItems(expected);
564
+ let o = Primitive_object.equal(actual, expectedJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
565
+ TAG: "TodoMismatch",
566
+ expected: expectedJson,
567
+ actual: actual
568
+ });
569
+ return recordOutcome(s, o);
570
+ };
571
+ let thenOutboundNothing = flowP => thenOutbound(flowP, []);
572
+ return {
573
+ consumedDecoder: consumedDecoder,
574
+ encItems: encItems,
575
+ thenOutbound: thenOutbound,
576
+ thenOutboundNothing: thenOutboundNothing
577
+ };
578
+ };
579
+ }
580
+
581
+ function InboundSlice(Spec) {
582
+ return Translation => {
583
+ let I = InboundTranslation_GWT$ReventlessGwt.FromSlice(Spec)(Translation);
584
+ let whenReceived = async (flowP, json) => {
585
+ let s = await flowP;
586
+ let pairs = I.whenReceived(json);
587
+ switch (pairs.TAG) {
588
+ case "Translated" :
589
+ return {
590
+ log: s.log,
591
+ outcome: s.outcome,
592
+ lastEvents: s.lastEvents,
593
+ lastError: s.lastError,
594
+ lastCommands: pairs._0.map(param => Message$ReventlessCore.encode(param[1], Spec.commandSchema)),
595
+ lastPublic: s.lastPublic,
596
+ lastAggregateId: s.lastAggregateId,
597
+ lastRefusal: undefined
598
+ };
599
+ case "NotUnderstood" :
600
+ return {
601
+ log: s.log,
602
+ outcome: s.outcome,
603
+ lastEvents: s.lastEvents,
604
+ lastError: s.lastError,
605
+ lastCommands: [],
606
+ lastPublic: s.lastPublic,
607
+ lastAggregateId: s.lastAggregateId,
608
+ lastRefusal: `not understood: ` + pairs._0
609
+ };
610
+ case "InputRefused" :
611
+ return {
612
+ log: s.log,
613
+ outcome: s.outcome,
614
+ lastEvents: s.lastEvents,
615
+ lastError: s.lastError,
616
+ lastCommands: [],
617
+ lastPublic: s.lastPublic,
618
+ lastAggregateId: s.lastAggregateId,
619
+ lastRefusal: `input refused: ` + pairs._0
620
+ };
621
+ }
622
+ };
623
+ let thenIssuesCommands = (flowP, expected) => encodedCommandsMatch(flowP, expected.map(c => Message$ReventlessCore.encode(c, Spec.commandSchema)));
624
+ let thenIssuesCommand = (flowP, command) => thenIssuesCommands(flowP, [command]);
625
+ let thenIssuesNoCommand = flowP => thenIssuesCommands(flowP, []);
626
+ return {
627
+ I: I,
628
+ whenReceived: whenReceived,
629
+ thenIssuesCommands: thenIssuesCommands,
630
+ thenIssuesCommand: thenIssuesCommand,
631
+ thenIssuesNoCommand: thenIssuesNoCommand
632
+ };
633
+ };
634
+ }
635
+
489
636
  function sortJson(arr) {
490
637
  return arr.toSorted((a, b) => Primitive_string.compare(JSON.stringify(a), JSON.stringify(b)));
491
638
  }
@@ -526,7 +673,8 @@ function ExtensionPointStep(M) {
526
673
  lastError: s.lastError,
527
674
  lastCommands: s.lastCommands,
528
675
  lastPublic: publicEvents.map(e => Message$ReventlessCore.encode(e, M.ExtensionPoint.eventSchema)),
529
- lastAggregateId: s.lastAggregateId
676
+ lastAggregateId: s.lastAggregateId,
677
+ lastRefusal: s.lastRefusal
530
678
  };
531
679
  };
532
680
  let thenPublicEvents = async (flowP, expected) => {
@@ -587,7 +735,8 @@ function ExtensionStep(M) {
587
735
  lastError: s.lastError,
588
736
  lastCommands: nested.flat(),
589
737
  lastPublic: s.lastPublic,
590
- lastAggregateId: s.lastAggregateId
738
+ lastAggregateId: s.lastAggregateId,
739
+ lastRefusal: s.lastRefusal
591
740
  };
592
741
  };
593
742
  let thenIssuesCommands = async (flowP, expected) => {
@@ -637,6 +786,11 @@ export {
637
786
  AutomationStep,
638
787
  ViewStep,
639
788
  OutboundStep,
789
+ sourceIdOf,
790
+ encodedCommandsMatch,
791
+ AutomationSlice,
792
+ OutboundSlice,
793
+ InboundSlice,
640
794
  sortJson,
641
795
  ExtensionPointStep,
642
796
  ExtensionStep,