@reventlessdev/reventless-gwt 1.0.0-alpha.231 → 1.0.0-alpha.233
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 +22 -0
- package/package.json +6 -6
- package/src/Behavior_GWT.res +294 -19
- package/src/Behavior_GWT.res.mjs +429 -41
- package/src/StateChange/ExternalAddCategorySlice.res +5 -0
- package/src/StateChange/ExternalAddCategorySlice.res.mjs +5 -0
- package/src/StateChange/WithFixtures.res +5 -0
- package/src/StateChange/WithFixtures.res.mjs +5 -0
- package/src/StateChange/WithManualOpen.res +5 -0
- package/src/StateChange/WithManualOpen.res.mjs +5 -0
- package/tests/CallerTest.res +63 -0
- package/tests/CallerTest.res.mjs +70 -0
- package/tests/DelegateGwtTest.res +10 -5
- package/tests/FlowCrossPluginGwtTest.res +8 -4
- package/tests/FlowCrossPluginGwtTest.res.mjs +4 -2
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +9 -0
- package/tests/StateChange/WithFixtures_GWT.res.mjs +9 -0
- package/tests/StateChange/WithManualOpen_GWT.res.mjs +9 -0
- package/tests/StateChangeSliceAuthorizeGwtTest.res +174 -0
- package/tests/StateChangeSliceAuthorizeGwtTest.res.mjs +326 -0
- package/tests/StateChangeSliceGwtTest.res.mjs +9 -0
package/src/Behavior_GWT.res.mjs
CHANGED
|
@@ -2,15 +2,188 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
import * as Owner$Reventless from "@reventlessdev/reventless-spec/src/components/Owner.res.mjs";
|
|
5
7
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
6
8
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
7
9
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
8
10
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
9
11
|
import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
|
|
10
12
|
import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
13
|
+
import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
|
|
11
14
|
import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
|
|
12
15
|
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
16
|
+
import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
|
|
13
17
|
import * as DcbScopeInference$Reventless from "@reventlessdev/reventless-spec/src/components/DcbScopeInference.res.mjs";
|
|
18
|
+
import * as CommandTopic_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs";
|
|
19
|
+
|
|
20
|
+
function roleNames(roles) {
|
|
21
|
+
return roles.map(role => {
|
|
22
|
+
if (OwnerScope$Reventless.isJsString(role)) {
|
|
23
|
+
return role;
|
|
24
|
+
} else {
|
|
25
|
+
return Stdlib_JsError.throwWithMessage("Caller: expected roles of the plugin's Roles.t (payload-less, so strings at runtime)");
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function owner(id, rolesOpt) {
|
|
31
|
+
let roles = rolesOpt !== undefined ? rolesOpt : [];
|
|
32
|
+
if (OwnerScope$Reventless.isJsString(id)) {
|
|
33
|
+
return {
|
|
34
|
+
claim: {
|
|
35
|
+
TAG: "Owned",
|
|
36
|
+
userId: id
|
|
37
|
+
},
|
|
38
|
+
roles: roleNames(roles),
|
|
39
|
+
signedIn: true
|
|
40
|
+
};
|
|
41
|
+
} else {
|
|
42
|
+
return Stdlib_JsError.throwWithMessage("Caller.owner: expected an id (a string at runtime, such as an Id.Make identity)");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function inRoles(roles) {
|
|
47
|
+
return {
|
|
48
|
+
claim: {
|
|
49
|
+
TAG: "Owned",
|
|
50
|
+
userId: "caller-in-roles"
|
|
51
|
+
},
|
|
52
|
+
roles: roleNames(roles),
|
|
53
|
+
signedIn: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let operator_roles = [];
|
|
58
|
+
|
|
59
|
+
let operator = {
|
|
60
|
+
claim: "Exempt",
|
|
61
|
+
roles: operator_roles,
|
|
62
|
+
signedIn: true
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
let anonymous_roles = [];
|
|
66
|
+
|
|
67
|
+
let anonymous = {
|
|
68
|
+
claim: "Unidentified",
|
|
69
|
+
roles: anonymous_roles,
|
|
70
|
+
signedIn: false
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let Caller = {
|
|
74
|
+
roleNames: roleNames,
|
|
75
|
+
owner: owner,
|
|
76
|
+
inRoles: inRoles,
|
|
77
|
+
operator: operator,
|
|
78
|
+
anonymous: anonymous
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function Callers(R) {
|
|
82
|
+
let owner$1 = (id, rolesOpt) => {
|
|
83
|
+
let roles = rolesOpt !== undefined ? rolesOpt : [];
|
|
84
|
+
return owner(id, roles);
|
|
85
|
+
};
|
|
86
|
+
let inRoles$1 = inRoles;
|
|
87
|
+
return {
|
|
88
|
+
owner: owner$1,
|
|
89
|
+
inRoles: inRoles$1,
|
|
90
|
+
operator: operator,
|
|
91
|
+
anonymous: anonymous
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function Acting(Spec) {
|
|
96
|
+
let caller = {
|
|
97
|
+
contents: undefined
|
|
98
|
+
};
|
|
99
|
+
let refusal = {
|
|
100
|
+
contents: undefined
|
|
101
|
+
};
|
|
102
|
+
let ownerFieldsByEventType = Owner$Reventless.fieldNamesByVariant(Spec.historySchema);
|
|
103
|
+
let asCaller = (history, who) => {
|
|
104
|
+
caller.contents = who;
|
|
105
|
+
return history;
|
|
106
|
+
};
|
|
107
|
+
let commandName = command => Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, Spec.commandSchema));
|
|
108
|
+
let refusedByRule = (who, command) => {
|
|
109
|
+
if (Authorization$Reventless.admits(Authorization$Reventless.named(Spec.commandAuthorization(command)), who.signedIn, role => who.roles.includes(role))) {
|
|
110
|
+
return false;
|
|
111
|
+
} else {
|
|
112
|
+
refusal.contents = {
|
|
113
|
+
errorCode: "Forbidden",
|
|
114
|
+
errorDetail: Spec.name + `.` + Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, Spec.commandSchema)) + `: the caller holds no role this command's rule admits`
|
|
115
|
+
};
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
let refuses = (history, command) => {
|
|
120
|
+
let who = caller.contents;
|
|
121
|
+
caller.contents = undefined;
|
|
122
|
+
refusal.contents = undefined;
|
|
123
|
+
let claim = Stdlib_Option.map(who, w => w.claim);
|
|
124
|
+
if (Stdlib_Option.mapOr(who, false, __x => refusedByRule(__x, command))) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
if (Object.keys(ownerFieldsByEventType).length === 0) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
let owners = Stdlib_Array.reduce(history, [], (owners, event) => {
|
|
131
|
+
let match = Message$ReventlessCore.splitMessage(Message$ReventlessCore.encode(event, Spec.historySchema));
|
|
132
|
+
let fields = ownerFieldsByEventType[match[0]];
|
|
133
|
+
if (fields !== undefined) {
|
|
134
|
+
return OwnerScope$Reventless.recordOwners(owners, fields, match[1]);
|
|
135
|
+
} else {
|
|
136
|
+
return owners;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
refusal.contents = Stdlib_Option.map(CommandTopic_Helpers$ReventlessCore.ownershipRefusal(OwnerScope$Reventless.decideActing(claim, owners), Spec.name, Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, Spec.commandSchema))), r => r.rejected);
|
|
140
|
+
return Stdlib_Option.isSome(refusal.contents);
|
|
141
|
+
};
|
|
142
|
+
let encRefusal = r => Object.fromEntries([
|
|
143
|
+
[
|
|
144
|
+
"errorCode",
|
|
145
|
+
r.errorCode
|
|
146
|
+
],
|
|
147
|
+
[
|
|
148
|
+
"errorDetail",
|
|
149
|
+
r.errorDetail
|
|
150
|
+
]
|
|
151
|
+
]);
|
|
152
|
+
let unexpectedRefusal = () => Stdlib_Option.map(refusal.contents, r => Outcome$ReventlessGwt.fail({
|
|
153
|
+
TAG: "ErrorMismatch",
|
|
154
|
+
expected: null,
|
|
155
|
+
actual: encRefusal(r),
|
|
156
|
+
actualEvents: []
|
|
157
|
+
}));
|
|
158
|
+
let thenRefused = (encEvents, decided, events) => {
|
|
159
|
+
let match = refusal.contents;
|
|
160
|
+
if (match !== undefined) {
|
|
161
|
+
return Outcome$ReventlessGwt.pass;
|
|
162
|
+
} else {
|
|
163
|
+
return Outcome$ReventlessGwt.fail({
|
|
164
|
+
TAG: "ErrorMismatch",
|
|
165
|
+
expected: Object.fromEntries([[
|
|
166
|
+
"errorCode",
|
|
167
|
+
"Forbidden"
|
|
168
|
+
]]),
|
|
169
|
+
actual: decided,
|
|
170
|
+
actualEvents: encEvents(events)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
return {
|
|
175
|
+
caller: caller,
|
|
176
|
+
refusal: refusal,
|
|
177
|
+
ownerFieldsByEventType: ownerFieldsByEventType,
|
|
178
|
+
asCaller: asCaller,
|
|
179
|
+
commandName: commandName,
|
|
180
|
+
refusedByRule: refusedByRule,
|
|
181
|
+
refuses: refuses,
|
|
182
|
+
encRefusal: encRefusal,
|
|
183
|
+
unexpectedRefusal: unexpectedRefusal,
|
|
184
|
+
thenRefused: thenRefused
|
|
185
|
+
};
|
|
186
|
+
}
|
|
14
187
|
|
|
15
188
|
function encodeTag(t) {
|
|
16
189
|
let d = {};
|
|
@@ -149,6 +322,7 @@ function Make(Spec) {
|
|
|
149
322
|
contents: []
|
|
150
323
|
};
|
|
151
324
|
let encEvent = e => Message$ReventlessCore.encode(e, Spec.eventSchema);
|
|
325
|
+
let encEvents = evs => evs.map(encEvent);
|
|
152
326
|
let encError = err => Message$ReventlessCore.encode(err, Spec.errorSchema);
|
|
153
327
|
let unexpectedError = events => {
|
|
154
328
|
let actual = Stdlib_Option.map(errors.contents[0], encError);
|
|
@@ -188,6 +362,81 @@ function Make(Spec) {
|
|
|
188
362
|
});
|
|
189
363
|
}
|
|
190
364
|
};
|
|
365
|
+
let owner$1 = (id, rolesOpt) => {
|
|
366
|
+
let roles = rolesOpt !== undefined ? rolesOpt : [];
|
|
367
|
+
return owner(id, roles);
|
|
368
|
+
};
|
|
369
|
+
let inRoles$1 = inRoles;
|
|
370
|
+
let Caller = {
|
|
371
|
+
owner: owner$1,
|
|
372
|
+
inRoles: inRoles$1,
|
|
373
|
+
operator: operator,
|
|
374
|
+
anonymous: anonymous
|
|
375
|
+
};
|
|
376
|
+
let name = Spec.name;
|
|
377
|
+
let historySchema = Spec.consumedEventSchema;
|
|
378
|
+
let commandSchema = Spec.commandSchema;
|
|
379
|
+
let commandAuthorization = Spec.commandAuthorization;
|
|
380
|
+
let caller = {
|
|
381
|
+
contents: undefined
|
|
382
|
+
};
|
|
383
|
+
let refusal = {
|
|
384
|
+
contents: undefined
|
|
385
|
+
};
|
|
386
|
+
let ownerFieldsByEventType = Owner$Reventless.fieldNamesByVariant(historySchema);
|
|
387
|
+
let asCaller = (history, who) => {
|
|
388
|
+
caller.contents = who;
|
|
389
|
+
return history;
|
|
390
|
+
};
|
|
391
|
+
let refuses = (history, command) => {
|
|
392
|
+
let who = caller.contents;
|
|
393
|
+
caller.contents = undefined;
|
|
394
|
+
refusal.contents = undefined;
|
|
395
|
+
let claim = Stdlib_Option.map(who, w => w.claim);
|
|
396
|
+
if (Stdlib_Option.mapOr(who, false, __x => {
|
|
397
|
+
if (Authorization$Reventless.admits(Authorization$Reventless.named(commandAuthorization(command)), __x.signedIn, role => __x.roles.includes(role))) {
|
|
398
|
+
return false;
|
|
399
|
+
} else {
|
|
400
|
+
refusal.contents = {
|
|
401
|
+
errorCode: "Forbidden",
|
|
402
|
+
errorDetail: name + `.` + Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, commandSchema)) + `: the caller holds no role this command's rule admits`
|
|
403
|
+
};
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
})) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
if (Object.keys(ownerFieldsByEventType).length === 0) {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
let owners = Stdlib_Array.reduce(history, [], (owners, event) => {
|
|
413
|
+
let match = Message$ReventlessCore.splitMessage(Message$ReventlessCore.encode(event, historySchema));
|
|
414
|
+
let fields = ownerFieldsByEventType[match[0]];
|
|
415
|
+
if (fields !== undefined) {
|
|
416
|
+
return OwnerScope$Reventless.recordOwners(owners, fields, match[1]);
|
|
417
|
+
} else {
|
|
418
|
+
return owners;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
refusal.contents = Stdlib_Option.map(CommandTopic_Helpers$ReventlessCore.ownershipRefusal(OwnerScope$Reventless.decideActing(claim, owners), name, Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, commandSchema))), r => r.rejected);
|
|
422
|
+
return Stdlib_Option.isSome(refusal.contents);
|
|
423
|
+
};
|
|
424
|
+
let encRefusal = r => Object.fromEntries([
|
|
425
|
+
[
|
|
426
|
+
"errorCode",
|
|
427
|
+
r.errorCode
|
|
428
|
+
],
|
|
429
|
+
[
|
|
430
|
+
"errorDetail",
|
|
431
|
+
r.errorDetail
|
|
432
|
+
]
|
|
433
|
+
]);
|
|
434
|
+
let unexpectedRefusal = () => Stdlib_Option.map(refusal.contents, r => Outcome$ReventlessGwt.fail({
|
|
435
|
+
TAG: "ErrorMismatch",
|
|
436
|
+
expected: null,
|
|
437
|
+
actual: encRefusal(r),
|
|
438
|
+
actualEvents: []
|
|
439
|
+
}));
|
|
191
440
|
let derivedCondition = {
|
|
192
441
|
contents: undefined
|
|
193
442
|
};
|
|
@@ -270,15 +519,41 @@ function Make(Spec) {
|
|
|
270
519
|
};
|
|
271
520
|
}
|
|
272
521
|
}
|
|
273
|
-
|
|
274
|
-
|
|
522
|
+
if (refuses(history, cmd)) {
|
|
523
|
+
return [];
|
|
524
|
+
}
|
|
525
|
+
let events = Behavior.decide(Stdlib_Array.reduce(history, Behavior.initialState, Behavior.evolve), cmd);
|
|
275
526
|
if (events.TAG === "Ok") {
|
|
276
527
|
return events._0;
|
|
277
528
|
}
|
|
278
529
|
errors.contents = [events._0];
|
|
279
530
|
return [];
|
|
280
531
|
};
|
|
281
|
-
let checkAppendCondition = () =>
|
|
532
|
+
let checkAppendCondition = () => {
|
|
533
|
+
let m = appendConditionFailure.contents;
|
|
534
|
+
if (m !== undefined) {
|
|
535
|
+
return Outcome$ReventlessGwt.fail(m);
|
|
536
|
+
} else {
|
|
537
|
+
return unexpectedRefusal();
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
let thenRefused = events => {
|
|
541
|
+
let decided = Stdlib_Option.map(errors.contents[0], encError);
|
|
542
|
+
let match = refusal.contents;
|
|
543
|
+
if (match !== undefined) {
|
|
544
|
+
return Outcome$ReventlessGwt.pass;
|
|
545
|
+
} else {
|
|
546
|
+
return Outcome$ReventlessGwt.fail({
|
|
547
|
+
TAG: "ErrorMismatch",
|
|
548
|
+
expected: Object.fromEntries([[
|
|
549
|
+
"errorCode",
|
|
550
|
+
"Forbidden"
|
|
551
|
+
]]),
|
|
552
|
+
actual: decided,
|
|
553
|
+
actualEvents: encEvents(events)
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
};
|
|
282
557
|
let thenEvents = (events, expectedEvents) => {
|
|
283
558
|
let o = checkAppendCondition();
|
|
284
559
|
if (o !== undefined) {
|
|
@@ -383,6 +658,8 @@ function Make(Spec) {
|
|
|
383
658
|
todo: JestBind$ReventlessGwt.todo,
|
|
384
659
|
test: test,
|
|
385
660
|
givenEvents: givenEvents,
|
|
661
|
+
Caller: Caller,
|
|
662
|
+
asCaller: asCaller,
|
|
386
663
|
whenCmd: whenCmd,
|
|
387
664
|
thenEvent: thenEvent,
|
|
388
665
|
thenEvents: thenEvents,
|
|
@@ -390,6 +667,7 @@ function Make(Spec) {
|
|
|
390
667
|
thenEventWithError: thenEventWithError,
|
|
391
668
|
thenEventsWithError: thenEventsWithError,
|
|
392
669
|
thenError: thenError,
|
|
670
|
+
thenRefused: thenRefused,
|
|
393
671
|
thenAppendsConditionedOn: thenAppendsConditionedOn,
|
|
394
672
|
thenAppendsConditionedOnExactly: thenAppendsConditionedOnExactly
|
|
395
673
|
};
|
|
@@ -403,6 +681,7 @@ function MakeFromAggregate(Spec) {
|
|
|
403
681
|
contents: []
|
|
404
682
|
};
|
|
405
683
|
let encEvent = e => Message$ReventlessCore.encode(e, Spec.eventSchema);
|
|
684
|
+
let encEvents = evs => evs.map(encEvent);
|
|
406
685
|
let encError = err => Message$ReventlessCore.encode(err, Spec.errorSchema);
|
|
407
686
|
let unexpectedError = events => {
|
|
408
687
|
let actual = Stdlib_Option.map(errors.contents[0], encError);
|
|
@@ -413,32 +692,6 @@ function MakeFromAggregate(Spec) {
|
|
|
413
692
|
actualEvents: events.map(encEvent)
|
|
414
693
|
});
|
|
415
694
|
};
|
|
416
|
-
let compareEvents = (events, expectedEvents) => {
|
|
417
|
-
if (errors.contents.length !== 0) {
|
|
418
|
-
return unexpectedError(events);
|
|
419
|
-
} else if (Primitive_object.equal(events.map(encEvent), expectedEvents.map(encEvent))) {
|
|
420
|
-
return Outcome$ReventlessGwt.pass;
|
|
421
|
-
} else {
|
|
422
|
-
return Outcome$ReventlessGwt.fail({
|
|
423
|
-
TAG: "EventsMismatch",
|
|
424
|
-
expected: expectedEvents.map(encEvent),
|
|
425
|
-
actual: events.map(encEvent)
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
let compareEventsWith = (events, expectedEvents, cmp) => {
|
|
430
|
-
if (errors.contents.length !== 0) {
|
|
431
|
-
return unexpectedError(events);
|
|
432
|
-
} else if (events.length === expectedEvents.length && Stdlib_Array.zip(events, expectedEvents).every(param => cmp(param[0], param[1]))) {
|
|
433
|
-
return Outcome$ReventlessGwt.pass;
|
|
434
|
-
} else {
|
|
435
|
-
return Outcome$ReventlessGwt.fail({
|
|
436
|
-
TAG: "EventsMismatch",
|
|
437
|
-
expected: expectedEvents.map(encEvent),
|
|
438
|
-
actual: events.map(encEvent)
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
695
|
let compareNoEvent = events => {
|
|
443
696
|
if (errors.contents.length !== 0) {
|
|
444
697
|
return unexpectedError(events);
|
|
@@ -480,41 +733,176 @@ function MakeFromAggregate(Spec) {
|
|
|
480
733
|
});
|
|
481
734
|
}
|
|
482
735
|
};
|
|
736
|
+
let owner$1 = (id, rolesOpt) => {
|
|
737
|
+
let roles = rolesOpt !== undefined ? rolesOpt : [];
|
|
738
|
+
return owner(id, roles);
|
|
739
|
+
};
|
|
740
|
+
let inRoles$1 = inRoles;
|
|
741
|
+
let Caller = {
|
|
742
|
+
owner: owner$1,
|
|
743
|
+
inRoles: inRoles$1,
|
|
744
|
+
operator: operator,
|
|
745
|
+
anonymous: anonymous
|
|
746
|
+
};
|
|
747
|
+
let name = Spec.name;
|
|
748
|
+
let historySchema = Spec.eventSchema;
|
|
749
|
+
let commandSchema = Spec.commandSchema;
|
|
750
|
+
let commandAuthorization = Spec.commandAuthorization;
|
|
751
|
+
let caller = {
|
|
752
|
+
contents: undefined
|
|
753
|
+
};
|
|
754
|
+
let refusal = {
|
|
755
|
+
contents: undefined
|
|
756
|
+
};
|
|
757
|
+
let ownerFieldsByEventType = Owner$Reventless.fieldNamesByVariant(historySchema);
|
|
758
|
+
let asCaller = (history, who) => {
|
|
759
|
+
caller.contents = who;
|
|
760
|
+
return history;
|
|
761
|
+
};
|
|
762
|
+
let refuses = (history, command) => {
|
|
763
|
+
let who = caller.contents;
|
|
764
|
+
caller.contents = undefined;
|
|
765
|
+
refusal.contents = undefined;
|
|
766
|
+
let claim = Stdlib_Option.map(who, w => w.claim);
|
|
767
|
+
if (Stdlib_Option.mapOr(who, false, __x => {
|
|
768
|
+
if (Authorization$Reventless.admits(Authorization$Reventless.named(commandAuthorization(command)), __x.signedIn, role => __x.roles.includes(role))) {
|
|
769
|
+
return false;
|
|
770
|
+
} else {
|
|
771
|
+
refusal.contents = {
|
|
772
|
+
errorCode: "Forbidden",
|
|
773
|
+
errorDetail: name + `.` + Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, commandSchema)) + `: the caller holds no role this command's rule admits`
|
|
774
|
+
};
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
})) {
|
|
778
|
+
return true;
|
|
779
|
+
}
|
|
780
|
+
if (Object.keys(ownerFieldsByEventType).length === 0) {
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
let owners = Stdlib_Array.reduce(history, [], (owners, event) => {
|
|
784
|
+
let match = Message$ReventlessCore.splitMessage(Message$ReventlessCore.encode(event, historySchema));
|
|
785
|
+
let fields = ownerFieldsByEventType[match[0]];
|
|
786
|
+
if (fields !== undefined) {
|
|
787
|
+
return OwnerScope$Reventless.recordOwners(owners, fields, match[1]);
|
|
788
|
+
} else {
|
|
789
|
+
return owners;
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
refusal.contents = Stdlib_Option.map(CommandTopic_Helpers$ReventlessCore.ownershipRefusal(OwnerScope$Reventless.decideActing(claim, owners), name, Message$ReventlessCore.variantNameOfJson(Message$ReventlessCore.encode(command, commandSchema))), r => r.rejected);
|
|
793
|
+
return Stdlib_Option.isSome(refusal.contents);
|
|
794
|
+
};
|
|
795
|
+
let encRefusal = r => Object.fromEntries([
|
|
796
|
+
[
|
|
797
|
+
"errorCode",
|
|
798
|
+
r.errorCode
|
|
799
|
+
],
|
|
800
|
+
[
|
|
801
|
+
"errorDetail",
|
|
802
|
+
r.errorDetail
|
|
803
|
+
]
|
|
804
|
+
]);
|
|
805
|
+
let unexpectedRefusal = () => Stdlib_Option.map(refusal.contents, r => Outcome$ReventlessGwt.fail({
|
|
806
|
+
TAG: "ErrorMismatch",
|
|
807
|
+
expected: null,
|
|
808
|
+
actual: encRefusal(r),
|
|
809
|
+
actualEvents: []
|
|
810
|
+
}));
|
|
483
811
|
let givenEvents = events => events;
|
|
484
812
|
let whenCmd = (history, cmd) => {
|
|
485
813
|
errors.contents = [];
|
|
486
|
-
|
|
487
|
-
|
|
814
|
+
if (refuses(history, cmd)) {
|
|
815
|
+
return [];
|
|
816
|
+
}
|
|
817
|
+
let events = Behavior.decide(Stdlib_Array.reduce(history, Behavior.initialState, Behavior.evolve), cmd);
|
|
488
818
|
if (events.TAG === "Ok") {
|
|
489
819
|
return events._0;
|
|
490
820
|
}
|
|
491
821
|
errors.contents = [events._0];
|
|
492
822
|
return [];
|
|
493
823
|
};
|
|
494
|
-
let
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
824
|
+
let unlessRefused = (events, compare) => {
|
|
825
|
+
let o = unexpectedRefusal();
|
|
826
|
+
if (o !== undefined) {
|
|
827
|
+
return o;
|
|
828
|
+
} else {
|
|
829
|
+
return compare(events);
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
let thenEvents = (events, expected) => unlessRefused(events, __x => {
|
|
833
|
+
if (errors.contents.length !== 0) {
|
|
834
|
+
return unexpectedError(__x);
|
|
835
|
+
} else if (Primitive_object.equal(__x.map(encEvent), expected.map(encEvent))) {
|
|
836
|
+
return Outcome$ReventlessGwt.pass;
|
|
837
|
+
} else {
|
|
838
|
+
return Outcome$ReventlessGwt.fail({
|
|
839
|
+
TAG: "EventsMismatch",
|
|
840
|
+
expected: expected.map(encEvent),
|
|
841
|
+
actual: __x.map(encEvent)
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
let thenCompareEvents = (events, expected, cmp) => unlessRefused(events, __x => {
|
|
846
|
+
if (errors.contents.length !== 0) {
|
|
847
|
+
return unexpectedError(__x);
|
|
848
|
+
} else if (__x.length === expected.length && Stdlib_Array.zip(__x, expected).every(param => cmp(param[0], param[1]))) {
|
|
849
|
+
return Outcome$ReventlessGwt.pass;
|
|
850
|
+
} else {
|
|
851
|
+
return Outcome$ReventlessGwt.fail({
|
|
852
|
+
TAG: "EventsMismatch",
|
|
853
|
+
expected: expected.map(encEvent),
|
|
854
|
+
actual: __x.map(encEvent)
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
let thenEvent = (events, expectedEvent) => thenEvents(events, [expectedEvent]);
|
|
859
|
+
let thenCompareEvent = (events, expectedEvent, cmp) => thenCompareEvents(events, [expectedEvent], cmp);
|
|
860
|
+
let thenNoEvent = events => unlessRefused(events, compareNoEvent);
|
|
861
|
+
let thenError = (events, expectedError) => unlessRefused(events, __x => matchesError(__x, [], expectedError));
|
|
862
|
+
let thenEventWithError = (events, expectedEvent, expectedError) => unlessRefused(events, __x => matchesError(__x, [expectedEvent], expectedError));
|
|
863
|
+
let thenEventsWithError = (events, expectedEvents, expectedError) => unlessRefused(events, __x => matchesError(__x, expectedEvents, expectedError));
|
|
864
|
+
let thenRefused = events => {
|
|
865
|
+
let decided = Stdlib_Option.map(errors.contents[0], encError);
|
|
866
|
+
let match = refusal.contents;
|
|
867
|
+
if (match !== undefined) {
|
|
868
|
+
return Outcome$ReventlessGwt.pass;
|
|
869
|
+
} else {
|
|
870
|
+
return Outcome$ReventlessGwt.fail({
|
|
871
|
+
TAG: "ErrorMismatch",
|
|
872
|
+
expected: Object.fromEntries([[
|
|
873
|
+
"errorCode",
|
|
874
|
+
"Forbidden"
|
|
875
|
+
]]),
|
|
876
|
+
actual: decided,
|
|
877
|
+
actualEvents: encEvents(events)
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
};
|
|
499
881
|
return {
|
|
500
882
|
Spec: Spec,
|
|
501
883
|
describe: JestBind$ReventlessGwt.describe,
|
|
502
884
|
test: test,
|
|
503
885
|
givenEvents: givenEvents,
|
|
886
|
+
Caller: Caller,
|
|
887
|
+
asCaller: asCaller,
|
|
504
888
|
whenCmd: whenCmd,
|
|
505
889
|
thenEvent: thenEvent,
|
|
506
890
|
thenCompareEvent: thenCompareEvent,
|
|
507
|
-
thenNoEvent:
|
|
891
|
+
thenNoEvent: thenNoEvent,
|
|
508
892
|
thenEventWithError: thenEventWithError,
|
|
509
|
-
thenEvents:
|
|
510
|
-
thenCompareEvents:
|
|
893
|
+
thenEvents: thenEvents,
|
|
894
|
+
thenCompareEvents: thenCompareEvents,
|
|
511
895
|
thenEventsWithError: thenEventsWithError,
|
|
512
|
-
thenError: thenError
|
|
896
|
+
thenError: thenError,
|
|
897
|
+
thenRefused: thenRefused
|
|
513
898
|
};
|
|
514
899
|
};
|
|
515
900
|
}
|
|
516
901
|
|
|
517
902
|
export {
|
|
903
|
+
Caller,
|
|
904
|
+
Callers,
|
|
905
|
+
Acting,
|
|
518
906
|
encodeTag,
|
|
519
907
|
encodeQueryItem,
|
|
520
908
|
encodeQuery,
|
|
@@ -523,4 +911,4 @@ export {
|
|
|
523
911
|
Make,
|
|
524
912
|
MakeFromAggregate,
|
|
525
913
|
}
|
|
526
|
-
/*
|
|
914
|
+
/* Owner-Reventless Not a pure module */
|
|
@@ -21,3 +21,8 @@ type error = CategoryAlreadyExists
|
|
|
21
21
|
|
|
22
22
|
@schema
|
|
23
23
|
type event = CategoryAdded({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
24
|
+
|
|
25
|
+
// What `@@reventless.spec` injects into a real spec: the default rule.
|
|
26
|
+
type role = Reventless.Role.name
|
|
27
|
+
let commandAuthorization = (_: command): Reventless.Authorization.rule<role> =>
|
|
28
|
+
Reventless.Authorization.AllowAuthenticated
|
|
@@ -22,6 +22,10 @@ let eventSchema = Sury.$schema(s => ({
|
|
|
22
22
|
name: s.m(Sury.string)
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
function commandAuthorization(param) {
|
|
26
|
+
return "AllowAuthenticated";
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
let name = "ExternalAddCategory";
|
|
26
30
|
|
|
27
31
|
export {
|
|
@@ -30,5 +34,6 @@ export {
|
|
|
30
34
|
commandSchema,
|
|
31
35
|
errorSchema,
|
|
32
36
|
eventSchema,
|
|
37
|
+
commandAuthorization,
|
|
33
38
|
}
|
|
34
39
|
/* consumedEventSchema Not a pure module */
|
|
@@ -17,3 +17,8 @@ type error = CategoryAlreadyExists
|
|
|
17
17
|
|
|
18
18
|
@schema
|
|
19
19
|
type event = CategoryAdded({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
20
|
+
|
|
21
|
+
// What `@@reventless.spec` injects into a real spec: the default rule.
|
|
22
|
+
type role = Reventless.Role.name
|
|
23
|
+
let commandAuthorization = (_: command): Reventless.Authorization.rule<role> =>
|
|
24
|
+
Reventless.Authorization.AllowAuthenticated
|
|
@@ -19,6 +19,10 @@ let eventSchema = Sury.$schema(s => ({
|
|
|
19
19
|
name: s.m(Sury.string)
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
+
function commandAuthorization(param) {
|
|
23
|
+
return "AllowAuthenticated";
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
let name = "WithFixtures";
|
|
23
27
|
|
|
24
28
|
export {
|
|
@@ -27,5 +31,6 @@ export {
|
|
|
27
31
|
commandSchema,
|
|
28
32
|
errorSchema,
|
|
29
33
|
eventSchema,
|
|
34
|
+
commandAuthorization,
|
|
30
35
|
}
|
|
31
36
|
/* consumedEventSchema Not a pure module */
|
|
@@ -18,3 +18,8 @@ type error = CategoryAlreadyExists
|
|
|
18
18
|
|
|
19
19
|
@schema
|
|
20
20
|
type event = CategoryAdded({categoryId: @s.matches(Reventless.DcbTag.string) string, name: string})
|
|
21
|
+
|
|
22
|
+
// What `@@reventless.spec` injects into a real spec: the default rule.
|
|
23
|
+
type role = Reventless.Role.name
|
|
24
|
+
let commandAuthorization = (_: command): Reventless.Authorization.rule<role> =>
|
|
25
|
+
Reventless.Authorization.AllowAuthenticated
|
|
@@ -19,6 +19,10 @@ let eventSchema = Sury.$schema(s => ({
|
|
|
19
19
|
name: s.m(Sury.string)
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
+
function commandAuthorization(param) {
|
|
23
|
+
return "AllowAuthenticated";
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
let name = "WithManualOpen";
|
|
23
27
|
|
|
24
28
|
export {
|
|
@@ -27,5 +31,6 @@ export {
|
|
|
27
31
|
commandSchema,
|
|
28
32
|
errorSchema,
|
|
29
33
|
eventSchema,
|
|
34
|
+
commandAuthorization,
|
|
30
35
|
}
|
|
31
36
|
/* consumedEventSchema Not a pure module */
|