@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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Derives the name used to query a task's S3 bucket.
|
|
3
|
+
|
|
4
|
+
- `~taskName` — the task's logical name
|
|
5
|
+
- `~bucketName` — optional override; defaults to the task's configured bucket name
|
|
6
|
+
*/
|
|
7
|
+
type queryBucketName = (~taskName: string, ~bucketName: string=?) => string
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
The `setup` function signature for a `Task.Spec`.
|
|
11
|
+
|
|
12
|
+
Called at deploy time to configure the task's infrastructure.
|
|
13
|
+
Receives the query engine, bucket name helper, and Pulumi options.
|
|
14
|
+
Returns the task's runtime `config`.
|
|
15
|
+
*/
|
|
16
|
+
type setup = (
|
|
17
|
+
Reventless.QueryEngine.operations,
|
|
18
|
+
queryBucketName,
|
|
19
|
+
Pulumi.ComponentResource.options,
|
|
20
|
+
) => Reventless.Task.config
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
Module type for a task's specification.
|
|
24
|
+
|
|
25
|
+
A `Task` is a serverless handler triggered by S3 events or schedules.
|
|
26
|
+
It can publish commands, manage schedules, and execute side effects.
|
|
27
|
+
|
|
28
|
+
@example
|
|
29
|
+
```rescript
|
|
30
|
+
module CatalogImportTask: Task.Spec = {
|
|
31
|
+
let name = "CatalogImport"
|
|
32
|
+
let setup = (_queryEngine, _queryBucketName, _opts) => {
|
|
33
|
+
buckets: [{bucketMode: Read}],
|
|
34
|
+
sideEffects: [],
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
*/
|
|
39
|
+
module type Spec = {
|
|
40
|
+
/** Logical task name (used as a Lambda function name prefix). */
|
|
41
|
+
let name: string
|
|
42
|
+
/** ESM specifier for this Spec module — populated by `@@reventless.task` PPX. */
|
|
43
|
+
let moduleUrl: string
|
|
44
|
+
let setup: setup
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
Deploy-time outputs produced when a `Task` is provisioned.
|
|
49
|
+
|
|
50
|
+
- `name` — the task's logical name
|
|
51
|
+
- `bucketNames` — resolved S3 bucket names keyed by bucket spec index
|
|
52
|
+
- `sideEffectSources` — names of aggregate event sources wired to side effects
|
|
53
|
+
*/
|
|
54
|
+
type outputs = {
|
|
55
|
+
name: string,
|
|
56
|
+
bucketNames?: dict<Pulumi.Output.t<string>>,
|
|
57
|
+
sideEffectSources?: array<string>,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
Runtime operations exposed by a `Task` component.
|
|
62
|
+
Allows the task handler to publish commands to aggregates by name.
|
|
63
|
+
*/
|
|
64
|
+
type operations = {
|
|
65
|
+
publishCommands: (string, array<Reventless.Message.commandJson>) => promise<unit>,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
Module type produced by `Platform.Task.Make(Spec)`.
|
|
70
|
+
*/
|
|
71
|
+
module type T = {
|
|
72
|
+
module Spec: Spec
|
|
73
|
+
type component
|
|
74
|
+
let make: (
|
|
75
|
+
~queryBucketName: queryBucketName,
|
|
76
|
+
~scheduler: Scheduler.operations,
|
|
77
|
+
/** URN identifying the principal/role the scheduler invokes targets as.
|
|
78
|
+
Bundled adapters thread this into the runtime config so the deployed
|
|
79
|
+
handler can call the underlying scheduler service. Adapters without
|
|
80
|
+
a real scheduler (e.g. in-memory) ignore it. */
|
|
81
|
+
~schedulerRoleUrn: Pulumi.Output.t<string>,
|
|
82
|
+
~publishToAggregates: dict<CommandTopic.publishJsons>,
|
|
83
|
+
~queryEngine: Reventless.QueryEngine.operations,
|
|
84
|
+
~resourceNaming: ResourceNaming.operations,
|
|
85
|
+
~allAggregates: dict<Aggregate.outputs>,
|
|
86
|
+
~opts: option<Pulumi.ComponentResource.options>,
|
|
87
|
+
) => component
|
|
88
|
+
let outputs: component => outputs
|
|
89
|
+
}
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
A pre-serialized command routed to a foreign extension point.
|
|
3
|
+
Used when an extension needs to dispatch to an extension point it does not
|
|
4
|
+
own — the command JSON is forwarded opaquely without re-encoding.
|
|
5
|
+
*/
|
|
6
|
+
type forwardCommand = {
|
|
7
|
+
extensionPointName: string,
|
|
8
|
+
id: string,
|
|
9
|
+
commandJson: JSON.t,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type id = string
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
Actions returned by an extension's `mapIncomingEvent` function.
|
|
16
|
+
|
|
17
|
+
When an extension point emits an event, the extension's mapping can:
|
|
18
|
+
- publish a command to the aggregate it wraps (`PublishAggregateCommand`)
|
|
19
|
+
- publish a command to the StateChangeSlice it wraps (`PublishStateChangeSliceCommand`)
|
|
20
|
+
— no id argument; the framework derives the FIFO grouping id from the command's
|
|
21
|
+
`@partitionTag` (or `@compositePartitionTag`) field.
|
|
22
|
+
- publish a command back to the extension point (`PublishExtensionPointCommand`)
|
|
23
|
+
- forward a command to another extension point opaquely (`ForwardCommand`)
|
|
24
|
+
- handle an extension-point-defined directive via an async handler (`HandleDirective`)
|
|
25
|
+
*/
|
|
26
|
+
type incomingCommandAction<'aggregateCommand, 'extensionPointCommand, 'directive> =
|
|
27
|
+
| PublishAggregateCommand(id, 'aggregateCommand)
|
|
28
|
+
| PublishAggregateCommandAsync(promise<(id, 'aggregateCommand)>)
|
|
29
|
+
| PublishAggregateCommandsAsync(promise<array<(id, 'aggregateCommand)>>)
|
|
30
|
+
| PublishStateChangeSliceCommand('aggregateCommand)
|
|
31
|
+
| PublishStateChangeSliceCommandAsync(promise<'aggregateCommand>)
|
|
32
|
+
| PublishStateChangeSliceCommandsAsync(promise<array<'aggregateCommand>>)
|
|
33
|
+
| PublishExtensionPointCommand(id, 'extensionPointCommand)
|
|
34
|
+
| ForwardCommand(forwardCommand)
|
|
35
|
+
| HandleDirective(Reventless.Handler.handler<'directive>, 'directive)
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
Actions returned by an extension's `mapOutgoingEvent` function.
|
|
39
|
+
|
|
40
|
+
When the wrapped aggregate emits an event, the extension can:
|
|
41
|
+
- publish a command to the extension point (`PublishExtensionPointCommand`)
|
|
42
|
+
- forward a command to another extension point opaquely (`ForwardCommand`)
|
|
43
|
+
- handle an extension-point-defined directive via an async handler (`HandleDirective`)
|
|
44
|
+
*/
|
|
45
|
+
type outgoingCommandAction<'extensionPointCommand, 'directive> =
|
|
46
|
+
| PublishExtensionPointCommand(id, 'extensionPointCommand)
|
|
47
|
+
| ForwardCommand(forwardCommand)
|
|
48
|
+
| HandleDirective(Reventless.Handler.handler<'directive>, 'directive)
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
Maps an extension point event to actions on the wrapped aggregate or extension point.
|
|
52
|
+
|
|
53
|
+
Called when the extension point emits an event that this extension handles.
|
|
54
|
+
Receives the entity ID, the event, message metadata, the plugin definition,
|
|
55
|
+
and the query engine.
|
|
56
|
+
*/
|
|
57
|
+
type mapIncomingEvent<
|
|
58
|
+
'extensionPointEvent,
|
|
59
|
+
'aggregateCommand,
|
|
60
|
+
'extensionPointCommand,
|
|
61
|
+
'extensionPointDirective,
|
|
62
|
+
> = (
|
|
63
|
+
string,
|
|
64
|
+
'extensionPointEvent,
|
|
65
|
+
Reventless.Message.meta,
|
|
66
|
+
Reventless.Plugin.pluginDefinition,
|
|
67
|
+
Reventless.QueryEngine.operations,
|
|
68
|
+
) => array<
|
|
69
|
+
incomingCommandAction<'aggregateCommand, 'extensionPointCommand, 'extensionPointDirective>,
|
|
70
|
+
>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
Maps an aggregate event to actions on the extension point.
|
|
74
|
+
|
|
75
|
+
Optionally defined — return `None` if the aggregate's outgoing events do not
|
|
76
|
+
need to be reflected back through the extension point.
|
|
77
|
+
*/
|
|
78
|
+
type mapOutgoingEvent<'aggregateEvent, 'extensionPointCommand, 'extensionPointDirective> = (
|
|
79
|
+
string,
|
|
80
|
+
'aggregateEvent,
|
|
81
|
+
Reventless.Message.meta,
|
|
82
|
+
Reventless.Plugin.pluginDefinition,
|
|
83
|
+
) => array<outgoingCommandAction<'extensionPointCommand, 'extensionPointDirective>>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
The extension point protocol that this `Extension` connects to.
|
|
87
|
+
Mirrors `ExtensionPoint.Spec` so the extension can be type-checked
|
|
88
|
+
against the extension point's command / event / directive types.
|
|
89
|
+
*/
|
|
90
|
+
module type Spec = {
|
|
91
|
+
let name: string
|
|
92
|
+
let moduleUrl: string
|
|
93
|
+
|
|
94
|
+
@schema
|
|
95
|
+
type command
|
|
96
|
+
@schema
|
|
97
|
+
type event
|
|
98
|
+
@schema
|
|
99
|
+
type directive
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
Application-level implementation of an extension's bidirectional mapping.
|
|
104
|
+
|
|
105
|
+
- `ExtensionPoint` — the extension point protocol this extension connects to
|
|
106
|
+
- `Delegate` — the component this extension delegates to (aggregate or DCB slice)
|
|
107
|
+
- `mapIncomingEvent` — routes extension point events to delegate commands
|
|
108
|
+
- `mapOutgoingEvent` — optionally routes delegate events back to the EP
|
|
109
|
+
*/
|
|
110
|
+
module type Mapping = {
|
|
111
|
+
module ExtensionPoint: Spec
|
|
112
|
+
module Delegate: Reventless.Aggregate.Spec
|
|
113
|
+
|
|
114
|
+
// npm-style specifier of the user extension file (the module exporting this
|
|
115
|
+
// Mapping). Used by Plugin_Helpers + the bundled Plugin EventCollector entry
|
|
116
|
+
// point to dynamic-import the user mapping for runtime reconstruction of
|
|
117
|
+
// mapIncomingEvent / mapOutgoingEvent. PPX-injected on @@reventless.extension
|
|
118
|
+
// files as the same specifier as the file-level moduleUrl.
|
|
119
|
+
let moduleUrl: string
|
|
120
|
+
|
|
121
|
+
// npm-style specifier of the Delegate's source module. Used by the bundled
|
|
122
|
+
// Plugin EventCollector entry point to dynamic-import the Delegate spec at
|
|
123
|
+
// cold start (Mapping.ExtensionPoint and Mapping.Delegate are erased in the
|
|
124
|
+
// compiled .res.mjs export). PPX-injected on @@reventless.extension files
|
|
125
|
+
// as `Delegate.moduleUrl`.
|
|
126
|
+
let delegateModuleUrl: string
|
|
127
|
+
|
|
128
|
+
let mapIncomingEvent: mapIncomingEvent<
|
|
129
|
+
ExtensionPoint.event,
|
|
130
|
+
Delegate.command,
|
|
131
|
+
ExtensionPoint.command,
|
|
132
|
+
ExtensionPoint.directive,
|
|
133
|
+
>
|
|
134
|
+
|
|
135
|
+
let mapOutgoingEvent: option<
|
|
136
|
+
mapOutgoingEvent<Delegate.event, ExtensionPoint.command, ExtensionPoint.directive>,
|
|
137
|
+
>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
A dummy target used when an extension does not wrap a real aggregate or slice.
|
|
142
|
+
Satisfies `Aggregate.Spec` with unit command / event / error types.
|
|
143
|
+
*/
|
|
144
|
+
module NoDelegate = {
|
|
145
|
+
let name = "NoDelegate"
|
|
146
|
+
|
|
147
|
+
module Id = {
|
|
148
|
+
@schema
|
|
149
|
+
type t = string
|
|
150
|
+
type input = string
|
|
151
|
+
external make: t => t = "%identity"
|
|
152
|
+
external makeFromString: string => t = "%identity"
|
|
153
|
+
external toString: t => t = "%identity"
|
|
154
|
+
let cmp = String.compare
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@schema
|
|
158
|
+
type command = unit
|
|
159
|
+
|
|
160
|
+
@schema
|
|
161
|
+
type event = unit
|
|
162
|
+
|
|
163
|
+
@schema
|
|
164
|
+
type error = unit
|
|
165
|
+
|
|
166
|
+
let commandSchema = S.unit
|
|
167
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
168
|
+
let commandAuthorization = (_: command): Reventless.Authorization.permission =>
|
|
169
|
+
AllowAuthenticated
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
open PluginExtensionPointSpec
|
|
174
|
+
|
|
175
|
+
type extensionPointName = string
|
|
176
|
+
|
|
177
|
+
/* these actions are internal to the Mapping Functor */
|
|
178
|
+
type abstractIncomingCommandAction =
|
|
179
|
+
| AbstractPublishAggregateCommand(string, Reventless.Message.commandJson)
|
|
180
|
+
| AbstractPublishAggregateCommandAsync(promise<(string, Reventless.Message.commandJson)>)
|
|
181
|
+
| AbstractPublishAggregateCommandsAsync(promise<array<(string, Reventless.Message.commandJson)>>)
|
|
182
|
+
| AbstractPublishPluginExtensionPointCommand(Reventless.Message.commandJson)
|
|
183
|
+
| AbstractPublishExtensionPointCommand(extensionPointName, Reventless.Message.commandJson)
|
|
184
|
+
| AbstractHandleDirective(Reventless.Handler.handler<unit>)
|
|
185
|
+
|
|
186
|
+
type abstractOutgoingCommandAction =
|
|
187
|
+
| AbstractPublishPluginExtensionPointCommand(Reventless.Message.commandJson)
|
|
188
|
+
| AbstractPublishExtensionPointCommand(extensionPointName, Reventless.Message.commandJson)
|
|
189
|
+
| AbstractHandleDirective(Reventless.Handler.handler<unit>)
|
|
190
|
+
|
|
191
|
+
module type T = {
|
|
192
|
+
module ExtensionPoint: Spec
|
|
193
|
+
|
|
194
|
+
let delegateName: string
|
|
195
|
+
|
|
196
|
+
let mapIncomingEvent: (
|
|
197
|
+
Reventless.Message.event'<string, ExtensionPoint.event>,
|
|
198
|
+
pluginDefinition,
|
|
199
|
+
Reventless.QueryEngine.operations,
|
|
200
|
+
) => array<abstractIncomingCommandAction>
|
|
201
|
+
|
|
202
|
+
let mapOutgoingEvent: option<(JSON.t, pluginDefinition) => array<abstractOutgoingCommandAction>>
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module type Mappings = {
|
|
206
|
+
module Spec: Spec
|
|
207
|
+
module type Mapping = T with module ExtensionPoint := Spec
|
|
208
|
+
let name: string
|
|
209
|
+
let moduleUrl: string
|
|
210
|
+
let mappings: array<module(Mapping)>
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
module Make = (MappingImpl: Mapping): (
|
|
214
|
+
T with module ExtensionPoint := MappingImpl.ExtensionPoint
|
|
215
|
+
) => {
|
|
216
|
+
module Spec = MappingImpl.ExtensionPoint
|
|
217
|
+
module Delegate = MappingImpl.Delegate
|
|
218
|
+
let delegateName = Delegate.name
|
|
219
|
+
let extensionPointName = Spec.name
|
|
220
|
+
|
|
221
|
+
// Variant TAGs the Delegate cares about — derived from the Delegate's event
|
|
222
|
+
// schema at functor instantiation. Used to pre-filter incoming envelopes:
|
|
223
|
+
// sibling variants on the source log (events the Delegate did not declare)
|
|
224
|
+
// are silently skipped without any decode attempt.
|
|
225
|
+
// extractAllVariantNames keeps payload-less variants (sury-compiled bare
|
|
226
|
+
// `S.literal("Name")` strings) — the JSON envelope's `event` TAG still
|
|
227
|
+
// carries the literal name, so the filter would otherwise drop them.
|
|
228
|
+
let acceptedTags = Reventless.DcbTag.extractAllVariantNames(Delegate.eventSchema)
|
|
229
|
+
|
|
230
|
+
// Lazily-computed partition-tag derivation for `PublishStateChangeSliceCommand*`.
|
|
231
|
+
// Derived from the Delegate's command schema; throws if no `@partitionTag` /
|
|
232
|
+
// `@compositePartitionTag` annotation exists (i.e. the Delegate is an Aggregate,
|
|
233
|
+
// not a StateChangeSlice — in which case the user should use
|
|
234
|
+
// `PublishAggregateCommand` instead).
|
|
235
|
+
let derivedPartitionTagLazy = ref(None)
|
|
236
|
+
let getDerivedPartitionTag = () =>
|
|
237
|
+
switch derivedPartitionTagLazy.contents {
|
|
238
|
+
| Some(d) => d
|
|
239
|
+
| None =>
|
|
240
|
+
let d = Reventless.DcbTag.derivePartitionTag([
|
|
241
|
+
(Delegate.name, Delegate.moduleUrl, Delegate.commandSchema->S.castToUnknown),
|
|
242
|
+
])
|
|
243
|
+
derivedPartitionTagLazy := Some(d)
|
|
244
|
+
d
|
|
245
|
+
}
|
|
246
|
+
let derivePartitionId = (targetCmd: Delegate.command): string =>
|
|
247
|
+
switch getDerivedPartitionTag() {
|
|
248
|
+
| Simple(pt) =>
|
|
249
|
+
let tags = Reventless.DcbTag.extractTags(Delegate.commandSchema, targetCmd)
|
|
250
|
+
Reventless.DcbTag.getPartitionTagValue([{tags: tags}], pt)->Option.getOr("")
|
|
251
|
+
| Composite(spec) =>
|
|
252
|
+
let tags = Reventless.DcbTag.extractTags(Delegate.commandSchema, targetCmd)
|
|
253
|
+
Reventless.DcbTag.getCompositePartitionKeyValue(tags, spec)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Carry `comp` as a structured Effect log annotation — EffectLogger.install
|
|
257
|
+
// (in reventless-core, wired at Lambda startup) lifts it to the top-level
|
|
258
|
+
// JSON `comp` field. Without the unified logger installed, Effect's default
|
|
259
|
+
// logger still renders the annotation, just less prettily.
|
|
260
|
+
let compLog = (comp, msg) =>
|
|
261
|
+
Effect.logInfo(msg)->Effect.annotateLogs("comp", comp)->Effect.runSync
|
|
262
|
+
|
|
263
|
+
let encodeMeta = (meta: Reventless.Message.meta, service) => {
|
|
264
|
+
...meta,
|
|
265
|
+
service,
|
|
266
|
+
msgId: Message.uuid(),
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let doMapIncomingEvent = (
|
|
270
|
+
mapIncomingEventImpl,
|
|
271
|
+
{Reventless.Message.id: id, event, meta},
|
|
272
|
+
pluginDef,
|
|
273
|
+
queryEngine,
|
|
274
|
+
) => {
|
|
275
|
+
let encodeTargetCommandJson = (targetCmd, targetId) => {
|
|
276
|
+
let cmdJson = targetCmd->Reventless.Message.encode(Delegate.commandSchema)
|
|
277
|
+
{
|
|
278
|
+
Reventless.Message.id: targetId,
|
|
279
|
+
meta: encodeMeta(meta, delegateName),
|
|
280
|
+
commandJson: cmdJson,
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let encodeExtensionPointCommandJson = (commandJson, ~id, ~extensionPointName, ~action) => {
|
|
285
|
+
compLog(`Extension(${extensionPointName})`, `${action}: ${commandJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id})`)
|
|
286
|
+
{
|
|
287
|
+
Reventless.Message.id,
|
|
288
|
+
meta: encodeMeta(meta, extensionPointName),
|
|
289
|
+
commandJson,
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let encodeExtensionPointCommand = (command, ~id, ~extensionPointName, ~action) =>
|
|
294
|
+
command
|
|
295
|
+
->Reventless.Message.encode(Spec.commandSchema)
|
|
296
|
+
->encodeExtensionPointCommandJson(~id, ~extensionPointName, ~action)
|
|
297
|
+
|
|
298
|
+
mapIncomingEventImpl(id, event, meta, pluginDef, queryEngine)->Array.map(x =>
|
|
299
|
+
switch x {
|
|
300
|
+
| PublishAggregateCommand(targetId, targetCmd) =>
|
|
301
|
+
AbstractPublishAggregateCommand(
|
|
302
|
+
delegateName,
|
|
303
|
+
targetCmd->encodeTargetCommandJson(targetId),
|
|
304
|
+
)
|
|
305
|
+
| PublishAggregateCommandAsync(promise) =>
|
|
306
|
+
let toCommandJson = async promise => {
|
|
307
|
+
let (targetId, targetCmd) = await promise
|
|
308
|
+
(delegateName, targetCmd->encodeTargetCommandJson(targetId))
|
|
309
|
+
}
|
|
310
|
+
AbstractPublishAggregateCommandAsync(promise->toCommandJson)
|
|
311
|
+
| PublishAggregateCommandsAsync(promise) =>
|
|
312
|
+
let toCommandJsons = async promise =>
|
|
313
|
+
(await promise)->Array.map(((targetId, targetCmd)) => (
|
|
314
|
+
delegateName,
|
|
315
|
+
targetCmd->encodeTargetCommandJson(targetId),
|
|
316
|
+
))
|
|
317
|
+
AbstractPublishAggregateCommandsAsync(promise->toCommandJsons)
|
|
318
|
+
| PublishStateChangeSliceCommand(targetCmd) =>
|
|
319
|
+
AbstractPublishAggregateCommand(
|
|
320
|
+
delegateName,
|
|
321
|
+
targetCmd->encodeTargetCommandJson(derivePartitionId(targetCmd)),
|
|
322
|
+
)
|
|
323
|
+
| PublishStateChangeSliceCommandAsync(promise) =>
|
|
324
|
+
let toCommandJson = async promise => {
|
|
325
|
+
let targetCmd = await promise
|
|
326
|
+
(delegateName, targetCmd->encodeTargetCommandJson(derivePartitionId(targetCmd)))
|
|
327
|
+
}
|
|
328
|
+
AbstractPublishAggregateCommandAsync(promise->toCommandJson)
|
|
329
|
+
| PublishStateChangeSliceCommandsAsync(promise) =>
|
|
330
|
+
let toCommandJsons = async promise =>
|
|
331
|
+
(await promise)->Array.map(targetCmd => (
|
|
332
|
+
delegateName,
|
|
333
|
+
targetCmd->encodeTargetCommandJson(derivePartitionId(targetCmd)),
|
|
334
|
+
))
|
|
335
|
+
AbstractPublishAggregateCommandsAsync(promise->toCommandJsons)
|
|
336
|
+
| PublishExtensionPointCommand(id, command)
|
|
337
|
+
if Spec.name == PluginExtensionPointSpec.name =>
|
|
338
|
+
AbstractPublishPluginExtensionPointCommand(
|
|
339
|
+
command->encodeExtensionPointCommand(
|
|
340
|
+
~id,
|
|
341
|
+
~extensionPointName,
|
|
342
|
+
~action="Publish PluginExtensionPoint command",
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
| PublishExtensionPointCommand(id, command) =>
|
|
346
|
+
AbstractPublishExtensionPointCommand(
|
|
347
|
+
extensionPointName,
|
|
348
|
+
command->encodeExtensionPointCommand(
|
|
349
|
+
~id,
|
|
350
|
+
~extensionPointName,
|
|
351
|
+
~action="Publish ExtensionPoint command",
|
|
352
|
+
),
|
|
353
|
+
)
|
|
354
|
+
| ForwardCommand({extensionPointName, id, commandJson}) =>
|
|
355
|
+
AbstractPublishExtensionPointCommand(
|
|
356
|
+
extensionPointName,
|
|
357
|
+
commandJson->encodeExtensionPointCommandJson(
|
|
358
|
+
~id,
|
|
359
|
+
~extensionPointName,
|
|
360
|
+
~action="Forward ExtensionPoint command",
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
| HandleDirective(handler, directive) =>
|
|
364
|
+
compLog(`Extension(${extensionPointName})`, "incoming directive")
|
|
365
|
+
AbstractHandleDirective(() => handler(directive))
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
let mapIncomingEvent = doMapIncomingEvent(MappingImpl.mapIncomingEvent, ...)
|
|
371
|
+
|
|
372
|
+
let variantTagOfEnvelope = json =>
|
|
373
|
+
json
|
|
374
|
+
->JSON.Decode.object
|
|
375
|
+
->Option.flatMap(d => d->Dict.get("event"))
|
|
376
|
+
->Option.map(Reventless.Message.variantNameOfJson)
|
|
377
|
+
->Option.getOr("unknown")
|
|
378
|
+
|
|
379
|
+
let doMapOutgoingEvent = (mapOutgoingEventImpl, targetEvent'Json, pluginDef) => {
|
|
380
|
+
let tag = variantTagOfEnvelope(targetEvent'Json)
|
|
381
|
+
// Pre-filter by TAG: sibling variants from the source log that the Delegate
|
|
382
|
+
// did not declare are not this mapping's concern — skip silently with no
|
|
383
|
+
// decode attempt.
|
|
384
|
+
if !(acceptedTags->Array.includes(tag)) {
|
|
385
|
+
[]
|
|
386
|
+
} else {
|
|
387
|
+
let {id, meta, event} =
|
|
388
|
+
targetEvent'Json->Reventless.Message.decodeEvent'(
|
|
389
|
+
Delegate.Id.schema,
|
|
390
|
+
Delegate.eventSchema,
|
|
391
|
+
)
|
|
392
|
+
let encodeExtensionPointCommandJson = (commandJson, ~id, ~extensionPointName, ~action) => {
|
|
393
|
+
compLog(`Extension(${delegateName})`, `${action}: ${commandJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id})`)
|
|
394
|
+
{
|
|
395
|
+
Reventless.Message.id,
|
|
396
|
+
meta: encodeMeta(meta, extensionPointName),
|
|
397
|
+
commandJson,
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
let encodeExtensionPointCommand = (command, ~id, ~extensionPointName, ~action) =>
|
|
402
|
+
command
|
|
403
|
+
->Reventless.Message.encode(Spec.commandSchema)
|
|
404
|
+
->encodeExtensionPointCommandJson(~id, ~extensionPointName, ~action)
|
|
405
|
+
|
|
406
|
+
mapOutgoingEventImpl(id->Delegate.Id.toString, event, meta, pluginDef)->Array.map(x =>
|
|
407
|
+
switch x {
|
|
408
|
+
| PublishExtensionPointCommand(id, command)
|
|
409
|
+
if Spec.name == PluginExtensionPointSpec.name =>
|
|
410
|
+
AbstractPublishPluginExtensionPointCommand(
|
|
411
|
+
command->encodeExtensionPointCommand(
|
|
412
|
+
~id,
|
|
413
|
+
~extensionPointName,
|
|
414
|
+
~action="Publish PluginExtensionPoint command",
|
|
415
|
+
),
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
| PublishExtensionPointCommand(id, command) =>
|
|
419
|
+
AbstractPublishExtensionPointCommand(
|
|
420
|
+
extensionPointName,
|
|
421
|
+
command->encodeExtensionPointCommand(
|
|
422
|
+
~id,
|
|
423
|
+
~extensionPointName,
|
|
424
|
+
~action="Publish ExtensionPoint command",
|
|
425
|
+
),
|
|
426
|
+
)
|
|
427
|
+
| ForwardCommand({extensionPointName, id, commandJson}) =>
|
|
428
|
+
AbstractPublishExtensionPointCommand(
|
|
429
|
+
extensionPointName,
|
|
430
|
+
commandJson->encodeExtensionPointCommandJson(
|
|
431
|
+
~id,
|
|
432
|
+
~extensionPointName,
|
|
433
|
+
~action="Forward ExtensionPoint command",
|
|
434
|
+
),
|
|
435
|
+
)
|
|
436
|
+
| HandleDirective(handler, directive) =>
|
|
437
|
+
compLog(`Extension(${delegateName})`, "outgoing directive")
|
|
438
|
+
AbstractHandleDirective(() => handler(directive))
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
let mapOutgoingEvent =
|
|
445
|
+
MappingImpl.mapOutgoingEvent->Option.map(mapOutgoingEventImpl =>
|
|
446
|
+
doMapOutgoingEvent(mapOutgoingEventImpl, ...)
|
|
447
|
+
)
|
|
448
|
+
}
|