@reventlessdev/reventless-core 3.0.0-alpha.285 → 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 +15 -0
- package/package.json +6 -6
- 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/AutomationSlice/AutomationSlice_Builder.res +2 -1
- 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/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 +2 -1
- package/src/plugin/component/Plugin_Helpers.res.mjs +2 -1
- 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/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
|
@@ -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",
|
|
@@ -27,7 +27,7 @@ let commandNamed = (name: string): commandDef =>
|
|
|
27
27
|
let activate = () => commandNamed("Activate")
|
|
28
28
|
|
|
29
29
|
describe("the Plugins read model declares its lifecycle", () => {
|
|
30
|
-
let rm: queryableDef = Platform_Admin_Structure.pluginReadModel
|
|
30
|
+
let rm: queryableDef = Platform_Admin_Structure.pluginReadModel()
|
|
31
31
|
|
|
32
32
|
testSync("names the field commands branch on", () =>
|
|
33
33
|
expect(rm.lifecycleField)->toEqual(Some("status"))
|
|
@@ -206,25 +206,28 @@ describe("the internal commands declare the edges they move rows along", () => {
|
|
|
206
206
|
// was open to everyone and the server refused anyone outside `Admin`. The
|
|
207
207
|
// mismatch is invisible until a non-admin clicks the page.
|
|
208
208
|
describe("the Plugins view publishes the rule the server enforces", () => {
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
// make everything *else* agree with the annotation, and nothing in the compiler
|
|
213
|
-
// spans that gap. The actual side here comes from the expanded annotation and
|
|
214
|
-
// the expected side from the constant, so the two are compared rather than
|
|
215
|
-
// restated. Writing the literal on both sides would pass while they diverged.
|
|
216
|
-
testSync("the spec declares the group, and it is the one the constant names", () =>
|
|
209
|
+
// The annotation names the role, so a renamed administrator group follows it
|
|
210
|
+
// without anyone editing the spec.
|
|
211
|
+
testSync("the spec declares the administrator role", () =>
|
|
217
212
|
expect(PluginsReadModelSpec.authorization)->toEqual(
|
|
218
|
-
Reventless.Authorization.
|
|
213
|
+
Reventless.Authorization.AllowRoles([Reventless.Role.admin]),
|
|
219
214
|
)
|
|
220
215
|
)
|
|
221
216
|
|
|
222
|
-
testSync("and the published access keys are
|
|
223
|
-
expect(Platform_Admin_Structure.pluginReadModel.requiredAccess)->toEqual(
|
|
224
|
-
Some([Reventless.
|
|
217
|
+
testSync("and the published access keys are the group it maps to", () =>
|
|
218
|
+
expect(Platform_Admin_Structure.pluginReadModel().requiredAccess)->toEqual(
|
|
219
|
+
Some([Reventless.Role.adminGroup()]),
|
|
225
220
|
)
|
|
226
221
|
)
|
|
227
222
|
|
|
223
|
+
testSync("a renamed administrator group reaches the access keys", () => {
|
|
224
|
+
Reventless.Role.clearGroups()
|
|
225
|
+
Reventless.Role.setGroups([(Reventless.Role.admin, "ops")])
|
|
226
|
+
let keys = Platform_Admin_Structure.pluginReadModel().requiredAccess
|
|
227
|
+
Reventless.Role.clearGroups()
|
|
228
|
+
expect(keys)->toEqual(Some(["ops"]))
|
|
229
|
+
})
|
|
230
|
+
|
|
228
231
|
// The API entry reads the same binding rather than restating it, so the two
|
|
229
232
|
// cannot be edited apart.
|
|
230
233
|
testSync("the query entry carries the spec's rule and no hand-written pair", () => {
|
|
@@ -243,7 +246,7 @@ describe("the Plugins view publishes the rule the server enforces", () => {
|
|
|
243
246
|
// looked special. Asserted rather than assumed — the whole case for deriving
|
|
244
247
|
// rests on the generic helpers reaching the same answers.
|
|
245
248
|
describe("the Plugins queryableDef is what the generic helpers produce", () => {
|
|
246
|
-
let rm: queryableDef = Platform_Admin_Structure.pluginReadModel
|
|
249
|
+
let rm: queryableDef = Platform_Admin_Structure.pluginReadModel()
|
|
247
250
|
|
|
248
251
|
testSync("the field names are the generic ones", () =>
|
|
249
252
|
expect((rm.queryField, rm.singleQueryField))->toEqual((
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
5
6
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
6
|
-
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
7
7
|
import * as Plugin_Structure$ReventlessCore from "../../src/plugin/component/Plugin_Structure.res.mjs";
|
|
8
8
|
import * as PluginBaseFragment$ReventlessCore from "../../src/plugin/api/PluginBaseFragment.res.mjs";
|
|
9
9
|
import * as PluginsReadModelSpec$ReventlessCore from "../../src/plugin/lifecycle/PluginsReadModelSpec.res.mjs";
|
|
@@ -19,19 +19,20 @@ function activate() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
globalThis.describe("the Plugins read model declares its lifecycle", () => {
|
|
22
|
+
let rm = Platform_Admin_Structure$ReventlessCore.pluginReadModel();
|
|
22
23
|
globalThis.test("names the field commands branch on", () => {
|
|
23
|
-
globalThis.expect(
|
|
24
|
+
globalThis.expect(rm.lifecycleField).toEqual("status");
|
|
24
25
|
});
|
|
25
26
|
globalThis.test("and the published wire name is unchanged by the rename", () => {
|
|
26
|
-
let sdlDeclaresStatus = Stdlib_Option.mapOr(
|
|
27
|
+
let sdlDeclaresStatus = Stdlib_Option.mapOr(rm.lifecycleField, false, f => f === "status") && rm.queryField.startsWith("Platform_");
|
|
27
28
|
globalThis.expect(sdlDeclaresStatus).toEqual(true);
|
|
28
29
|
});
|
|
29
30
|
globalThis.test("is served on the encoded def under the new key", () => {
|
|
30
|
-
let json = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef(
|
|
31
|
+
let json = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef(rm));
|
|
31
32
|
globalThis.expect(json.includes(`"lifecycleField":"status"`)).toEqual(true);
|
|
32
33
|
});
|
|
33
34
|
globalThis.test("and never under the old one", () => {
|
|
34
|
-
globalThis.expect(JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef(
|
|
35
|
+
globalThis.expect(JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodeQueryableDef(rm)).includes(`"statusField"`)).toEqual(false);
|
|
35
36
|
});
|
|
36
37
|
});
|
|
37
38
|
|
|
@@ -155,14 +156,24 @@ globalThis.describe("the internal commands declare the edges they move rows alon
|
|
|
155
156
|
});
|
|
156
157
|
|
|
157
158
|
globalThis.describe("the Plugins view publishes the rule the server enforces", () => {
|
|
158
|
-
globalThis.test("the spec declares the
|
|
159
|
+
globalThis.test("the spec declares the administrator role", () => {
|
|
159
160
|
globalThis.expect(PluginsReadModelSpec$ReventlessCore.authorization).toEqual({
|
|
160
|
-
TAG: "
|
|
161
|
-
_0: [
|
|
161
|
+
TAG: "AllowRoles",
|
|
162
|
+
_0: [Role$Reventless.admin]
|
|
162
163
|
});
|
|
163
164
|
});
|
|
164
|
-
globalThis.test("and the published access keys are
|
|
165
|
-
globalThis.expect(Platform_Admin_Structure$ReventlessCore.pluginReadModel.requiredAccess).toEqual([
|
|
165
|
+
globalThis.test("and the published access keys are the group it maps to", () => {
|
|
166
|
+
globalThis.expect(Platform_Admin_Structure$ReventlessCore.pluginReadModel().requiredAccess).toEqual([Role$Reventless.adminGroup()]);
|
|
167
|
+
});
|
|
168
|
+
globalThis.test("a renamed administrator group reaches the access keys", () => {
|
|
169
|
+
Role$Reventless.clearGroups();
|
|
170
|
+
Role$Reventless.setGroups([[
|
|
171
|
+
Role$Reventless.admin,
|
|
172
|
+
"ops"
|
|
173
|
+
]]);
|
|
174
|
+
let keys = Platform_Admin_Structure$ReventlessCore.pluginReadModel().requiredAccess;
|
|
175
|
+
Role$Reventless.clearGroups();
|
|
176
|
+
globalThis.expect(keys).toEqual(["ops"]);
|
|
166
177
|
});
|
|
167
178
|
globalThis.test("the query entry carries the spec's rule and no hand-written pair", () => {
|
|
168
179
|
let entry = PluginBaseFragment$ReventlessCore.queryEntries[0];
|
|
@@ -177,10 +188,11 @@ globalThis.describe("the Plugins view publishes the rule the server enforces", (
|
|
|
177
188
|
});
|
|
178
189
|
|
|
179
190
|
globalThis.describe("the Plugins queryableDef is what the generic helpers produce", () => {
|
|
191
|
+
let rm = Platform_Admin_Structure$ReventlessCore.pluginReadModel();
|
|
180
192
|
globalThis.test("the field names are the generic ones", () => {
|
|
181
193
|
globalThis.expect([
|
|
182
|
-
|
|
183
|
-
|
|
194
|
+
rm.queryField,
|
|
195
|
+
rm.singleQueryField
|
|
184
196
|
]).toEqual([
|
|
185
197
|
"Platform_Plugins",
|
|
186
198
|
"Platform_Plugin"
|
|
@@ -188,9 +200,9 @@ globalThis.describe("the Plugins queryableDef is what the generic helpers produc
|
|
|
188
200
|
});
|
|
189
201
|
globalThis.test("the label ladder reaches the same field, by convention", () => {
|
|
190
202
|
globalThis.expect([
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
203
|
+
rm.labelField,
|
|
204
|
+
rm.labelFieldSource,
|
|
205
|
+
rm.searchableFields
|
|
194
206
|
]).toEqual([
|
|
195
207
|
"name",
|
|
196
208
|
"convention",
|
|
@@ -199,15 +211,15 @@ globalThis.describe("the Plugins queryableDef is what the generic helpers produc
|
|
|
199
211
|
});
|
|
200
212
|
globalThis.test("the key ladder finds no id field, as the hand-written def claimed", () => {
|
|
201
213
|
globalThis.expect([
|
|
202
|
-
|
|
203
|
-
|
|
214
|
+
rm.idField,
|
|
215
|
+
rm.idFieldSource
|
|
204
216
|
]).toEqual([
|
|
205
217
|
undefined,
|
|
206
218
|
undefined
|
|
207
219
|
]);
|
|
208
220
|
});
|
|
209
221
|
globalThis.test("the blob fields are published, and marked hidden", () => {
|
|
210
|
-
let props = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(
|
|
222
|
+
let props = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(rm.schema)), o => o["properties"]), Stdlib_JSON.Decode.object), {});
|
|
211
223
|
let isHidden = key => Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(props[key], Stdlib_JSON.Decode.object), o => o["x-reventless-hidden"]), Stdlib_JSON.Decode.bool);
|
|
212
224
|
globalThis.expect([
|
|
213
225
|
isHidden("apiSchemaFragment"),
|
|
@@ -218,7 +230,7 @@ globalThis.describe("the Plugins queryableDef is what the generic helpers produc
|
|
|
218
230
|
]);
|
|
219
231
|
});
|
|
220
232
|
globalThis.test("while the internal fields are not published at all", () => {
|
|
221
|
-
let props = Stdlib_Option.mapOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(
|
|
233
|
+
let props = Stdlib_Option.mapOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(rm.schema)), o => o["properties"]), Stdlib_JSON.Decode.object), [], prim => Object.keys(prim));
|
|
222
234
|
globalThis.expect([
|
|
223
235
|
props.includes("eventCollector"),
|
|
224
236
|
props.includes("extensionNames"),
|
|
@@ -311,40 +311,14 @@ globalThis.describe("visibility filtering (deployed AutoUI hides Internal)", ()
|
|
|
311
311
|
idFieldSource: internalQbl_idFieldSource,
|
|
312
312
|
requiredAccess: undefined
|
|
313
313
|
};
|
|
314
|
-
let
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
let internalSlice_labelFieldSource = "convention";
|
|
318
|
-
let internalSlice_visibility = "Internal";
|
|
319
|
-
let internalSlice_singleQueryField = "Ordering_AvailableProduct";
|
|
320
|
-
let internalSlice_idField = "productId";
|
|
321
|
-
let internalSlice_idFieldSource = "sole";
|
|
322
|
-
let internalSlice = {
|
|
323
|
-
name: "PendingShipments",
|
|
324
|
-
queryField: "Ordering_PendingShipments",
|
|
325
|
-
schema: "{}",
|
|
326
|
-
consumedEventTypes: internalSlice_consumedEventTypes,
|
|
327
|
-
linkedWriteSide: internalSlice_linkedWriteSide,
|
|
328
|
-
labelField: "name",
|
|
329
|
-
searchableFields: internalSlice_searchableFields,
|
|
330
|
-
labelFieldSource: internalSlice_labelFieldSource,
|
|
331
|
-
lifecycleField: undefined,
|
|
332
|
-
ownerField: undefined,
|
|
333
|
-
retiredField: undefined,
|
|
334
|
-
retiredValues: undefined,
|
|
335
|
-
namedWhenRetired: undefined,
|
|
336
|
-
visibility: internalSlice_visibility,
|
|
337
|
-
chapter: undefined,
|
|
338
|
-
singleQueryField: internalSlice_singleQueryField,
|
|
339
|
-
idField: internalSlice_idField,
|
|
340
|
-
idFieldSource: internalSlice_idFieldSource,
|
|
341
|
-
requiredAccess: undefined
|
|
342
|
-
};
|
|
314
|
+
let newrecord = {...internalQbl};
|
|
315
|
+
newrecord.queryField = "Ordering_PendingShipments";
|
|
316
|
+
newrecord.name = "PendingShipments";
|
|
343
317
|
let mixed_readModels = [
|
|
344
318
|
qbl,
|
|
345
319
|
internalQbl
|
|
346
320
|
];
|
|
347
|
-
let mixed_stateViewSlices = [
|
|
321
|
+
let mixed_stateViewSlices = [newrecord];
|
|
348
322
|
let mixed_stateChangeSlices = structure_stateChangeSlices;
|
|
349
323
|
let mixed_aggregates = structure_aggregates;
|
|
350
324
|
let mixed_automationSlices = structure_automationSlices;
|
|
@@ -515,28 +489,9 @@ globalThis.describe("singleQueryField reaches the caller", () => {
|
|
|
515
489
|
globalThis.expect(json.includes("\"singleQueryField\":\"Catalog_Product\"")).toEqual(true);
|
|
516
490
|
});
|
|
517
491
|
globalThis.test("encodes an unstated singular as null", () => {
|
|
492
|
+
let newrecord = {...qbl};
|
|
518
493
|
let unstated = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Catalog", undefined, {
|
|
519
|
-
readModels: [
|
|
520
|
-
name: "Products",
|
|
521
|
-
queryField: "Catalog_Products",
|
|
522
|
-
schema: "{}",
|
|
523
|
-
consumedEventTypes: qbl_consumedEventTypes,
|
|
524
|
-
linkedWriteSide: qbl_linkedWriteSide,
|
|
525
|
-
labelField: "displayName",
|
|
526
|
-
searchableFields: qbl_searchableFields,
|
|
527
|
-
labelFieldSource: "annotation",
|
|
528
|
-
lifecycleField: undefined,
|
|
529
|
-
ownerField: undefined,
|
|
530
|
-
retiredField: undefined,
|
|
531
|
-
retiredValues: undefined,
|
|
532
|
-
namedWhenRetired: undefined,
|
|
533
|
-
visibility: undefined,
|
|
534
|
-
chapter: undefined,
|
|
535
|
-
singleQueryField: undefined,
|
|
536
|
-
idField: "productId",
|
|
537
|
-
idFieldSource: "convention",
|
|
538
|
-
requiredAccess: undefined
|
|
539
|
-
}],
|
|
494
|
+
readModels: [(newrecord.singleQueryField = undefined, newrecord)],
|
|
540
495
|
stateViewSlices: structure_stateViewSlices,
|
|
541
496
|
stateChangeSlices: structure_stateChangeSlices,
|
|
542
497
|
aggregates: structure_aggregates,
|
|
@@ -565,28 +520,9 @@ globalThis.describe("idField / idFieldSource reach the caller", () => {
|
|
|
565
520
|
globalThis.expect(json.includes("\"idFieldSource\":\"convention\"")).toEqual(true);
|
|
566
521
|
});
|
|
567
522
|
globalThis.test("encodes an unresolved key as null on both fields", () => {
|
|
523
|
+
let newrecord = {...qbl};
|
|
568
524
|
let unresolved = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry("Catalog", undefined, {
|
|
569
|
-
readModels: [
|
|
570
|
-
name: "Products",
|
|
571
|
-
queryField: "Catalog_Products",
|
|
572
|
-
schema: "{}",
|
|
573
|
-
consumedEventTypes: qbl_consumedEventTypes,
|
|
574
|
-
linkedWriteSide: qbl_linkedWriteSide,
|
|
575
|
-
labelField: "displayName",
|
|
576
|
-
searchableFields: qbl_searchableFields,
|
|
577
|
-
labelFieldSource: "annotation",
|
|
578
|
-
lifecycleField: undefined,
|
|
579
|
-
ownerField: undefined,
|
|
580
|
-
retiredField: undefined,
|
|
581
|
-
retiredValues: undefined,
|
|
582
|
-
namedWhenRetired: undefined,
|
|
583
|
-
visibility: undefined,
|
|
584
|
-
chapter: undefined,
|
|
585
|
-
singleQueryField: "Catalog_Product",
|
|
586
|
-
idField: undefined,
|
|
587
|
-
idFieldSource: undefined,
|
|
588
|
-
requiredAccess: undefined
|
|
589
|
-
}],
|
|
525
|
+
readModels: [(newrecord.idFieldSource = undefined, newrecord.idField = undefined, newrecord)],
|
|
590
526
|
stateViewSlices: structure_stateViewSlices,
|
|
591
527
|
stateChangeSlices: structure_stateChangeSlices,
|
|
592
528
|
aggregates: structure_aggregates,
|