@reventlessdev/reventless-core 3.0.0-alpha.284 → 3.0.0-alpha.286
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 +23 -0
- package/package.json +7 -7
- package/src/Message.res.mjs +3 -0
- package/src/Roles.res +3 -0
- package/src/Roles.res.mjs +2 -0
- package/src/admin/Platform_AdminGroup.res +12 -0
- package/src/admin/Platform_AdminGroup.res.mjs +23 -0
- package/src/admin/Platform_Admin_Structure.res +14 -10
- package/src/admin/Platform_Admin_Structure.res.mjs +22 -33
- package/src/components/Aggregate/Aggregate_Callback.res +116 -39
- package/src/components/Aggregate/Aggregate_Callback.res.mjs +96 -37
- package/src/components/AutomationSlice/AutomationSlice_Builder.res +2 -1
- package/src/components/CommandGenerator/CommandGenerator_Callback.res +9 -8
- package/src/components/CommandGenerator/CommandGenerator_Callback.res.mjs +10 -8
- package/src/components/CommandTopic/CommandTopic.res.mjs +3 -0
- package/src/components/CommandTopic/CommandTopic_Helpers.res +37 -1
- package/src/components/CommandTopic/CommandTopic_Helpers.res.mjs +29 -0
- package/src/components/Counter/Counter_Builder.res +4 -2
- package/src/components/Dcb/Dcb_Builder.res +9 -6
- package/src/components/Dcb/Dcb_Builder.res.mjs +6 -6
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +2 -1
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +2 -1
- package/src/components/QueryDb/QueryDb_Builder.res +1 -1
- package/src/components/QueryDb/QueryDb_Builder.res.mjs +3 -2
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +191 -141
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +66 -12
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +1 -0
- package/src/plugin/api/PluginBaseFragment.res +1 -1
- package/src/plugin/api/PluginBaseFragment.res.mjs +2 -1
- package/src/plugin/component/Plugin_Builder.res +7 -3
- package/src/plugin/component/Plugin_Builder.res.mjs +6 -3
- package/src/plugin/component/Plugin_Helpers.res +35 -6
- package/src/plugin/component/Plugin_Helpers.res.mjs +47 -5
- package/src/plugin/component/Plugin_Structure.res +52 -19
- package/src/plugin/component/Plugin_Structure.res.mjs +40 -15
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +1 -1
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +1 -1
- package/tests/admin/AdminApiSchemaDriftTest.res +9 -3
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +3 -3
- package/tests/admin/Platform_Admin_StructureTest.res +17 -14
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +31 -19
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +8 -72
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +9 -39
- package/tests/api/GraphQL_FragmentGeneratorTest.res +1 -1
- package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +1 -1
- package/tests/commandgenerator/OwnerActingTest.res +601 -0
- package/tests/commandgenerator/OwnerActingTest.res.mjs +1018 -0
- package/tests/commandgenerator/OwnerStampingTest.res +90 -0
- package/tests/commandgenerator/OwnerStampingTest.res.mjs +87 -0
- package/tests/commandtopic/CommandOutcomeHookTest.res +1 -0
- package/tests/commandtopic/CommandOutcomeHookTest.res.mjs +11 -1
- package/tests/plugin/PluginStructureAccessTest.res +30 -1
- package/tests/plugin/PluginStructureAccessTest.res.mjs +28 -1
- package/tests/plugin/StateChange/PsGatedCommands.res +9 -1
- package/tests/plugin/StateChange/PsGatedCommands.res.mjs +4 -1
- package/tests/plugin/StateView/PsGatedView.res +1 -1
- package/tests/plugin/StateView/PsGatedView.res.mjs +1 -1
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +5 -0
|
@@ -14,7 +14,9 @@ import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
|
|
|
14
14
|
import * as Plugin$ReventlessCore from "./Plugin.res.mjs";
|
|
15
15
|
import * as StackReference$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/StackReference.res.mjs";
|
|
16
16
|
import * as Adapter$ReventlessCore from "../../adapter/Adapter.res.mjs";
|
|
17
|
+
import * as RoleCoverage$Reventless from "@reventlessdev/reventless-spec/src/types/RoleCoverage.res.mjs";
|
|
17
18
|
import * as Aggregate$ReventlessCore from "../../components/Aggregate/Aggregate.res.mjs";
|
|
19
|
+
import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
|
|
18
20
|
import * as Component$ReventlessCore from "../../components/Component.res.mjs";
|
|
19
21
|
import * as ReadModel$ReventlessCore from "../../components/ReadModel/ReadModel.res.mjs";
|
|
20
22
|
import * as Api_Naming$ReventlessCore from "../../components/Api/Api_Naming.res.mjs";
|
|
@@ -124,6 +126,7 @@ function Make(Spec) {
|
|
|
124
126
|
let systemCallableComponents = systemCallableComponentsOpt !== undefined ? systemCallableComponentsOpt : [];
|
|
125
127
|
let componentRuntime = componentRuntimeOpt !== undefined ? componentRuntimeOpt : ({});
|
|
126
128
|
PluginRuntimeBuilder.registerPluginName(name);
|
|
129
|
+
Stdlib_Option.forEach(pluginStructure, s => RoleCoverage$Reventless.check(s, name));
|
|
127
130
|
let version = PackageVersion$Reventless.fromCaller();
|
|
128
131
|
return Component$ReventlessCore.make(ComponentType$ReventlessCore.toString(Plugin$ReventlessCore.componentType), name, (extra, extra$1) => {
|
|
129
132
|
let _prevPluginName = Logger$ReventlessCore.currentPluginName.contents;
|
|
@@ -173,7 +176,7 @@ function Make(Spec) {
|
|
|
173
176
|
if (fieldNames.length === 0) {
|
|
174
177
|
return [];
|
|
175
178
|
}
|
|
176
|
-
Stdlib_Option.forEach(Spec.hooks.mutationResolverHook, registerResolver => registerResolver("Aggregate", fieldNames, commandSchema, M.Spec.commandAuthorization));
|
|
179
|
+
Stdlib_Option.forEach(Spec.hooks.mutationResolverHook, registerResolver => registerResolver("Aggregate", fieldNames, commandSchema, command => Authorization$Reventless.named(M.Spec.commandAuthorization(command))));
|
|
177
180
|
let aggDef = Stdlib_Option.flatMap(pluginStructure, s => s.aggregates.find(d => d.name === M.Spec.name));
|
|
178
181
|
let fieldPermissions = {};
|
|
179
182
|
filteredConstructorNames.forEach((cname, idx) => {
|
|
@@ -182,7 +185,7 @@ function Make(Spec) {
|
|
|
182
185
|
let syntheticCmd = hasPayload ? ({
|
|
183
186
|
TAG: cname
|
|
184
187
|
}) : cname;
|
|
185
|
-
let rule = M.Spec.commandAuthorization(syntheticCmd);
|
|
188
|
+
let rule = Authorization$Reventless.named(M.Spec.commandAuthorization(syntheticCmd));
|
|
186
189
|
fieldPermissions[fieldName] = rule;
|
|
187
190
|
});
|
|
188
191
|
return [{
|
|
@@ -205,7 +208,7 @@ function Make(Spec) {
|
|
|
205
208
|
returnTypeName: qn.returnTypeName,
|
|
206
209
|
stateSchema: R.Spec.stateSchema,
|
|
207
210
|
authorization: undefined,
|
|
208
|
-
permission: R.Spec.authorization,
|
|
211
|
+
permission: Authorization$Reventless.named(R.Spec.authorization),
|
|
209
212
|
connectionSpec: true,
|
|
210
213
|
subIdField: subIdField,
|
|
211
214
|
indexQueries: indexes.length !== 0 ? indexes : undefined,
|
|
@@ -783,6 +783,15 @@ type pluginDeployedInfo = {
|
|
|
783
783
|
deployedAt: string,
|
|
784
784
|
actor: string,
|
|
785
785
|
deploymentId: string,
|
|
786
|
+
/** The commit the deployment was built from, when CI or git could say. */
|
|
787
|
+
commit?: string,
|
|
788
|
+
/** The working tree had changes the commit does not hold. */
|
|
789
|
+
dirty?: bool,
|
|
790
|
+
/** What the environment is for, from the app's `environments.yaml`. */
|
|
791
|
+
purpose?: string,
|
|
792
|
+
tag?: string,
|
|
793
|
+
/** The pull request a review environment was deployed for. */
|
|
794
|
+
pullRequest?: int,
|
|
786
795
|
kind?: pluginKind,
|
|
787
796
|
displayName?: string,
|
|
788
797
|
vendor?: string,
|
|
@@ -1141,7 +1150,8 @@ let registerAdminAggregateMutations = (
|
|
|
1141
1150
|
~kind=Aggregate,
|
|
1142
1151
|
~fields=fieldNames,
|
|
1143
1152
|
~commandSchema,
|
|
1144
|
-
~commandAuthorization=
|
|
1153
|
+
~commandAuthorization=command =>
|
|
1154
|
+
M.Spec.commandAuthorization(command->Obj.magic)->Reventless.Authorization.named,
|
|
1145
1155
|
)
|
|
1146
1156
|
)
|
|
1147
1157
|
}
|
|
@@ -1237,21 +1247,34 @@ let getInteropMeta = (): Pulumi.Output.t<JSON.t> => {
|
|
|
1237
1247
|
}
|
|
1238
1248
|
|
|
1239
1249
|
// ---------------------------------------------------------------------------
|
|
1240
|
-
// Stack metadata export — environment, region, timestamp, actor,
|
|
1250
|
+
// Stack metadata export — environment, region, timestamp, actor, commit.
|
|
1241
1251
|
// Called from both exportPluginOutputs and exportPlatformOutputs.
|
|
1242
1252
|
// ---------------------------------------------------------------------------
|
|
1243
1253
|
let exportDeploymentMetadata = () => {
|
|
1254
|
+
let provenance = Reventless.DeploymentProvenance.current()
|
|
1255
|
+
let stack = Pulumi.Pulumi.getStackName()
|
|
1256
|
+
let optional =
|
|
1257
|
+
[
|
|
1258
|
+
("commit", provenance.commit),
|
|
1259
|
+
("purpose", provenance.purpose),
|
|
1260
|
+
("tag", provenance.tag),
|
|
1261
|
+
("pullRequest", provenance.pullRequest->Option.map(n => n->Int.toString)),
|
|
1262
|
+
]->Array.filterMap(((key, value)) => value->Option.map(v => (key, v)))
|
|
1244
1263
|
let metadata =
|
|
1245
1264
|
[
|
|
1246
|
-
("environment",
|
|
1265
|
+
("environment", provenance.environment->Option.getOr(stack)),
|
|
1266
|
+
("stack", stack),
|
|
1247
1267
|
(
|
|
1248
1268
|
"region",
|
|
1249
1269
|
Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->Option.getOr("unknown"),
|
|
1250
1270
|
),
|
|
1251
1271
|
("timestamp", Date.make()->Date.toISOString),
|
|
1252
|
-
("gitSha",
|
|
1272
|
+
("gitSha", provenance.commit->Option.getOr("unknown")),
|
|
1273
|
+
("dirty", provenance.dirty ? "true" : "false"),
|
|
1253
1274
|
("actor", NodeProcess.env->Dict.get("GITHUB_ACTOR")->Option.getOr("unknown")),
|
|
1254
|
-
]
|
|
1275
|
+
]
|
|
1276
|
+
->Array.concat(optional)
|
|
1277
|
+
->Dict.fromArray
|
|
1255
1278
|
Pulumi.Pulumi.export(
|
|
1256
1279
|
"deploymentMetadata",
|
|
1257
1280
|
metadata->Dict.mapValues(JSON.Encode.string)->JSON.Encode.object->Pulumi.Output.make,
|
|
@@ -1595,6 +1618,7 @@ let exportPluginOutputs = (pluginOutputs: Plugin.outputs) => {
|
|
|
1595
1618
|
->Dict.get("GITHUB_SHA")
|
|
1596
1619
|
->Option.orElse(NodeProcess.env->Dict.get("CI_COMMIT_SHA"))
|
|
1597
1620
|
->Option.getOr(Date.make()->Date.toISOString)
|
|
1621
|
+
let provenance = Reventless.DeploymentProvenance.current()
|
|
1598
1622
|
let schemaFor = name => componentSchemaRegistry->Dict.get(name)->Option.getOr({})
|
|
1599
1623
|
let resolveAggregates = pluginOutputs.aggregates->Pulumi.Output.flatMap(aggs =>
|
|
1600
1624
|
aggs
|
|
@@ -1855,11 +1879,16 @@ let exportPluginOutputs = (pluginOutputs: Plugin.outputs) => {
|
|
|
1855
1879
|
let info: pluginDeployedInfo = {
|
|
1856
1880
|
name,
|
|
1857
1881
|
version,
|
|
1858
|
-
environment: Pulumi.Pulumi.getStackName(),
|
|
1882
|
+
environment: provenance.environment->Option.getOr(Pulumi.Pulumi.getStackName()),
|
|
1859
1883
|
stackName: Pulumi.Pulumi.getStackName(),
|
|
1860
1884
|
deployedAt: Date.make()->Date.toISOString,
|
|
1861
1885
|
actor,
|
|
1862
1886
|
deploymentId,
|
|
1887
|
+
commit: ?provenance.commit,
|
|
1888
|
+
dirty: ?(provenance.dirty ? Some(true) : None),
|
|
1889
|
+
purpose: ?provenance.purpose,
|
|
1890
|
+
tag: ?provenance.tag,
|
|
1891
|
+
pullRequest: ?provenance.pullRequest,
|
|
1863
1892
|
kind: ?(meta->Option.flatMap(m => m.kind)),
|
|
1864
1893
|
displayName: ?(meta->Option.flatMap(m => m.displayName)),
|
|
1865
1894
|
vendor: ?(meta->Option.flatMap(m => m.vendor)),
|
|
@@ -21,6 +21,7 @@ import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
|
|
|
21
21
|
import * as Adapter$ReventlessCore from "../../adapter/Adapter.res.mjs";
|
|
22
22
|
import * as Task$ReventlessInterop from "@reventlessdev/reventless-interop/src/components/Task.res.mjs";
|
|
23
23
|
import * as Aggregate$ReventlessCore from "../../components/Aggregate/Aggregate.res.mjs";
|
|
24
|
+
import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
|
|
24
25
|
import * as Component$ReventlessCore from "../../components/Component.res.mjs";
|
|
25
26
|
import * as ReadModel$ReventlessCore from "../../components/ReadModel/ReadModel.res.mjs";
|
|
26
27
|
import * as Api_Naming$ReventlessCore from "../../components/Api/Api_Naming.res.mjs";
|
|
@@ -42,6 +43,7 @@ import * as ApiNoApiHelpers$ReventlessCore from "../../components/Api/ApiNoApiHe
|
|
|
42
43
|
import * as AutomationSlice$ReventlessCore from "../../components/AutomationSlice/AutomationSlice.res.mjs";
|
|
43
44
|
import * as Builder_Helpers$ReventlessCore from "../../components/Builder_Helpers.res.mjs";
|
|
44
45
|
import * as Plugin_Callback$ReventlessCore from "./Plugin_Callback.res.mjs";
|
|
46
|
+
import * as DeploymentProvenance$Reventless from "@reventlessdev/reventless-spec/src/types/DeploymentProvenance.res.mjs";
|
|
45
47
|
import * as Plugin_BuiltHook$ReventlessCore from "./Plugin_BuiltHook.res.mjs";
|
|
46
48
|
import * as StateChangeSlice$ReventlessCore from "../../components/StateChangeSlice/StateChangeSlice.res.mjs";
|
|
47
49
|
import * as ExtensionPoint$ReventlessInterop from "@reventlessdev/reventless-interop/src/components/ExtensionPoint.res.mjs";
|
|
@@ -539,7 +541,7 @@ function registerAdminAggregateMutations(aggregates, hooks) {
|
|
|
539
541
|
let fieldNames = filteredConstructorNames.map(cname => Api_Naming$ReventlessCore.adminField(M.Spec.name + "_" + cname));
|
|
540
542
|
aggregateMutationFieldsRegistry[M.Spec.name] = fieldNames;
|
|
541
543
|
if (fieldNames.length !== 0) {
|
|
542
|
-
return Stdlib_Option.forEach(hooks.mutationResolverHook, registerResolver => registerResolver("Aggregate", fieldNames, commandSchema, M.Spec.commandAuthorization));
|
|
544
|
+
return Stdlib_Option.forEach(hooks.mutationResolverHook, registerResolver => registerResolver("Aggregate", fieldNames, commandSchema, command => Authorization$Reventless.named(M.Spec.commandAuthorization(command))));
|
|
543
545
|
}
|
|
544
546
|
});
|
|
545
547
|
}
|
|
@@ -607,10 +609,40 @@ function getInteropMeta() {
|
|
|
607
609
|
}
|
|
608
610
|
|
|
609
611
|
function exportDeploymentMetadata() {
|
|
612
|
+
let provenance = DeploymentProvenance$Reventless.current();
|
|
613
|
+
let stack = Pulumi.getStack();
|
|
614
|
+
let optional = Stdlib_Array.filterMap([
|
|
615
|
+
[
|
|
616
|
+
"commit",
|
|
617
|
+
provenance.commit
|
|
618
|
+
],
|
|
619
|
+
[
|
|
620
|
+
"purpose",
|
|
621
|
+
provenance.purpose
|
|
622
|
+
],
|
|
623
|
+
[
|
|
624
|
+
"tag",
|
|
625
|
+
provenance.tag
|
|
626
|
+
],
|
|
627
|
+
[
|
|
628
|
+
"pullRequest",
|
|
629
|
+
Stdlib_Option.map(provenance.pullRequest, n => n.toString())
|
|
630
|
+
]
|
|
631
|
+
], param => {
|
|
632
|
+
let key = param[0];
|
|
633
|
+
return Stdlib_Option.map(param[1], v => [
|
|
634
|
+
key,
|
|
635
|
+
v
|
|
636
|
+
]);
|
|
637
|
+
});
|
|
610
638
|
let metadata = Object.fromEntries([
|
|
611
639
|
[
|
|
612
640
|
"environment",
|
|
613
|
-
|
|
641
|
+
Stdlib_Option.getOr(provenance.environment, stack)
|
|
642
|
+
],
|
|
643
|
+
[
|
|
644
|
+
"stack",
|
|
645
|
+
stack
|
|
614
646
|
],
|
|
615
647
|
[
|
|
616
648
|
"region",
|
|
@@ -622,13 +654,17 @@ function exportDeploymentMetadata() {
|
|
|
622
654
|
],
|
|
623
655
|
[
|
|
624
656
|
"gitSha",
|
|
625
|
-
Stdlib_Option.getOr(
|
|
657
|
+
Stdlib_Option.getOr(provenance.commit, "unknown")
|
|
658
|
+
],
|
|
659
|
+
[
|
|
660
|
+
"dirty",
|
|
661
|
+
provenance.dirty ? "true" : "false"
|
|
626
662
|
],
|
|
627
663
|
[
|
|
628
664
|
"actor",
|
|
629
665
|
Stdlib_Option.getOr(process.env["GITHUB_ACTOR"], "unknown")
|
|
630
666
|
]
|
|
631
|
-
]);
|
|
667
|
+
].concat(optional));
|
|
632
668
|
Pulumi$Pulumi.$$export("deploymentMetadata", Pulumi.output(Stdlib_Dict.mapValues(metadata, prim => prim)));
|
|
633
669
|
}
|
|
634
670
|
|
|
@@ -725,6 +761,7 @@ function exportPluginOutputs(pluginOutputs) {
|
|
|
725
761
|
}
|
|
726
762
|
let actor = Stdlib_Option.getOr(Stdlib_Option.orElse(Stdlib_Option.orElse(process.env["GITHUB_ACTOR"], process.env["CI_COMMIT_AUTHOR"]), process.env["USER"]), "local");
|
|
727
763
|
let deploymentId = Stdlib_Option.getOr(Stdlib_Option.orElse(process.env["GITHUB_SHA"], process.env["CI_COMMIT_SHA"]), new Date().toISOString());
|
|
764
|
+
let provenance = DeploymentProvenance$Reventless.current();
|
|
728
765
|
let resolveAggregates = Output$Pulumi.flatMap(pluginOutputs.aggregates, aggs => Pulumi.all(Object.entries(aggs).map(param => {
|
|
729
766
|
let name = param[0];
|
|
730
767
|
return Aggregate$ReventlessCore.toResolvedOutputs(param[1]).apply(resolved => ({
|
|
@@ -904,11 +941,16 @@ function exportPluginOutputs(pluginOutputs) {
|
|
|
904
941
|
return hook({
|
|
905
942
|
name: name,
|
|
906
943
|
version: version,
|
|
907
|
-
environment: Pulumi.getStack(),
|
|
944
|
+
environment: Stdlib_Option.getOr(provenance.environment, Pulumi.getStack()),
|
|
908
945
|
stackName: Pulumi.getStack(),
|
|
909
946
|
deployedAt: new Date().toISOString(),
|
|
910
947
|
actor: actor,
|
|
911
948
|
deploymentId: deploymentId,
|
|
949
|
+
commit: provenance.commit,
|
|
950
|
+
dirty: provenance.dirty ? true : undefined,
|
|
951
|
+
purpose: provenance.purpose,
|
|
952
|
+
tag: provenance.tag,
|
|
953
|
+
pullRequest: provenance.pullRequest,
|
|
912
954
|
kind: Stdlib_Option.flatMap(meta, m => m.kind),
|
|
913
955
|
displayName: Stdlib_Option.flatMap(meta, m => m.displayName),
|
|
914
956
|
vendor: Stdlib_Option.flatMap(meta, m => m.vendor),
|
|
@@ -793,15 +793,32 @@ let commandLevelAndId = (
|
|
|
793
793
|
}
|
|
794
794
|
|
|
795
795
|
// The server's rule as keys a client checks against `identity.groups ++
|
|
796
|
-
// config.accessTiers`; an any-of, since `isAllowed` is `some`.
|
|
797
|
-
//
|
|
796
|
+
// config.accessTiers`; an any-of, since `isAllowed` is `some`. Mapped groups, not
|
|
797
|
+
// role names: a key no token carries would gate the surface shut. The rules
|
|
798
|
+
// asking for nothing checkable — and `DenyAll` — publish no keys rather than an
|
|
798
799
|
// unsatisfiable one.
|
|
799
800
|
let accessKeysFor = (rule: Reventless.Authorization.permission): option<array<string>> =>
|
|
800
|
-
switch rule {
|
|
801
|
-
|
|
|
802
|
-
|
|
|
801
|
+
switch Reventless.Authorization.groupsOf(rule) {
|
|
802
|
+
| [] => None
|
|
803
|
+
| groups => Some(groups)
|
|
803
804
|
}
|
|
804
805
|
|
|
806
|
+
let nonEmpty = (xs: array<string>): option<array<string>> => xs->Array.length > 0 ? Some(xs) : None
|
|
807
|
+
|
|
808
|
+
// The same rule's roles before mapping, which the deploy check compares with what
|
|
809
|
+
// the platform provides.
|
|
810
|
+
let rolesFor = (rule: Reventless.Authorization.permission): option<array<string>> =>
|
|
811
|
+
rule->Reventless.Authorization.rolesOf->Array.map(role => (role :> string))->nonEmpty
|
|
812
|
+
|
|
813
|
+
// A stand-in for one constructor, the same shape the resolver builds at call time:
|
|
814
|
+
// a payload-bearing variant compiles to `{TAG, ...}`, a payload-less one to a bare
|
|
815
|
+
// string. Enough for the per-command switches the PPX generates, which match on
|
|
816
|
+
// the constructor alone.
|
|
817
|
+
let syntheticCommand = (~schema: S.t<unknown>, variantName: string): 'command =>
|
|
818
|
+
Reventless.DcbTag.isVariantPayloadBearing(schema, variantName)
|
|
819
|
+
? {"TAG": variantName}->Obj.magic
|
|
820
|
+
: variantName->Obj.magic
|
|
821
|
+
|
|
805
822
|
// Each mutation argument's GraphQL type onto its property, so a consumer declares
|
|
806
823
|
// the variable the server expects. Mutates the freshly derived schema in place.
|
|
807
824
|
let annotateArgTypes = (schema: JSON.t, argTypes: dict<string>): JSON.t => {
|
|
@@ -860,15 +877,8 @@ let toCommandDef = (
|
|
|
860
877
|
~variantName,
|
|
861
878
|
properties,
|
|
862
879
|
)
|
|
863
|
-
// Evaluated against a synthetic value per constructor
|
|
864
|
-
|
|
865
|
-
// `{TAG, ...}`, a payload-less one to a bare string.
|
|
866
|
-
let syntheticCommand: unknown = Reventless.DcbTag.isVariantPayloadBearing(
|
|
867
|
-
parentSchema,
|
|
868
|
-
variantName,
|
|
869
|
-
)
|
|
870
|
-
? {"TAG": variantName}->Obj.magic
|
|
871
|
-
: variantName->Obj.magic
|
|
880
|
+
// Evaluated against a synthetic value per constructor; see `syntheticCommand`.
|
|
881
|
+
let syntheticCommand: unknown = syntheticCommand(~schema=parentSchema, variantName)
|
|
872
882
|
// The spec's own switch, which is exhaustive — so it also speaks for a
|
|
873
883
|
// constructor the host did not declare but spliced from a trait.
|
|
874
884
|
//
|
|
@@ -937,7 +947,8 @@ let toCommandDef = (
|
|
|
937
947
|
| Some(excluded) => !(excluded->Set.has(variantName))
|
|
938
948
|
| None => true
|
|
939
949
|
}
|
|
940
|
-
let
|
|
950
|
+
let rule = commandAuthorization(syntheticCommand)
|
|
951
|
+
let requiredAccess = accessKeysFor(rule)
|
|
941
952
|
// See the note on the record's `mutationField` for why a non-exposed
|
|
942
953
|
// variant gets the empty sentinel. It has no callable field, and the
|
|
943
954
|
// argument type names are composed *from* that field name, so there is
|
|
@@ -975,6 +986,7 @@ let toCommandDef = (
|
|
|
975
986
|
?allowedStatesSource,
|
|
976
987
|
apiExposed: Some(apiExposed),
|
|
977
988
|
requiredAccess,
|
|
989
|
+
requiredRoles: ?rolesFor(rule),
|
|
978
990
|
// Resolved from this constructor's own properties, not the union's: two
|
|
979
991
|
// commands in one slice can disagree about whether they record an owner,
|
|
980
992
|
// and the write path stamps per constructor for the same reason.
|
|
@@ -1094,6 +1106,7 @@ let queryableDefFromSpec = (
|
|
|
1094
1106
|
idField: keyField->Option.map(((f, _)) => f),
|
|
1095
1107
|
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1096
1108
|
requiredAccess: accessKeysFor(authorization),
|
|
1109
|
+
requiredRoles: ?rolesFor(authorization),
|
|
1097
1110
|
}
|
|
1098
1111
|
}
|
|
1099
1112
|
|
|
@@ -1550,7 +1563,8 @@ let make = (
|
|
|
1550
1563
|
singleQueryField: Some(qf.singleFieldName),
|
|
1551
1564
|
idField: keyField->Option.map(((f, _)) => f),
|
|
1552
1565
|
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1553
|
-
requiredAccess: accessKeysFor(R.Spec.authorization),
|
|
1566
|
+
requiredAccess: accessKeysFor(R.Spec.authorization->Reventless.Authorization.named),
|
|
1567
|
+
requiredRoles: ?rolesFor(R.Spec.authorization->Reventless.Authorization.named),
|
|
1554
1568
|
}: Reventless.Plugin.queryableDef
|
|
1555
1569
|
)
|
|
1556
1570
|
})
|
|
@@ -1590,7 +1604,8 @@ let make = (
|
|
|
1590
1604
|
singleQueryField: Some(qf.singleFieldName),
|
|
1591
1605
|
idField: keyField->Option.map(((f, _)) => f),
|
|
1592
1606
|
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1593
|
-
requiredAccess: accessKeysFor(SVS.Spec.authorization),
|
|
1607
|
+
requiredAccess: accessKeysFor(SVS.Spec.authorization->Reventless.Authorization.named),
|
|
1608
|
+
requiredRoles: ?rolesFor(SVS.Spec.authorization->Reventless.Authorization.named),
|
|
1594
1609
|
}: Reventless.Plugin.queryableDef
|
|
1595
1610
|
)
|
|
1596
1611
|
})
|
|
@@ -1690,7 +1705,8 @@ let make = (
|
|
|
1690
1705
|
~commandSchema=SCS.Spec.commandSchema->S.castToUnknown,
|
|
1691
1706
|
~variant=variantName,
|
|
1692
1707
|
),
|
|
1693
|
-
~commandAuthorization=
|
|
1708
|
+
~commandAuthorization=command =>
|
|
1709
|
+
SCS.Spec.commandAuthorization(command->Obj.magic)->Reventless.Authorization.named,
|
|
1694
1710
|
~commandTransition=SCS.Spec.commandTransition->Obj.magic,
|
|
1695
1711
|
~derivedEdgeFor=derivedEdgeFor(~component=SCS.Spec.name, ...),
|
|
1696
1712
|
SCS.Spec.commandSchema->S.castToUnknown,
|
|
@@ -1724,7 +1740,8 @@ let make = (
|
|
|
1724
1740
|
~aggregate=A.Spec.name,
|
|
1725
1741
|
~command=variantName,
|
|
1726
1742
|
),
|
|
1727
|
-
~commandAuthorization=
|
|
1743
|
+
~commandAuthorization=command =>
|
|
1744
|
+
A.Spec.commandAuthorization(command->Obj.magic)->Reventless.Authorization.named,
|
|
1728
1745
|
~commandTransition=A.Spec.commandTransition->Obj.magic,
|
|
1729
1746
|
~derivedEdgeFor=derivedEdgeFor(~component=A.Spec.name, ...),
|
|
1730
1747
|
A.Spec.commandSchema->S.castToUnknown,
|
|
@@ -1798,6 +1815,22 @@ let make = (
|
|
|
1798
1815
|
targetName: ITS.Spec.targetName,
|
|
1799
1816
|
externalSystem: ITS.Spec.externalSystem,
|
|
1800
1817
|
chapter: chapterOf(ITS.Spec.name),
|
|
1818
|
+
// Per command, as the resolver evaluates the rule, then joined: the slice
|
|
1819
|
+
// publishes no command defs to carry them.
|
|
1820
|
+
requiredRoles: ?(
|
|
1821
|
+
Reventless.DcbTag.extractAllVariantNames(ITS.Spec.commandSchema)
|
|
1822
|
+
->Array.flatMap(variantName =>
|
|
1823
|
+
syntheticCommand(~schema=ITS.Spec.commandSchema->S.castToUnknown, variantName)
|
|
1824
|
+
->ITS.Spec.commandAuthorization
|
|
1825
|
+
->Reventless.Authorization.named
|
|
1826
|
+
->Reventless.Authorization.rolesOf
|
|
1827
|
+
->Array.map(role => (role :> string))
|
|
1828
|
+
)
|
|
1829
|
+
->Set.fromArray
|
|
1830
|
+
->Set.values
|
|
1831
|
+
->Array.fromIterator
|
|
1832
|
+
->nonEmpty
|
|
1833
|
+
),
|
|
1801
1834
|
})
|
|
1802
1835
|
|
|
1803
1836
|
// ── Extensions ───────────────────────────────────────────────────────────
|
|
@@ -21,6 +21,7 @@ import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
|
|
|
21
21
|
import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
|
|
22
22
|
import * as Transition$Reventless from "@reventlessdev/reventless-spec/src/types/Transition.res.mjs";
|
|
23
23
|
import * as DisplayName$Reventless from "@reventlessdev/reventless-spec/src/components/DisplayName.res.mjs";
|
|
24
|
+
import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
|
|
24
25
|
import * as Api_Naming$ReventlessCore from "../../components/Api/Api_Naming.res.mjs";
|
|
25
26
|
import * as CapabilityNeed$Reventless from "@reventlessdev/reventless-spec/src/semantic/CapabilityNeed.res.mjs";
|
|
26
27
|
import * as SchemaType$ReventlessCore from "../../components/Api/SchemaType.res.mjs";
|
|
@@ -617,15 +618,32 @@ function commandLevelAndId(isAggregate, partitionKey, variantName, properties) {
|
|
|
617
618
|
}
|
|
618
619
|
|
|
619
620
|
function accessKeysFor(rule) {
|
|
620
|
-
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
let groups = rule._0;
|
|
621
|
+
let groups = Authorization$Reventless.groupsOf(rule);
|
|
624
622
|
if (groups.length !== 0) {
|
|
625
623
|
return groups;
|
|
626
624
|
}
|
|
627
625
|
}
|
|
628
626
|
|
|
627
|
+
function nonEmpty(xs) {
|
|
628
|
+
if (xs.length !== 0) {
|
|
629
|
+
return xs;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function rolesFor(rule) {
|
|
634
|
+
return nonEmpty(Authorization$Reventless.rolesOf(rule).map(role => role));
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function syntheticCommand(schema, variantName) {
|
|
638
|
+
if (DcbTag$Reventless.isVariantPayloadBearing(schema, variantName)) {
|
|
639
|
+
return {
|
|
640
|
+
TAG: variantName
|
|
641
|
+
};
|
|
642
|
+
} else {
|
|
643
|
+
return variantName;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
629
647
|
function annotateArgTypes(schema, argTypes) {
|
|
630
648
|
Stdlib_Option.forEach(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(schema), o => o["properties"]), Stdlib_JSON.Decode.object), props => {
|
|
631
649
|
Object.entries(props).forEach(param => {
|
|
@@ -644,10 +662,8 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
|
|
|
644
662
|
let mkDef = (variantName, properties) => {
|
|
645
663
|
let match = commandLevelAndId(isAggregate, partitionKey, variantName, properties);
|
|
646
664
|
let aggregateIdField = match[1];
|
|
647
|
-
let syntheticCommand =
|
|
648
|
-
|
|
649
|
-
}) : variantName;
|
|
650
|
-
let declared = commandTransition(syntheticCommand);
|
|
665
|
+
let syntheticCommand$1 = syntheticCommand(parentSchema, variantName);
|
|
666
|
+
let declared = commandTransition(syntheticCommand$1);
|
|
651
667
|
let derived = derivedEdgeFor(variantName);
|
|
652
668
|
let unrestricted = Transition$Reventless.isUnrestricted(declared);
|
|
653
669
|
let match$1 = unrestricted ? undefined : Stdlib_Option.flatMap(derived, d => {
|
|
@@ -684,7 +700,8 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
|
|
|
684
700
|
let excluded = ApiNoApiHelpers$ReventlessCore.getExcludedVariants(parentSchema);
|
|
685
701
|
apiExposed = excluded !== undefined ? !excluded.has(variantName) : true;
|
|
686
702
|
}
|
|
687
|
-
let
|
|
703
|
+
let rule = commandAuthorization(syntheticCommand$1);
|
|
704
|
+
let requiredAccess = accessKeysFor(rule);
|
|
688
705
|
let mutationField = apiExposed ? mutationFieldFor(variantName) : "";
|
|
689
706
|
let jsonSchema = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(apiExposed, mutationField, v);
|
|
690
707
|
let annotatedSchema = apiExposed ? Stdlib_Option.mapOr(GraphQL_FragmentGenerator$ReventlessCore.mutationArgTypes(mutationField, v), jsonSchema, __x => annotateArgTypes(jsonSchema, __x)) : jsonSchema;
|
|
@@ -700,6 +717,7 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
|
|
|
700
717
|
allowedStatesSource: match$3[1],
|
|
701
718
|
apiExposed: apiExposed,
|
|
702
719
|
requiredAccess: requiredAccess,
|
|
720
|
+
requiredRoles: rolesFor(rule),
|
|
703
721
|
ownerField: Owner$Reventless.fieldNamesOfProperties(properties)[0]
|
|
704
722
|
};
|
|
705
723
|
};
|
|
@@ -770,7 +788,8 @@ function queryableDefFromSpec(plugin, name, stateSchema, authorization, visibili
|
|
|
770
788
|
singleQueryField: qf.singleFieldName,
|
|
771
789
|
idField: Stdlib_Option.map(keyField, param => param[0]),
|
|
772
790
|
idFieldSource: Stdlib_Option.map(keyField, param => param[1]),
|
|
773
|
-
requiredAccess: accessKeysFor(authorization)
|
|
791
|
+
requiredAccess: accessKeysFor(authorization),
|
|
792
|
+
requiredRoles: rolesFor(authorization)
|
|
774
793
|
};
|
|
775
794
|
}
|
|
776
795
|
|
|
@@ -1080,7 +1099,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
1080
1099
|
singleQueryField: qf.singleFieldName,
|
|
1081
1100
|
idField: Stdlib_Option.map(keyField, param => param[0]),
|
|
1082
1101
|
idFieldSource: Stdlib_Option.map(keyField, param => param[1]),
|
|
1083
|
-
requiredAccess: accessKeysFor(R.Spec.authorization)
|
|
1102
|
+
requiredAccess: accessKeysFor(Authorization$Reventless.named(R.Spec.authorization)),
|
|
1103
|
+
requiredRoles: rolesFor(Authorization$Reventless.named(R.Spec.authorization))
|
|
1084
1104
|
};
|
|
1085
1105
|
});
|
|
1086
1106
|
let stateViewDefs = stateViewSlices.map((SVS, i) => {
|
|
@@ -1112,7 +1132,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
1112
1132
|
singleQueryField: qf.singleFieldName,
|
|
1113
1133
|
idField: Stdlib_Option.map(keyField, param => param[0]),
|
|
1114
1134
|
idFieldSource: Stdlib_Option.map(keyField, param => param[1]),
|
|
1115
|
-
requiredAccess: accessKeysFor(SVS.Spec.authorization)
|
|
1135
|
+
requiredAccess: accessKeysFor(Authorization$Reventless.named(SVS.Spec.authorization)),
|
|
1136
|
+
requiredRoles: rolesFor(Authorization$Reventless.named(SVS.Spec.authorization))
|
|
1116
1137
|
};
|
|
1117
1138
|
});
|
|
1118
1139
|
let dcbSliceSchemas = stateChangeSlices.map(SCS => ({
|
|
@@ -1197,7 +1218,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
1197
1218
|
let consumed = match$1[1];
|
|
1198
1219
|
return {
|
|
1199
1220
|
name: SCS.Spec.name,
|
|
1200
|
-
commands: extractCommandDefs(identityViews, false, partitionBySlice[SCS.Spec.name], variantName => Api_Naming$ReventlessCore.sliceMutationFieldFor(name, SCS.Spec.name, SCS.Spec.commandSchema, variantName), SCS.Spec.commandAuthorization, SCS.Spec.commandTransition, extra => derivedEdgeFor(SCS.Spec.name, extra), SCS.Spec.commandSchema),
|
|
1221
|
+
commands: extractCommandDefs(identityViews, false, partitionBySlice[SCS.Spec.name], variantName => Api_Naming$ReventlessCore.sliceMutationFieldFor(name, SCS.Spec.name, SCS.Spec.commandSchema, variantName), command => Authorization$Reventless.named(SCS.Spec.commandAuthorization(command)), SCS.Spec.commandTransition, extra => derivedEdgeFor(SCS.Spec.name, extra), SCS.Spec.commandSchema),
|
|
1201
1222
|
producedEventTypes: produced,
|
|
1202
1223
|
consumedEventTypes: consumed,
|
|
1203
1224
|
linkedViews: linkedSvsFor(produced),
|
|
@@ -1212,7 +1233,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
1212
1233
|
let produced = match[1];
|
|
1213
1234
|
return {
|
|
1214
1235
|
name: A.Spec.name,
|
|
1215
|
-
commands: extractCommandDefs(identityViews, true, undefined, variantName => Api_Naming$ReventlessCore.aggregateMutationField(name, A.Spec.name, variantName), A.Spec.commandAuthorization, A.Spec.commandTransition, extra => derivedEdgeFor(A.Spec.name, extra), A.Spec.commandSchema),
|
|
1236
|
+
commands: extractCommandDefs(identityViews, true, undefined, variantName => Api_Naming$ReventlessCore.aggregateMutationField(name, A.Spec.name, variantName), command => Authorization$Reventless.named(A.Spec.commandAuthorization(command)), A.Spec.commandTransition, extra => derivedEdgeFor(A.Spec.name, extra), A.Spec.commandSchema),
|
|
1216
1237
|
producedEventTypes: produced,
|
|
1217
1238
|
consumedEventTypes: [],
|
|
1218
1239
|
linkedViews: linkedSvsFor(produced).concat(linkedReadModelsFor(A.Spec.name)),
|
|
@@ -1246,7 +1267,8 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
1246
1267
|
commandTypes: qualify(name, DcbTag$Reventless.extractAllVariantNames(ITS.Spec.commandSchema)),
|
|
1247
1268
|
targetName: ITS.Spec.targetName,
|
|
1248
1269
|
externalSystem: ITS.Spec.externalSystem,
|
|
1249
|
-
chapter: componentChapters[ITS.Spec.name]
|
|
1270
|
+
chapter: componentChapters[ITS.Spec.name],
|
|
1271
|
+
requiredRoles: nonEmpty(Array.from(new Set(DcbTag$Reventless.extractAllVariantNames(ITS.Spec.commandSchema).flatMap(variantName => Authorization$Reventless.rolesOf(Authorization$Reventless.named(ITS.Spec.commandAuthorization(syntheticCommand(ITS.Spec.commandSchema, variantName)))).map(role => role))).values()))
|
|
1250
1272
|
}));
|
|
1251
1273
|
let tableFailures = [];
|
|
1252
1274
|
let tableWarnings = [];
|
|
@@ -1549,6 +1571,9 @@ export {
|
|
|
1549
1571
|
isCreateCommandName,
|
|
1550
1572
|
commandLevelAndId,
|
|
1551
1573
|
accessKeysFor,
|
|
1574
|
+
nonEmpty,
|
|
1575
|
+
rolesFor,
|
|
1576
|
+
syntheticCommand,
|
|
1552
1577
|
annotateArgTypes,
|
|
1553
1578
|
toCommandDef,
|
|
1554
1579
|
extractCommandDefs,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// this (spec-level `permission` outranks the legacy `{tableName, group}` pair),
|
|
11
11
|
// and so does the `requiredAccess` the platform publishes — so a shell can gate
|
|
12
12
|
// the Plugins view up front instead of offering it and meeting a refusal.
|
|
13
|
-
@@reventless.authorize(
|
|
13
|
+
@@reventless.authorize(AllowRoles([Admin]))
|
|
14
14
|
|
|
15
15
|
// Current view — one row per plugin **name**, holding the currently-active
|
|
16
16
|
// version's definition. Keyed by the aggregate id (= plugin name). The status
|
|
@@ -254,7 +254,9 @@ let guards: array<guard> = [
|
|
|
254
254
|
recordFields: schemaKeys(commandDefSchema),
|
|
255
255
|
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeCommandDef(cmd)),
|
|
256
256
|
wireOnly: [],
|
|
257
|
-
|
|
257
|
+
// The roles behind `requiredAccess`, read by the deploy check in-process.
|
|
258
|
+
// A client gates on the mapped groups and has no use for role names.
|
|
259
|
+
recordOnly: ["requiredRoles"],
|
|
258
260
|
},
|
|
259
261
|
{
|
|
260
262
|
gqlType: "Platform_EventDef",
|
|
@@ -286,7 +288,9 @@ let guards: array<guard> = [
|
|
|
286
288
|
recordFields: schemaKeys(queryableDefSchema),
|
|
287
289
|
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeQueryableDef(queryable)),
|
|
288
290
|
wireOnly: [],
|
|
289
|
-
|
|
291
|
+
// The roles behind `requiredAccess`, read by the deploy check in-process.
|
|
292
|
+
// A client gates on the mapped groups and has no use for role names.
|
|
293
|
+
recordOnly: ["requiredRoles"],
|
|
290
294
|
},
|
|
291
295
|
{
|
|
292
296
|
gqlType: "Platform_AutomationSliceDef",
|
|
@@ -314,7 +318,9 @@ let guards: array<guard> = [
|
|
|
314
318
|
Platform_ComponentDefinitionsApi.encodeInboundTranslationSliceDef(inbound),
|
|
315
319
|
),
|
|
316
320
|
wireOnly: [],
|
|
317
|
-
|
|
321
|
+
// The roles behind `requiredAccess`, read by the deploy check in-process.
|
|
322
|
+
// A client gates on the mapped groups and has no use for role names.
|
|
323
|
+
recordOnly: ["requiredRoles"],
|
|
318
324
|
},
|
|
319
325
|
{
|
|
320
326
|
gqlType: "Platform_ExtensionDef",
|
|
@@ -390,7 +390,7 @@ let guards = [
|
|
|
390
390
|
recordFields: schemaKeys(Plugin$Reventless.commandDefSchema),
|
|
391
391
|
wireFields: jsonKeys(Platform_ComponentDefinitionsApi$ReventlessCore.encodeCommandDef(cmd)),
|
|
392
392
|
wireOnly: [],
|
|
393
|
-
recordOnly: []
|
|
393
|
+
recordOnly: ["requiredRoles"]
|
|
394
394
|
},
|
|
395
395
|
{
|
|
396
396
|
gqlType: "Platform_EventDef",
|
|
@@ -422,7 +422,7 @@ let guards = [
|
|
|
422
422
|
recordFields: schemaKeys(Plugin$Reventless.queryableDefSchema),
|
|
423
423
|
wireFields: jsonKeys(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef(queryable)),
|
|
424
424
|
wireOnly: [],
|
|
425
|
-
recordOnly: []
|
|
425
|
+
recordOnly: ["requiredRoles"]
|
|
426
426
|
},
|
|
427
427
|
{
|
|
428
428
|
gqlType: "Platform_AutomationSliceDef",
|
|
@@ -446,7 +446,7 @@ let guards = [
|
|
|
446
446
|
recordFields: schemaKeys(Plugin$Reventless.inboundTranslationSliceDefSchema),
|
|
447
447
|
wireFields: jsonKeys(Platform_ComponentDefinitionsApi$ReventlessCore.encodeInboundTranslationSliceDef(inbound)),
|
|
448
448
|
wireOnly: [],
|
|
449
|
-
recordOnly: []
|
|
449
|
+
recordOnly: ["requiredRoles"]
|
|
450
450
|
},
|
|
451
451
|
{
|
|
452
452
|
gqlType: "Platform_ExtensionDef",
|