@reventlessdev/reventless-core 3.0.0-alpha.290 → 3.0.0-alpha.292
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 +18 -0
- package/package.json +7 -7
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +25 -46
- package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +27 -46
- package/src/components/AutomationSlice/AutomationSlice_Route.res +50 -0
- package/src/components/AutomationSlice/AutomationSlice_Route.res.mjs +39 -0
- package/src/components/Dcb/Dcb_Builder.res +27 -43
- package/src/components/Dcb/Dcb_Builder.res.mjs +10 -21
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +2 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res.mjs +2 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +118 -160
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +121 -130
- package/src/plugin/component/Plugin_Helpers.res +11 -3
- package/src/plugin/component/Plugin_Structure.res +22 -9
- package/src/plugin/component/Plugin_Structure.res.mjs +11 -1
- package/tests/plugin/PluginStructureAccessTest.res +56 -0
- package/tests/plugin/PluginStructureAccessTest.res.mjs +87 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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.292 (2026-10-10)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* an inbound translation is gated alike on every door, and encodes its commands by schema ([6ec3ce6](https://github.com/ReventlessDev/reventless-core/commit/6ec3ce672e53e2c162c64a90d103be691391be5e))
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **gwt:** automation and translation tests take the slice as written, and the sidecars read them ([93a0919](https://github.com/ReventlessDev/reventless-core/commit/93a0919a0d66730cf9bf89144ad8fba3cb025332))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# 3.0.0-alpha.291 (2026-10-08)
|
|
17
|
+
|
|
18
|
+
**Note:** Version bump only for package @reventlessdev/reventless-core
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
# 3.0.0-alpha.290 (2026-10-08)
|
|
7
25
|
|
|
8
26
|
**Note:** Version bump only for package @reventlessdev/reventless-core
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.292",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"sury": "11.0.0",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.34",
|
|
32
|
+
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.22",
|
|
32
33
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.15",
|
|
33
|
-
"@reventlessdev/rescript-
|
|
34
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.22",
|
|
34
35
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.11",
|
|
35
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.21",
|
|
36
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
|
|
37
36
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
|
|
38
|
-
"@reventlessdev/rescript-
|
|
37
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
|
|
38
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.189",
|
|
39
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.39",
|
|
40
|
-
"@reventlessdev/reventless-
|
|
41
|
-
"@reventlessdev/
|
|
40
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.161",
|
|
41
|
+
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.23"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.1",
|
|
@@ -166,50 +166,33 @@ module Make = (
|
|
|
166
166
|
None
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
type dispatch = {
|
|
171
|
-
sourceName: string,
|
|
172
|
-
handle: (JSON.t, ~sourceId: string, Reventless.AutomationSlice.context) => unit,
|
|
173
|
-
}
|
|
169
|
+
module Route = AutomationSlice_Route.Make(Spec, Automation)
|
|
174
170
|
|
|
175
|
-
let
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
| None =>
|
|
188
|
-
let row: todoRow = {
|
|
189
|
-
item: item->Reventless.Util_Sury.toJson(Spec.todoItemSchema),
|
|
190
|
-
status: Pending,
|
|
191
|
-
createdAt: now(),
|
|
192
|
-
retryCount: 0,
|
|
193
|
-
maxRetries: Spec.maxRetries,
|
|
194
|
-
}
|
|
195
|
-
todoItems->Dict.set(id, row)
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
// Resolve — mark a pending item as completed.
|
|
199
|
-
switch M.resolve(event) {
|
|
200
|
-
| Some(id) =>
|
|
201
|
-
todoItems
|
|
202
|
-
->Dict.get(id)
|
|
203
|
-
->Option.forEach(row => {
|
|
204
|
-
todoItems->Dict.set(id, {...row, status: Completed, completedAt: now()})
|
|
205
|
-
})
|
|
206
|
-
| None => ()
|
|
171
|
+
let apply = ({collected, resolved}: AutomationSlice_Route.routed<Spec.todoItem>) => {
|
|
172
|
+
// Collect — append new items keyed by ID; first writer wins (idempotent).
|
|
173
|
+
collected->Array.forEach(((id, item)) => {
|
|
174
|
+
switch todoItems->Dict.get(id) {
|
|
175
|
+
| Some(_) => () // Already exists — skip
|
|
176
|
+
| None =>
|
|
177
|
+
let row: todoRow = {
|
|
178
|
+
item: item->Reventless.Util_Sury.toJson(Spec.todoItemSchema),
|
|
179
|
+
status: Pending,
|
|
180
|
+
createdAt: now(),
|
|
181
|
+
retryCount: 0,
|
|
182
|
+
maxRetries: Spec.maxRetries,
|
|
207
183
|
}
|
|
208
|
-
|
|
184
|
+
todoItems->Dict.set(id, row)
|
|
209
185
|
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
186
|
+
})
|
|
187
|
+
// Resolve — mark a pending item as completed.
|
|
188
|
+
resolved->Option.forEach(id =>
|
|
189
|
+
todoItems
|
|
190
|
+
->Dict.get(id)
|
|
191
|
+
->Option.forEach(row => {
|
|
192
|
+
todoItems->Dict.set(id, {...row, status: Completed, completedAt: now()})
|
|
193
|
+
})
|
|
194
|
+
)
|
|
195
|
+
}
|
|
213
196
|
|
|
214
197
|
let phase1 = (events: array<JSON.t>, ctx: Reventless.AutomationSlice.context) => {
|
|
215
198
|
events->Array.forEach(json => {
|
|
@@ -223,11 +206,7 @@ module Make = (
|
|
|
223
206
|
| None => json
|
|
224
207
|
}
|
|
225
208
|
// Multiple mappings can share a sourceName; all matching dispatches run.
|
|
226
|
-
|
|
227
|
-
if d.sourceName == sourceName {
|
|
228
|
-
d.handle(eventPayload, ~sourceId=context.id, ctx)
|
|
229
|
-
}
|
|
230
|
-
})
|
|
209
|
+
Route.route(eventPayload, ~sourceName, ~sourceId=context.id, ctx)->Array.forEach(apply)
|
|
231
210
|
})
|
|
232
211
|
}
|
|
233
212
|
|
|
@@ -8,11 +8,11 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
9
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
10
10
|
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
11
|
-
import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
|
|
12
11
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
13
12
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
14
13
|
import * as Message$ReventlessCore from "../../Message.res.mjs";
|
|
15
14
|
import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
|
|
15
|
+
import * as AutomationSlice_Route$ReventlessCore from "./AutomationSlice_Route.res.mjs";
|
|
16
16
|
|
|
17
17
|
let todoStatusSchema = Sury.union([
|
|
18
18
|
Sury.literal("Pending"),
|
|
@@ -55,59 +55,40 @@ function Make(Spec) {
|
|
|
55
55
|
newrecord.status = retryCount >= Spec.maxRetries ? "Abandoned" : "Failed";
|
|
56
56
|
return newrecord;
|
|
57
57
|
};
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let
|
|
62
|
-
let
|
|
63
|
-
if (
|
|
58
|
+
let Route = AutomationSlice_Route$ReventlessCore.Make(Spec)(Automation);
|
|
59
|
+
let apply = param => {
|
|
60
|
+
param.collected.forEach(param => {
|
|
61
|
+
let id = param[0];
|
|
62
|
+
let match = todoItems[id];
|
|
63
|
+
if (match !== undefined) {
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
let
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
let id = M.resolve(event$1);
|
|
86
|
-
if (id !== undefined) {
|
|
87
|
-
return Stdlib_Option.forEach(todoItems[id], row => {
|
|
88
|
-
let newrecord = {...row};
|
|
89
|
-
newrecord.completedAt = new Date().toISOString();
|
|
90
|
-
newrecord.status = "Completed";
|
|
91
|
-
todoItems[id] = newrecord;
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
return {
|
|
96
|
-
sourceName: M.sourceName,
|
|
97
|
-
handle: handle
|
|
98
|
-
};
|
|
99
|
-
});
|
|
66
|
+
let row_item = Util_Sury$Reventless.toJson(param[1], Spec.todoItemSchema);
|
|
67
|
+
let row_createdAt = new Date().toISOString();
|
|
68
|
+
let row_maxRetries = Spec.maxRetries;
|
|
69
|
+
let row = {
|
|
70
|
+
item: row_item,
|
|
71
|
+
status: "Pending",
|
|
72
|
+
createdAt: row_createdAt,
|
|
73
|
+
retryCount: 0,
|
|
74
|
+
maxRetries: row_maxRetries
|
|
75
|
+
};
|
|
76
|
+
todoItems[id] = row;
|
|
77
|
+
});
|
|
78
|
+
Stdlib_Option.forEach(param.resolved, id => Stdlib_Option.forEach(todoItems[id], row => {
|
|
79
|
+
let newrecord = {...row};
|
|
80
|
+
newrecord.completedAt = new Date().toISOString();
|
|
81
|
+
newrecord.status = "Completed";
|
|
82
|
+
todoItems[id] = newrecord;
|
|
83
|
+
}));
|
|
84
|
+
};
|
|
100
85
|
let phase1 = (events, ctx) => {
|
|
101
86
|
events.forEach(json => {
|
|
102
87
|
let context = Message$ReventlessCore.decode(json, Message$Reventless.contextSchema);
|
|
103
88
|
let sourceName = context.meta.service;
|
|
104
89
|
let dict = Stdlib_JSON.Decode.object(json);
|
|
105
90
|
let eventPayload = dict !== undefined ? Stdlib_Option.getOr(dict["event"], json) : json;
|
|
106
|
-
|
|
107
|
-
if (d.sourceName === sourceName) {
|
|
108
|
-
return d.handle(eventPayload, context.id, ctx);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
91
|
+
Route.route(eventPayload, sourceName, context.id, ctx).forEach(apply);
|
|
111
92
|
});
|
|
112
93
|
};
|
|
113
94
|
let phase2 = async publishJsons => {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Decode one source event and run its mappings' `collect`/`resolve`, with no
|
|
2
|
+
// side effects. Shared by the runtime callback, which applies the result to its
|
|
3
|
+
// to-do list, and by the GWT sweep, which must route exactly as the runtime does.
|
|
4
|
+
|
|
5
|
+
/** What one mapping made of one event. */
|
|
6
|
+
type routed<'item> = {
|
|
7
|
+
collected: array<(string, 'item)>,
|
|
8
|
+
resolved: option<string>,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module Make = (
|
|
12
|
+
Spec: Reventless.AutomationSlice.Spec,
|
|
13
|
+
Automation: Reventless.AutomationSlice.Automation with module Spec := Spec,
|
|
14
|
+
) => {
|
|
15
|
+
type dispatch = {
|
|
16
|
+
sourceName: string,
|
|
17
|
+
handle: (
|
|
18
|
+
JSON.t,
|
|
19
|
+
~sourceId: string,
|
|
20
|
+
Reventless.AutomationSlice.context,
|
|
21
|
+
) => option<routed<Spec.todoItem>>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Decoders are compiled once, per mapping.
|
|
25
|
+
let dispatches: array<
|
|
26
|
+
dispatch,
|
|
27
|
+
> = Automation.mappings->Array.map((module(M: Automation.Mapping)) => {
|
|
28
|
+
let decoder = Reventless.DcbDecode.makeDecoder(M.sourceEventSchema)
|
|
29
|
+
let handle = (json: JSON.t, ~sourceId: string, ctx: Reventless.AutomationSlice.context) => {
|
|
30
|
+
let (eventType, dataDict) = json->Message.splitMessage
|
|
31
|
+
decoder.decode(~eventType, ~data=dataDict)->Option.map(event => {
|
|
32
|
+
collected: M.collect(event, ~sourceId, ctx),
|
|
33
|
+
resolved: M.resolve(event),
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
{sourceName: M.sourceName, handle}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/** Every mapping registered for `sourceName` that understood the payload, in
|
|
40
|
+
registration order. `payload` is the inner event, not the envelope. */
|
|
41
|
+
let route = (
|
|
42
|
+
payload: JSON.t,
|
|
43
|
+
~sourceName: string,
|
|
44
|
+
~sourceId: string,
|
|
45
|
+
ctx: Reventless.AutomationSlice.context,
|
|
46
|
+
): array<routed<Spec.todoItem>> =>
|
|
47
|
+
dispatches->Array.filterMap(d =>
|
|
48
|
+
d.sourceName == sourceName ? d.handle(payload, ~sourceId, ctx) : None
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
|
|
6
|
+
import * as Message$ReventlessCore from "../../Message.res.mjs";
|
|
7
|
+
|
|
8
|
+
function Make(Spec) {
|
|
9
|
+
return Automation => {
|
|
10
|
+
let dispatches = Automation.mappings.map(M => {
|
|
11
|
+
let decoder = DcbDecode$Reventless.makeDecoder(M.sourceEventSchema);
|
|
12
|
+
let handle = (json, sourceId, ctx) => {
|
|
13
|
+
let match = Message$ReventlessCore.splitMessage(json);
|
|
14
|
+
return Stdlib_Option.map(decoder.decode(match[0], match[1]), event => ({
|
|
15
|
+
collected: M.collect(event, sourceId, ctx),
|
|
16
|
+
resolved: M.resolve(event)
|
|
17
|
+
}));
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
sourceName: M.sourceName,
|
|
21
|
+
handle: handle
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
let route = (payload, sourceName, sourceId, ctx) => Stdlib_Array.filterMap(dispatches, d => {
|
|
25
|
+
if (d.sourceName === sourceName) {
|
|
26
|
+
return d.handle(payload, sourceId, ctx);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
dispatches: dispatches,
|
|
31
|
+
route: route
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
Make,
|
|
38
|
+
}
|
|
39
|
+
/* DcbDecode-Reventless Not a pure module */
|
|
@@ -89,6 +89,16 @@ let emptyResult: dcbResult = {
|
|
|
89
89
|
eventLogEntries: [],
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// An inbound slice's door rule, read off its spec.
|
|
93
|
+
let inboundDoorPermission = (
|
|
94
|
+
commandSchema: S.t<'command>,
|
|
95
|
+
commandAuthorization: 'command => Reventless.Authorization.rule<'role>,
|
|
96
|
+
) =>
|
|
97
|
+
Plugin_Structure.inboundDoorPermission(
|
|
98
|
+
~commandSchema=commandSchema->S.castToUnknown,
|
|
99
|
+
~commandAuthorization=cmd => cmd->commandAuthorization->Reventless.Authorization.named,
|
|
100
|
+
)
|
|
101
|
+
|
|
92
102
|
module Make = (
|
|
93
103
|
DcbEventLogStorage: DcbEventLog_Adapter.Storage,
|
|
94
104
|
DcbEventTopicPublisher: EventTopic_Adapter.Publisher,
|
|
@@ -749,6 +759,10 @@ module Make = (
|
|
|
749
759
|
registerResolver(
|
|
750
760
|
~fieldName,
|
|
751
761
|
~externalInputSchema=ITS.Spec.externalInputSchema->S.castToUnknown,
|
|
762
|
+
~permission=?inboundDoorPermission(
|
|
763
|
+
ITS.Spec.commandSchema,
|
|
764
|
+
ITS.Spec.commandAuthorization,
|
|
765
|
+
),
|
|
752
766
|
)
|
|
753
767
|
| None => ()
|
|
754
768
|
}
|
|
@@ -865,16 +879,14 @@ module Make = (
|
|
|
865
879
|
let raw: dict<JSON.t> = event->Obj.magic
|
|
866
880
|
switch raw->Dict.get("__inboundTranslation") {
|
|
867
881
|
| Some(_) =>
|
|
868
|
-
let
|
|
869
|
-
|
|
870
|
-
->Dict.get("fieldName")
|
|
871
|
-
->Option.flatMap(JSON.Decode.string)
|
|
872
|
-
->Option.getOr("")
|
|
873
|
-
let args = raw->Dict.get("arguments")->Option.getOr(JSON.Encode.null)
|
|
874
|
-
switch receivers->Dict.get(fieldName) {
|
|
882
|
+
let invocation: InboundTranslationSlice_Callback.doorInvocation = event->Obj.magic
|
|
883
|
+
switch receivers->Dict.get(invocation.fieldName) {
|
|
875
884
|
| Some(receiveFn) =>
|
|
876
885
|
Effect.promise(async () => {
|
|
877
|
-
let result = await receiveFn(
|
|
886
|
+
let result = await receiveFn(
|
|
887
|
+
invocation.arguments,
|
|
888
|
+
~caller=InboundTranslationSlice_Callback.doorCaller(invocation.identity),
|
|
889
|
+
)
|
|
878
890
|
let response =
|
|
879
891
|
result
|
|
880
892
|
->InboundTranslationSlice_Callback.receiveResultToOutcome
|
|
@@ -1034,33 +1046,9 @@ module Make = (
|
|
|
1034
1046
|
})
|
|
1035
1047
|
}
|
|
1036
1048
|
|
|
1037
|
-
// DCB-specific API schema entries
|
|
1038
|
-
//
|
|
1039
|
-
//
|
|
1040
|
-
// command type may declare multiple constructors with different
|
|
1041
|
-
// authorization rules. The Cognito group directive operates at field
|
|
1042
|
-
// granularity, so we read the auth for the first constructor (matching
|
|
1043
|
-
// the existing dcbTags convention at line 549 above). When all
|
|
1044
|
-
// constructors share the file-level default, this is exact; when they
|
|
1045
|
-
// differ, resolver-level enforcement still fires inside the per-slice
|
|
1046
|
-
// handler (the file-level rule applied here is the least-restrictive
|
|
1047
|
-
// bound).
|
|
1048
|
-
let permissionForFirstConstructor = (
|
|
1049
|
-
~commandSchema: S.t<unknown>,
|
|
1050
|
-
~commandAuthorization: unknown => Reventless.Authorization.permission,
|
|
1051
|
-
): option<Reventless.Authorization.permission> => {
|
|
1052
|
-
let names = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
|
|
1053
|
-
switch names->Array.get(0) {
|
|
1054
|
-
| None => None
|
|
1055
|
-
| Some(first) =>
|
|
1056
|
-
let hasPayload = Reventless.DcbTag.isVariantPayloadBearing(
|
|
1057
|
-
commandSchema->Obj.magic,
|
|
1058
|
-
first,
|
|
1059
|
-
)
|
|
1060
|
-
let syntheticCmd: unknown = hasPayload ? {"TAG": first}->Obj.magic : first->Obj.magic
|
|
1061
|
-
Some(commandAuthorization(syntheticCmd))
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1049
|
+
// DCB-specific API schema entries. A state change slice has one field per
|
|
1050
|
+
// constructor, each gated by its own rule; an inbound slice has one field,
|
|
1051
|
+
// gated by `inboundDoorPermission`, and checks each command in `receive`.
|
|
1064
1052
|
let mutationEntriesFromSlices =
|
|
1065
1053
|
stateChangeSlices->Array.filterMap((module(S: StateChangeSlice.T)) => {
|
|
1066
1054
|
let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
|
|
@@ -1108,14 +1096,10 @@ module Make = (
|
|
|
1108
1096
|
inboundTranslationSlices->Array.map((module(ITS: InboundTranslationSlice.T)) => {
|
|
1109
1097
|
let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=ITS.Spec.name)
|
|
1110
1098
|
let fieldPermissions = Dict.make()
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
) {
|
|
1116
|
-
| Some(rule) => fieldPermissions->Dict.set(fieldName, rule)
|
|
1117
|
-
| None => ()
|
|
1118
|
-
}
|
|
1099
|
+
inboundDoorPermission(
|
|
1100
|
+
ITS.Spec.commandSchema,
|
|
1101
|
+
ITS.Spec.commandAuthorization,
|
|
1102
|
+
)->Option.forEach(rule => fieldPermissions->Dict.set(fieldName, rule))
|
|
1119
1103
|
{
|
|
1120
1104
|
ReventlessInfra.Api.fieldNames: [fieldName],
|
|
1121
1105
|
commandSchema: ITS.Spec.externalInputSchema->S.castToUnknown,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Sury from "sury";
|
|
4
|
-
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
4
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
5
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
7
6
|
import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
|
|
@@ -119,6 +118,10 @@ let emptyResult = {
|
|
|
119
118
|
eventLogEntries: emptyResult_eventLogEntries
|
|
120
119
|
};
|
|
121
120
|
|
|
121
|
+
function inboundDoorPermission(commandSchema, commandAuthorization) {
|
|
122
|
+
return Plugin_Structure$ReventlessCore.inboundDoorPermission(commandSchema, cmd => Authorization$Reventless.named(commandAuthorization(cmd)));
|
|
123
|
+
}
|
|
124
|
+
|
|
122
125
|
function Make(DcbEventLogStorage) {
|
|
123
126
|
return DcbEventTopicPublisher => (DcbCommandTopicChannel => (DcbCommandTopicChannelAsync => (RuntimeBuilder => (HooksConfig => {
|
|
124
127
|
let construct = (name, childName, apiNamePrefixOpt, onAdminApiOpt, environmentOpt, platformNameOpt, aggregateEventTopicsOpt, aggregateProducedEventsOpt, publishToAggregatesOpt, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, systemCallableComponentsOpt, componentRuntimeOpt, pluginStructure, opts) => {
|
|
@@ -394,7 +397,7 @@ function Make(DcbEventLogStorage) {
|
|
|
394
397
|
let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, ITS.Spec.name);
|
|
395
398
|
let registerResolver = HooksConfig.hooks.inboundMutationResolverHook;
|
|
396
399
|
if (registerResolver !== undefined) {
|
|
397
|
-
registerResolver(fieldName, ITS.Spec.externalInputSchema);
|
|
400
|
+
registerResolver(fieldName, ITS.Spec.externalInputSchema, inboundDoorPermission(ITS.Spec.commandSchema, ITS.Spec.commandAuthorization));
|
|
398
401
|
}
|
|
399
402
|
let bindReceive = HooksConfig.hooks.inboundMutationBindReceiveHook;
|
|
400
403
|
if (bindReceive !== undefined) {
|
|
@@ -464,12 +467,10 @@ function Make(DcbEventLogStorage) {
|
|
|
464
467
|
return (event, ctx) => {
|
|
465
468
|
let match = event["__inboundTranslation"];
|
|
466
469
|
if (match !== undefined) {
|
|
467
|
-
let
|
|
468
|
-
let args = Stdlib_Option.getOr(event["arguments"], null);
|
|
469
|
-
let receiveFn = receivers[fieldName];
|
|
470
|
+
let receiveFn = receivers[event.fieldName];
|
|
470
471
|
if (receiveFn !== undefined) {
|
|
471
472
|
return Effect.promise(async () => {
|
|
472
|
-
let result = await receiveFn(
|
|
473
|
+
let result = await receiveFn(event.arguments, InboundTranslationSlice_Callback$ReventlessCore.doorCaller(event.identity));
|
|
473
474
|
return CommandTopic$ReventlessCore.commandOutcomeToJson(InboundTranslationSlice_Callback$ReventlessCore.receiveResultToOutcome(result));
|
|
474
475
|
});
|
|
475
476
|
} else {
|
|
@@ -544,18 +545,6 @@ function Make(DcbEventLogStorage) {
|
|
|
544
545
|
RuntimeBuilder.forDcbCommandTopic(asyncDcbHandler, asyncDcbConnectFn, sliceMemoryFloor(asyncSlices), sliceTimeoutFloor(asyncSlices), asyncDcbCommandTopic);
|
|
545
546
|
});
|
|
546
547
|
};
|
|
547
|
-
let permissionForFirstConstructor = (commandSchema, commandAuthorization) => {
|
|
548
|
-
let names = DcbTag$Reventless.extractAllVariantNames(commandSchema);
|
|
549
|
-
let first = names[0];
|
|
550
|
-
if (first === undefined) {
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
let hasPayload = DcbTag$Reventless.isVariantPayloadBearing(commandSchema, first);
|
|
554
|
-
let syntheticCmd = hasPayload ? ({
|
|
555
|
-
TAG: first
|
|
556
|
-
}) : first;
|
|
557
|
-
return commandAuthorization(syntheticCmd);
|
|
558
|
-
};
|
|
559
548
|
let mutationEntriesFromSlices = Stdlib_Array.filterMap(stateChangeSlices, S => {
|
|
560
549
|
let commandSchema = S.Spec.commandSchema;
|
|
561
550
|
let fieldSpecs = Api_Naming$ReventlessCore.sliceMutationFields(apiNamePrefix, S.Spec.name, commandSchema);
|
|
@@ -585,10 +574,9 @@ function Make(DcbEventLogStorage) {
|
|
|
585
574
|
let mutationEntriesFromInboundSlices = inboundTranslationSlices.map(ITS => {
|
|
586
575
|
let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, ITS.Spec.name);
|
|
587
576
|
let fieldPermissions = {};
|
|
588
|
-
|
|
589
|
-
if (rule !== undefined) {
|
|
577
|
+
Stdlib_Option.forEach(inboundDoorPermission(ITS.Spec.commandSchema, ITS.Spec.commandAuthorization), rule => {
|
|
590
578
|
fieldPermissions[fieldName] = rule;
|
|
591
|
-
}
|
|
579
|
+
});
|
|
592
580
|
return {
|
|
593
581
|
fieldNames: [fieldName],
|
|
594
582
|
commandSchema: ITS.Spec.externalInputSchema,
|
|
@@ -691,6 +679,7 @@ export {
|
|
|
691
679
|
variantTag,
|
|
692
680
|
mergedEventSchema,
|
|
693
681
|
emptyResult,
|
|
682
|
+
inboundDoorPermission,
|
|
694
683
|
Make,
|
|
695
684
|
}
|
|
696
685
|
/* log Not a pure module */
|
|
@@ -81,8 +81,8 @@ module Make = (
|
|
|
81
81
|
->Pulumi.Output.all2
|
|
82
82
|
->Pulumi.Output.apply(((queryDbOps, publishJsonsFn)) => {
|
|
83
83
|
let ops: InboundTranslationSlice.operations = {
|
|
84
|
-
receive: async inputJson => {
|
|
85
|
-
let result = await Callback.receive(publishJsonsFn, inputJson)
|
|
84
|
+
receive: async (inputJson, ~caller=?) => {
|
|
85
|
+
let result = await Callback.receive(publishJsonsFn, inputJson, ~caller?)
|
|
86
86
|
await syncToQueryDb(queryDbOps, result->InboundTranslationSlice_Callback.requestIdOf)
|
|
87
87
|
result
|
|
88
88
|
},
|
|
@@ -55,8 +55,8 @@ function Make(QueryDbStorage) {
|
|
|
55
55
|
let publishJsonsFn = param[1];
|
|
56
56
|
let queryDbOps = param[0];
|
|
57
57
|
return {
|
|
58
|
-
receive: async inputJson => {
|
|
59
|
-
let result = await Callback.receive(publishJsonsFn, inputJson);
|
|
58
|
+
receive: async (inputJson, caller) => {
|
|
59
|
+
let result = await Callback.receive(publishJsonsFn, inputJson, caller);
|
|
60
60
|
await syncToQueryDb(queryDbOps, InboundTranslationSlice_Callback$ReventlessCore.requestIdOf(result));
|
|
61
61
|
return result;
|
|
62
62
|
}
|