@reventlessdev/reventless-core 3.0.0-alpha.290 → 3.0.0-alpha.292
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/package.json +7 -7
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +25 -46
- package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +27 -46
- package/src/components/AutomationSlice/AutomationSlice_Route.res +50 -0
- package/src/components/AutomationSlice/AutomationSlice_Route.res.mjs +39 -0
- package/src/components/Dcb/Dcb_Builder.res +27 -43
- package/src/components/Dcb/Dcb_Builder.res.mjs +10 -21
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +2 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res.mjs +2 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +118 -160
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +121 -130
- package/src/plugin/component/Plugin_Helpers.res +11 -3
- package/src/plugin/component/Plugin_Structure.res +22 -9
- package/src/plugin/component/Plugin_Structure.res.mjs +11 -1
- package/tests/plugin/PluginStructureAccessTest.res +56 -0
- package/tests/plugin/PluginStructureAccessTest.res.mjs +87 -0
|
@@ -819,6 +819,20 @@ let syntheticCommand = (~schema: S.t<unknown>, variantName: string): 'command =>
|
|
|
819
819
|
? {"TAG": variantName}->Obj.magic
|
|
820
820
|
: variantName->Obj.magic
|
|
821
821
|
|
|
822
|
+
// Each constructor's rule, read off its stand-in.
|
|
823
|
+
let constructorPermissions = (
|
|
824
|
+
~commandSchema: S.t<unknown>,
|
|
825
|
+
~commandAuthorization: 'command => Reventless.Authorization.permission,
|
|
826
|
+
): array<Reventless.Authorization.permission> =>
|
|
827
|
+
Reventless.DcbTag.extractAllVariantNames(commandSchema)->Array.map(variantName =>
|
|
828
|
+
syntheticCommand(~schema=commandSchema, variantName)->commandAuthorization
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
// An inbound slice's gate where no command exists yet: any constructor's rule.
|
|
832
|
+
// Both the deployed gate and the local resolver apply this one.
|
|
833
|
+
let inboundDoorPermission = (~commandSchema, ~commandAuthorization) =>
|
|
834
|
+
constructorPermissions(~commandSchema, ~commandAuthorization)->Reventless.Authorization.anyOf
|
|
835
|
+
|
|
822
836
|
// Each mutation argument's GraphQL type onto its property, so a consumer declares
|
|
823
837
|
// the variable the server expects. Mutates the freshly derived schema in place.
|
|
824
838
|
let annotateArgTypes = (schema: JSON.t, argTypes: dict<string>): JSON.t => {
|
|
@@ -1815,17 +1829,16 @@ let make = (
|
|
|
1815
1829
|
targetName: ITS.Spec.targetName,
|
|
1816
1830
|
externalSystem: ITS.Spec.externalSystem,
|
|
1817
1831
|
chapter: chapterOf(ITS.Spec.name),
|
|
1818
|
-
//
|
|
1819
|
-
//
|
|
1832
|
+
// Every role a constructor names: the door's, plus those only the
|
|
1833
|
+
// per-command check reads, which must map to a group all the same.
|
|
1820
1834
|
requiredRoles: ?(
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
->Reventless.Authorization.named
|
|
1826
|
-
->Reventless.Authorization.rolesOf
|
|
1827
|
-
->Array.map(role => (role :> string))
|
|
1835
|
+
constructorPermissions(
|
|
1836
|
+
~commandSchema=ITS.Spec.commandSchema->S.castToUnknown,
|
|
1837
|
+
~commandAuthorization=cmd =>
|
|
1838
|
+
ITS.Spec.commandAuthorization(cmd)->Reventless.Authorization.named,
|
|
1828
1839
|
)
|
|
1840
|
+
->Array.flatMap(Reventless.Authorization.rolesOf)
|
|
1841
|
+
->Array.map(role => (role :> string))
|
|
1829
1842
|
->Set.fromArray
|
|
1830
1843
|
->Set.values
|
|
1831
1844
|
->Array.fromIterator
|
|
@@ -644,6 +644,14 @@ function syntheticCommand(schema, variantName) {
|
|
|
644
644
|
}
|
|
645
645
|
}
|
|
646
646
|
|
|
647
|
+
function constructorPermissions(commandSchema, commandAuthorization) {
|
|
648
|
+
return DcbTag$Reventless.extractAllVariantNames(commandSchema).map(variantName => commandAuthorization(syntheticCommand(commandSchema, variantName)));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function inboundDoorPermission(commandSchema, commandAuthorization) {
|
|
652
|
+
return Authorization$Reventless.anyOf(constructorPermissions(commandSchema, commandAuthorization));
|
|
653
|
+
}
|
|
654
|
+
|
|
647
655
|
function annotateArgTypes(schema, argTypes) {
|
|
648
656
|
Stdlib_Option.forEach(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(schema), o => o["properties"]), Stdlib_JSON.Decode.object), props => {
|
|
649
657
|
Object.entries(props).forEach(param => {
|
|
@@ -1268,7 +1276,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
|
|
|
1268
1276
|
targetName: ITS.Spec.targetName,
|
|
1269
1277
|
externalSystem: ITS.Spec.externalSystem,
|
|
1270
1278
|
chapter: componentChapters[ITS.Spec.name],
|
|
1271
|
-
requiredRoles: nonEmpty(Array.from(new Set(
|
|
1279
|
+
requiredRoles: nonEmpty(Array.from(new Set(constructorPermissions(ITS.Spec.commandSchema, cmd => Authorization$Reventless.named(ITS.Spec.commandAuthorization(cmd))).flatMap(Authorization$Reventless.rolesOf).map(role => role)).values()))
|
|
1272
1280
|
}));
|
|
1273
1281
|
let tableFailures = [];
|
|
1274
1282
|
let tableWarnings = [];
|
|
@@ -1574,6 +1582,8 @@ export {
|
|
|
1574
1582
|
nonEmpty,
|
|
1575
1583
|
rolesFor,
|
|
1576
1584
|
syntheticCommand,
|
|
1585
|
+
constructorPermissions,
|
|
1586
|
+
inboundDoorPermission,
|
|
1577
1587
|
annotateArgTypes,
|
|
1578
1588
|
toCommandDef,
|
|
1579
1589
|
extractCommandDefs,
|
|
@@ -138,3 +138,59 @@ describe("requiredAccess derived from the authorization rule", () => {
|
|
|
138
138
|
)->toEqual(None)
|
|
139
139
|
)
|
|
140
140
|
})
|
|
141
|
+
|
|
142
|
+
module PsTwoRuleFeed = {
|
|
143
|
+
let name = "PsTwoRuleFeed"
|
|
144
|
+
let moduleUrl = "ps-two-rule-feed://spec"
|
|
145
|
+
@schema type externalInput = {sku: string}
|
|
146
|
+
@schema type command = AddProduct({sku: string}) | AddCategory({sku: string})
|
|
147
|
+
let targetName = "AddProduct"
|
|
148
|
+
let externalSystem = None
|
|
149
|
+
type role = Reventless.Role.name
|
|
150
|
+
let commandAuthorization = (command: command): Reventless.Authorization.rule<role> =>
|
|
151
|
+
switch command {
|
|
152
|
+
| AddProduct(_) => AllowRoles([Reventless.Role.make("Merchandiser")])
|
|
153
|
+
| AddCategory(_) => AllowRoles([Reventless.Role.make("Admin")])
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module PsTwoRuleFeedSlice: ReventlessInfra.InboundTranslationSlice.T = {
|
|
158
|
+
module Spec = PsTwoRuleFeed
|
|
159
|
+
module Translation = {
|
|
160
|
+
let translate = (_: PsTwoRuleFeed.externalInput) => Ok([])
|
|
161
|
+
let moduleUrl = PsTwoRuleFeed.moduleUrl
|
|
162
|
+
}
|
|
163
|
+
type component = Component.t<
|
|
164
|
+
ReventlessInfra.InboundTranslationSlice.t,
|
|
165
|
+
ReventlessInfra.InboundTranslationSlice.outputs,
|
|
166
|
+
ReventlessInfra.InboundTranslationSlice.operations,
|
|
167
|
+
>
|
|
168
|
+
let queryDbName = "PsTwoRuleFeedAudit"
|
|
169
|
+
let make = (~publishJsons as _, ~runtime as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
describe("an inbound slice's roles", () => {
|
|
173
|
+
let door = Dcb_Builder.inboundDoorPermission(
|
|
174
|
+
PsTwoRuleFeed.commandSchema,
|
|
175
|
+
PsTwoRuleFeed.commandAuthorization,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
testSync("the door admits any constructor's roles", () =>
|
|
179
|
+
expect(door->Option.map(Reventless.Authorization.rolesOf))->toEqual(
|
|
180
|
+
Some([Reventless.Role.make("Merchandiser"), Reventless.Role.make("Admin")]),
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
testSync("requiredRoles are the door's roles", () => {
|
|
185
|
+
let inbound =
|
|
186
|
+
Plugin_Structure.make(
|
|
187
|
+
~name="AccessPlugin",
|
|
188
|
+
~inboundTranslationSlices=[module(PsTwoRuleFeedSlice)],
|
|
189
|
+
).inboundTranslationSlices->Array.get(0)
|
|
190
|
+
let doorRoles =
|
|
191
|
+
door->Option.map(
|
|
192
|
+
rule => rule->Reventless.Authorization.rolesOf->Array.map(r => (r :> string)),
|
|
193
|
+
)
|
|
194
|
+
expect(inbound->Option.flatMap(s => s.requiredRoles))->toEqual(doorRoles)
|
|
195
|
+
})
|
|
196
|
+
})
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Sury from "sury";
|
|
3
4
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
4
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
6
|
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
7
|
+
import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
|
|
8
|
+
import * as Dcb_Builder$ReventlessCore from "../../src/components/Dcb/Dcb_Builder.res.mjs";
|
|
6
9
|
import * as PsGatedView$ReventlessCore from "./StateView/PsGatedView.res.mjs";
|
|
7
10
|
import * as PsOrdersView$ReventlessCore from "./StateView/PsOrdersView.res.mjs";
|
|
8
11
|
import * as PsGatedCommands$ReventlessCore from "./StateChange/PsGatedCommands.res.mjs";
|
|
@@ -175,6 +178,88 @@ globalThis.describe("requiredAccess derived from the authorization rule", () =>
|
|
|
175
178
|
});
|
|
176
179
|
});
|
|
177
180
|
|
|
181
|
+
let moduleUrl = "ps-two-rule-feed://spec";
|
|
182
|
+
|
|
183
|
+
let externalInputSchema = Sury.$schema(s => ({
|
|
184
|
+
sku: s.m(Sury.string)
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
let commandSchema = Sury.union([
|
|
188
|
+
Sury.$schema(s => ({
|
|
189
|
+
TAG: "AddProduct",
|
|
190
|
+
sku: s.m(Sury.string)
|
|
191
|
+
})),
|
|
192
|
+
Sury.$schema(s => ({
|
|
193
|
+
TAG: "AddCategory",
|
|
194
|
+
sku: s.m(Sury.string)
|
|
195
|
+
}))
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
function commandAuthorization(command) {
|
|
199
|
+
if (command.TAG === "AddProduct") {
|
|
200
|
+
return {
|
|
201
|
+
TAG: "AllowRoles",
|
|
202
|
+
_0: ["Merchandiser"]
|
|
203
|
+
};
|
|
204
|
+
} else {
|
|
205
|
+
return {
|
|
206
|
+
TAG: "AllowRoles",
|
|
207
|
+
_0: ["Admin"]
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let traits = [];
|
|
213
|
+
|
|
214
|
+
let PsTwoRuleFeed = {
|
|
215
|
+
name: "PsTwoRuleFeed",
|
|
216
|
+
moduleUrl: moduleUrl,
|
|
217
|
+
externalInputSchema: externalInputSchema,
|
|
218
|
+
commandSchema: commandSchema,
|
|
219
|
+
targetName: "AddProduct",
|
|
220
|
+
externalSystem: undefined,
|
|
221
|
+
commandAuthorization: commandAuthorization,
|
|
222
|
+
traits: traits
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
function translate(param) {
|
|
226
|
+
return {
|
|
227
|
+
TAG: "Ok",
|
|
228
|
+
_0: []
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let Translation = {
|
|
233
|
+
translate: translate,
|
|
234
|
+
moduleUrl: moduleUrl
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
function make$3(param, param$1, param$2) {
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let PsTwoRuleFeedSlice = {
|
|
242
|
+
Spec: PsTwoRuleFeed,
|
|
243
|
+
Translation: Translation,
|
|
244
|
+
queryDbName: "PsTwoRuleFeedAudit",
|
|
245
|
+
make: make$3
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
globalThis.describe("an inbound slice's roles", () => {
|
|
249
|
+
let door = Dcb_Builder$ReventlessCore.inboundDoorPermission(commandSchema, commandAuthorization);
|
|
250
|
+
globalThis.test("the door admits any constructor's roles", () => {
|
|
251
|
+
globalThis.expect(Stdlib_Option.map(door, Authorization$Reventless.rolesOf)).toEqual([
|
|
252
|
+
"Merchandiser",
|
|
253
|
+
"Admin"
|
|
254
|
+
]);
|
|
255
|
+
});
|
|
256
|
+
globalThis.test("requiredRoles are the door's roles", () => {
|
|
257
|
+
let inbound = Plugin_Structure$ReventlessCore.make("AccessPlugin", undefined, undefined, undefined, undefined, undefined, undefined, [PsTwoRuleFeedSlice], undefined, undefined, undefined, undefined, undefined).inboundTranslationSlices[0];
|
|
258
|
+
let doorRoles = Stdlib_Option.map(door, rule => Authorization$Reventless.rolesOf(rule).map(r => r));
|
|
259
|
+
globalThis.expect(Stdlib_Option.flatMap(inbound, s => s.requiredRoles)).toEqual(doorRoles);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
178
263
|
export {
|
|
179
264
|
PsGatedCommandsSlice,
|
|
180
265
|
PsGatedViewSlice,
|
|
@@ -182,5 +267,7 @@ export {
|
|
|
182
267
|
structure,
|
|
183
268
|
commandNamed,
|
|
184
269
|
viewNamed,
|
|
270
|
+
PsTwoRuleFeed,
|
|
271
|
+
PsTwoRuleFeedSlice,
|
|
185
272
|
}
|
|
186
273
|
/* structure Not a pure module */
|