@reventlessdev/reventless-core 3.0.0-alpha.152 → 3.0.0-alpha.154
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 +6 -6
- package/src/Message.res +8 -2
- package/src/Message.res.mjs +8 -2
- package/src/adapter/Monitoring/Monitoring.res +96 -0
- package/src/adapter/Monitoring/Monitoring.res.mjs +31 -0
- package/src/admin/Platform_Admin_Structure.res +2 -0
- package/src/admin/Platform_Admin_Structure.res.mjs +4 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res +11 -0
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +28 -0
- package/src/plugin/component/Plugin.res +1 -0
- package/src/plugin/component/Plugin_Builder.res +40 -1
- package/src/plugin/component/Plugin_Builder.res.mjs +37 -9
- package/src/plugin/component/Plugin_BuiltHook.res +11 -0
- package/src/plugin/component/Plugin_Structure.res +15 -0
- package/src/plugin/component/Plugin_Structure.res.mjs +16 -8
- package/tests/adapter/MonitoringTest.res +53 -0
- package/tests/adapter/MonitoringTest.res.mjs +82 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +69 -1
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +64 -8
- package/tests/message/MessageTest.res +146 -0
- package/tests/message/MessageTest.res.mjs +127 -0
- package/tests/plugin/ManifestVisibilityTest.res.mjs +1 -1
- package/tests/plugin/PluginStructureTest.res +34 -0
- package/tests/plugin/PluginStructureTest.res.mjs +32 -1
|
@@ -86,7 +86,15 @@ let make = (
|
|
|
86
86
|
~inboundTranslationSlices: array<module(ReventlessInfra.InboundTranslationSlice.T)>=[],
|
|
87
87
|
~extensions: array<module(ReventlessInfra.Extension.Blueprint)>=[],
|
|
88
88
|
~extensionPoints: array<module(ReventlessInfra.ExtensionPointMapping.Mapping)>=[],
|
|
89
|
+
// Component name → chapter (intra-plugin grouping band), captured at build time
|
|
90
|
+
// from each component's source folder by the plugin generator. A component whose
|
|
91
|
+
// spec name has no entry (or lives directly under a kind-folder) carries no chapter
|
|
92
|
+
// and renders flat. Keyed by `Spec.name`, which equals the source filename stem for
|
|
93
|
+
// every graph-node kind, so the generator can build this map from the discovered
|
|
94
|
+
// file paths. See `Codegen.chapterOf` and docs/plans/deployed-chapter-grouping.md.
|
|
95
|
+
~componentChapters: dict<string>=Dict.make(),
|
|
89
96
|
): Reventless.Plugin.pluginStructure => {
|
|
97
|
+
let chapterOf = (compName: string): option<string> => componentChapters->Dict.get(compName)
|
|
90
98
|
// Event schemas: filter out payload-less variants — DCB event-type lookups
|
|
91
99
|
// can't WHERE-clause on bare-string events, so the plugin graph mustn't
|
|
92
100
|
// claim cross-component edges that the runtime can't honour.
|
|
@@ -405,6 +413,7 @@ let make = (
|
|
|
405
413
|
searchableFields,
|
|
406
414
|
statusField: statusFieldFromStateSchema(stateSchema),
|
|
407
415
|
visibility: visibilityTag(R.Spec.visibility),
|
|
416
|
+
chapter: chapterOf(R.Spec.name),
|
|
408
417
|
}: Reventless.Plugin.queryableDef)
|
|
409
418
|
})
|
|
410
419
|
|
|
@@ -427,6 +436,7 @@ let make = (
|
|
|
427
436
|
searchableFields,
|
|
428
437
|
statusField: statusFieldFromStateSchema(stateSchema),
|
|
429
438
|
visibility: visibilityTag(SVS.Spec.visibility),
|
|
439
|
+
chapter: chapterOf(SVS.Spec.name),
|
|
430
440
|
}: Reventless.Plugin.queryableDef)
|
|
431
441
|
})
|
|
432
442
|
|
|
@@ -454,6 +464,7 @@ let make = (
|
|
|
454
464
|
linkedViews: linkedSvsFor(produced),
|
|
455
465
|
consistencyRead: consistencyReadFor(consumed),
|
|
456
466
|
events: extractEventDefs(SCS.Spec.eventSchema->S.castToUnknown),
|
|
467
|
+
chapter: chapterOf(SCS.Spec.name),
|
|
457
468
|
}: Reventless.Plugin.writableDef)
|
|
458
469
|
})
|
|
459
470
|
|
|
@@ -475,6 +486,7 @@ let make = (
|
|
|
475
486
|
linkedViews: Array.concat(linkedSvsFor(produced), linkedReadModelsFor(A.Spec.name)),
|
|
476
487
|
consistencyRead: None,
|
|
477
488
|
events: extractEventDefs(A.Spec.eventSchema->S.castToUnknown),
|
|
489
|
+
chapter: chapterOf(A.Spec.name),
|
|
478
490
|
}: Reventless.Plugin.writableDef)
|
|
479
491
|
})
|
|
480
492
|
|
|
@@ -497,6 +509,7 @@ let make = (
|
|
|
497
509
|
consumedEventTypes: qualify(~prefix=name, allConsumedVariants),
|
|
498
510
|
producedCommandTypes: qualify(~prefix=name, commandVariantNames(AS.Spec.commandSchema)),
|
|
499
511
|
targetName: AS.Spec.targetName,
|
|
512
|
+
chapter: chapterOf(AS.Spec.name),
|
|
500
513
|
}: Reventless.Plugin.automationSliceDef)
|
|
501
514
|
})
|
|
502
515
|
|
|
@@ -509,6 +522,7 @@ let make = (
|
|
|
509
522
|
inboundCommandTypes: qualify(~prefix=name, commandVariantNames(OTS.Spec.inboundCommandSchema)),
|
|
510
523
|
targetName: OTS.Spec.targetName,
|
|
511
524
|
externalSystem: OTS.Spec.externalSystem,
|
|
525
|
+
chapter: chapterOf(OTS.Spec.name),
|
|
512
526
|
}: Reventless.Plugin.outboundTranslationSliceDef))
|
|
513
527
|
|
|
514
528
|
// ── Inbound translation slices ────────────────────────────────────────────
|
|
@@ -519,6 +533,7 @@ let make = (
|
|
|
519
533
|
commandTypes: qualify(~prefix=name, commandVariantNames(ITS.Spec.commandSchema)),
|
|
520
534
|
targetName: ITS.Spec.targetName,
|
|
521
535
|
externalSystem: ITS.Spec.externalSystem,
|
|
536
|
+
chapter: chapterOf(ITS.Spec.name),
|
|
522
537
|
}: Reventless.Plugin.inboundTranslationSliceDef))
|
|
523
538
|
|
|
524
539
|
// ── Extensions ───────────────────────────────────────────────────────────
|
|
@@ -67,7 +67,7 @@ function labelFieldsFromStateSchema(entityName, stateSchema) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt) {
|
|
70
|
+
function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt, componentChaptersOpt) {
|
|
71
71
|
let aggregates = aggregatesOpt !== undefined ? aggregatesOpt : [];
|
|
72
72
|
let readModels = readModelsOpt !== undefined ? readModelsOpt : [];
|
|
73
73
|
let stateViewSlices = stateViewSlicesOpt !== undefined ? stateViewSlicesOpt : [];
|
|
@@ -77,6 +77,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
77
77
|
let inboundTranslationSlices = inboundTranslationSlicesOpt !== undefined ? inboundTranslationSlicesOpt : [];
|
|
78
78
|
let extensions = extensionsOpt !== undefined ? extensionsOpt : [];
|
|
79
79
|
let extensionPoints = extensionPointsOpt !== undefined ? extensionPointsOpt : [];
|
|
80
|
+
let componentChapters = componentChaptersOpt !== undefined ? componentChaptersOpt : ({});
|
|
80
81
|
let qualify = (prefix, names) => names.map(n => prefix + "." + n);
|
|
81
82
|
let isCreateCommandName = name => [
|
|
82
83
|
"Add",
|
|
@@ -333,7 +334,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
333
334
|
labelField: match[0],
|
|
334
335
|
searchableFields: match[1],
|
|
335
336
|
statusField: statusFieldFromStateSchema(stateSchema),
|
|
336
|
-
visibility: visibilityTag(R.Spec.visibility)
|
|
337
|
+
visibility: visibilityTag(R.Spec.visibility),
|
|
338
|
+
chapter: componentChapters[R.Spec.name]
|
|
337
339
|
};
|
|
338
340
|
});
|
|
339
341
|
let stateViewDefs = stateViewSlices.map((SVS, i) => {
|
|
@@ -351,7 +353,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
351
353
|
labelField: match$1[0],
|
|
352
354
|
searchableFields: match$1[1],
|
|
353
355
|
statusField: statusFieldFromStateSchema(stateSchema),
|
|
354
|
-
visibility: visibilityTag(SVS.Spec.visibility)
|
|
356
|
+
visibility: visibilityTag(SVS.Spec.visibility),
|
|
357
|
+
chapter: componentChapters[SVS.Spec.name]
|
|
355
358
|
};
|
|
356
359
|
});
|
|
357
360
|
let stateChangeDefs = stateChangeSlices.map((SCS, i) => {
|
|
@@ -366,7 +369,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
366
369
|
consumedEventTypes: consumed,
|
|
367
370
|
linkedViews: linkedSvsFor(produced),
|
|
368
371
|
consistencyRead: consistencyReadFor(consumed),
|
|
369
|
-
events: extractEventDefs(SCS.Spec.eventSchema)
|
|
372
|
+
events: extractEventDefs(SCS.Spec.eventSchema),
|
|
373
|
+
chapter: componentChapters[SCS.Spec.name]
|
|
370
374
|
};
|
|
371
375
|
});
|
|
372
376
|
let aggregateDefs = aggregates.map((A, i) => {
|
|
@@ -379,7 +383,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
379
383
|
consumedEventTypes: [],
|
|
380
384
|
linkedViews: linkedSvsFor(produced).concat(linkedReadModelsFor(A.Spec.name)),
|
|
381
385
|
consistencyRead: undefined,
|
|
382
|
-
events: extractEventDefs(A.Spec.eventSchema)
|
|
386
|
+
events: extractEventDefs(A.Spec.eventSchema),
|
|
387
|
+
chapter: componentChapters[A.Spec.name]
|
|
383
388
|
};
|
|
384
389
|
});
|
|
385
390
|
let automationSliceDefs = automationSlices.map(AS => {
|
|
@@ -388,7 +393,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
388
393
|
name: AS.Spec.name,
|
|
389
394
|
consumedEventTypes: qualify(name, allConsumedVariants),
|
|
390
395
|
producedCommandTypes: qualify(name, DcbTag$Reventless.extractAllVariantNames(AS.Spec.commandSchema)),
|
|
391
|
-
targetName: AS.Spec.targetName
|
|
396
|
+
targetName: AS.Spec.targetName,
|
|
397
|
+
chapter: componentChapters[AS.Spec.name]
|
|
392
398
|
};
|
|
393
399
|
});
|
|
394
400
|
let outboundTranslationSliceDefs = outboundTranslationSlices.map(OTS => ({
|
|
@@ -396,13 +402,15 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
396
402
|
consumedEventTypes: qualify(name, DcbTag$Reventless.extractVariantNames(OTS.Spec.consumedEventSchema)),
|
|
397
403
|
inboundCommandTypes: qualify(name, DcbTag$Reventless.extractAllVariantNames(OTS.Spec.inboundCommandSchema)),
|
|
398
404
|
targetName: OTS.Spec.targetName,
|
|
399
|
-
externalSystem: OTS.Spec.externalSystem
|
|
405
|
+
externalSystem: OTS.Spec.externalSystem,
|
|
406
|
+
chapter: componentChapters[OTS.Spec.name]
|
|
400
407
|
}));
|
|
401
408
|
let inboundTranslationSliceDefs = inboundTranslationSlices.map(ITS => ({
|
|
402
409
|
name: ITS.Spec.name,
|
|
403
410
|
commandTypes: qualify(name, DcbTag$Reventless.extractAllVariantNames(ITS.Spec.commandSchema)),
|
|
404
411
|
targetName: ITS.Spec.targetName,
|
|
405
|
-
externalSystem: ITS.Spec.externalSystem
|
|
412
|
+
externalSystem: ITS.Spec.externalSystem,
|
|
413
|
+
chapter: componentChapters[ITS.Spec.name]
|
|
406
414
|
}));
|
|
407
415
|
let extensionDefs = extensions.map(E => {
|
|
408
416
|
let delegateNames = E.mappings.map(M => M.delegateName);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// The registry only forwards `~component`; it never reads its fields. A
|
|
4
|
+
// structural stand-in keeps @pulumi/pulumi (whose Output.make would build a real
|
|
5
|
+
// resource) out of the Jest run — its ESM entry can't be imported under Jest.
|
|
6
|
+
let stubResource: ReventlessInfra.Adapter.resource = %raw(`{}`)
|
|
7
|
+
|
|
8
|
+
describe("Monitoring", () => {
|
|
9
|
+
testSync("Noop default: notify is silent and does not throw", () => {
|
|
10
|
+
// No backend registered — the default Noop backend swallows the call.
|
|
11
|
+
Monitoring.notify(~kind=CommandHandler, ~name="before", ~component=stubResource)
|
|
12
|
+
expect(true)->toBe(true)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
testSync("use registers a backend that receives every notify, role and name", () => {
|
|
16
|
+
let recorded: array<(Monitoring.unitKind, string)> = []
|
|
17
|
+
module Recorder: Monitoring.Backend = {
|
|
18
|
+
let onProvisioned = (~kind, ~name, ~component as _) => recorded->Array.push((kind, name))
|
|
19
|
+
}
|
|
20
|
+
Monitoring.use(module(Recorder: Monitoring.Backend))
|
|
21
|
+
|
|
22
|
+
Monitoring.notify(~kind=CommandHandler, ~name="AllAggregatesCmdHandler", ~component=stubResource)
|
|
23
|
+
Monitoring.notify(~kind=Projection, ~name="AllStateViewSlices", ~component=stubResource)
|
|
24
|
+
Monitoring.notify(~kind=DeadLetterSink, ~name="DeadLetterQueue", ~component=stubResource)
|
|
25
|
+
Monitoring.notify(~kind=Other("Counter"), ~name="ProductCounter", ~component=stubResource)
|
|
26
|
+
|
|
27
|
+
expect(recorded)->toEqual([
|
|
28
|
+
(Monitoring.CommandHandler, "AllAggregatesCmdHandler"),
|
|
29
|
+
(Monitoring.Projection, "AllStateViewSlices"),
|
|
30
|
+
(Monitoring.DeadLetterSink, "DeadLetterQueue"),
|
|
31
|
+
(Monitoring.Other("Counter"), "ProductCounter"),
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
// Restore the default so registry state doesn't leak to other cases.
|
|
35
|
+
Monitoring.use(module(Monitoring.Noop))
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
testSync("notify forwards the static name and component resource to the backend", () => {
|
|
39
|
+
let seen: ref<option<(string, ReventlessInfra.Adapter.resource)>> = ref(None)
|
|
40
|
+
module Capture: Monitoring.Backend = {
|
|
41
|
+
let onProvisioned = (~kind as _, ~name, ~component) => seen := Some((name, component))
|
|
42
|
+
}
|
|
43
|
+
Monitoring.use(module(Capture: Monitoring.Backend))
|
|
44
|
+
|
|
45
|
+
Monitoring.notify(~kind=Scheduler, ~name="HeartbeatUnit", ~component=stubResource)
|
|
46
|
+
switch seen.contents {
|
|
47
|
+
| Some((name, _component)) => expect(name)->toBe("HeartbeatUnit")
|
|
48
|
+
| None => expect("no notify received")->toBe("a notify")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Monitoring.use(module(Monitoring.Noop))
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Monitoring$ReventlessCore from "../../src/adapter/Monitoring/Monitoring.res.mjs";
|
|
4
|
+
|
|
5
|
+
let stubResource = {};
|
|
6
|
+
|
|
7
|
+
globalThis.describe("Monitoring", () => {
|
|
8
|
+
globalThis.test("Noop default: notify is silent and does not throw", () => {
|
|
9
|
+
Monitoring$ReventlessCore.notify("CommandHandler", "before", stubResource);
|
|
10
|
+
globalThis.expect(true).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
globalThis.test("use registers a backend that receives every notify, role and name", () => {
|
|
13
|
+
let recorded = [];
|
|
14
|
+
let onProvisioned = (kind, name, param) => {
|
|
15
|
+
recorded.push([
|
|
16
|
+
kind,
|
|
17
|
+
name
|
|
18
|
+
]);
|
|
19
|
+
};
|
|
20
|
+
let Recorder = {
|
|
21
|
+
onProvisioned: onProvisioned
|
|
22
|
+
};
|
|
23
|
+
Monitoring$ReventlessCore.use(Recorder);
|
|
24
|
+
Monitoring$ReventlessCore.notify("CommandHandler", "AllAggregatesCmdHandler", stubResource);
|
|
25
|
+
Monitoring$ReventlessCore.notify("Projection", "AllStateViewSlices", stubResource);
|
|
26
|
+
Monitoring$ReventlessCore.notify("DeadLetterSink", "DeadLetterQueue", stubResource);
|
|
27
|
+
Monitoring$ReventlessCore.notify({
|
|
28
|
+
TAG: "Other",
|
|
29
|
+
_0: "Counter"
|
|
30
|
+
}, "ProductCounter", stubResource);
|
|
31
|
+
globalThis.expect(recorded).toEqual([
|
|
32
|
+
[
|
|
33
|
+
"CommandHandler",
|
|
34
|
+
"AllAggregatesCmdHandler"
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
"Projection",
|
|
38
|
+
"AllStateViewSlices"
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
"DeadLetterSink",
|
|
42
|
+
"DeadLetterQueue"
|
|
43
|
+
],
|
|
44
|
+
[
|
|
45
|
+
{
|
|
46
|
+
TAG: "Other",
|
|
47
|
+
_0: "Counter"
|
|
48
|
+
},
|
|
49
|
+
"ProductCounter"
|
|
50
|
+
]
|
|
51
|
+
]);
|
|
52
|
+
Monitoring$ReventlessCore.use(Monitoring$ReventlessCore.Noop);
|
|
53
|
+
});
|
|
54
|
+
globalThis.test("notify forwards the static name and component resource to the backend", () => {
|
|
55
|
+
let seen = {
|
|
56
|
+
contents: undefined
|
|
57
|
+
};
|
|
58
|
+
let onProvisioned = (param, name, component) => {
|
|
59
|
+
seen.contents = [
|
|
60
|
+
name,
|
|
61
|
+
component
|
|
62
|
+
];
|
|
63
|
+
};
|
|
64
|
+
let Capture = {
|
|
65
|
+
onProvisioned: onProvisioned
|
|
66
|
+
};
|
|
67
|
+
Monitoring$ReventlessCore.use(Capture);
|
|
68
|
+
Monitoring$ReventlessCore.notify("Scheduler", "HeartbeatUnit", stubResource);
|
|
69
|
+
let match = seen.contents;
|
|
70
|
+
if (match !== undefined) {
|
|
71
|
+
globalThis.expect(match[0]).toBe("HeartbeatUnit");
|
|
72
|
+
} else {
|
|
73
|
+
globalThis.expect("no notify received").toBe("a notify");
|
|
74
|
+
}
|
|
75
|
+
Monitoring$ReventlessCore.use(Monitoring$ReventlessCore.Noop);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
stubResource,
|
|
81
|
+
}
|
|
82
|
+
/* Not a pure module */
|
|
@@ -28,6 +28,7 @@ let qbl: queryableDef = {
|
|
|
28
28
|
searchableFields: ["displayName"],
|
|
29
29
|
statusField: None,
|
|
30
30
|
visibility: None,
|
|
31
|
+
chapter: None,
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
let wbl: writableDef = {
|
|
@@ -38,6 +39,7 @@ let wbl: writableDef = {
|
|
|
38
39
|
producedEventTypes: ["ProductAdded"],
|
|
39
40
|
consumedEventTypes: [],
|
|
40
41
|
events: [{name: "ProductAdded", schema: "{}", references: []}],
|
|
42
|
+
chapter: None,
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
let structure: pluginStructure = {
|
|
@@ -51,6 +53,7 @@ let structure: pluginStructure = {
|
|
|
51
53
|
consumedEventTypes: ["StockLow"],
|
|
52
54
|
producedCommandTypes: ["Restock"],
|
|
53
55
|
targetName: "Product",
|
|
56
|
+
chapter: None,
|
|
54
57
|
},
|
|
55
58
|
],
|
|
56
59
|
outboundTranslationSlices: [
|
|
@@ -60,10 +63,17 @@ let structure: pluginStructure = {
|
|
|
60
63
|
inboundCommandTypes: ["Ship"],
|
|
61
64
|
targetName: None,
|
|
62
65
|
externalSystem: None,
|
|
66
|
+
chapter: None,
|
|
63
67
|
},
|
|
64
68
|
],
|
|
65
69
|
inboundTranslationSlices: [
|
|
66
|
-
{
|
|
70
|
+
{
|
|
71
|
+
name: "FromBilling",
|
|
72
|
+
commandTypes: ["RecordPayment"],
|
|
73
|
+
targetName: "Order",
|
|
74
|
+
externalSystem: None,
|
|
75
|
+
chapter: None,
|
|
76
|
+
},
|
|
67
77
|
],
|
|
68
78
|
extensions: [
|
|
69
79
|
{
|
|
@@ -143,6 +153,61 @@ describe("encodePluginStructureEntry", () => {
|
|
|
143
153
|
testSync("encodes writableDef events", () =>
|
|
144
154
|
expect(json->String.includes("\"events\":[{\"name\":\"ProductAdded\""))->toEqual(true)
|
|
145
155
|
)
|
|
156
|
+
|
|
157
|
+
// Translation-slice externalSystem: absent on the fixture above (both None), so it
|
|
158
|
+
// must serialize as null — the "no external box" case.
|
|
159
|
+
testSync("encodes None outbound externalSystem as null", () =>
|
|
160
|
+
expect(json->String.includes("\"inboundCommandTypes\":[\"Ship\"],\"externalSystem\":null"))->toEqual(
|
|
161
|
+
true,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
testSync("encodes None inbound externalSystem as null", () =>
|
|
166
|
+
expect(json->String.includes("\"commandTypes\":[\"RecordPayment\"],\"externalSystem\":null"))->toEqual(
|
|
167
|
+
true,
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// A deployed-graph consumer draws the external-system boundary box off this field, so
|
|
173
|
+
// a slice whose spec sets `externalSystem = Some("X")` must surface "X" on the wire for
|
|
174
|
+
// both directions — the round-trip the plan pins.
|
|
175
|
+
describe("translation-slice externalSystem round-trip", () => {
|
|
176
|
+
let externalStructure: pluginStructure = {
|
|
177
|
+
...structure,
|
|
178
|
+
outboundTranslationSlices: [
|
|
179
|
+
{
|
|
180
|
+
name: "ToShipper",
|
|
181
|
+
consumedEventTypes: ["OrderPlaced"],
|
|
182
|
+
inboundCommandTypes: ["Ship"],
|
|
183
|
+
targetName: None,
|
|
184
|
+
externalSystem: Some("ShipperGateway"),
|
|
185
|
+
chapter: None,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
inboundTranslationSlices: [
|
|
189
|
+
{
|
|
190
|
+
name: "FromBilling",
|
|
191
|
+
commandTypes: ["RecordPayment"],
|
|
192
|
+
targetName: "Order",
|
|
193
|
+
externalSystem: Some("BillingProvider"),
|
|
194
|
+
chapter: None,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
}
|
|
198
|
+
let externalJson =
|
|
199
|
+
Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
200
|
+
~pluginId="Catalog",
|
|
201
|
+
externalStructure,
|
|
202
|
+
)->JSON.stringify
|
|
203
|
+
|
|
204
|
+
testSync("surfaces Some outbound externalSystem as its inner string", () =>
|
|
205
|
+
expect(externalJson->String.includes("\"externalSystem\":\"ShipperGateway\""))->toEqual(true)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
testSync("surfaces Some inbound externalSystem as its inner string", () =>
|
|
209
|
+
expect(externalJson->String.includes("\"externalSystem\":\"BillingProvider\""))->toEqual(true)
|
|
210
|
+
)
|
|
146
211
|
})
|
|
147
212
|
|
|
148
213
|
describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
@@ -159,6 +224,7 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
159
224
|
searchableFields: ["name"],
|
|
160
225
|
statusField: None,
|
|
161
226
|
visibility: Some("Internal"),
|
|
227
|
+
chapter: None,
|
|
162
228
|
}
|
|
163
229
|
let mixed: pluginStructure = {
|
|
164
230
|
...structure,
|
|
@@ -199,6 +265,7 @@ describe("allowedStates + statusField populated", () => {
|
|
|
199
265
|
searchableFields: ["name"],
|
|
200
266
|
statusField: Some("status"),
|
|
201
267
|
visibility: None,
|
|
268
|
+
chapter: None,
|
|
202
269
|
}
|
|
203
270
|
|
|
204
271
|
let wblWithStates: writableDef = {
|
|
@@ -209,6 +276,7 @@ describe("allowedStates + statusField populated", () => {
|
|
|
209
276
|
producedEventTypes: [],
|
|
210
277
|
consumedEventTypes: [],
|
|
211
278
|
events: [],
|
|
279
|
+
chapter: None,
|
|
212
280
|
}
|
|
213
281
|
|
|
214
282
|
let structureWithStates: pluginStructure = {
|
|
@@ -39,7 +39,8 @@ let qbl = {
|
|
|
39
39
|
labelField: "displayName",
|
|
40
40
|
searchableFields: qbl_searchableFields,
|
|
41
41
|
statusField: undefined,
|
|
42
|
-
visibility: undefined
|
|
42
|
+
visibility: undefined,
|
|
43
|
+
chapter: undefined
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
let wbl_commands = [cmd];
|
|
@@ -63,7 +64,8 @@ let wbl = {
|
|
|
63
64
|
consumedEventTypes: wbl_consumedEventTypes,
|
|
64
65
|
linkedViews: wbl_linkedViews,
|
|
65
66
|
consistencyRead: undefined,
|
|
66
|
-
events: wbl_events
|
|
67
|
+
events: wbl_events,
|
|
68
|
+
chapter: undefined
|
|
67
69
|
};
|
|
68
70
|
|
|
69
71
|
let structure_readModels = [qbl];
|
|
@@ -78,7 +80,8 @@ let structure_automationSlices = [{
|
|
|
78
80
|
name: "Restocker",
|
|
79
81
|
consumedEventTypes: ["StockLow"],
|
|
80
82
|
producedCommandTypes: ["Restock"],
|
|
81
|
-
targetName: "Product"
|
|
83
|
+
targetName: "Product",
|
|
84
|
+
chapter: undefined
|
|
82
85
|
}];
|
|
83
86
|
|
|
84
87
|
let structure_outboundTranslationSlices = [{
|
|
@@ -86,14 +89,16 @@ let structure_outboundTranslationSlices = [{
|
|
|
86
89
|
consumedEventTypes: ["OrderPlaced"],
|
|
87
90
|
inboundCommandTypes: ["Ship"],
|
|
88
91
|
targetName: undefined,
|
|
89
|
-
externalSystem: undefined
|
|
92
|
+
externalSystem: undefined,
|
|
93
|
+
chapter: undefined
|
|
90
94
|
}];
|
|
91
95
|
|
|
92
96
|
let structure_inboundTranslationSlices = [{
|
|
93
97
|
name: "FromBilling",
|
|
94
98
|
commandTypes: ["RecordPayment"],
|
|
95
99
|
targetName: "Order",
|
|
96
|
-
externalSystem: undefined
|
|
100
|
+
externalSystem: undefined,
|
|
101
|
+
chapter: undefined
|
|
97
102
|
}];
|
|
98
103
|
|
|
99
104
|
let structure_extensions = [{
|
|
@@ -168,6 +173,54 @@ globalThis.describe("encodePluginStructureEntry", () => {
|
|
|
168
173
|
globalThis.test("encodes writableDef events", () => {
|
|
169
174
|
globalThis.expect(json.includes("\"events\":[{\"name\":\"ProductAdded\"")).toEqual(true);
|
|
170
175
|
});
|
|
176
|
+
globalThis.test("encodes None outbound externalSystem as null", () => {
|
|
177
|
+
globalThis.expect(json.includes("\"inboundCommandTypes\":[\"Ship\"],\"externalSystem\":null")).toEqual(true);
|
|
178
|
+
});
|
|
179
|
+
globalThis.test("encodes None inbound externalSystem as null", () => {
|
|
180
|
+
globalThis.expect(json.includes("\"commandTypes\":[\"RecordPayment\"],\"externalSystem\":null")).toEqual(true);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
globalThis.describe("translation-slice externalSystem round-trip", () => {
|
|
185
|
+
let externalStructure_readModels = structure_readModels;
|
|
186
|
+
let externalStructure_stateViewSlices = structure_stateViewSlices;
|
|
187
|
+
let externalStructure_stateChangeSlices = structure_stateChangeSlices;
|
|
188
|
+
let externalStructure_aggregates = structure_aggregates;
|
|
189
|
+
let externalStructure_automationSlices = structure_automationSlices;
|
|
190
|
+
let externalStructure_outboundTranslationSlices = [{
|
|
191
|
+
name: "ToShipper",
|
|
192
|
+
consumedEventTypes: ["OrderPlaced"],
|
|
193
|
+
inboundCommandTypes: ["Ship"],
|
|
194
|
+
targetName: undefined,
|
|
195
|
+
externalSystem: "ShipperGateway",
|
|
196
|
+
chapter: undefined
|
|
197
|
+
}];
|
|
198
|
+
let externalStructure_inboundTranslationSlices = [{
|
|
199
|
+
name: "FromBilling",
|
|
200
|
+
commandTypes: ["RecordPayment"],
|
|
201
|
+
targetName: "Order",
|
|
202
|
+
externalSystem: "BillingProvider",
|
|
203
|
+
chapter: undefined
|
|
204
|
+
}];
|
|
205
|
+
let externalStructure_extensions = structure_extensions;
|
|
206
|
+
let externalStructure = {
|
|
207
|
+
readModels: externalStructure_readModels,
|
|
208
|
+
stateViewSlices: externalStructure_stateViewSlices,
|
|
209
|
+
stateChangeSlices: externalStructure_stateChangeSlices,
|
|
210
|
+
aggregates: externalStructure_aggregates,
|
|
211
|
+
automationSlices: externalStructure_automationSlices,
|
|
212
|
+
outboundTranslationSlices: externalStructure_outboundTranslationSlices,
|
|
213
|
+
inboundTranslationSlices: externalStructure_inboundTranslationSlices,
|
|
214
|
+
extensions: externalStructure_extensions,
|
|
215
|
+
extensionPoints: undefined
|
|
216
|
+
};
|
|
217
|
+
let externalJson = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Catalog", externalStructure));
|
|
218
|
+
globalThis.test("surfaces Some outbound externalSystem as its inner string", () => {
|
|
219
|
+
globalThis.expect(externalJson.includes("\"externalSystem\":\"ShipperGateway\"")).toEqual(true);
|
|
220
|
+
});
|
|
221
|
+
globalThis.test("surfaces Some inbound externalSystem as its inner string", () => {
|
|
222
|
+
globalThis.expect(externalJson.includes("\"externalSystem\":\"BillingProvider\"")).toEqual(true);
|
|
223
|
+
});
|
|
171
224
|
});
|
|
172
225
|
|
|
173
226
|
globalThis.describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
@@ -184,7 +237,8 @@ globalThis.describe("visibility filtering (deployed AutoUI hides Internal)", ()
|
|
|
184
237
|
labelField: "name",
|
|
185
238
|
searchableFields: internalQbl_searchableFields,
|
|
186
239
|
statusField: undefined,
|
|
187
|
-
visibility: internalQbl_visibility
|
|
240
|
+
visibility: internalQbl_visibility,
|
|
241
|
+
chapter: undefined
|
|
188
242
|
};
|
|
189
243
|
let mixed_readModels = [
|
|
190
244
|
qbl,
|
|
@@ -245,7 +299,8 @@ globalThis.describe("allowedStates + statusField populated", () => {
|
|
|
245
299
|
labelField: "name",
|
|
246
300
|
searchableFields: qblWithStatus_searchableFields,
|
|
247
301
|
statusField: qblWithStatus_statusField,
|
|
248
|
-
visibility: undefined
|
|
302
|
+
visibility: undefined,
|
|
303
|
+
chapter: undefined
|
|
249
304
|
};
|
|
250
305
|
let wblWithStates_commands = [cmdWithStates];
|
|
251
306
|
let wblWithStates_producedEventTypes = [];
|
|
@@ -259,7 +314,8 @@ globalThis.describe("allowedStates + statusField populated", () => {
|
|
|
259
314
|
consumedEventTypes: wblWithStates_consumedEventTypes,
|
|
260
315
|
linkedViews: wblWithStates_linkedViews,
|
|
261
316
|
consistencyRead: undefined,
|
|
262
|
-
events: wblWithStates_events
|
|
317
|
+
events: wblWithStates_events,
|
|
318
|
+
chapter: undefined
|
|
263
319
|
};
|
|
264
320
|
let structureWithStates_readModels = [qblWithStatus];
|
|
265
321
|
let structureWithStates_stateViewSlices = [];
|