@reventlessdev/reventless-core 3.0.0-alpha.146 → 3.0.0-alpha.148
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 +14 -0
- package/package.json +4 -4
- package/src/components/Api/Api_Naming.res +42 -0
- package/src/components/Api/Api_Naming.res.mjs +34 -1
- package/src/components/Dcb/Dcb_Builder.res +40 -23
- package/src/components/Dcb/Dcb_Builder.res.mjs +20 -22
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +2 -7
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +2 -6
- package/src/plugin/component/Plugin_Structure.res +7 -1
- package/src/plugin/component/Plugin_Structure.res.mjs +1 -1
- package/src/plugin/component/Plugin_SubscriptionSchema.res +2 -3
- package/src/plugin/component/Plugin_SubscriptionSchema.res.mjs +1 -1
- package/tests/dcb/DcbStateChangeSliceTest.res +6 -5
- package/tests/dcb/DcbStateChangeSliceTest.res.mjs +3 -9
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.148 (2026-07-09)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **reventless-core:** DCB StateChangeSlices expose one mutation per command constructor ([136fe93](https://github.com/ReventlessDev/reventless-core/commit/136fe93c56c2e5115eb60d792e9d5e9c07eecc2b))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.147 (2026-07-08)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **reventless-core:** composite DCB slices can read back their own events ([4604a91](https://github.com/ReventlessDev/reventless-core/commit/4604a9159fb8bf59b2191ad69fee6613c7f75cd9))
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
# 3.0.0-alpha.146 (2026-07-08)
|
|
7
21
|
|
|
8
22
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.148",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"sury": "11.0.0-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.25",
|
|
31
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.10",
|
|
32
33
|
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.11",
|
|
33
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.25",
|
|
34
34
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.6",
|
|
35
35
|
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.10",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
37
37
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
|
|
38
38
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.91",
|
|
39
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.24",
|
|
40
|
-
"@reventlessdev/
|
|
41
|
-
"@reventlessdev/
|
|
40
|
+
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.11",
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.69"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "^12.3.0",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
open ReventlessInfra.Api
|
|
2
|
+
|
|
1
3
|
type queryNames = {
|
|
2
4
|
singleFieldName: string,
|
|
3
5
|
listFieldName: string,
|
|
@@ -39,6 +41,46 @@ let aggregateMutationField = (~plugin: string, ~aggregate: string, ~command: str
|
|
|
39
41
|
|
|
40
42
|
let sliceMutationField = (~plugin: string, ~slice: string) => `${plugin}_${slice}`
|
|
41
43
|
|
|
44
|
+
// DCB StateChangeSlice mutation fields — one per API-exposed command constructor,
|
|
45
|
+
// mirroring the aggregate path (which emits one field per constructor). A slice whose
|
|
46
|
+
// command union has a single API-exposed constructor keeps the byte-identical
|
|
47
|
+
// `Plugin_Slice` name (no schema churn); a multi-command slice uses aggregate-style
|
|
48
|
+
// `Plugin_Slice_Ctor` per constructor so the SDL generator and command resolvers
|
|
49
|
+
// derive the constructor from the trailing `_` segment. @noApi variants are filtered
|
|
50
|
+
// out. Returns [(fieldName, constructorName)] in declaration order.
|
|
51
|
+
let sliceMutationFields = (
|
|
52
|
+
~plugin: string,
|
|
53
|
+
~slice: string,
|
|
54
|
+
~commandSchema: S.t<unknown>,
|
|
55
|
+
): array<(string, string)> => {
|
|
56
|
+
let allNames = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
|
|
57
|
+
let filtered = ApiNoApiHelpers.filterNoApiVariants(allNames, commandSchema)
|
|
58
|
+
switch filtered {
|
|
59
|
+
| [] => []
|
|
60
|
+
| [single] => [(sliceMutationField(~plugin, ~slice), single)]
|
|
61
|
+
| _ =>
|
|
62
|
+
filtered->Array.map(ctor => (
|
|
63
|
+
aggregateMutationField(~plugin, ~aggregate=slice, ~command=ctor),
|
|
64
|
+
ctor,
|
|
65
|
+
))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The mutation field name for a single command constructor of a DCB StateChangeSlice,
|
|
70
|
+
// consistent with `sliceMutationFields`: single-command slices resolve every variant
|
|
71
|
+
// to the slice name, multi-command slices to the aggregate-style per-constructor name.
|
|
72
|
+
let sliceMutationFieldFor = (
|
|
73
|
+
~plugin: string,
|
|
74
|
+
~slice: string,
|
|
75
|
+
~commandSchema: S.t<unknown>,
|
|
76
|
+
~variant: string,
|
|
77
|
+
): string =>
|
|
78
|
+
if sliceMutationFields(~plugin, ~slice, ~commandSchema)->Array.length <= 1 {
|
|
79
|
+
sliceMutationField(~plugin, ~slice)
|
|
80
|
+
} else {
|
|
81
|
+
aggregateMutationField(~plugin, ~aggregate=slice, ~command=variant)
|
|
82
|
+
}
|
|
83
|
+
|
|
42
84
|
// Pluralize via the singular stem so already-plural names normalise correctly:
|
|
43
85
|
// "Orders" → "Order" → "Orders" (was "Orderses"); "Categories" → "Category" →
|
|
44
86
|
// "Categories" (was "Categorieses"). For singular entity names like
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
4
|
+
import * as ApiNoApiHelpers$ReventlessCore from "./ApiNoApiHelpers.res.mjs";
|
|
3
5
|
|
|
4
6
|
function pluralize(n) {
|
|
5
7
|
if (/[^aeiou]y$/i.test(n)) {
|
|
@@ -39,6 +41,35 @@ function sliceMutationField(plugin, slice) {
|
|
|
39
41
|
return plugin + `_` + slice;
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
function sliceMutationFields(plugin, slice, commandSchema) {
|
|
45
|
+
let allNames = DcbTag$Reventless.extractAllVariantNames(commandSchema);
|
|
46
|
+
let filtered = ApiNoApiHelpers$ReventlessCore.filterNoApiVariants(allNames, commandSchema);
|
|
47
|
+
let len = filtered.length;
|
|
48
|
+
if (len !== 1) {
|
|
49
|
+
if (len !== 0) {
|
|
50
|
+
return filtered.map(ctor => [
|
|
51
|
+
aggregateMutationField(plugin, slice, ctor),
|
|
52
|
+
ctor
|
|
53
|
+
]);
|
|
54
|
+
} else {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let single = filtered[0];
|
|
59
|
+
return [[
|
|
60
|
+
sliceMutationField(plugin, slice),
|
|
61
|
+
single
|
|
62
|
+
]];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function sliceMutationFieldFor(plugin, slice, commandSchema, variant) {
|
|
66
|
+
if (sliceMutationFields(plugin, slice, commandSchema).length <= 1) {
|
|
67
|
+
return sliceMutationField(plugin, slice);
|
|
68
|
+
} else {
|
|
69
|
+
return aggregateMutationField(plugin, slice, variant);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
42
73
|
function canonicalPlural(n) {
|
|
43
74
|
return pluralize(singularize(n));
|
|
44
75
|
}
|
|
@@ -94,10 +125,12 @@ export {
|
|
|
94
125
|
stripViewSuffix,
|
|
95
126
|
aggregateMutationField,
|
|
96
127
|
sliceMutationField,
|
|
128
|
+
sliceMutationFields,
|
|
129
|
+
sliceMutationFieldFor,
|
|
97
130
|
canonicalPlural,
|
|
98
131
|
queryFieldNamesForReadModel,
|
|
99
132
|
queryFieldNamesForStateView,
|
|
100
133
|
queryFieldNamesForSliceQueryDb,
|
|
101
134
|
adminField,
|
|
102
135
|
}
|
|
103
|
-
/*
|
|
136
|
+
/* DcbTag-Reventless Not a pure module */
|
|
@@ -410,7 +410,11 @@ module Make = (
|
|
|
410
410
|
if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
|
|
411
411
|
registerResolver(
|
|
412
412
|
~kind=Dcb,
|
|
413
|
-
~fields=
|
|
413
|
+
~fields=Api_Naming.sliceMutationFields(
|
|
414
|
+
~plugin=name,
|
|
415
|
+
~slice=S.Spec.name,
|
|
416
|
+
~commandSchema,
|
|
417
|
+
)->Array.map(((f, _)) => f),
|
|
414
418
|
~commandSchema,
|
|
415
419
|
~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
|
|
416
420
|
)
|
|
@@ -423,7 +427,11 @@ module Make = (
|
|
|
423
427
|
if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
|
|
424
428
|
registerResolver(
|
|
425
429
|
~kind=Dcb,
|
|
426
|
-
~fields=
|
|
430
|
+
~fields=Api_Naming.sliceMutationFields(
|
|
431
|
+
~plugin=name,
|
|
432
|
+
~slice=S.Spec.name,
|
|
433
|
+
~commandSchema,
|
|
434
|
+
)->Array.map(((f, _)) => f),
|
|
427
435
|
~commandSchema,
|
|
428
436
|
~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
|
|
429
437
|
)
|
|
@@ -446,7 +454,8 @@ module Make = (
|
|
|
446
454
|
) => {
|
|
447
455
|
let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
|
|
448
456
|
if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
|
|
449
|
-
|
|
457
|
+
// One generateCommand backs every constructor's field — the TAG is
|
|
458
|
+
// injected by the resolver per field, so a single binding suffices.
|
|
450
459
|
let generateCommand = CommandGenerator_Callback.makeGenerateCommand(
|
|
451
460
|
~publishJsons=ops.publishJsons,
|
|
452
461
|
~publishJsonsAndWait=?ops.publishJsonsAndWait,
|
|
@@ -455,7 +464,8 @@ module Make = (
|
|
|
455
464
|
~componentKind=CommandGenerator_Callback.StateChangeSlice,
|
|
456
465
|
~stripIdFromParams=false,
|
|
457
466
|
)
|
|
458
|
-
|
|
467
|
+
Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
|
|
468
|
+
->Array.forEach(((fieldName, _)) => bindHandler(~field=fieldName, ~generateCommand))
|
|
459
469
|
}
|
|
460
470
|
})
|
|
461
471
|
})
|
|
@@ -471,7 +481,8 @@ module Make = (
|
|
|
471
481
|
) => {
|
|
472
482
|
let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
|
|
473
483
|
if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
|
|
474
|
-
|
|
484
|
+
// One generateCommand backs every constructor's field — the TAG is
|
|
485
|
+
// injected by the resolver per field, so a single binding suffices.
|
|
475
486
|
let generateCommand = CommandGenerator_Callback.makeGenerateCommand(
|
|
476
487
|
~publishJsons=asyncOps.publishJsons,
|
|
477
488
|
~publishJsonsAndWait=?asyncOps.publishJsonsAndWait,
|
|
@@ -480,7 +491,8 @@ module Make = (
|
|
|
480
491
|
~componentKind=CommandGenerator_Callback.StateChangeSlice,
|
|
481
492
|
~stripIdFromParams=false,
|
|
482
493
|
)
|
|
483
|
-
|
|
494
|
+
Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
|
|
495
|
+
->Array.forEach(((fieldName, _)) => bindHandler(~field=fieldName, ~generateCommand))
|
|
484
496
|
}
|
|
485
497
|
})
|
|
486
498
|
})
|
|
@@ -706,19 +718,19 @@ module Make = (
|
|
|
706
718
|
|
|
707
719
|
// Collect DCB mutation field names + TAGs for AppSync resolver creation
|
|
708
720
|
// (excludes @noApi slices — includes both sync and async)
|
|
721
|
+
// One (fieldName, TAG) pair per API-exposed command constructor. The AWS
|
|
722
|
+
// resolver layer (CommandGeneratorResolvers_AppSync.makeDcb) zips these into
|
|
723
|
+
// one resolver per field, each injecting its own constructor TAG into the
|
|
724
|
+
// Lambda payload — so a multi-command slice's every command is dispatchable.
|
|
709
725
|
let dcbMutationData =
|
|
710
|
-
stateChangeSlices->Array.
|
|
726
|
+
stateChangeSlices->Array.flatMap((
|
|
711
727
|
module(S: StateChangeSlice.T),
|
|
712
728
|
) => {
|
|
713
729
|
let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
|
|
714
730
|
if ApiNoApiHelpers.isNoApi(commandSchema) {
|
|
715
|
-
|
|
731
|
+
[]
|
|
716
732
|
} else {
|
|
717
|
-
|
|
718
|
-
let constructorNames =
|
|
719
|
-
Reventless.DcbTag.extractAllVariantNames(S.Spec.commandSchema->Obj.magic)
|
|
720
|
-
let tag = constructorNames->Array.get(0)->Option.getOr(S.Spec.name)
|
|
721
|
-
Some((fieldName, tag))
|
|
733
|
+
Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
|
|
722
734
|
}
|
|
723
735
|
})
|
|
724
736
|
let dcbFieldNames = dcbMutationData->Array.map(((f, _)) => f)
|
|
@@ -812,24 +824,29 @@ module Make = (
|
|
|
812
824
|
module(S: StateChangeSlice.T),
|
|
813
825
|
) => {
|
|
814
826
|
let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
|
|
815
|
-
|
|
827
|
+
let fieldSpecs =
|
|
828
|
+
Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
|
|
829
|
+
if ApiNoApiHelpers.isNoApi(commandSchema) || fieldSpecs->Array.length == 0 {
|
|
816
830
|
None
|
|
817
831
|
} else {
|
|
818
832
|
let sliceDef =
|
|
819
833
|
pluginStructure->Option.flatMap(s =>
|
|
820
834
|
s.stateChangeSlices->Array.find(d => d.name == S.Spec.name)
|
|
821
835
|
)
|
|
822
|
-
|
|
836
|
+
// One SDL field per constructor, each with its own constructor's args and
|
|
837
|
+
// its own per-constructor authorization rule.
|
|
838
|
+
let commandAuthorization: unknown => Reventless.Authorization.permission =
|
|
839
|
+
S.Spec.commandAuthorization->Obj.magic
|
|
823
840
|
let fieldPermissions = Dict.make()
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}
|
|
841
|
+
fieldSpecs->Array.forEach(((fieldName, ctor)) => {
|
|
842
|
+
let hasPayload =
|
|
843
|
+
Reventless.DcbTag.isVariantPayloadBearing(commandSchema->Obj.magic, ctor)
|
|
844
|
+
let syntheticCmd: unknown =
|
|
845
|
+
hasPayload ? {"TAG": ctor}->Obj.magic : ctor->Obj.magic
|
|
846
|
+
fieldPermissions->Dict.set(fieldName, commandAuthorization(syntheticCmd))
|
|
847
|
+
})
|
|
831
848
|
Some({
|
|
832
|
-
ReventlessInfra.Api.fieldNames:
|
|
849
|
+
ReventlessInfra.Api.fieldNames: fieldSpecs->Array.map(((f, _)) => f),
|
|
833
850
|
commandSchema,
|
|
834
851
|
fieldPermissions,
|
|
835
852
|
systemCallable: systemCallableComponents->Array.includes(S.Spec.name),
|
|
@@ -225,13 +225,13 @@ function Make(DcbEventLogStorage) {
|
|
|
225
225
|
syncSlices.forEach(S => {
|
|
226
226
|
let commandSchema = S.Spec.commandSchema;
|
|
227
227
|
if (!ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
|
|
228
|
-
return registerResolver("Dcb",
|
|
228
|
+
return registerResolver("Dcb", Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).map(param => param[0]), commandSchema, S.Spec.commandAuthorization);
|
|
229
229
|
}
|
|
230
230
|
});
|
|
231
231
|
asyncSlices.forEach(S => {
|
|
232
232
|
let commandSchema = S.Spec.commandSchema;
|
|
233
233
|
if (!ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
|
|
234
|
-
return registerResolver("Dcb",
|
|
234
|
+
return registerResolver("Dcb", Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).map(param => param[0]), commandSchema, S.Spec.commandAuthorization);
|
|
235
235
|
}
|
|
236
236
|
});
|
|
237
237
|
}
|
|
@@ -243,9 +243,8 @@ function Make(DcbEventLogStorage) {
|
|
|
243
243
|
if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
|
|
244
244
|
return;
|
|
245
245
|
}
|
|
246
|
-
let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
|
|
247
246
|
let generateCommand = CommandGenerator_Callback$ReventlessCore.makeGenerateCommand(ops.publishJsons, ops.publishJsonsAndWait, S.Spec.name, S.Spec.commandSchema, "StateChangeSlice", false);
|
|
248
|
-
bindHandler(
|
|
247
|
+
Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).forEach(param => bindHandler(param[0], generateCommand));
|
|
249
248
|
});
|
|
250
249
|
});
|
|
251
250
|
if (asyncDcbCommandTopicOpt !== undefined) {
|
|
@@ -255,9 +254,8 @@ function Make(DcbEventLogStorage) {
|
|
|
255
254
|
if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
|
|
256
255
|
return;
|
|
257
256
|
}
|
|
258
|
-
let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
|
|
259
257
|
let generateCommand = CommandGenerator_Callback$ReventlessCore.makeGenerateCommand(asyncOps.publishJsons, asyncOps.publishJsonsAndWait, S.Spec.name, S.Spec.commandSchema, "StateChangeSlice", false);
|
|
260
|
-
bindHandler(
|
|
258
|
+
Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).forEach(param => bindHandler(param[0], generateCommand));
|
|
261
259
|
});
|
|
262
260
|
});
|
|
263
261
|
}
|
|
@@ -394,18 +392,13 @@ function Make(DcbEventLogStorage) {
|
|
|
394
392
|
let asyncDcbResources = Object.values(asyncStateChangeSlicesOutputs).flatMap(outputs => outputs.resources);
|
|
395
393
|
let inboundFieldNames = inboundTranslationSliceData.map(param => param[1]);
|
|
396
394
|
let inboundSchemas = inboundTranslationSliceData.map(param => param[3]);
|
|
397
|
-
let dcbMutationData =
|
|
395
|
+
let dcbMutationData = stateChangeSlices.flatMap(S => {
|
|
398
396
|
let commandSchema = S.Spec.commandSchema;
|
|
399
397
|
if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
|
|
400
|
-
return;
|
|
398
|
+
return [];
|
|
399
|
+
} else {
|
|
400
|
+
return Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema);
|
|
401
401
|
}
|
|
402
|
-
let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
|
|
403
|
-
let constructorNames = DcbTag$Reventless.extractAllVariantNames(S.Spec.commandSchema);
|
|
404
|
-
let tag = Stdlib_Option.getOr(constructorNames[0], S.Spec.name);
|
|
405
|
-
return [
|
|
406
|
-
fieldName,
|
|
407
|
-
tag
|
|
408
|
-
];
|
|
409
402
|
});
|
|
410
403
|
let dcbFieldNames = dcbMutationData.map(param => param[0]);
|
|
411
404
|
let dcbTags = dcbMutationData.map(param => param[1]);
|
|
@@ -458,18 +451,23 @@ function Make(DcbEventLogStorage) {
|
|
|
458
451
|
};
|
|
459
452
|
let mutationEntriesFromSlices = Stdlib_Array.filterMap(stateChangeSlices, S => {
|
|
460
453
|
let commandSchema = S.Spec.commandSchema;
|
|
461
|
-
|
|
454
|
+
let fieldSpecs = Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema);
|
|
455
|
+
if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema) || fieldSpecs.length === 0) {
|
|
462
456
|
return;
|
|
463
457
|
}
|
|
464
458
|
let sliceDef = Stdlib_Option.flatMap(pluginStructure, s => s.stateChangeSlices.find(d => d.name === S.Spec.name));
|
|
465
|
-
let
|
|
459
|
+
let commandAuthorization = S.Spec.commandAuthorization;
|
|
466
460
|
let fieldPermissions = {};
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
461
|
+
fieldSpecs.forEach(param => {
|
|
462
|
+
let ctor = param[1];
|
|
463
|
+
let hasPayload = DcbTag$Reventless.isVariantPayloadBearing(commandSchema, ctor);
|
|
464
|
+
let syntheticCmd = hasPayload ? ({
|
|
465
|
+
TAG: ctor
|
|
466
|
+
}) : ctor;
|
|
467
|
+
fieldPermissions[param[0]] = commandAuthorization(syntheticCmd);
|
|
468
|
+
});
|
|
471
469
|
return {
|
|
472
|
-
fieldNames: [
|
|
470
|
+
fieldNames: fieldSpecs.map(param => param[0]),
|
|
473
471
|
commandSchema: commandSchema,
|
|
474
472
|
fieldPermissions: fieldPermissions,
|
|
475
473
|
systemCallable: systemCallableComponents.includes(S.Spec.name),
|
|
@@ -61,8 +61,7 @@ module Make = (
|
|
|
61
61
|
| Some(keys) => baseTags->Array.filter(t => keys->Array.includes(t.key))
|
|
62
62
|
| None => baseTags
|
|
63
63
|
}
|
|
64
|
-
let tags =
|
|
65
|
-
indexedTags->Array.concat([{Reventless.DcbTag.key: "originatorSlice", value: Spec.name}])
|
|
64
|
+
let tags = indexedTags
|
|
66
65
|
// Inherit service from the triggering command — the DcbEventLog publish path
|
|
67
66
|
// overrides service to `<name>DcbEventLog` for routing so EventCollector
|
|
68
67
|
// subscriptions still match.
|
|
@@ -83,12 +82,8 @@ module Make = (
|
|
|
83
82
|
// tag value(s) instead of logging no id.
|
|
84
83
|
let readEventId = (tags: array<Reventless.DcbTag.tag>): option<string> => {
|
|
85
84
|
let ownTagValues = () => {
|
|
86
|
-
// Skip the `originatorSlice` metadata tag that `encodeEvent` appends to
|
|
87
|
-
// every stored event — it's the producing slice's name, not an entity id.
|
|
88
85
|
let vals =
|
|
89
|
-
tags
|
|
90
|
-
->Array.filter((t: Reventless.DcbTag.tag) => t.key != "originatorSlice")
|
|
91
|
-
->Array.reduce([], (acc, t: Reventless.DcbTag.tag) =>
|
|
86
|
+
tags->Array.reduce([], (acc, t: Reventless.DcbTag.tag) =>
|
|
92
87
|
acc->Array.includes(t.value) ? acc : acc->Array.concat([t.value])
|
|
93
88
|
)
|
|
94
89
|
vals->Array.length == 0 ? None : Some(vals->Array.join(","))
|
|
@@ -29,7 +29,7 @@ function Make(Spec) {
|
|
|
29
29
|
]]);
|
|
30
30
|
let readEventId = tags => {
|
|
31
31
|
let ownTagValues = () => {
|
|
32
|
-
let vals = Stdlib_Array.reduce(tags
|
|
32
|
+
let vals = Stdlib_Array.reduce(tags, [], (acc, t) => {
|
|
33
33
|
if (acc.includes(t.value)) {
|
|
34
34
|
return acc;
|
|
35
35
|
} else {
|
|
@@ -188,15 +188,11 @@ function Make(Spec) {
|
|
|
188
188
|
let baseTags = DcbTag$Reventless.extractTagsExpanded(Spec.eventSchema, e);
|
|
189
189
|
let keys = tagKeysByEventType[eventType];
|
|
190
190
|
let indexedTags = keys !== undefined ? baseTags.filter(t => keys.includes(t.key)) : baseTags;
|
|
191
|
-
let tags = indexedTags.concat([{
|
|
192
|
-
key: "originatorSlice",
|
|
193
|
-
value: Spec.name
|
|
194
|
-
}]);
|
|
195
191
|
let meta = Message$ReventlessCore.deriveMeta(parentMeta, undefined);
|
|
196
192
|
return {
|
|
197
193
|
eventType: eventType,
|
|
198
194
|
data: match[1],
|
|
199
|
-
tags:
|
|
195
|
+
tags: indexedTags,
|
|
200
196
|
meta: meta
|
|
201
197
|
};
|
|
202
198
|
});
|
|
@@ -440,7 +440,13 @@ let make = (
|
|
|
440
440
|
Reventless.Plugin.name: SCS.Spec.name,
|
|
441
441
|
commands: extractCommandDefs(
|
|
442
442
|
~isAggregate=false,
|
|
443
|
-
~mutationFieldFor=
|
|
443
|
+
~mutationFieldFor=variantName =>
|
|
444
|
+
Api_Naming.sliceMutationFieldFor(
|
|
445
|
+
~plugin=name,
|
|
446
|
+
~slice=SCS.Spec.name,
|
|
447
|
+
~commandSchema=SCS.Spec.commandSchema->S.castToUnknown,
|
|
448
|
+
~variant=variantName,
|
|
449
|
+
),
|
|
444
450
|
SCS.Spec.commandSchema->S.castToUnknown,
|
|
445
451
|
),
|
|
446
452
|
producedEventTypes: produced,
|
|
@@ -361,7 +361,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
361
361
|
let consumed = match$1[1];
|
|
362
362
|
return {
|
|
363
363
|
name: SCS.Spec.name,
|
|
364
|
-
commands: extractCommandDefs(false,
|
|
364
|
+
commands: extractCommandDefs(false, variantName => Api_Naming$ReventlessCore.sliceMutationFieldFor(name, SCS.Spec.name, SCS.Spec.commandSchema, variantName), SCS.Spec.commandSchema),
|
|
365
365
|
producedEventTypes: produced,
|
|
366
366
|
consumedEventTypes: consumed,
|
|
367
367
|
linkedViews: linkedSvsFor(produced),
|
|
@@ -61,8 +61,7 @@ let sourceCFields = (
|
|
|
61
61
|
// Pushed via AppSync Events API. Admin / observability only.
|
|
62
62
|
//
|
|
63
63
|
// Returns (subscriptionFields, supportingTypes) — the supporting type is the
|
|
64
|
-
// {DisplayName}EventLogEvent type that carries position, eventType, payload
|
|
65
|
-
// the optional originatorSlice tag injected by StateChangeSlice_Callback.
|
|
64
|
+
// {DisplayName}EventLogEvent type that carries position, eventType, and payload.
|
|
66
65
|
|
|
67
66
|
let sourceAFieldsAndTypes = (
|
|
68
67
|
~eventLogEntries: array<eventLogSchemaEntry>,
|
|
@@ -76,7 +75,7 @@ let sourceAFieldsAndTypes = (
|
|
|
76
75
|
seen->Set.add(n)
|
|
77
76
|
fields->Array.push(` on${n}EventLog_eventAppended: ${n}EventLogEvent`)
|
|
78
77
|
types->Array.push(
|
|
79
|
-
`type ${n}EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n
|
|
78
|
+
`type ${n}EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n}`,
|
|
80
79
|
)
|
|
81
80
|
}
|
|
82
81
|
})
|
|
@@ -41,7 +41,7 @@ function sourceAFieldsAndTypes(eventLogEntries) {
|
|
|
41
41
|
if (!seen.has(n)) {
|
|
42
42
|
seen.add(n);
|
|
43
43
|
fields.push(` on` + n + `EventLog_eventAppended: ` + n + `EventLogEvent`);
|
|
44
|
-
types.push(`type ` + n + `EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n
|
|
44
|
+
types.push(`type ` + n + `EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n}`);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
});
|
|
@@ -81,7 +81,7 @@ describe("StateChangeSlice_Callback:", () => {
|
|
|
81
81
|
))->toEqual(([Ok("ref-1")], 1, 1))
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
testPromise("stored event
|
|
84
|
+
testPromise("stored event carries only its entity tags (no framework provenance tag)", async () => {
|
|
85
85
|
let _ = await TestHandler.handleCommands(
|
|
86
86
|
testDcbEventLog,
|
|
87
87
|
Stream.fromIterable([
|
|
@@ -90,10 +90,11 @@ describe("StateChangeSlice_Callback:", () => {
|
|
|
90
90
|
)->Effect.runPromise
|
|
91
91
|
let storedEvent = mock.getEvents()->Array.getUnsafe(0)
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
// No `originatorSlice` (or any other) metadata tag is smuggled into the tag
|
|
94
|
+
// set — a stored event's tags are exactly its DCB entity tags, so the
|
|
95
|
+
// composite read/write keys stay aligned. See
|
|
96
|
+
// docs/analysis/dcb-event-provenance-and-metadata.md.
|
|
97
|
+
expect(storedEvent.tags)->toEqual([{Reventless.DcbTag.key: "itemId", value: "item-1"}])
|
|
97
98
|
})
|
|
98
99
|
})
|
|
99
100
|
|
|
@@ -201,23 +201,17 @@ globalThis.describe("StateChangeSlice_Callback:", () => {
|
|
|
201
201
|
1
|
|
202
202
|
]);
|
|
203
203
|
});
|
|
204
|
-
globalThis.test("stored event
|
|
204
|
+
globalThis.test("stored event carries only its entity tags (no framework provenance tag)", async () => {
|
|
205
205
|
await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
|
|
206
206
|
TAG: "CreateItem",
|
|
207
207
|
itemId: "item-1",
|
|
208
208
|
name: "Test"
|
|
209
209
|
})])));
|
|
210
210
|
let storedEvent = mock.getEvents()[0];
|
|
211
|
-
globalThis.expect(storedEvent.tags).toEqual([
|
|
212
|
-
{
|
|
211
|
+
globalThis.expect(storedEvent.tags).toEqual([{
|
|
213
212
|
key: "itemId",
|
|
214
213
|
value: "item-1"
|
|
215
|
-
}
|
|
216
|
-
{
|
|
217
|
-
key: "originatorSlice",
|
|
218
|
-
value: "TestStateChangeSlice"
|
|
219
|
-
}
|
|
220
|
-
]);
|
|
214
|
+
}]);
|
|
221
215
|
});
|
|
222
216
|
});
|
|
223
217
|
globalThis.describe("handleCommands - decide returns Ok([])", () => {
|