@reventlessdev/reventless-infra 3.0.0-alpha.81
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 +709 -0
- package/LICENSE +202 -0
- package/package.json +35 -0
- package/rescript.json +28 -0
- package/src/adapter/Adapter.res +88 -0
- package/src/adapter/Adapter.res.mjs +29 -0
- package/src/components/Aggregate.res +73 -0
- package/src/components/Aggregate.res.mjs +2 -0
- package/src/components/Api.res +124 -0
- package/src/components/Api.res.mjs +31 -0
- package/src/components/Api_Adapter.res +39 -0
- package/src/components/Api_Adapter.res.mjs +2 -0
- package/src/components/AutomationSlice.res +48 -0
- package/src/components/AutomationSlice.res.mjs +2 -0
- package/src/components/CommandGenerator.res +8 -0
- package/src/components/CommandGenerator.res.mjs +2 -0
- package/src/components/CommandTopic.res +70 -0
- package/src/components/CommandTopic.res.mjs +2 -0
- package/src/components/Component.js +56 -0
- package/src/components/Component.mjs +10 -0
- package/src/components/Component.res +90 -0
- package/src/components/Component.res.mjs +57 -0
- package/src/components/Component.resi +21 -0
- package/src/components/Counter.res +81 -0
- package/src/components/Counter.res.mjs +2 -0
- package/src/components/DcbEventLog.res +115 -0
- package/src/components/DcbEventLog.res.mjs +2 -0
- package/src/components/EventCollector.res +19 -0
- package/src/components/EventCollector.res.mjs +2 -0
- package/src/components/EventLog.res +24 -0
- package/src/components/EventLog.res.mjs +2 -0
- package/src/components/EventMapper.res +37 -0
- package/src/components/EventMapper.res.mjs +2 -0
- package/src/components/EventTopic.res +51 -0
- package/src/components/EventTopic.res.mjs +2 -0
- package/src/components/Extension.res +64 -0
- package/src/components/Extension.res.mjs +2 -0
- package/src/components/ExtensionPoint.res +62 -0
- package/src/components/ExtensionPoint.res.mjs +2 -0
- package/src/components/Heartbeat.res +7 -0
- package/src/components/Heartbeat.res.mjs +2 -0
- package/src/components/InboundTranslationSlice.res +41 -0
- package/src/components/InboundTranslationSlice.res.mjs +2 -0
- package/src/components/OutboundTranslationSlice.res +44 -0
- package/src/components/OutboundTranslationSlice.res.mjs +2 -0
- package/src/components/Plugin.res +84 -0
- package/src/components/Plugin.res.mjs +2 -0
- package/src/components/QueryDb.res +48 -0
- package/src/components/QueryDb.res.mjs +33 -0
- package/src/components/ReadModel.res +50 -0
- package/src/components/ReadModel.res.mjs +2 -0
- package/src/components/Scheduler.res +32 -0
- package/src/components/Scheduler.res.mjs +2 -0
- package/src/components/StateChangeSlice.res +45 -0
- package/src/components/StateChangeSlice.res.mjs +2 -0
- package/src/components/StateViewSlice.res +38 -0
- package/src/components/StateViewSlice.res.mjs +2 -0
- package/src/components/Task.res +89 -0
- package/src/components/Task.res.mjs +2 -0
- package/src/types/ExtensionMapping.res +448 -0
- package/src/types/ExtensionMapping.res.mjs +252 -0
- package/src/types/ExtensionPointMapping.res +303 -0
- package/src/types/ExtensionPointMapping.res.mjs +116 -0
- package/src/types/Message.res +3 -0
- package/src/types/Message.res.mjs +2 -0
- package/src/types/NoEventMappings.res +25 -0
- package/src/types/NoEventMappings.res.mjs +17 -0
- package/src/types/Platform.res +279 -0
- package/src/types/Platform.res.mjs +2 -0
- package/src/types/PluginExtensionPointSpec.res +43 -0
- package/src/types/PluginExtensionPointSpec.res.mjs +204 -0
- package/src/types/ResourceNaming.res +12 -0
- package/src/types/ResourceNaming.res.mjs +2 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Uuid from "uuid";
|
|
5
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
9
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
10
|
+
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
11
|
+
import * as AnsiStyle$Reventless from "@reventlessdev/reventless-spec/src/AnsiStyle.res.mjs";
|
|
12
|
+
import * as PluginExtensionPointSpec$ReventlessInfra from "./PluginExtensionPointSpec.res.mjs";
|
|
13
|
+
|
|
14
|
+
let cmp = Primitive_string.compare;
|
|
15
|
+
|
|
16
|
+
let Id = {
|
|
17
|
+
schema: S.string,
|
|
18
|
+
cmp: cmp
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let moduleUrl = import.meta.url;
|
|
22
|
+
|
|
23
|
+
function commandAuthorization() {
|
|
24
|
+
return "AllowAuthenticated";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let NoDelegate = {
|
|
28
|
+
name: "NoDelegate",
|
|
29
|
+
Id: Id,
|
|
30
|
+
eventSchema: S.unit,
|
|
31
|
+
errorSchema: S.unit,
|
|
32
|
+
commandSchema: S.unit,
|
|
33
|
+
moduleUrl: moduleUrl,
|
|
34
|
+
commandAuthorization: commandAuthorization
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function Make(MappingImpl) {
|
|
38
|
+
let Spec = MappingImpl.ExtensionPoint;
|
|
39
|
+
let Delegate = MappingImpl.Delegate;
|
|
40
|
+
let delegateName = Delegate.name;
|
|
41
|
+
let extensionPointName = Spec.name;
|
|
42
|
+
let acceptedTags = DcbTag$Reventless.extractAllVariantNames(Delegate.eventSchema);
|
|
43
|
+
let derivedPartitionTagLazy = {
|
|
44
|
+
contents: undefined
|
|
45
|
+
};
|
|
46
|
+
let getDerivedPartitionTag = () => {
|
|
47
|
+
let d = derivedPartitionTagLazy.contents;
|
|
48
|
+
if (d !== undefined) {
|
|
49
|
+
return d;
|
|
50
|
+
}
|
|
51
|
+
let d$1 = DcbTag$Reventless.derivePartitionTag([[
|
|
52
|
+
Delegate.name,
|
|
53
|
+
Delegate.moduleUrl,
|
|
54
|
+
Delegate.commandSchema
|
|
55
|
+
]]);
|
|
56
|
+
derivedPartitionTagLazy.contents = d$1;
|
|
57
|
+
return d$1;
|
|
58
|
+
};
|
|
59
|
+
let derivePartitionId = targetCmd => {
|
|
60
|
+
let pt = getDerivedPartitionTag();
|
|
61
|
+
if (pt.TAG === "Simple") {
|
|
62
|
+
let tags = DcbTag$Reventless.extractTags(Delegate.commandSchema, targetCmd);
|
|
63
|
+
return Stdlib_Option.getOr(DcbTag$Reventless.getPartitionTagValue([{
|
|
64
|
+
tags: tags
|
|
65
|
+
}], pt._0), "");
|
|
66
|
+
}
|
|
67
|
+
let tags$1 = DcbTag$Reventless.extractTags(Delegate.commandSchema, targetCmd);
|
|
68
|
+
return DcbTag$Reventless.getCompositePartitionKeyValue(tags$1, pt._0);
|
|
69
|
+
};
|
|
70
|
+
let compLog = (comp, msg) => Effect.runSync(Effect.annotateLogs(Effect.logInfo(msg), "comp", comp));
|
|
71
|
+
let encodeMeta = (meta, service) => {
|
|
72
|
+
let newrecord = {...meta};
|
|
73
|
+
newrecord.msgId = Uuid.v4();
|
|
74
|
+
newrecord.service = service;
|
|
75
|
+
return newrecord;
|
|
76
|
+
};
|
|
77
|
+
let mapIncomingEvent = (extra, extra$1, extra$2) => {
|
|
78
|
+
let mapIncomingEventImpl = MappingImpl.mapIncomingEvent;
|
|
79
|
+
let meta = extra.meta;
|
|
80
|
+
let encodeTargetCommandJson = (targetCmd, targetId) => {
|
|
81
|
+
let cmdJson = Message$Reventless.encode(targetCmd, Delegate.commandSchema);
|
|
82
|
+
return {
|
|
83
|
+
id: targetId,
|
|
84
|
+
meta: encodeMeta(meta, delegateName),
|
|
85
|
+
commandJson: cmdJson
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
let encodeExtensionPointCommandJson = (commandJson, id, extensionPointName, action) => {
|
|
89
|
+
compLog(`Extension(` + extensionPointName + `)`, action + `: ` + AnsiStyle$Reventless.bold(Message$Reventless.variantNameOfJson(commandJson)) + `(` + id + `)`);
|
|
90
|
+
return {
|
|
91
|
+
id: id,
|
|
92
|
+
meta: encodeMeta(meta, extensionPointName),
|
|
93
|
+
commandJson: commandJson
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
let encodeExtensionPointCommand = (command, id, extensionPointName, action) => encodeExtensionPointCommandJson(Message$Reventless.encode(command, Spec.commandSchema), id, extensionPointName, action);
|
|
97
|
+
return mapIncomingEventImpl(extra.id, extra.event, meta, extra$1, extra$2).map(x => {
|
|
98
|
+
switch (x.TAG) {
|
|
99
|
+
case "PublishAggregateCommand" :
|
|
100
|
+
return {
|
|
101
|
+
TAG: "AbstractPublishAggregateCommand",
|
|
102
|
+
_0: delegateName,
|
|
103
|
+
_1: encodeTargetCommandJson(x._1, x._0)
|
|
104
|
+
};
|
|
105
|
+
case "PublishAggregateCommandAsync" :
|
|
106
|
+
let toCommandJson = async promise => {
|
|
107
|
+
let match = await promise;
|
|
108
|
+
return [
|
|
109
|
+
delegateName,
|
|
110
|
+
encodeTargetCommandJson(match[1], match[0])
|
|
111
|
+
];
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
TAG: "AbstractPublishAggregateCommandAsync",
|
|
115
|
+
_0: toCommandJson(x._0)
|
|
116
|
+
};
|
|
117
|
+
case "PublishAggregateCommandsAsync" :
|
|
118
|
+
let toCommandJsons = async promise => (await promise).map(param => [
|
|
119
|
+
delegateName,
|
|
120
|
+
encodeTargetCommandJson(param[1], param[0])
|
|
121
|
+
]);
|
|
122
|
+
return {
|
|
123
|
+
TAG: "AbstractPublishAggregateCommandsAsync",
|
|
124
|
+
_0: toCommandJsons(x._0)
|
|
125
|
+
};
|
|
126
|
+
case "PublishStateChangeSliceCommand" :
|
|
127
|
+
let targetCmd = x._0;
|
|
128
|
+
return {
|
|
129
|
+
TAG: "AbstractPublishAggregateCommand",
|
|
130
|
+
_0: delegateName,
|
|
131
|
+
_1: encodeTargetCommandJson(targetCmd, derivePartitionId(targetCmd))
|
|
132
|
+
};
|
|
133
|
+
case "PublishStateChangeSliceCommandAsync" :
|
|
134
|
+
let toCommandJson$1 = async promise => {
|
|
135
|
+
let targetCmd = await promise;
|
|
136
|
+
return [
|
|
137
|
+
delegateName,
|
|
138
|
+
encodeTargetCommandJson(targetCmd, derivePartitionId(targetCmd))
|
|
139
|
+
];
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
TAG: "AbstractPublishAggregateCommandAsync",
|
|
143
|
+
_0: toCommandJson$1(x._0)
|
|
144
|
+
};
|
|
145
|
+
case "PublishStateChangeSliceCommandsAsync" :
|
|
146
|
+
let toCommandJsons$1 = async promise => (await promise).map(targetCmd => [
|
|
147
|
+
delegateName,
|
|
148
|
+
encodeTargetCommandJson(targetCmd, derivePartitionId(targetCmd))
|
|
149
|
+
]);
|
|
150
|
+
return {
|
|
151
|
+
TAG: "AbstractPublishAggregateCommandsAsync",
|
|
152
|
+
_0: toCommandJsons$1(x._0)
|
|
153
|
+
};
|
|
154
|
+
case "PublishExtensionPointCommand" :
|
|
155
|
+
let command = x._1;
|
|
156
|
+
let id = x._0;
|
|
157
|
+
if (Spec.name === PluginExtensionPointSpec$ReventlessInfra.name) {
|
|
158
|
+
return {
|
|
159
|
+
TAG: "AbstractPublishPluginExtensionPointCommand",
|
|
160
|
+
_0: encodeExtensionPointCommand(command, id, extensionPointName, "Publish PluginExtensionPoint command")
|
|
161
|
+
};
|
|
162
|
+
} else {
|
|
163
|
+
return {
|
|
164
|
+
TAG: "AbstractPublishExtensionPointCommand",
|
|
165
|
+
_0: extensionPointName,
|
|
166
|
+
_1: encodeExtensionPointCommand(command, id, extensionPointName, "Publish ExtensionPoint command")
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
case "ForwardCommand" :
|
|
170
|
+
let match = x._0;
|
|
171
|
+
let extensionPointName$1 = match.extensionPointName;
|
|
172
|
+
return {
|
|
173
|
+
TAG: "AbstractPublishExtensionPointCommand",
|
|
174
|
+
_0: extensionPointName$1,
|
|
175
|
+
_1: encodeExtensionPointCommandJson(match.commandJson, match.id, extensionPointName$1, "Forward ExtensionPoint command")
|
|
176
|
+
};
|
|
177
|
+
case "HandleDirective" :
|
|
178
|
+
let directive = x._1;
|
|
179
|
+
let handler = x._0;
|
|
180
|
+
compLog(`Extension(` + extensionPointName + `)`, "incoming directive");
|
|
181
|
+
return {
|
|
182
|
+
TAG: "AbstractHandleDirective",
|
|
183
|
+
_0: () => handler(directive)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
let variantTagOfEnvelope = json => Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["event"]), Message$Reventless.variantNameOfJson), "unknown");
|
|
189
|
+
let mapOutgoingEvent = Stdlib_Option.map(MappingImpl.mapOutgoingEvent, mapOutgoingEventImpl => ((extra, extra$1) => {
|
|
190
|
+
let tag = variantTagOfEnvelope(extra);
|
|
191
|
+
if (!acceptedTags.includes(tag)) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
let match = Message$Reventless.decodeEvent$p(extra, Delegate.Id.schema, Delegate.eventSchema);
|
|
195
|
+
let meta = match.meta;
|
|
196
|
+
let encodeExtensionPointCommandJson = (commandJson, id, extensionPointName, action) => {
|
|
197
|
+
compLog(`Extension(` + delegateName + `)`, action + `: ` + AnsiStyle$Reventless.bold(Message$Reventless.variantNameOfJson(commandJson)) + `(` + id + `)`);
|
|
198
|
+
return {
|
|
199
|
+
id: id,
|
|
200
|
+
meta: encodeMeta(meta, extensionPointName),
|
|
201
|
+
commandJson: commandJson
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
let encodeExtensionPointCommand = (command, id, extensionPointName, action) => encodeExtensionPointCommandJson(Message$Reventless.encode(command, Spec.commandSchema), id, extensionPointName, action);
|
|
205
|
+
return mapOutgoingEventImpl(Delegate.Id.toString(match.id), match.event, meta, extra$1).map(x => {
|
|
206
|
+
switch (x.TAG) {
|
|
207
|
+
case "PublishExtensionPointCommand" :
|
|
208
|
+
let command = x._1;
|
|
209
|
+
let id = x._0;
|
|
210
|
+
if (Spec.name === PluginExtensionPointSpec$ReventlessInfra.name) {
|
|
211
|
+
return {
|
|
212
|
+
TAG: "AbstractPublishPluginExtensionPointCommand",
|
|
213
|
+
_0: encodeExtensionPointCommand(command, id, extensionPointName, "Publish PluginExtensionPoint command")
|
|
214
|
+
};
|
|
215
|
+
} else {
|
|
216
|
+
return {
|
|
217
|
+
TAG: "AbstractPublishExtensionPointCommand",
|
|
218
|
+
_0: extensionPointName,
|
|
219
|
+
_1: encodeExtensionPointCommand(command, id, extensionPointName, "Publish ExtensionPoint command")
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
case "ForwardCommand" :
|
|
223
|
+
let match = x._0;
|
|
224
|
+
let extensionPointName$1 = match.extensionPointName;
|
|
225
|
+
return {
|
|
226
|
+
TAG: "AbstractPublishExtensionPointCommand",
|
|
227
|
+
_0: extensionPointName$1,
|
|
228
|
+
_1: encodeExtensionPointCommandJson(match.commandJson, match.id, extensionPointName$1, "Forward ExtensionPoint command")
|
|
229
|
+
};
|
|
230
|
+
case "HandleDirective" :
|
|
231
|
+
let directive = x._1;
|
|
232
|
+
let handler = x._0;
|
|
233
|
+
compLog(`Extension(` + delegateName + `)`, "outgoing directive");
|
|
234
|
+
return {
|
|
235
|
+
TAG: "AbstractHandleDirective",
|
|
236
|
+
_0: () => handler(directive)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}));
|
|
241
|
+
return {
|
|
242
|
+
delegateName: delegateName,
|
|
243
|
+
mapIncomingEvent: mapIncomingEvent,
|
|
244
|
+
mapOutgoingEvent: mapOutgoingEvent
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export {
|
|
249
|
+
NoDelegate,
|
|
250
|
+
Make,
|
|
251
|
+
}
|
|
252
|
+
/* moduleUrl Not a pure module */
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
type extensionPointName = string
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
An async handler for a typed extension-point directive. May call the scheduler
|
|
5
|
+
or query engine as a side effect. Used as the handler argument in `HandleDirective`
|
|
6
|
+
on both `commandAction` and `eventAction`.
|
|
7
|
+
*/
|
|
8
|
+
type directiveHandler<'directive> = (
|
|
9
|
+
Reventless.Schedule.create,
|
|
10
|
+
Reventless.Schedule.delete,
|
|
11
|
+
Reventless.QueryEngine.operations,
|
|
12
|
+
'directive,
|
|
13
|
+
) => promise<unit>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
Actions returned by `mapIncomingCommand` — what to do when the extension point
|
|
17
|
+
receives a command from an extension.
|
|
18
|
+
|
|
19
|
+
- `PublishCommand(id, cmd)` — publish a command to the wrapped aggregate
|
|
20
|
+
- `HandleDirective(handler, directive)` — invoke an async handler for an
|
|
21
|
+
extension-point-defined directive
|
|
22
|
+
*/
|
|
23
|
+
/* these actions are needed for Mapping */
|
|
24
|
+
type commandAction<'command, 'directive> =
|
|
25
|
+
| PublishCommand(string, 'command)
|
|
26
|
+
| HandleDirective(directiveHandler<'directive>, 'directive)
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
Actions returned by `mapOutgoingEvent` — what to do when the wrapped aggregate
|
|
30
|
+
emits an event that should be reflected through the extension point.
|
|
31
|
+
|
|
32
|
+
- `PublishEvent(id, event)` — synchronously emit an extension point event
|
|
33
|
+
- `PublishEventAsync(promise)` — resolve a promise and emit the resulting event
|
|
34
|
+
- `HandleDirective(handler, directive)` — invoke an async handler for an
|
|
35
|
+
extension-point-defined directive
|
|
36
|
+
*/
|
|
37
|
+
type eventAction<'event, 'directive> =
|
|
38
|
+
| PublishEvent(string, 'event)
|
|
39
|
+
| PublishEventAsync(promise<(string, 'event)>)
|
|
40
|
+
| HandleDirective(directiveHandler<'directive>, 'directive)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
The extension point protocol — defines the command, event, and directive types
|
|
44
|
+
that extensions and aggregates exchange through this extension point.
|
|
45
|
+
*/
|
|
46
|
+
module type Spec = {
|
|
47
|
+
let name: string
|
|
48
|
+
let moduleUrl: string
|
|
49
|
+
|
|
50
|
+
@schema
|
|
51
|
+
type command
|
|
52
|
+
@schema
|
|
53
|
+
type event
|
|
54
|
+
@schema
|
|
55
|
+
type directive
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
Maps an incoming extension point command to zero or more aggregate commands
|
|
60
|
+
(or side-effect calls).
|
|
61
|
+
|
|
62
|
+
Called when an extension publishes a command to this extension point.
|
|
63
|
+
Receives the entity ID, the command, and the message metadata.
|
|
64
|
+
*/
|
|
65
|
+
type mapIncomingCommand<'extensionPointCommand, 'aggregateCommand, 'extensionPointDirective> = (
|
|
66
|
+
string,
|
|
67
|
+
'extensionPointCommand,
|
|
68
|
+
Reventless.Message.meta,
|
|
69
|
+
) => array<commandAction<'aggregateCommand, 'extensionPointDirective>>
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
Maps an aggregate outgoing event to zero or more extension point events
|
|
73
|
+
(or side-effect calls).
|
|
74
|
+
|
|
75
|
+
Called when the wrapped aggregate emits an event. Receives the entity ID,
|
|
76
|
+
the event, message metadata, and the query engine.
|
|
77
|
+
*/
|
|
78
|
+
type mapOutgoingEvent<'aggregateEvent, 'extensionPointEvent, 'extensionPointDirective> = (
|
|
79
|
+
string,
|
|
80
|
+
'aggregateEvent,
|
|
81
|
+
Reventless.Message.meta,
|
|
82
|
+
Reventless.QueryEngine.operations,
|
|
83
|
+
) => array<eventAction<'extensionPointEvent, 'extensionPointDirective>>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
Application-level implementation of the command / event mapping for one
|
|
87
|
+
aggregate connected to an extension point.
|
|
88
|
+
|
|
89
|
+
Pass this to `ExtensionPointMapping.Make(Spec, Mapping)` to produce a compiled
|
|
90
|
+
`ExtensionPointMapping.T` module.
|
|
91
|
+
*/
|
|
92
|
+
module type Mapping = {
|
|
93
|
+
module ExtensionPoint: Spec
|
|
94
|
+
module Delegate: Reventless.Aggregate.Spec
|
|
95
|
+
|
|
96
|
+
// Module URL of the mapping file (e.g. catalog/src/ExtensionPoint/
|
|
97
|
+
// Products_ExtensionPointMapping.res.mjs). Distinct from ExtensionPoint.moduleUrl
|
|
98
|
+
// (the spec, in catalog-spec). Auto-injected by `@@reventless.spec` on
|
|
99
|
+
// ExtensionPointMapping files. The EventCollector runtime needs THIS url
|
|
100
|
+
// (not the spec's) to dynamic-import the mapping file's mapOutgoingEvent.
|
|
101
|
+
let moduleUrl: string
|
|
102
|
+
|
|
103
|
+
let mapIncomingCommand: mapIncomingCommand<
|
|
104
|
+
ExtensionPoint.command,
|
|
105
|
+
Delegate.command,
|
|
106
|
+
ExtensionPoint.directive,
|
|
107
|
+
>
|
|
108
|
+
|
|
109
|
+
let mapOutgoingEvent: option<
|
|
110
|
+
mapOutgoingEvent<Delegate.event, ExtensionPoint.event, ExtensionPoint.directive>,
|
|
111
|
+
>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Internal pre-compiled action types used by the ExtensionPoint runtime.
|
|
115
|
+
// Created by ExtensionPointMapping.Make (in reventless); consumed by ExtensionPoint_Callback
|
|
116
|
+
// and ExtensionPoint_Operations.
|
|
117
|
+
|
|
118
|
+
/** Internal runtime action produced after pre-encoding a `commandAction`. Not for direct use. */
|
|
119
|
+
type abstractCommandAction =
|
|
120
|
+
| AbstractPublishCommand(string, string, Reventless.Message.commandJson)
|
|
121
|
+
| AbstractHandleDirective(string, unit => promise<unit>)
|
|
122
|
+
|
|
123
|
+
/** Internal runtime action produced after pre-encoding an `eventAction`. Not for direct use. */
|
|
124
|
+
type abstractEventAction<'extensionPointEvent> =
|
|
125
|
+
| AbstractPublishEvent(string, Reventless.Message.meta, JSON.t)
|
|
126
|
+
| AbstractPublishEventAsync(promise<(string, Reventless.Message.meta, JSON.t)>)
|
|
127
|
+
| AbstractHandleDirective(unit => promise<unit>)
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
A pre-compiled mapping module produced by `ExtensionPointMapping.Make(Spec, Mapping)`.
|
|
131
|
+
|
|
132
|
+
The runtime uses `mapIncomingCommands` and `mapOutgoingEvent` to dispatch commands
|
|
133
|
+
and events without knowing the concrete extension point or aggregate types.
|
|
134
|
+
Application developers call `Make` themselves; the result satisfies this type.
|
|
135
|
+
*/
|
|
136
|
+
// Pre-compiled mapping module type. Created by ExtensionPointMapping.Make(Spec, Mapping).
|
|
137
|
+
// App developers call Make themselves; the result satisfies this type.
|
|
138
|
+
module type T = {
|
|
139
|
+
module ExtensionPoint: Spec
|
|
140
|
+
|
|
141
|
+
/** Name of the target this mapping connects to the extension point. */
|
|
142
|
+
let delegateName: string
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
Converts a batch of typed extension point commands into pre-encoded abstract actions.
|
|
146
|
+
Called by the extension point runtime for each incoming command batch.
|
|
147
|
+
*/
|
|
148
|
+
let mapIncomingCommands: (
|
|
149
|
+
array<CommandTopic.topicItem<Reventless.Message.command'<Reventless.Id.String.t, ExtensionPoint.command>>>,
|
|
150
|
+
Reventless.Schedule.create,
|
|
151
|
+
Reventless.Schedule.delete,
|
|
152
|
+
Reventless.QueryEngine.operations,
|
|
153
|
+
) => array<abstractCommandAction>
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
Converts a raw aggregate event JSON into pre-encoded abstract event actions.
|
|
157
|
+
`None` if this mapping does not produce outgoing extension point events.
|
|
158
|
+
*/
|
|
159
|
+
let mapOutgoingEvent: option<
|
|
160
|
+
(
|
|
161
|
+
JSON.t,
|
|
162
|
+
Reventless.Schedule.create,
|
|
163
|
+
Reventless.Schedule.delete,
|
|
164
|
+
Reventless.QueryEngine.operations,
|
|
165
|
+
) => array<abstractEventAction<ExtensionPoint.event>>,
|
|
166
|
+
>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module Make = (MappingImpl: Mapping): (
|
|
170
|
+
T with module ExtensionPoint := MappingImpl.ExtensionPoint
|
|
171
|
+
) => {
|
|
172
|
+
module Spec = MappingImpl.ExtensionPoint
|
|
173
|
+
module Delegate = MappingImpl.Delegate
|
|
174
|
+
let delegateName = Delegate.name
|
|
175
|
+
let extensionPointName = Spec.name
|
|
176
|
+
|
|
177
|
+
// Variant TAGs the Delegate cares about — derived from the Delegate's event
|
|
178
|
+
// schema at functor instantiation. Used to pre-filter incoming envelopes:
|
|
179
|
+
// sibling variants on the source log (events the Delegate did not declare)
|
|
180
|
+
// are silently skipped without any decode attempt.
|
|
181
|
+
// Pre-filter incoming envelopes against the Delegate's full constructor
|
|
182
|
+
// set — includes payload-less variants (e.g. UnknownPluginDetected
|
|
183
|
+
// compiled to `S.literal("…")`) since the JSON envelope's `event` TAG
|
|
184
|
+
// still carries the literal name even when the variant has no payload.
|
|
185
|
+
let acceptedTags = Reventless.DcbTag.extractAllVariantNames(Delegate.eventSchema)
|
|
186
|
+
|
|
187
|
+
// Carry `comp` as a structured Effect log annotation — EffectLogger.install
|
|
188
|
+
// (in reventless-core, wired at Lambda startup) lifts it to the top-level
|
|
189
|
+
// JSON `comp` field. Without the unified logger installed, Effect's default
|
|
190
|
+
// logger still renders the annotation, just less prettily.
|
|
191
|
+
let compLog = (comp, msg) =>
|
|
192
|
+
Effect.logInfo(msg)->Effect.annotateLogs("comp", comp)->Effect.runSync
|
|
193
|
+
|
|
194
|
+
let doMapIncomingCommands = (
|
|
195
|
+
mapIncomingEventImpl,
|
|
196
|
+
topicItems,
|
|
197
|
+
createSchedule,
|
|
198
|
+
deleteSchedule,
|
|
199
|
+
queryEngine,
|
|
200
|
+
) =>
|
|
201
|
+
topicItems
|
|
202
|
+
->Array.map(({CommandTopic.reference: reference, command: {Reventless.Message.id: id, command, meta}}) =>
|
|
203
|
+
mapIncomingEventImpl(id->Reventless.Id.String.toString, command, meta)->Array.map(x =>
|
|
204
|
+
switch x {
|
|
205
|
+
| PublishCommand(targetId, targetCmd) =>
|
|
206
|
+
let cmdJson = targetCmd->Reventless.Message.encode(Delegate.commandSchema)
|
|
207
|
+
compLog(`ExtensionPoint(${extensionPointName})`, `EP→${delegateName}: ${cmdJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${targetId})`)
|
|
208
|
+
|
|
209
|
+
AbstractPublishCommand(
|
|
210
|
+
delegateName,
|
|
211
|
+
reference,
|
|
212
|
+
{
|
|
213
|
+
id: targetId,
|
|
214
|
+
meta: {
|
|
215
|
+
...meta,
|
|
216
|
+
service: Delegate.name,
|
|
217
|
+
msgId: Message.uuid(),
|
|
218
|
+
},
|
|
219
|
+
commandJson: targetCmd->Reventless.Message.encode(Delegate.commandSchema),
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
| HandleDirective(handler, directive) =>
|
|
223
|
+
compLog(`ExtensionPoint(${extensionPointName})`, "incoming directive")
|
|
224
|
+
|
|
225
|
+
AbstractHandleDirective(
|
|
226
|
+
reference,
|
|
227
|
+
() => handler(createSchedule, deleteSchedule, queryEngine, directive),
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
->Array.flat
|
|
233
|
+
|
|
234
|
+
let mapIncomingCommands = doMapIncomingCommands(MappingImpl.mapIncomingCommand, ...)
|
|
235
|
+
|
|
236
|
+
let variantTagOfEnvelope = json =>
|
|
237
|
+
json
|
|
238
|
+
->JSON.Decode.object
|
|
239
|
+
->Option.flatMap(d => d->Dict.get("event"))
|
|
240
|
+
->Option.map(Reventless.Message.variantNameOfJson)
|
|
241
|
+
->Option.getOr("unknown")
|
|
242
|
+
|
|
243
|
+
let doMapOutgoingEvent = (
|
|
244
|
+
mapOutgoingEventImpl,
|
|
245
|
+
targetEventJson',
|
|
246
|
+
createSchedule,
|
|
247
|
+
deleteSchedule,
|
|
248
|
+
queryEngine,
|
|
249
|
+
) => {
|
|
250
|
+
let tag = variantTagOfEnvelope(targetEventJson')
|
|
251
|
+
// Pre-filter by TAG: sibling variants from the source log that the Delegate
|
|
252
|
+
// did not declare are not this mapping's concern — skip silently with no
|
|
253
|
+
// decode attempt.
|
|
254
|
+
if !(acceptedTags->Array.includes(tag)) {
|
|
255
|
+
[]
|
|
256
|
+
} else {
|
|
257
|
+
let {id, meta, event} =
|
|
258
|
+
targetEventJson'->Reventless.Message.decodeEvent'(
|
|
259
|
+
Delegate.Id.schema,
|
|
260
|
+
Delegate.eventSchema,
|
|
261
|
+
)
|
|
262
|
+
mapOutgoingEventImpl(
|
|
263
|
+
id->Delegate.Id.toString,
|
|
264
|
+
event,
|
|
265
|
+
meta,
|
|
266
|
+
queryEngine,
|
|
267
|
+
)->Array.map(eventAction =>
|
|
268
|
+
switch eventAction {
|
|
269
|
+
| PublishEvent(id, event) =>
|
|
270
|
+
let eventJson = event->Reventless.Message.encode(Spec.eventSchema)
|
|
271
|
+
compLog(`ExtensionPoint(${extensionPointName})`, `mapped ${delegateName} → ${eventJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id})`)
|
|
272
|
+
let meta = {
|
|
273
|
+
...meta,
|
|
274
|
+
service: Spec.name,
|
|
275
|
+
msgId: Message.uuid(),
|
|
276
|
+
}
|
|
277
|
+
let eventJson' = Reventless.Message.composeEventJson'(id, meta, eventJson)
|
|
278
|
+
AbstractPublishEvent(id, meta, eventJson')
|
|
279
|
+
| PublishEventAsync(promise) =>
|
|
280
|
+
let toEvent' = async promise => {
|
|
281
|
+
let (id, event) = await promise
|
|
282
|
+
let eventJson = event->Reventless.Message.encode(Spec.eventSchema)
|
|
283
|
+
compLog(`ExtensionPoint(${extensionPointName})`, `mapped ${delegateName} → ${eventJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id}) (async)`)
|
|
284
|
+
let eventJson' = Reventless.Message.composeEventJson'(id, meta, eventJson) // TODO: check if meta is correct
|
|
285
|
+
(id, meta, eventJson')
|
|
286
|
+
}
|
|
287
|
+
AbstractPublishEventAsync(promise->toEvent')
|
|
288
|
+
| HandleDirective(handler, directive) =>
|
|
289
|
+
compLog(`ExtensionPoint(${extensionPointName})`, `mapped ${delegateName} → directive`)
|
|
290
|
+
|
|
291
|
+
AbstractHandleDirective(() =>
|
|
292
|
+
handler(createSchedule, deleteSchedule, queryEngine, directive)
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let mapOutgoingEvent =
|
|
300
|
+
MappingImpl.mapOutgoingEvent->Option.map(mapOutgoingEventImpl =>
|
|
301
|
+
doMapOutgoingEvent(mapOutgoingEventImpl, ...)
|
|
302
|
+
)
|
|
303
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Uuid from "uuid";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
9
|
+
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
10
|
+
import * as AnsiStyle$Reventless from "@reventlessdev/reventless-spec/src/AnsiStyle.res.mjs";
|
|
11
|
+
|
|
12
|
+
function Make(MappingImpl) {
|
|
13
|
+
let Spec = MappingImpl.ExtensionPoint;
|
|
14
|
+
let Delegate = MappingImpl.Delegate;
|
|
15
|
+
let delegateName = Delegate.name;
|
|
16
|
+
let extensionPointName = Spec.name;
|
|
17
|
+
let acceptedTags = DcbTag$Reventless.extractAllVariantNames(Delegate.eventSchema);
|
|
18
|
+
let compLog = (comp, msg) => Effect.runSync(Effect.annotateLogs(Effect.logInfo(msg), "comp", comp));
|
|
19
|
+
let mapIncomingCommands = (extra, extra$1, extra$2, extra$3) => {
|
|
20
|
+
let mapIncomingEventImpl = MappingImpl.mapIncomingCommand;
|
|
21
|
+
return extra.map(param => {
|
|
22
|
+
let reference = param.reference;
|
|
23
|
+
let match = param.command;
|
|
24
|
+
let meta = match.meta;
|
|
25
|
+
return mapIncomingEventImpl(Id$Reventless.$$String.toString(match.id), match.command, meta).map(x => {
|
|
26
|
+
if (x.TAG === "PublishCommand") {
|
|
27
|
+
let targetCmd = x._1;
|
|
28
|
+
let targetId = x._0;
|
|
29
|
+
let cmdJson = Message$Reventless.encode(targetCmd, Delegate.commandSchema);
|
|
30
|
+
compLog(`ExtensionPoint(` + extensionPointName + `)`, `EP→` + delegateName + `: ` + AnsiStyle$Reventless.bold(Message$Reventless.variantNameOfJson(cmdJson)) + `(` + targetId + `)`);
|
|
31
|
+
let newrecord = {...meta};
|
|
32
|
+
return {
|
|
33
|
+
TAG: "AbstractPublishCommand",
|
|
34
|
+
_0: delegateName,
|
|
35
|
+
_1: reference,
|
|
36
|
+
_2: {
|
|
37
|
+
id: targetId,
|
|
38
|
+
meta: (newrecord.msgId = Uuid.v4(), newrecord.service = Delegate.name, newrecord),
|
|
39
|
+
commandJson: Message$Reventless.encode(targetCmd, Delegate.commandSchema)
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
let directive = x._1;
|
|
44
|
+
let handler = x._0;
|
|
45
|
+
compLog(`ExtensionPoint(` + extensionPointName + `)`, "incoming directive");
|
|
46
|
+
return {
|
|
47
|
+
TAG: "AbstractHandleDirective",
|
|
48
|
+
_0: reference,
|
|
49
|
+
_1: () => handler(extra$1, extra$2, extra$3, directive)
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}).flat();
|
|
53
|
+
};
|
|
54
|
+
let variantTagOfEnvelope = json => Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["event"]), Message$Reventless.variantNameOfJson), "unknown");
|
|
55
|
+
let mapOutgoingEvent = Stdlib_Option.map(MappingImpl.mapOutgoingEvent, mapOutgoingEventImpl => ((extra, extra$1, extra$2, extra$3) => {
|
|
56
|
+
let tag = variantTagOfEnvelope(extra);
|
|
57
|
+
if (!acceptedTags.includes(tag)) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
let match = Message$Reventless.decodeEvent$p(extra, Delegate.Id.schema, Delegate.eventSchema);
|
|
61
|
+
let meta = match.meta;
|
|
62
|
+
return mapOutgoingEventImpl(Delegate.Id.toString(match.id), match.event, meta, extra$3).map(eventAction => {
|
|
63
|
+
switch (eventAction.TAG) {
|
|
64
|
+
case "PublishEvent" :
|
|
65
|
+
let id = eventAction._0;
|
|
66
|
+
let eventJson = Message$Reventless.encode(eventAction._1, Spec.eventSchema);
|
|
67
|
+
compLog(`ExtensionPoint(` + extensionPointName + `)`, `mapped ` + delegateName + ` → ` + AnsiStyle$Reventless.bold(Message$Reventless.variantNameOfJson(eventJson)) + `(` + id + `)`);
|
|
68
|
+
let newrecord = {...meta};
|
|
69
|
+
newrecord.msgId = Uuid.v4();
|
|
70
|
+
newrecord.service = Spec.name;
|
|
71
|
+
let eventJson$p = Message$Reventless.composeEventJson$p(id, newrecord, eventJson);
|
|
72
|
+
return {
|
|
73
|
+
TAG: "AbstractPublishEvent",
|
|
74
|
+
_0: id,
|
|
75
|
+
_1: newrecord,
|
|
76
|
+
_2: eventJson$p
|
|
77
|
+
};
|
|
78
|
+
case "PublishEventAsync" :
|
|
79
|
+
let toEvent$p = async promise => {
|
|
80
|
+
let match = await promise;
|
|
81
|
+
let id = match[0];
|
|
82
|
+
let eventJson = Message$Reventless.encode(match[1], Spec.eventSchema);
|
|
83
|
+
compLog(`ExtensionPoint(` + extensionPointName + `)`, `mapped ` + delegateName + ` → ` + AnsiStyle$Reventless.bold(Message$Reventless.variantNameOfJson(eventJson)) + `(` + id + `) (async)`);
|
|
84
|
+
let eventJson$p = Message$Reventless.composeEventJson$p(id, meta, eventJson);
|
|
85
|
+
return [
|
|
86
|
+
id,
|
|
87
|
+
meta,
|
|
88
|
+
eventJson$p
|
|
89
|
+
];
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
TAG: "AbstractPublishEventAsync",
|
|
93
|
+
_0: toEvent$p(eventAction._0)
|
|
94
|
+
};
|
|
95
|
+
case "HandleDirective" :
|
|
96
|
+
let directive = eventAction._1;
|
|
97
|
+
let handler = eventAction._0;
|
|
98
|
+
compLog(`ExtensionPoint(` + extensionPointName + `)`, `mapped ` + delegateName + ` → directive`);
|
|
99
|
+
return {
|
|
100
|
+
TAG: "AbstractHandleDirective",
|
|
101
|
+
_0: () => handler(extra$1, extra$2, extra$3, directive)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}));
|
|
106
|
+
return {
|
|
107
|
+
delegateName: delegateName,
|
|
108
|
+
mapIncomingCommands: mapIncomingCommands,
|
|
109
|
+
mapOutgoingEvent: mapOutgoingEvent
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
Make,
|
|
115
|
+
}
|
|
116
|
+
/* uuid Not a pure module */
|