@reventlessdev/reventless-core 3.0.0-alpha.173 → 3.0.0-alpha.175
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 +5 -5
- package/src/ComponentType.res +8 -1
- package/src/ComponentType.res.mjs +6 -0
- package/src/RequestContext.res +45 -1
- package/src/RequestContext.res.mjs +12 -2
- package/src/ResourceAttribution.res +133 -0
- package/src/ResourceAttribution.res.mjs +96 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +24 -5
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res.mjs +9 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +6 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +3 -3
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +6 -1
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +3 -1
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +25 -7
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +11 -8
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +2 -0
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +1 -1
- package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +4 -0
- package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res.mjs +2 -2
- package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +4 -0
- package/src/adapter/Runtime/PluginRuntime_Builder_Single.res.mjs +2 -2
- package/src/adapter/Runtime/Runtime.res +77 -13
- package/src/adapter/Runtime/Runtime.res.mjs +24 -8
- package/src/admin/Platform_Admin.res +1 -1
- package/src/admin/Platform_Admin.res.mjs +8 -2
- package/src/components/Aggregate/Aggregate_Builder.res +10 -2
- package/src/components/Aggregate/Aggregate_Builder.res.mjs +8 -2
- package/src/components/AutomationSlice/AutomationSlice_Builder.res +12 -2
- package/src/components/AutomationSlice/AutomationSlice_Builder.res.mjs +8 -2
- package/src/components/CommandTopic/CommandTopic.res +5 -1
- package/src/components/CommandTopic/CommandTopic_Adapter.res +1 -0
- package/src/components/CommandTopic/CommandTopic_Builder.res +4 -4
- package/src/components/CommandTopic/CommandTopic_Builder.res.mjs +29 -30
- package/src/components/Counter/Counter_Builder.res +7 -1
- package/src/components/Counter/Counter_Builder.res.mjs +5 -2
- package/src/components/Dcb/Dcb_Builder.res +2 -2
- package/src/components/Dcb/Dcb_Builder.res.mjs +8 -2
- package/src/components/DcbEventLog/DcbEventLog_Builder.res +3 -0
- package/src/components/DcbEventLog/DcbEventLog_Builder.res.mjs +4 -1
- package/src/components/EventCollector/EventCollector.res +1 -0
- package/src/components/EventCollector/EventCollector_Adapter.res +1 -0
- package/src/components/EventCollector/EventCollector_Builder.res +4 -4
- package/src/components/EventCollector/EventCollector_Builder.res.mjs +2 -2
- package/src/components/EventLog/EventLog.res +5 -1
- package/src/components/EventLog/EventLog_Adapter.res +5 -1
- package/src/components/EventLog/EventLog_Builder.res +9 -4
- package/src/components/EventLog/EventLog_Builder.res.mjs +7 -8
- package/src/components/EventMapper/EventMapper_Builder.res +6 -1
- package/src/components/EventMapper/EventMapper_Builder.res.mjs +4 -1
- package/src/components/EventTopic/EventTopic.res +1 -0
- package/src/components/EventTopic/EventTopic_Adapter.res +1 -0
- package/src/components/EventTopic/EventTopic_Builder.res +4 -3
- package/src/components/EventTopic/EventTopic_Builder.res.mjs +2 -2
- package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +11 -2
- package/src/components/ExtensionPoint/ExtensionPoint_Builder.res.mjs +8 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +6 -1
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res.mjs +4 -1
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +12 -2
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +8 -2
- package/src/components/QueryDb/QueryDb.res +1 -0
- package/src/components/QueryDb/QueryDb_Adapter.res +1 -0
- package/src/components/QueryDb/QueryDb_Builder.res +4 -3
- package/src/components/QueryDb/QueryDb_Builder.res.mjs +2 -2
- package/src/components/ReadModel/ReadModel_Builder.res +12 -2
- package/src/components/ReadModel/ReadModel_Builder.res.mjs +8 -2
- package/src/components/SideEffectHandler/SideEffectHandler_Builder.res +6 -1
- package/src/components/SideEffectHandler/SideEffectHandler_Builder.res.mjs +4 -1
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +12 -2
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +8 -2
- package/src/plugin/component/Plugin_Builder.res +6 -0
- package/src/plugin/component/Plugin_Builder.res.mjs +3 -0
- package/src/plugin/component/Plugin_Helpers.res +1 -1
- package/src/plugin/component/Plugin_Helpers.res.mjs +4 -1
- package/tests/ResourceAttributionTest.res +70 -0
- package/tests/aggregate/AggregateCacheTest.res +1 -1
- package/tests/aggregate/AggregateConflictTest.res +1 -1
- package/tests/aggregate/AggregateFixtures.res +1 -1
- package/tests/aggregate/AggregateSnapshotTest.res +1 -1
- package/tests/eventlog/EventLogFixtures.res +1 -1
- package/tests/logger/LogFormatTest.res +43 -0
|
@@ -58,7 +58,12 @@ module Make = (
|
|
|
58
58
|
let timeout = ReventlessInfra.RuntimeHints.resolveTimeout(runtime, ~default=30)
|
|
59
59
|
|
|
60
60
|
module SpecificQueryDb = QueryDb_Builder.Make(Spec, QueryDbStorage, QueryDbResolvers)
|
|
61
|
-
let queryDb = SpecificQueryDb.make(
|
|
61
|
+
let queryDb = SpecificQueryDb.make(
|
|
62
|
+
~api,
|
|
63
|
+
~apiRole,
|
|
64
|
+
~owner={kind: ComponentType.ReadModel, name: Spec.name},
|
|
65
|
+
~opts,
|
|
66
|
+
)
|
|
62
67
|
|
|
63
68
|
let toProjectionOperations: SpecificQueryDb.operations => projectionOperations = ({
|
|
64
69
|
load,
|
|
@@ -114,7 +119,12 @@ module Make = (
|
|
|
114
119
|
->Component.operations
|
|
115
120
|
->Pulumi.Output.apply(operations => {
|
|
116
121
|
let eventTopics = allEventTopics->EventTopic.filter(sourceNames)
|
|
117
|
-
let eventCollector = SpecificEventCollector.make(
|
|
122
|
+
let eventCollector = SpecificEventCollector.make(
|
|
123
|
+
~name,
|
|
124
|
+
~eventTopics,
|
|
125
|
+
~owner={kind: ComponentType.ReadModel, name: Spec.name},
|
|
126
|
+
~opts,
|
|
127
|
+
)
|
|
118
128
|
|
|
119
129
|
module Callback = ReadModel_Callback.Make(
|
|
120
130
|
Spec,
|
|
@@ -27,7 +27,10 @@ function Make(Spec) {
|
|
|
27
27
|
let memorySize = RuntimeHints$ReventlessInfra.resolveMemory(runtime, 1024);
|
|
28
28
|
let timeout = RuntimeHints$ReventlessInfra.resolveTimeout(runtime, 30);
|
|
29
29
|
let SpecificQueryDb = QueryDb_Builder$ReventlessCore.Make(Spec)(QueryDbStorage)(QueryDbResolvers);
|
|
30
|
-
let queryDb = SpecificQueryDb.make(api, apiRole, undefined,
|
|
30
|
+
let queryDb = SpecificQueryDb.make(api, apiRole, undefined, {
|
|
31
|
+
kind: "ReadModel",
|
|
32
|
+
name: Spec.name
|
|
33
|
+
}, opts);
|
|
31
34
|
let toProjectionOperations = param => {
|
|
32
35
|
let deleteBatch = param.deleteBatch;
|
|
33
36
|
let $$delete = param.delete;
|
|
@@ -64,7 +67,10 @@ function Make(Spec) {
|
|
|
64
67
|
let SpecificEventCollector = EventCollector_Builder$ReventlessCore.Make(RuntimeEnvironment)(EventCollectorChannel);
|
|
65
68
|
let eventCollector = Component$ReventlessCore.operations(queryDb).apply(operations => {
|
|
66
69
|
let eventTopics = EventTopic$ReventlessCore.filter(allEventTopics, sourceNames);
|
|
67
|
-
let eventCollector = SpecificEventCollector.make(name, eventTopics,
|
|
70
|
+
let eventCollector = SpecificEventCollector.make(name, eventTopics, {
|
|
71
|
+
kind: "ReadModel",
|
|
72
|
+
name: Spec.name
|
|
73
|
+
}, opts);
|
|
68
74
|
let operations$1 = toProjectionOperations(operations);
|
|
69
75
|
let Callback = ReadModel_Callback$ReventlessCore.Make(Spec)(Mappings)({
|
|
70
76
|
ReadModelSpec: Spec,
|
|
@@ -27,7 +27,12 @@ module Make = (
|
|
|
27
27
|
->Belt.Set.String.fromArray
|
|
28
28
|
|
|
29
29
|
let eventTopics = allEventTopics->EventTopic.filter(aggregateNames)
|
|
30
|
-
let eventCollector = SpecificEventCollector.make(
|
|
30
|
+
let eventCollector = SpecificEventCollector.make(
|
|
31
|
+
~name,
|
|
32
|
+
~eventTopics,
|
|
33
|
+
~owner={kind: ComponentType.SideEffectHandler, name},
|
|
34
|
+
~opts,
|
|
35
|
+
)
|
|
31
36
|
|
|
32
37
|
module Callback = SideEffectHandler_Callback.Make({
|
|
33
38
|
let sideEffects = sideEffects
|
|
@@ -24,7 +24,10 @@ function Make(RuntimeEnvironment) {
|
|
|
24
24
|
};
|
|
25
25
|
let aggregateNames = Belt_SetString.fromArray(sideEffects.map(SideEffect => SideEffect.Source.name));
|
|
26
26
|
let eventTopics = EventTopic$ReventlessCore.filter(allEventTopics, aggregateNames);
|
|
27
|
-
let eventCollector = SpecificEventCollector.make(extra$1, eventTopics,
|
|
27
|
+
let eventCollector = SpecificEventCollector.make(extra$1, eventTopics, {
|
|
28
|
+
kind: "SideEffectHandler",
|
|
29
|
+
name: extra$1
|
|
30
|
+
}, opts);
|
|
28
31
|
let Callback = SideEffectHandler_Callback$ReventlessCore.Make({
|
|
29
32
|
sideEffects: sideEffects,
|
|
30
33
|
queryEngine: queryEngine
|
|
@@ -63,7 +63,12 @@ module Make = (
|
|
|
63
63
|
let memorySize = ReventlessInfra.RuntimeHints.resolveMemory(runtime, ~default=1024)
|
|
64
64
|
let timeout = ReventlessInfra.RuntimeHints.resolveTimeout(runtime, ~default=30)
|
|
65
65
|
|
|
66
|
-
let queryDb = SpecificQueryDb.make(
|
|
66
|
+
let queryDb = SpecificQueryDb.make(
|
|
67
|
+
~api=Api.api(),
|
|
68
|
+
~apiRole=Api.apiRole(),
|
|
69
|
+
~owner={kind: ComponentType.StateViewSlice, name: Spec.name},
|
|
70
|
+
~opts,
|
|
71
|
+
)
|
|
67
72
|
|
|
68
73
|
let dcbEventTopicOutputs: EventTopic.outputs = (dcbEventLog->Component.outputs).eventTopic
|
|
69
74
|
let allEventTopics = Dict.fromArray([(Spec.name, dcbEventTopicOutputs)])
|
|
@@ -74,7 +79,12 @@ module Make = (
|
|
|
74
79
|
->Pulumi.Output.apply(queryDbOps => {
|
|
75
80
|
let projectionOps = toProjectionOps(queryDbOps)
|
|
76
81
|
|
|
77
|
-
let ec = SpecificEventCollector.make(
|
|
82
|
+
let ec = SpecificEventCollector.make(
|
|
83
|
+
~name=Spec.name,
|
|
84
|
+
~eventTopics=allEventTopics,
|
|
85
|
+
~owner={kind: ComponentType.StateViewSlice, name: Spec.name},
|
|
86
|
+
~opts,
|
|
87
|
+
)
|
|
78
88
|
|
|
79
89
|
let comp = `StateViewSlice(${Spec.name})`
|
|
80
90
|
|
|
@@ -61,7 +61,10 @@ function Make(RuntimeEnvironment) {
|
|
|
61
61
|
};
|
|
62
62
|
let memorySize = RuntimeHints$ReventlessInfra.resolveMemory(runtime, 1024);
|
|
63
63
|
let timeout = RuntimeHints$ReventlessInfra.resolveTimeout(runtime, 30);
|
|
64
|
-
let queryDb = SpecificQueryDb.make(Api.api(), Api.apiRole(), undefined,
|
|
64
|
+
let queryDb = SpecificQueryDb.make(Api.api(), Api.apiRole(), undefined, {
|
|
65
|
+
kind: "StateViewSlice",
|
|
66
|
+
name: Spec.name
|
|
67
|
+
}, opts);
|
|
65
68
|
let dcbEventTopicOutputs = Component$ReventlessCore.outputs(dcbEventLog).eventTopic;
|
|
66
69
|
let allEventTopics = Object.fromEntries([[
|
|
67
70
|
Spec.name,
|
|
@@ -69,7 +72,10 @@ function Make(RuntimeEnvironment) {
|
|
|
69
72
|
]]);
|
|
70
73
|
let eventCollector = Component$ReventlessCore.operations(queryDb).apply(queryDbOps => {
|
|
71
74
|
let projectionOps = toProjectionOps(queryDbOps);
|
|
72
|
-
let ec = SpecificEventCollector.make(Spec.name, allEventTopics,
|
|
75
|
+
let ec = SpecificEventCollector.make(Spec.name, allEventTopics, {
|
|
76
|
+
kind: "StateViewSlice",
|
|
77
|
+
name: Spec.name
|
|
78
|
+
}, opts);
|
|
73
79
|
let comp = `StateViewSlice(` + Spec.name + `)`;
|
|
74
80
|
let jsonEventsHandler = stream => Effect.flatMap(Stream.runCollect(stream), events => {
|
|
75
81
|
let total = events.length.toString();
|
|
@@ -67,6 +67,11 @@ module Make = (
|
|
|
67
67
|
let _prevPluginName = Logger.currentPluginName.contents
|
|
68
68
|
Logger.currentPluginName := Some(name)
|
|
69
69
|
|
|
70
|
+
// Publish the owning plugin/platform for resource attribution, so adapters
|
|
71
|
+
// creating infrastructure below this point can tag it without every adapter
|
|
72
|
+
// signature having to carry the two names. Restored at the end of construct.
|
|
73
|
+
let _prevAttribution = ResourceAttribution.enter(~platform=Spec.platformName, ~plugin=name)
|
|
74
|
+
|
|
70
75
|
// Register every component → plugin so runtime logs (fired after construct
|
|
71
76
|
// returns) can resolve a comp like `StateChangeSlice(AddProduct)` to its
|
|
72
77
|
// owning plugin via `Logger.componentPluginRegistry`. Also register the
|
|
@@ -975,6 +980,7 @@ module Make = (
|
|
|
975
980
|
)
|
|
976
981
|
|
|
977
982
|
Logger.currentPluginName := _prevPluginName
|
|
983
|
+
ResourceAttribution.restore(_prevAttribution)
|
|
978
984
|
}
|
|
979
985
|
|
|
980
986
|
let makeAutoUIManifest = (
|
|
@@ -31,6 +31,7 @@ import * as AdapterDeploytime$ReventlessCore from "../../adapter/AdapterDeployti
|
|
|
31
31
|
import * as ExtensionMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionMapping.res.mjs";
|
|
32
32
|
import * as ExtensionPoint$ReventlessInterop from "@reventlessdev/reventless-interop/src/components/ExtensionPoint.res.mjs";
|
|
33
33
|
import * as Heartbeat_Builder$ReventlessCore from "../../components/Heartbeat/Heartbeat_Builder.res.mjs";
|
|
34
|
+
import * as ResourceAttribution$ReventlessCore from "../../ResourceAttribution.res.mjs";
|
|
34
35
|
import * as PluginExtensionPointSpec$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
|
|
35
36
|
import * as Plugin_SubscriptionSchema$ReventlessCore from "./Plugin_SubscriptionSchema.res.mjs";
|
|
36
37
|
|
|
@@ -121,6 +122,7 @@ function Make(Spec) {
|
|
|
121
122
|
return Component$ReventlessCore.make(ComponentType$ReventlessCore.toString(Plugin$ReventlessCore.componentType), name, (extra, extra$1) => {
|
|
122
123
|
let _prevPluginName = Logger$ReventlessCore.currentPluginName.contents;
|
|
123
124
|
Logger$ReventlessCore.currentPluginName.contents = extra$1;
|
|
125
|
+
let _prevAttribution = ResourceAttribution$ReventlessCore.enter(Spec.platformName, extra$1);
|
|
124
126
|
Logger$ReventlessCore.registerComponentPlugin(extra$1, extra$1);
|
|
125
127
|
aggregates.forEach(M => Logger$ReventlessCore.registerComponentPlugin(M.Spec.name, extra$1));
|
|
126
128
|
readModels.forEach(R => Logger$ReventlessCore.registerComponentPlugin(R.Spec.name, extra$1));
|
|
@@ -626,6 +628,7 @@ function Make(Spec) {
|
|
|
626
628
|
Component$ReventlessCore.setOutputs(extra, pluginOutputs);
|
|
627
629
|
Plugin_Helpers$ReventlessCore.interopMetaOutput.contents = builderOutputs.apply(outputs => S.reverseConvertToJsonOrThrow(Plugin_Helpers$ReventlessCore.toInteropMeta(outputs), ExportMeta$ReventlessInterop.schema));
|
|
628
630
|
Logger$ReventlessCore.currentPluginName.contents = _prevPluginName;
|
|
631
|
+
return ResourceAttribution$ReventlessCore.restore(_prevAttribution);
|
|
629
632
|
}, opts);
|
|
630
633
|
};
|
|
631
634
|
return {
|
|
@@ -449,7 +449,7 @@ module MakeEventCollectorHelper = (
|
|
|
449
449
|
EventCollectorChannel,
|
|
450
450
|
)
|
|
451
451
|
let make = (~name, ~eventTopics, ~opts) => {
|
|
452
|
-
let eventCollector = PluginEventCollector.make(~name, ~eventTopics, ~opts)
|
|
452
|
+
let eventCollector = PluginEventCollector.make(~name, ~eventTopics, ~owner={kind: ComponentType.Plugin, name}, ~opts)
|
|
453
453
|
let eventCollectorOutputs = eventCollector->Component.outputs
|
|
454
454
|
// pluginDefinition.eventCollector must identify the EC's inbound queue
|
|
455
455
|
// ARN — admin's `manageSubscriptions` uses it as the subscription target
|
|
@@ -255,7 +255,10 @@ function MakeEventCollectorHelper(RuntimeEnvironment) {
|
|
|
255
255
|
return EventCollectorChannel => (PluginRuntimeBuilder => {
|
|
256
256
|
let PluginEventCollector = EventCollector_Builder$ReventlessCore.Make(RuntimeEnvironment)(EventCollectorChannel);
|
|
257
257
|
let make = (name, eventTopics, opts) => {
|
|
258
|
-
let eventCollector = PluginEventCollector.make(name, eventTopics,
|
|
258
|
+
let eventCollector = PluginEventCollector.make(name, eventTopics, {
|
|
259
|
+
kind: "Plugin",
|
|
260
|
+
name: name
|
|
261
|
+
}, opts);
|
|
259
262
|
let eventCollectorOutputs = Component$ReventlessCore.outputs(eventCollector);
|
|
260
263
|
let resources = eventCollectorOutputs.resources;
|
|
261
264
|
let r = resources[resources.length - 1 | 0];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// The attribution vocabulary is the shared contract between the AWS tag renderer
|
|
4
|
+
// and the Kubernetes label renderer. These guard the wire spellings (which land
|
|
5
|
+
// in deployed resource metadata and in downstream inventories) and the ambient
|
|
6
|
+
// context's save/restore discipline, which is what keeps a plugin's resources
|
|
7
|
+
// from being attributed to whichever plugin was constructed before it.
|
|
8
|
+
|
|
9
|
+
module Attribution = ResourceAttribution
|
|
10
|
+
|
|
11
|
+
describe("ResourceAttribution.Scope — wire spellings", () => {
|
|
12
|
+
testSync("scopes render lower-case", () => {
|
|
13
|
+
expect(
|
|
14
|
+
[Attribution.Scope.Component, Plugin, Platform]->Array.map(Attribution.Scope.toString),
|
|
15
|
+
)->toEqual(["component", "plugin", "platform"])
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe("ResourceAttribution.Role — wire spellings", () => {
|
|
20
|
+
testSync("piece roles render as their constructor name", () => {
|
|
21
|
+
expect(
|
|
22
|
+
[Attribution.Role.EventLog, QueryDb, Runtime, DeadLetter]->Array.map(
|
|
23
|
+
Attribution.Role.toString,
|
|
24
|
+
),
|
|
25
|
+
)->toEqual(["EventLog", "QueryDb", "Runtime", "DeadLetter"])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
testSync("Other carries the caller's name verbatim", () => {
|
|
29
|
+
expect(Attribution.Role.Other("ChangeFeed")->Attribution.Role.toString)->toBe("ChangeFeed")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
testSync("platform-level things are Platform, not Core", () => {
|
|
33
|
+
// `Core` predates the platform/plugin split and names nothing the framework
|
|
34
|
+
// builds; it also reads as the built-in core PLUGIN, which is a different
|
|
35
|
+
// thing. Platform substrate is attributed to `Platform`, matching
|
|
36
|
+
// `reventless:platform` and `Scope.Platform`.
|
|
37
|
+
expect(ComponentType.Platform->ComponentType.toString)->toBe("Platform")
|
|
38
|
+
expect(ComponentType.ofString("Platform"))->toEqual(Some(ComponentType.Platform))
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
testSync("a role is NOT a component kind — Runtime has no ComponentType twin", () => {
|
|
42
|
+
// The whole point of splitting role from kind: `Runtime` is a deployment
|
|
43
|
+
// piece, not something the domain model can be. If this ever resolves, the
|
|
44
|
+
// two vocabularies have started to merge again.
|
|
45
|
+
expect(ComponentType.ofString("Runtime"))->toEqual(None)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe("ResourceAttribution — ambient plugin context", () => {
|
|
50
|
+
testSync("outside any plugin construct, nothing is attributed", () => {
|
|
51
|
+
expect(Attribution.current.contents.plugin)->toEqual(None)
|
|
52
|
+
expect(Attribution.current.contents.platform)->toEqual(None)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
testSync("enter publishes the plugin and platform under construction", () => {
|
|
56
|
+
let previous = Attribution.enter(~platform="online-shop", ~plugin="Ordering")
|
|
57
|
+
expect(Attribution.current.contents.plugin)->toEqual(Some("Ordering"))
|
|
58
|
+
expect(Attribution.current.contents.platform)->toEqual(Some("online-shop"))
|
|
59
|
+
Attribution.restore(previous)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
testSync("restore unwinds to the previous context, so plugins don't leak", () => {
|
|
63
|
+
let outer = Attribution.enter(~platform="online-shop", ~plugin="Catalog")
|
|
64
|
+
let inner = Attribution.enter(~platform="online-shop", ~plugin="Ordering")
|
|
65
|
+
Attribution.restore(inner)
|
|
66
|
+
expect(Attribution.current.contents.plugin)->toEqual(Some("Catalog"))
|
|
67
|
+
Attribution.restore(outer)
|
|
68
|
+
expect(Attribution.current.contents.plugin)->toEqual(None)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
@@ -166,7 +166,7 @@ module TestOps = {
|
|
|
166
166
|
}
|
|
167
167
|
type component = Component.t<OuterEventLog.t, OuterEventLog.outputs, operations>
|
|
168
168
|
// Never called — satisfies module type only
|
|
169
|
-
let make = (~name as _: string, ~opts as _=?): component => Obj.magic(0)
|
|
169
|
+
let make = (~name as _: string, ~owner as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
170
170
|
}
|
|
171
171
|
let eventLog: EventLog.operations = {
|
|
172
172
|
append: mock.appendFn,
|
|
@@ -126,7 +126,7 @@ module TestOps = {
|
|
|
126
126
|
writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
|
|
127
127
|
}
|
|
128
128
|
type component = Component.t<OuterEventLog.t, OuterEventLog.outputs, operations>
|
|
129
|
-
let make = (~name as _: string, ~opts as _=?): component => Obj.magic(0)
|
|
129
|
+
let make = (~name as _: string, ~owner as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
130
130
|
}
|
|
131
131
|
let eventLog: EventLog.operations = {
|
|
132
132
|
append: mock.appendFn,
|
|
@@ -142,7 +142,7 @@ module TestOps = {
|
|
|
142
142
|
}
|
|
143
143
|
type component = Component.t<OuterEventLog.t, OuterEventLog.outputs, operations>
|
|
144
144
|
// Never called — satisfies module type only
|
|
145
|
-
let make = (~name as _: string, ~opts as _=?): component => Obj.magic(0)
|
|
145
|
+
let make = (~name as _: string, ~owner as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
146
146
|
}
|
|
147
147
|
let eventLog: EventLog.operations = {
|
|
148
148
|
append: mock.appendFn,
|
|
@@ -188,7 +188,7 @@ module MakeOps = (B: {let name: string}) => {
|
|
|
188
188
|
writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
|
|
189
189
|
}
|
|
190
190
|
type component = Component.t<OuterEventLog.t, OuterEventLog.outputs, operations>
|
|
191
|
-
let make = (~name as _: string, ~opts as _=?): component => Obj.magic(0)
|
|
191
|
+
let make = (~name as _: string, ~owner as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
192
192
|
}
|
|
193
193
|
let eventLog: EventLog.operations = {
|
|
194
194
|
append: mock.appendFn,
|
|
@@ -84,7 +84,7 @@ module MockEventTopic: EventTopic.T
|
|
|
84
84
|
publishJsonStream: ReventlessInfra.EventTopic.publishJsonStream,
|
|
85
85
|
}
|
|
86
86
|
type component = Component.t<EventTopic.t, EventTopic.outputs, operations>
|
|
87
|
-
let make = (~name as _, ~storageResources as _, ~opts as _=?): component => Obj.magic(0)
|
|
87
|
+
let make = (~name as _, ~storageResources as _, ~owner as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
let mockEventTopicOps: MockEventTopic.operations = {
|
|
@@ -223,6 +223,49 @@ describe("JSON sink", () => {
|
|
|
223
223
|
expect(lines->Array.length == 1 && line->fieldOf("plugin") == Some("Catalog"))->toBe(true)
|
|
224
224
|
})
|
|
225
225
|
|
|
226
|
+
testSync("Logger.info resolves plugin from a kind-suffixed component name", () => {
|
|
227
|
+
// Event-collector comps carry the component *resource* name (`<Spec><Kind>`),
|
|
228
|
+
// while the registry holds the bare spec name — the prefix candidate bridges them.
|
|
229
|
+
Logger.registerComponentPlugin(~componentName="Products", ~pluginName="Catalog")
|
|
230
|
+
let lines = captureLogs(() =>
|
|
231
|
+
log.info(~comp="EventCollector(ProductsReadModel)", "projecting event")
|
|
232
|
+
)
|
|
233
|
+
let line = lines->Array.length == 1 ? lines->Array.getUnsafe(0) : ""
|
|
234
|
+
expect(lines->Array.length == 1 && line->fieldOf("plugin") == Some("Catalog"))->toBe(true)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
testSync("Plugin resolution prefers the longest registered prefix", () => {
|
|
238
|
+
// `Order` (Ordering) must not win over `OrderLines` (Billing) for `OrderLinesReadModel`.
|
|
239
|
+
Logger.registerComponentPlugin(~componentName="Order", ~pluginName="Ordering")
|
|
240
|
+
Logger.registerComponentPlugin(~componentName="OrderLines", ~pluginName="Billing")
|
|
241
|
+
let lines = captureLogs(() =>
|
|
242
|
+
log.info(~comp="EventCollector(OrderLinesReadModel)", "projecting event")
|
|
243
|
+
)
|
|
244
|
+
let line = lines->Array.length == 1 ? lines->Array.getUnsafe(0) : ""
|
|
245
|
+
expect(lines->Array.length == 1 && line->fieldOf("plugin") == Some("Billing"))->toBe(true)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
testSync("dispatch annotation carries comp onto a handler line that passes none", () => {
|
|
249
|
+
// What makes two collectors in one runtime process separable: the element's comp
|
|
250
|
+
// comes from the dispatch boundary, not from the application's own log call.
|
|
251
|
+
let lines = captureLogs(() =>
|
|
252
|
+
EffectLogger.logInfo("projected")
|
|
253
|
+
->Runtime.annotateInvocation(
|
|
254
|
+
~correlationId="cid-1",
|
|
255
|
+
~causationId="parent-1",
|
|
256
|
+
~comp="EventCollector(ProductsReadModel)",
|
|
257
|
+
)
|
|
258
|
+
->Effect.runSync
|
|
259
|
+
)
|
|
260
|
+
let line = lines->Array.length >= 1 ? lines->Array.getUnsafe(0) : ""
|
|
261
|
+
let ok =
|
|
262
|
+
line->fieldOf("comp") == Some("EventCollector(ProductsReadModel)") &&
|
|
263
|
+
line->fieldOf("correlationId") == Some("cid-1") &&
|
|
264
|
+
line->fieldOf("causationId") == Some("parent-1") &&
|
|
265
|
+
line->fieldOf("message") == Some("projected")
|
|
266
|
+
expect(ok)->toBe(true)
|
|
267
|
+
})
|
|
268
|
+
|
|
226
269
|
testSync("Logger.warn emits one clean JSON record", () => {
|
|
227
270
|
let lines = captureLogs(() => log.warn(~comp="Platform", "heartbeat skipped"))
|
|
228
271
|
expect(lines->Array.length == 1 && isCleanRecord(lines->Array.getUnsafe(0)))->toBe(true)
|