@reventlessdev/reventless-aws 3.0.0-alpha.282 → 3.0.0-alpha.284

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.
@@ -6,29 +6,32 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
7
7
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
8
8
  import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
9
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
10
+ import * as Api_Ids$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/Api_Ids.res.mjs";
9
11
  import * as QueryDb_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/QueryDb/QueryDb_Callback.res.mjs";
10
12
  import * as PgQueryResolver_Lambda$ReventlessAws from "../src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs";
11
13
 
12
- function mk(id, status, name) {
14
+ function mk(id, status, name, owner) {
13
15
  let o = {};
14
16
  o["id"] = id;
15
17
  o["status"] = status;
16
18
  o["name"] = name;
19
+ o["owner"] = owner;
17
20
  return o;
18
21
  }
19
22
 
20
23
  let store = Object.fromEntries([
21
24
  [
22
25
  "p-1",
23
- mk("p-1", "active", "Alpha")
26
+ mk("p-1", "active", "Alpha", "u-a")
24
27
  ],
25
28
  [
26
29
  "p-2",
27
- mk("p-2", "inactive", "Bravo")
30
+ mk("p-2", "inactive", "Bravo", "u-b")
28
31
  ],
29
32
  [
30
33
  "p-3",
31
- mk("p-3", "active", "Charlie")
34
+ mk("p-3", "active", "Charlie", "u-a")
32
35
  ]
33
36
  ]);
34
37
 
@@ -44,6 +47,10 @@ let listPageReturn = {
44
47
  contents: undefined
45
48
  };
46
49
 
50
+ let lastOwnerScope = {
51
+ contents: undefined
52
+ };
53
+
47
54
  async function ops_load(id) {
48
55
  return {
49
56
  TAG: "Ok",
@@ -69,11 +76,12 @@ async function pushdowns_byIds(param, ids) {
69
76
  return Stdlib_Array.filterMap(ids, id => store[id]);
70
77
  }
71
78
 
72
- async function pushdowns_listPage(param, param$1, param$2, param$3) {
79
+ async function pushdowns_listPage(param, param$1, param$2, param$3, ownerScope) {
80
+ lastOwnerScope.contents = ownerScope;
73
81
  return listPageReturn.contents;
74
82
  }
75
83
 
76
- async function pushdowns_itemsPage(param, param$1, id, param$2) {
84
+ async function pushdowns_itemsPage(param, param$1, id, param$2, param$3) {
77
85
  return Object.fromEntries([[
78
86
  "itemsFor",
79
87
  id
@@ -108,9 +116,10 @@ let capability = {
108
116
  sortFields: capability_sortFields
109
117
  };
110
118
 
111
- function makeBinding(authorizationOpt, subIdFieldOpt, param) {
119
+ function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, param) {
112
120
  let authorization = authorizationOpt !== undefined ? authorizationOpt : "AllowAnonymous";
113
121
  let subIdField = subIdFieldOpt !== undefined ? Primitive_option.valFromOption(subIdFieldOpt) : undefined;
122
+ let ownerField = ownerFieldOpt !== undefined ? Primitive_option.valFromOption(ownerFieldOpt) : undefined;
114
123
  return {
115
124
  ops: ops,
116
125
  pushdowns: pushdowns,
@@ -124,18 +133,29 @@ function makeBinding(authorizationOpt, subIdFieldOpt, param) {
124
133
  capability: capability,
125
134
  labelField: "name",
126
135
  includeIdParam: true,
127
- authorization: authorization
136
+ authorization: authorization,
137
+ ownerField: ownerField
128
138
  };
129
139
  }
130
140
 
131
- function mkPayload(kind, index, argsOpt, param) {
141
+ function mkPayload(kind, index, argsOpt, identityOpt, param) {
132
142
  let args = argsOpt !== undefined ? argsOpt : ({});
143
+ let identity = identityOpt !== undefined ? identityOpt : Identity$Reventless.anonymous;
133
144
  return {
134
145
  readModelName: "Things",
135
146
  kind: kind,
136
147
  index: index,
137
148
  arguments: args,
138
- identity: Identity$Reventless.anonymous
149
+ identity: identity
150
+ };
151
+ }
152
+
153
+ function cognito(userId, groups) {
154
+ return {
155
+ userId: userId,
156
+ username: userId,
157
+ groups: groups,
158
+ provider: "Cognito"
139
159
  };
140
160
  }
141
161
 
@@ -157,39 +177,39 @@ function ids(arr) {
157
177
 
158
178
  globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
159
179
  globalThis.test("getById returns the item (id-carrying)", async () => {
160
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
180
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
161
181
  "id",
162
182
  "p-2"
163
- ]]), undefined));
183
+ ]]), undefined, undefined));
164
184
  globalThis.expect(str(r, "id")).toEqual("p-2");
165
185
  globalThis.expect(str(r, "name")).toEqual("Bravo");
166
186
  });
167
187
  globalThis.test("getById miss → null", async () => {
168
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
188
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
169
189
  "id",
170
190
  "absent"
171
- ]]), undefined));
191
+ ]]), undefined, undefined));
172
192
  globalThis.expect(r).toBe(null);
173
193
  });
174
194
  globalThis.test("byIds returns matches, drops missing", async () => {
175
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
195
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
176
196
  "ids",
177
197
  [
178
198
  "p-1",
179
199
  "absent",
180
200
  "p-3"
181
201
  ].map(prim => prim)
182
- ]]), undefined));
202
+ ]]), undefined, undefined));
183
203
  globalThis.expect(ids(r)).toEqual([
184
204
  "p-1",
185
205
  "p-3"
186
206
  ]);
187
207
  });
188
208
  globalThis.test("index routes to indexLookup on the idField", async () => {
189
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
209
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
190
210
  "byStatus",
191
211
  "active"
192
- ]]), undefined));
212
+ ]]), undefined, undefined));
193
213
  globalThis.expect(ids(r)).toEqual([
194
214
  "p-1",
195
215
  "p-3"
@@ -197,13 +217,13 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
197
217
  });
198
218
  globalThis.test("list with push-down → returns the connection listPage gives", async () => {
199
219
  listPageReturn.contents = "PUSHED";
200
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined));
220
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
201
221
  listPageReturn.contents = undefined;
202
222
  globalThis.expect(r).toEqual("PUSHED");
203
223
  });
204
224
  globalThis.test("list without push-down → falls back to the shared spec", async () => {
205
225
  listPageReturn.contents = undefined;
206
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined));
226
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
207
227
  let edgeIds = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(r, "edges"), Stdlib_JSON.Decode.array), []), e => Stdlib_Option.flatMap(field(e, "node"), n => str(n, "id")));
208
228
  globalThis.expect(edgeIds).toEqual([
209
229
  "p-1",
@@ -212,32 +232,32 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
212
232
  ]);
213
233
  });
214
234
  globalThis.test("items with subId routes to itemsPage (id echoed)", async () => {
215
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, "seq", undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
235
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, "seq", undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
216
236
  "id",
217
237
  "p-2"
218
- ]]), undefined));
238
+ ]]), undefined, undefined));
219
239
  globalThis.expect(str(r, "itemsFor")).toEqual("p-2");
220
240
  });
221
241
  globalThis.test("items without subId → empty connection", async () => {
222
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
242
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
223
243
  "id",
224
244
  "p-2"
225
- ]]), undefined));
245
+ ]]), undefined, undefined));
226
246
  globalThis.expect(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(r, "edges"), Stdlib_JSON.Decode.array), []).length).toBe(0);
227
247
  });
228
248
  globalThis.test("authorization DenyAll → empty shape per kind", async () => {
229
- let b = makeBinding("DenyAll", undefined, undefined);
249
+ let b = makeBinding("DenyAll", undefined, undefined, undefined);
230
250
  let single = await PgQueryResolver_Lambda$ReventlessAws.dispatch(b, undefined, mkPayload("getById", undefined, Object.fromEntries([[
231
251
  "id",
232
252
  "p-1"
233
- ]]), undefined));
253
+ ]]), undefined, undefined));
234
254
  globalThis.expect(single).toBe(null);
235
- let list = await PgQueryResolver_Lambda$ReventlessAws.dispatch(b, undefined, mkPayload("list", undefined, undefined, undefined));
255
+ let list = await PgQueryResolver_Lambda$ReventlessAws.dispatch(b, undefined, mkPayload("list", undefined, undefined, undefined, undefined));
236
256
  globalThis.expect(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(list, "edges"), Stdlib_JSON.Decode.array), []).length).toBe(0);
237
257
  let byIds = await PgQueryResolver_Lambda$ReventlessAws.dispatch(b, undefined, mkPayload("byIds", undefined, Object.fromEntries([[
238
258
  "ids",
239
259
  []
240
- ]]), undefined));
260
+ ]]), undefined, undefined));
241
261
  globalThis.expect(Stdlib_Option.getOr(Stdlib_JSON.Decode.array(byIds), []).length).toBe(0);
242
262
  });
243
263
  globalThis.test("queryInterceptor hook Deny → empty shape (auth allowed)", async () => {
@@ -245,10 +265,10 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
245
265
  TAG: "Deny",
246
266
  _0: "nope"
247
267
  }));
248
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
268
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
249
269
  "id",
250
270
  "p-1"
251
- ]]), undefined));
271
+ ]]), undefined, undefined));
252
272
  QueryDb_Callback$ReventlessCore.clearQueryInterceptor();
253
273
  globalThis.expect(r).toBe(null);
254
274
  });
@@ -271,7 +291,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
271
291
  arguments: payload_arguments,
272
292
  identity: Identity$Reventless.anonymous
273
293
  };
274
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, payload);
294
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, payload);
275
295
  globalThis.expect(str(r, "id")).toEqual("p-2");
276
296
  });
277
297
  globalThis.test("resolveOne multi via target index → array", async () => {
@@ -297,7 +317,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
297
317
  arguments: payload_arguments,
298
318
  identity: Identity$Reventless.anonymous
299
319
  };
300
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, payload);
320
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, payload);
301
321
  globalThis.expect(ids(r)).toEqual([
302
322
  "p-1",
303
323
  "p-3"
@@ -324,14 +344,14 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
324
344
  arguments: payload_arguments,
325
345
  identity: Identity$Reventless.anonymous
326
346
  };
327
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, payload);
347
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, payload);
328
348
  globalThis.expect(ids(r)).toEqual([
329
349
  "p-1",
330
350
  "p-3"
331
351
  ]);
332
352
  });
333
353
  globalThis.test("node decodes global id → __typename + item", async () => {
334
- PgQueryResolver_Lambda$ReventlessAws.register("Things", makeBinding(undefined, undefined, undefined));
354
+ PgQueryResolver_Lambda$ReventlessAws.register("Things", makeBinding(undefined, undefined, undefined, undefined));
335
355
  PgQueryResolver_Lambda$ReventlessAws.registerNodeType("Thing", "Things");
336
356
  let payload_arguments = Object.fromEntries([[
337
357
  "id",
@@ -381,7 +401,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
381
401
  delete: 0,
382
402
  deleteBatch: 0
383
403
  };
384
- let init = makeBinding(undefined, undefined, undefined);
404
+ let init = makeBinding(undefined, undefined, undefined, undefined);
385
405
  let authBinding_pushdowns = init.pushdowns;
386
406
  let authBinding_indexes = init.indexes;
387
407
  let authBinding_subIdField = init.subIdField;
@@ -389,6 +409,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
389
409
  let authBinding_labelField = init.labelField;
390
410
  let authBinding_includeIdParam = init.includeIdParam;
391
411
  let authBinding_authorization = init.authorization;
412
+ let authBinding_ownerField = init.ownerField;
392
413
  let authBinding = {
393
414
  ops: authOps,
394
415
  pushdowns: authBinding_pushdowns,
@@ -397,7 +418,8 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
397
418
  capability: authBinding_capability,
398
419
  labelField: authBinding_labelField,
399
420
  includeIdParam: authBinding_includeIdParam,
400
- authorization: authBinding_authorization
421
+ authorization: authBinding_authorization,
422
+ ownerField: authBinding_ownerField
401
423
  };
402
424
  let lookupAuth = name => {
403
425
  if (name === "AuthTable") {
@@ -423,18 +445,18 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
423
445
  identity: ident(username, groups)
424
446
  });
425
447
  globalThis.test("auth index: group member who owns → results", async () => {
426
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), lookupAuth, authIndexPayload("alice", ["Owner"]));
448
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("alice", ["Owner"]));
427
449
  globalThis.expect(ids(r)).toEqual([
428
450
  "p-1",
429
451
  "p-3"
430
452
  ]);
431
453
  });
432
454
  globalThis.test("auth index: group member who does NOT own → empty", async () => {
433
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", ["Owner"]));
455
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", ["Owner"]));
434
456
  globalThis.expect(Stdlib_Option.getOr(Stdlib_JSON.Decode.array(r), []).length).toBe(0);
435
457
  });
436
458
  globalThis.test("auth index: non-member passes through → results", async () => {
437
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", []));
459
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", []));
438
460
  globalThis.expect(ids(r)).toEqual([
439
461
  "p-1",
440
462
  "p-3"
@@ -443,7 +465,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
443
465
  globalThis.test("unmapped kind throws", async () => {
444
466
  let threw;
445
467
  try {
446
- await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined), undefined, mkPayload("frobnicate", undefined, undefined, undefined));
468
+ await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("frobnicate", undefined, undefined, undefined, undefined));
447
469
  threw = false;
448
470
  } catch (exn) {
449
471
  threw = true;
@@ -452,17 +474,157 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
452
474
  });
453
475
  });
454
476
 
477
+ globalThis.describe("id form — the typed doors take either", () => {
478
+ globalThis.test("getById resolves a Relay global id", async () => {
479
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
480
+ "id",
481
+ Api_Ids$ReventlessCore.encode("Thing", "p-2")
482
+ ]]), undefined, undefined));
483
+ globalThis.expect(str(r, "name")).toEqual("Bravo");
484
+ });
485
+ globalThis.test("getById reports the storage key it resolved to", async () => {
486
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
487
+ "id",
488
+ Api_Ids$ReventlessCore.encode("Thing", "p-2")
489
+ ]]), undefined, undefined));
490
+ globalThis.expect(str(r, "id")).toEqual("p-2");
491
+ });
492
+ globalThis.test("getById still resolves a plain storage key", async () => {
493
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
494
+ "id",
495
+ "p-2"
496
+ ]]), undefined, undefined));
497
+ globalThis.expect(str(r, "name")).toEqual("Bravo");
498
+ });
499
+ globalThis.test("getById on a global id for a row that does not exist is still null", async () => {
500
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
501
+ "id",
502
+ Api_Ids$ReventlessCore.encode("Thing", "absent")
503
+ ]]), undefined, undefined));
504
+ globalThis.expect(r).toBe(null);
505
+ });
506
+ globalThis.test("byIds accepts the two forms mixed in one call", async () => {
507
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
508
+ "ids",
509
+ [
510
+ "p-1",
511
+ Api_Ids$ReventlessCore.encode("Thing", "p-3")
512
+ ]
513
+ ]]), undefined, undefined));
514
+ globalThis.expect(ids(r)).toEqual([
515
+ "p-1",
516
+ "p-3"
517
+ ]);
518
+ });
519
+ globalThis.test("byIds with every id found does not double-count", async () => {
520
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
521
+ "ids",
522
+ [
523
+ "p-1",
524
+ "p-3"
525
+ ].map(prim => prim)
526
+ ]]), undefined, undefined));
527
+ globalThis.expect(ids(r)).toEqual([
528
+ "p-1",
529
+ "p-3"
530
+ ]);
531
+ });
532
+ });
533
+
534
+ globalThis.describe("owner scoping", () => {
535
+ let ownedBinding = () => makeBinding(undefined, undefined, "owner", undefined);
536
+ let shopper = cognito("u-a", ["User"]);
537
+ let listWith = async (binding, identity) => {
538
+ lastOwnerScope.contents = undefined;
539
+ listPageReturn.contents = "pushed";
540
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(binding, undefined, mkPayload("list", undefined, undefined, identity, undefined));
541
+ return [
542
+ r,
543
+ lastOwnerScope.contents
544
+ ];
545
+ };
546
+ globalThis.test("a shopper's list is scoped to their own id", async () => {
547
+ let match = await listWith(ownedBinding(), shopper);
548
+ globalThis.expect(match[1]).toEqual([
549
+ "owner",
550
+ "u-a"
551
+ ]);
552
+ });
553
+ globalThis.test("an elevated caller's list is not scoped", async () => {
554
+ OwnerScope$Reventless.setElevatedGroups(["Admin"]);
555
+ let match = await listWith(ownedBinding(), cognito("ops-1", ["Admin"]));
556
+ OwnerScope$Reventless.setElevatedGroups([]);
557
+ globalThis.expect(match[1]).toBe(undefined);
558
+ });
559
+ globalThis.test("a view with no owner field is never scoped", async () => {
560
+ let match = await listWith(makeBinding(undefined, undefined, undefined, undefined), shopper);
561
+ globalThis.expect(match[1]).toBe(undefined);
562
+ });
563
+ globalThis.test("an unidentified caller is refused before the push-down runs", async () => {
564
+ lastOwnerScope.contents = undefined;
565
+ listPageReturn.contents = "pushed";
566
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(ownedBinding(), undefined, mkPayload("list", undefined, undefined, Identity$Reventless.anonymous, undefined));
567
+ globalThis.expect([
568
+ field(r, "edges"),
569
+ lastOwnerScope.contents
570
+ ]).toEqual([
571
+ [],
572
+ undefined
573
+ ]);
574
+ });
575
+ globalThis.test("getById hides a row the caller does not own", async () => {
576
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(ownedBinding(), undefined, mkPayload("getById", undefined, Object.fromEntries([[
577
+ "id",
578
+ "p-2"
579
+ ]]), shopper, undefined));
580
+ globalThis.expect(r).toBe(null);
581
+ });
582
+ globalThis.test("getById still returns a row the caller does own", async () => {
583
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(ownedBinding(), undefined, mkPayload("getById", undefined, Object.fromEntries([[
584
+ "id",
585
+ "p-1"
586
+ ]]), shopper, undefined));
587
+ globalThis.expect(str(r, "id")).toEqual("p-1");
588
+ });
589
+ globalThis.test("byIds drops rows the caller does not own", async () => {
590
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(ownedBinding(), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
591
+ "ids",
592
+ [
593
+ "p-1",
594
+ "p-2",
595
+ "p-3"
596
+ ].map(prim => prim)
597
+ ]]), shopper, undefined));
598
+ globalThis.expect(ids(r)).toEqual([
599
+ "p-1",
600
+ "p-3"
601
+ ]);
602
+ });
603
+ globalThis.test("an index query drops rows the caller does not own", async () => {
604
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(ownedBinding(), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
605
+ "byStatus",
606
+ "active"
607
+ ]]), shopper, undefined));
608
+ globalThis.expect(ids(r)).toEqual([
609
+ "p-1",
610
+ "p-3"
611
+ ]);
612
+ });
613
+ });
614
+
455
615
  export {
456
616
  mk,
457
617
  store,
458
618
  allItems,
459
619
  fieldEq,
460
620
  listPageReturn,
621
+ lastOwnerScope,
461
622
  ops,
462
623
  pushdowns,
463
624
  capability,
464
625
  makeBinding,
465
626
  mkPayload,
627
+ cognito,
466
628
  objArgs,
467
629
  field,
468
630
  str,
@@ -19,18 +19,18 @@ globalThis.describe("QueryDbResolvers_AppSync.internalRowRequiredAttr", () => {
19
19
 
20
20
  globalThis.describe("AppSync_Resolver_Retrying.Functions.listAllItemsConnection", () => {
21
21
  globalThis.test("with ~requireAttribute emits an attribute_exists filter that excludes internal rows", () => {
22
- let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, "name");
22
+ let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, "name", undefined, undefined);
23
23
  globalThis.expect(code.includes("attribute_exists(#name)")).toBe(true);
24
24
  globalThis.expect(code.includes("names['#name'] = 'name'")).toBe(true);
25
25
  });
26
26
  globalThis.test("without ~requireAttribute emits no attribute_exists filter (default read models unchanged)", () => {
27
- let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, undefined);
27
+ let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, undefined, undefined, undefined);
28
28
  globalThis.expect(code.includes("attribute_exists")).toBe(false);
29
29
  });
30
30
  });
31
31
 
32
32
  globalThis.describe("listAllItemsConnection — Scan cursor round-trip (paging fixes 1–3)", () => {
33
- let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, undefined);
33
+ let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, undefined, undefined, undefined);
34
34
  globalThis.test("Fix 1: response encodes the real nextToken as the cursor", () => {
35
35
  globalThis.expect(code.includes("const next = ctx.result?.nextToken ?? null;")).toBe(true);
36
36
  globalThis.expect(code.includes("util.base64Encode(JSON.stringify({ token: next, index: i }))")).toBe(true);