@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.
- package/CHANGELOG.md +19 -0
- package/package.json +10 -10
- package/src/Platform.res +21 -12
- package/src/Platform.res.mjs +2 -2
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +85 -31
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +71 -36
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +36 -1
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +13 -2
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +4 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +5 -3
- package/tests/PgQueryResolver_LambdaTest.res +239 -9
- package/tests/PgQueryResolver_LambdaTest.res.mjs +202 -40
- package/tests/QueryDbResolvers_AppSyncTest.res.mjs +3 -3
|
@@ -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:
|
|
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);
|