@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.
@@ -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
- // Per command, as the resolver evaluates the rule, then joined: the slice
1819
- // publishes no command defs to carry them.
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
- 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))
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(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()))
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 */