@reventlessdev/reventless-aws 3.0.0-alpha.291 → 3.0.0-alpha.293

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +9 -9
  3. package/src/Platform.res +85 -5
  4. package/src/Platform.res.mjs +62 -6
  5. package/src/Platform_Stack.res +78 -0
  6. package/src/Platform_Stack.res.mjs +17 -2
  7. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -0
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +8 -1
  9. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +89 -12
  10. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +55 -4
  11. package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +309 -0
  12. package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res.mjs +230 -0
  13. package/src/adapter/Auth/Auth_ActiveRoleStore.res +280 -0
  14. package/src/adapter/Auth/Auth_ActiveRoleStore.res.mjs +163 -0
  15. package/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res +148 -0
  16. package/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res.mjs +128 -0
  17. package/src/adapter/Auth/Auth_ActiveRoleTrigger.res +180 -0
  18. package/src/adapter/Auth/Auth_ActiveRoleTrigger.res.mjs +105 -0
  19. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +201 -0
  20. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res.mjs +126 -0
  21. package/src/plugin/runtime/PluginRuntime_Builder.res +16 -0
  22. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +6 -0
  23. package/tests/Auth_ActiveRolePoolAttachmentTest.res +211 -0
  24. package/tests/Auth_ActiveRolePoolAttachmentTest.res.mjs +232 -0
  25. package/tests/Auth_ActiveRoleStoreTest.res +42 -0
  26. package/tests/Auth_ActiveRoleStoreTest.res.mjs +41 -0
  27. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +169 -0
  28. package/tests/Auth_ActiveRoleTrigger_OpsTest.res.mjs +189 -0
  29. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +110 -0
  30. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res.mjs +87 -0
@@ -240,3 +240,113 @@ describe("bakeSelection", () => {
240
240
  expect(sel(`{"views": ["Orders"]}`)->Option.isNone)->toBe(true)
241
241
  )
242
242
  })
243
+
244
+ // The bake reads a read model the deploy updates asynchronously, so "is this the
245
+ // deployment I was asked to bake" is a question it has to be able to answer. The
246
+ // answer is an equality check against the key each plugin stack just wrote.
247
+ describe("pendingRegistrations", () => {
248
+ let row = (~name, ~key=?, ()) => {
249
+ let item = Dict.fromArray([("name", JSON.Encode.string(name))])
250
+ key->Option.forEach(k =>
251
+ item->Dict.set(
252
+ "structure",
253
+ JSON.parseOrThrow(`{"$offload": {"store": "pluginStructures", "key": "${k}"}}`),
254
+ )
255
+ )
256
+ item
257
+ }
258
+
259
+ let pending = (rows, expect) =>
260
+ Platform_ComponentDefinitions_Lambda_Ops.pendingRegistrations(
261
+ rows,
262
+ ~expect=Dict.fromArray(expect),
263
+ )
264
+
265
+ testSync("nothing is pending when every row carries the key the deploy wrote", () =>
266
+ expect(
267
+ pending(
268
+ [row(~name="Catalog", ~key="sha256/a", ()), row(~name="Ordering", ~key="sha256/b", ())],
269
+ [("Catalog", "sha256/a"), ("Ordering", "sha256/b")],
270
+ ),
271
+ )->toEqual([])
272
+ )
273
+
274
+ // The window this exists for: the stack wrote a new structure, the row still
275
+ // points at the one before it.
276
+ testSync("names a plugin still carrying the previous structure", () =>
277
+ expect(
278
+ pending(
279
+ [row(~name="Catalog", ~key="sha256/a", ()), row(~name="Ordering", ~key="sha256/old", ())],
280
+ [("Catalog", "sha256/a"), ("Ordering", "sha256/new")],
281
+ ),
282
+ )->toEqual(["Ordering"])
283
+ )
284
+
285
+ // A plugin that has never registered is behind, not absent — baking without it
286
+ // would ship a shop missing a section.
287
+ testSync("names a plugin with no row at all", () =>
288
+ expect(pending([], [("Ordering", "sha256/new")]))->toEqual(["Ordering"])
289
+ )
290
+
291
+ // Deployed before the stack exported its key, or invoked by hand: bake what is
292
+ // current rather than wait for an expectation nobody stated.
293
+ testSync("waits for nothing when the caller expects nothing", () =>
294
+ expect(pending([row(~name="Ordering", ~key="sha256/old", ())], []))->toEqual([])
295
+ )
296
+
297
+ testSync("reads the expectations off the invocation payload", () =>
298
+ expect(
299
+ Platform_ComponentDefinitions_Lambda_Ops.bakeExpectations(
300
+ JSON.parseOrThrow(`{"bake": true, "expect": {"Ordering": "sha256/b"}}`),
301
+ )->Dict.get("Ordering"),
302
+ )->toEqual(Some("sha256/b"))
303
+ )
304
+ })
305
+
306
+ describe("resolveStructure", () => {
307
+ let item = (structure: string) =>
308
+ Dict.fromArray([
309
+ ("name", JSON.Encode.string("Ordering")),
310
+ ("structure", JSON.parseOrThrow(structure)),
311
+ ])
312
+
313
+ let offloaded = `{"$offload": {"store": "pluginStructures", "key": "sha256/abc", "bytes": 4}}`
314
+
315
+ test("substitutes the fetched bytes for the reference", async () => {
316
+ let resolved = await Platform_ComponentDefinitions_Lambda_Ops.resolveStructure(
317
+ _ => Promise.resolve(`{"aggregates": []}`),
318
+ item(offloaded),
319
+ )
320
+ expect(resolved->Dict.get("structure")->Option.map(j => JSON.stringify(j)))->toEqual(
321
+ Some(`{"aggregates":[]}`),
322
+ )
323
+ })
324
+
325
+ // One offload bucket serves every plugin, so the S3 error names only the key —
326
+ // which plugin's row carries the unreadable reference is the part worth having.
327
+ // A missing object is the shape a deploy leaves behind while the read model
328
+ // still points at the previous structure.
329
+ test("names the plugin whose reference cannot be read", async () => {
330
+ let failed = await Platform_ComponentDefinitions_Lambda_Ops.resolveStructure(
331
+ _ => Promise.reject(JsExn.anyToExnInternal(JsError.make("AccessDenied"))),
332
+ item(offloaded),
333
+ )
334
+ ->Promise.thenResolve(_ => None)
335
+ ->Promise.catch(e =>
336
+ Promise.resolve(e->JsExn.fromException->Option.flatMap(JsExn.message))
337
+ )
338
+ expect(failed)->toEqual(
339
+ Some("offloaded structure for plugin Ordering is unreadable at sha256/abc: AccessDenied"),
340
+ )
341
+ })
342
+
343
+ test("passes an inline structure through untouched", async () => {
344
+ let resolved = await Platform_ComponentDefinitions_Lambda_Ops.resolveStructure(
345
+ _ => Promise.reject(JsExn.anyToExnInternal(JsError.make("must not fetch"))),
346
+ item(`{"aggregates": []}`),
347
+ )
348
+ expect(resolved->Dict.get("structure")->Option.map(j => JSON.stringify(j)))->toEqual(
349
+ Some(`{"aggregates":[]}`),
350
+ )
351
+ })
352
+ })
@@ -2,7 +2,10 @@
2
2
 
3
3
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
4
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
5
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
8
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
6
9
  import * as Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws from "../src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs";
7
10
 
8
11
  function entry(filter, structure) {
@@ -151,6 +154,90 @@ globalThis.describe("bakeSelection", () => {
151
154
  });
152
155
  });
153
156
 
157
+ globalThis.describe("pendingRegistrations", () => {
158
+ let row = (name, key, param) => {
159
+ let item = Object.fromEntries([[
160
+ "name",
161
+ name
162
+ ]]);
163
+ Stdlib_Option.forEach(key, k => {
164
+ item["structure"] = JSON.parse(`{"$offload": {"store": "pluginStructures", "key": "` + k + `"}}`);
165
+ });
166
+ return item;
167
+ };
168
+ globalThis.test("nothing is pending when every row carries the key the deploy wrote", () => {
169
+ let rows = [
170
+ row("Catalog", "sha256/a", undefined),
171
+ row("Ordering", "sha256/b", undefined)
172
+ ];
173
+ globalThis.expect(Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.pendingRegistrations(rows, Object.fromEntries([
174
+ [
175
+ "Catalog",
176
+ "sha256/a"
177
+ ],
178
+ [
179
+ "Ordering",
180
+ "sha256/b"
181
+ ]
182
+ ]))).toEqual([]);
183
+ });
184
+ globalThis.test("names a plugin still carrying the previous structure", () => {
185
+ let rows = [
186
+ row("Catalog", "sha256/a", undefined),
187
+ row("Ordering", "sha256/old", undefined)
188
+ ];
189
+ globalThis.expect(Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.pendingRegistrations(rows, Object.fromEntries([
190
+ [
191
+ "Catalog",
192
+ "sha256/a"
193
+ ],
194
+ [
195
+ "Ordering",
196
+ "sha256/new"
197
+ ]
198
+ ]))).toEqual(["Ordering"]);
199
+ });
200
+ globalThis.test("names a plugin with no row at all", () => {
201
+ globalThis.expect(Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.pendingRegistrations([], Object.fromEntries([[
202
+ "Ordering",
203
+ "sha256/new"
204
+ ]]))).toEqual(["Ordering"]);
205
+ });
206
+ globalThis.test("waits for nothing when the caller expects nothing", () => {
207
+ let rows = [row("Ordering", "sha256/old", undefined)];
208
+ globalThis.expect(Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.pendingRegistrations(rows, Object.fromEntries([]))).toEqual([]);
209
+ });
210
+ globalThis.test("reads the expectations off the invocation payload", () => {
211
+ globalThis.expect(Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.bakeExpectations(JSON.parse(`{"bake": true, "expect": {"Ordering": "sha256/b"}}`))["Ordering"]).toEqual("sha256/b");
212
+ });
213
+ });
214
+
215
+ globalThis.describe("resolveStructure", () => {
216
+ let item = structure => Object.fromEntries([
217
+ [
218
+ "name",
219
+ "Ordering"
220
+ ],
221
+ [
222
+ "structure",
223
+ JSON.parse(structure)
224
+ ]
225
+ ]);
226
+ let offloaded = `{"$offload": {"store": "pluginStructures", "key": "sha256/abc", "bytes": 4}}`;
227
+ globalThis.test("substitutes the fetched bytes for the reference", async () => {
228
+ let resolved = await Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.resolveStructure(param => Promise.resolve(`{"aggregates": []}`), item(offloaded));
229
+ globalThis.expect(Stdlib_Option.map(resolved["structure"], j => JSON.stringify(j))).toEqual(`{"aggregates":[]}`);
230
+ });
231
+ globalThis.test("names the plugin whose reference cannot be read", async () => {
232
+ let failed = await Stdlib_Promise.$$catch(Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.resolveStructure(param => Promise.reject(Primitive_exceptions.internalToException(new Error("AccessDenied"))), item(offloaded)).then(param => {}), e => Promise.resolve(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(e), Stdlib_JsExn.message)));
233
+ globalThis.expect(failed).toEqual("offloaded structure for plugin Ordering is unreadable at sha256/abc: AccessDenied");
234
+ });
235
+ globalThis.test("passes an inline structure through untouched", async () => {
236
+ let resolved = await Platform_ComponentDefinitions_Lambda_Ops$ReventlessAws.resolveStructure(param => Promise.reject(Primitive_exceptions.internalToException(new Error("must not fetch"))), item(`{"aggregates": []}`));
237
+ globalThis.expect(Stdlib_Option.map(resolved["structure"], j => JSON.stringify(j))).toEqual(`{"aggregates":[]}`);
238
+ });
239
+ });
240
+
154
241
  export {
155
242
  entry,
156
243
  members,