@reventlessdev/rescript-pulumi-aws 3.0.0-alpha.3 → 3.0.0-alpha.4
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.
|
@@ -388,25 +388,66 @@ export function request(ctx) {
|
|
|
388
388
|
`;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
391
|
+
function pageWindowBudget(filtered) {
|
|
392
|
+
return `(` + filtered + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function cursorDecode(args) {
|
|
396
|
+
return `
|
|
397
|
+
let _window = null;
|
|
398
|
+
let _from = 0;
|
|
399
|
+
let _cursorPath = null;
|
|
400
|
+
if (` + args + `.after != null && ` + args + `.after !== '') {
|
|
401
|
+
const _c = JSON.parse(util.base64Decode(` + args + `.after));
|
|
402
|
+
_window = (_c.t !== undefined ? _c.t : _c.token) ?? null;
|
|
403
|
+
_from = _c.n !== undefined ? _c.n + 1 : 0;
|
|
404
|
+
_cursorPath = _c.p ?? 's';
|
|
405
|
+
}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let cursorPathGuard = `
|
|
409
|
+
if (_cursorPath !== null && _cursorPath !== (_exempt ? 's' : 'q')) {
|
|
410
|
+
util.error('This cursor belongs to a different read of this list; restart from the first page.', 'CursorPathMismatch');
|
|
411
|
+
}`;
|
|
412
|
+
|
|
413
|
+
function connectionPageResponse(pathExpr) {
|
|
414
|
+
let tag = pathExpr !== undefined ? `, p: ` + pathExpr : "";
|
|
415
|
+
return `
|
|
416
|
+
const _first = ctx.args.first ?? 50;
|
|
417
|
+
const _rest = items.slice(_from);
|
|
418
|
+
const _page = _rest.slice(0, _first);
|
|
419
|
+
const _more = _rest.length > _first;
|
|
420
|
+
const _next = ctx.result?.nextToken ?? null;
|
|
421
|
+
const _lastIndex = _page.length - 1;
|
|
422
|
+
// A row's cursor names its own position. The last row of a page that closes its
|
|
423
|
+
// window is the exception — no position follows it there, so it names the next
|
|
424
|
+
// window, or resuming from it answers blank.
|
|
425
|
+
const edges = _page.map((item, i) => ({
|
|
397
426
|
node: item,
|
|
398
|
-
cursor: util.base64Encode(JSON.stringify(
|
|
427
|
+
cursor: util.base64Encode(JSON.stringify(
|
|
428
|
+
(!_more && _next && i === _lastIndex)
|
|
429
|
+
? { t: _next, n: -1` + tag + ` }
|
|
430
|
+
: { t: _window, n: _from + i` + tag + ` }
|
|
431
|
+
)),
|
|
399
432
|
}));
|
|
400
|
-
|
|
433
|
+
// A window the filter emptied leaves no row to cut a cursor from; the token is
|
|
434
|
+
// the window's, so a client can step past it rather than restart.
|
|
435
|
+
const _boundary = _next ? util.base64Encode(JSON.stringify({ t: _next, n: -1` + tag + ` })) : null;
|
|
401
436
|
return {
|
|
402
437
|
edges,
|
|
403
438
|
pageInfo: {
|
|
404
|
-
hasNextPage: !!
|
|
439
|
+
hasNextPage: _more || !!_next,
|
|
405
440
|
hasPreviousPage: !!ctx.args.after,
|
|
406
|
-
startCursor: edges.length > 0 ? edges[0].cursor :
|
|
407
|
-
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor :
|
|
441
|
+
startCursor: edges.length > 0 ? edges[0].cursor : _boundary,
|
|
442
|
+
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : _boundary,
|
|
408
443
|
},
|
|
409
|
-
}
|
|
444
|
+
};`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
let indexConnectionResponseCode = `
|
|
448
|
+
export function response(ctx) {
|
|
449
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
450
|
+
const items = ctx.result?.items ?? [];` + cursorDecode("ctx.args") + connectionPageResponse(undefined) + `
|
|
410
451
|
}`;
|
|
411
452
|
|
|
412
453
|
let indexBackwardPagingGuard = `
|
|
@@ -414,12 +455,8 @@ let indexBackwardPagingGuard = `
|
|
|
414
455
|
util.error('Backward pagination (last/before) is not supported on by-index connections; use first/after.', 'UnsupportedPagination');
|
|
415
456
|
}`;
|
|
416
457
|
|
|
417
|
-
let indexCursorPreamble = `
|
|
418
|
-
|
|
419
|
-
if (args.after != null && args.after !== '') {
|
|
420
|
-
const parsed = JSON.parse(util.base64Decode(args.after));
|
|
421
|
-
after = parsed.token ?? null;
|
|
422
|
-
}`;
|
|
458
|
+
let indexCursorPreamble = cursorDecode("args") + `
|
|
459
|
+
const _first = args.first ?? 50;`;
|
|
423
460
|
|
|
424
461
|
let indexReservedArgs = `key === 'first' || key === 'after' || key === 'last' || key === 'before' || key === 'includeRetired' || key === 'limit' || key === 'nextToken' || key === 'forward'`;
|
|
425
462
|
|
|
@@ -458,8 +495,8 @@ export function request(ctx) {
|
|
|
458
495
|
operation: 'Query',
|
|
459
496
|
query,
|
|
460
497
|
index: '` + index + `',
|
|
461
|
-
limit: (
|
|
462
|
-
nextToken:
|
|
498
|
+
limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
|
|
499
|
+
nextToken: _window,
|
|
463
500
|
scanIndexForward: (args.forward ?? true)
|
|
464
501
|
};
|
|
465
502
|
if (expression) {
|
|
@@ -515,8 +552,8 @@ export function request(ctx) {
|
|
|
515
552
|
operation: 'Query',
|
|
516
553
|
query,
|
|
517
554
|
index: '` + index + `',
|
|
518
|
-
limit: (
|
|
519
|
-
nextToken:
|
|
555
|
+
limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
|
|
556
|
+
nextToken: _window,
|
|
520
557
|
scanIndexForward: (args.forward ?? true)
|
|
521
558
|
};
|
|
522
559
|
if (expression) {
|
|
@@ -539,18 +576,17 @@ export function request(ctx) {
|
|
|
539
576
|
` + resultResponseCode + `
|
|
540
577
|
`;
|
|
541
578
|
|
|
542
|
-
function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sortFieldsOpt, requireAttribute, ownerField, elevatedGroupsOpt, retiredField, retiredValues) {
|
|
579
|
+
function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sortFieldsOpt, requireAttribute, ownerField, elevatedGroupsOpt, retiredField, retiredValues, ownerIndex, ownerIndexSortField) {
|
|
543
580
|
let filterFields = filterFieldsOpt !== undefined ? filterFieldsOpt : [];
|
|
544
581
|
let rangeFields = rangeFieldsOpt !== undefined ? rangeFieldsOpt : [];
|
|
545
582
|
let sortFields = sortFieldsOpt !== undefined ? sortFieldsOpt : [];
|
|
546
583
|
let elevatedGroups = elevatedGroupsOpt !== undefined ? elevatedGroupsOpt : [];
|
|
584
|
+
let ownerIndex$1 = Stdlib_Option.isSome(ownerField) ? ownerIndex : undefined;
|
|
547
585
|
let requireAttributeClause = requireAttribute !== undefined ? `
|
|
548
586
|
names['#` + requireAttribute + `'] = '` + requireAttribute + `';
|
|
549
587
|
parts.push('attribute_exists(#` + requireAttribute + `)');` : "";
|
|
550
|
-
let
|
|
551
|
-
|
|
552
|
-
let elevatedLiteral = elevatedGroups.map(g => `'` + g + `'`).join(", ");
|
|
553
|
-
ownerClause = `
|
|
588
|
+
let elevatedLiteral = elevatedGroups.map(g => `'` + g + `'`).join(", ");
|
|
589
|
+
let ownerIdentityPreamble = `
|
|
554
590
|
// ── owner scoping (generated) ──
|
|
555
591
|
// Not read from ctx.args: a predicate deciding what the caller may see must
|
|
556
592
|
// arrive on a channel the caller cannot name, and this field is usually absent
|
|
@@ -561,15 +597,15 @@ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sor
|
|
|
561
597
|
const _elevated = [` + elevatedLiteral + `];
|
|
562
598
|
// No identity at all, or an identity with no \`sub\`, is the IAM service caller
|
|
563
599
|
// the API also accepts — inside the trust boundary, and exempt.
|
|
564
|
-
const _exempt = _sub == null || _groups.some(g => _elevated.indexOf(g) >= 0)
|
|
600
|
+
const _exempt = _sub == null || _groups.some(g => _elevated.indexOf(g) >= 0);`;
|
|
601
|
+
let ownerClause = ownerField !== undefined ? (
|
|
602
|
+
ownerIndex$1 !== undefined ? ownerIdentityPreamble : ownerIdentityPreamble + `
|
|
565
603
|
if (!_exempt) {
|
|
566
604
|
names['#owner'] = '` + ownerField + `';
|
|
567
605
|
values[':owner'] = util.dynamodb.toDynamoDB(_sub);
|
|
568
606
|
parts.push('#owner = :owner');
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
ownerClause = "";
|
|
572
|
-
}
|
|
607
|
+
}`
|
|
608
|
+
) : "";
|
|
573
609
|
let retiredClause;
|
|
574
610
|
if (retiredField !== undefined) {
|
|
575
611
|
let elevatedLiteral$1 = elevatedGroups.map(g => `'` + g + `'`).join(", ");
|
|
@@ -621,13 +657,14 @@ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sor
|
|
|
621
657
|
parts.push('#` + f + ` <= :` + f + `To');
|
|
622
658
|
}`).join("");
|
|
623
659
|
let sortFieldsLiteral = sortFields.map(f => `'` + f + `'`).join(", ");
|
|
660
|
+
let sortGuard = ownerIndex$1 !== undefined && ownerIndexSortField !== undefined ? "!_indexOrdered && " : "";
|
|
624
661
|
let sortBlock = sortFields.length === 0 ? "" : `
|
|
625
662
|
// Per-page sort (Scan returns items in indeterminate order; ScanIndexForward
|
|
626
663
|
// does not apply to Scan). Global ordering across pages requires v1.5 index
|
|
627
664
|
// promotion; @scanSort is per-page even then.
|
|
628
665
|
const orderBy = ctx.args.orderBy;
|
|
629
666
|
const sortFields = [` + sortFieldsLiteral + `];
|
|
630
|
-
if (orderBy && orderBy.field && sortFields.indexOf(orderBy.field) >= 0) {
|
|
667
|
+
if (` + sortGuard + `orderBy && orderBy.field && sortFields.indexOf(orderBy.field) >= 0) {
|
|
631
668
|
const field = orderBy.field;
|
|
632
669
|
const nulls = items.filter(it => it[field] === null || it[field] === undefined);
|
|
633
670
|
const nonNulls = items.filter(it => it[field] !== null && it[field] !== undefined);
|
|
@@ -642,6 +679,44 @@ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sor
|
|
|
642
679
|
if (orderBy.direction === 'DESC') encoded.reverse();
|
|
643
680
|
items = encoded.map(e => JSON.parse(e.split('\\x01')[1])).concat(nulls);
|
|
644
681
|
}`;
|
|
682
|
+
let indexOrderedExpr = ownerIndex$1 !== undefined && ownerIndexSortField !== undefined ? `!_exempt && !!(ctx.args.orderBy && ctx.args.orderBy.field === '` + ownerIndexSortField + `')` : "false";
|
|
683
|
+
let requestOperation = ownerField !== undefined ? (
|
|
684
|
+
ownerIndex$1 !== undefined ? `
|
|
685
|
+
const _indexOrdered = ` + indexOrderedExpr + `;
|
|
686
|
+
const req = _exempt
|
|
687
|
+
? {
|
|
688
|
+
operation: 'Scan',
|
|
689
|
+
limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
|
|
690
|
+
nextToken: _window,
|
|
691
|
+
}
|
|
692
|
+
: {
|
|
693
|
+
operation: 'Query',
|
|
694
|
+
index: '` + ownerIndex$1 + `',
|
|
695
|
+
query: {
|
|
696
|
+
expression: '#owner = :owner',
|
|
697
|
+
expressionNames: { '#owner': '` + ownerField + `' },
|
|
698
|
+
expressionValues: { ':owner': util.dynamodb.toDynamoDB(_sub) },
|
|
699
|
+
},
|
|
700
|
+
limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
|
|
701
|
+
nextToken: _window,
|
|
702
|
+
scanIndexForward: !(_indexOrdered && ctx.args.orderBy.direction === 'DESC'),
|
|
703
|
+
};` : `
|
|
704
|
+
const req = {
|
|
705
|
+
operation: 'Scan',
|
|
706
|
+
limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
|
|
707
|
+
nextToken: _window,
|
|
708
|
+
};`
|
|
709
|
+
) : `
|
|
710
|
+
const req = {
|
|
711
|
+
operation: 'Scan',
|
|
712
|
+
limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
|
|
713
|
+
nextToken: _window,
|
|
714
|
+
};`;
|
|
715
|
+
let requestPathGuard = Stdlib_Option.isSome(ownerIndex$1) ? cursorPathGuard : "";
|
|
716
|
+
let responsePathPreamble = ownerIndex$1 !== undefined ? ownerIdentityPreamble + `
|
|
717
|
+
const _path = _exempt ? 's' : 'q';
|
|
718
|
+
const _indexOrdered = ` + indexOrderedExpr + `;` : "";
|
|
719
|
+
let pageResponse = ownerIndex$1 !== undefined ? connectionPageResponse("_path") : connectionPageResponse(undefined);
|
|
645
720
|
return importUtil + `
|
|
646
721
|
export function request(ctx) {
|
|
647
722
|
// Scan cannot page backward (ScanIndexForward is Query-only). Fail loud rather than
|
|
@@ -673,18 +748,8 @@ export function request(ctx) {
|
|
|
673
748
|
});
|
|
674
749
|
parts.push('#id IN (' + placeholders.join(', ') + ')');
|
|
675
750
|
}` + filterClauses + rangeClauses + requireAttributeClause + ownerClause + retiredClause + `
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
let after = null;
|
|
679
|
-
if (ctx.args.after != null && ctx.args.after !== '') {
|
|
680
|
-
const parsed = JSON.parse(util.base64Decode(ctx.args.after));
|
|
681
|
-
after = parsed.token ?? null;
|
|
682
|
-
}
|
|
683
|
-
const req = {
|
|
684
|
-
operation: 'Scan',
|
|
685
|
-
limit: (ctx.args.first ?? 50),
|
|
686
|
-
nextToken: after,
|
|
687
|
-
};
|
|
751
|
+
` + cursorDecode("ctx.args") + requestPathGuard + `
|
|
752
|
+
const _first = ctx.args.first ?? 50;` + requestOperation + `
|
|
688
753
|
if (parts.length > 0) {
|
|
689
754
|
req.filter = {
|
|
690
755
|
expression: parts.join(' AND '),
|
|
@@ -696,30 +761,7 @@ export function request(ctx) {
|
|
|
696
761
|
}
|
|
697
762
|
export function response(ctx) {
|
|
698
763
|
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
699
|
-
let items = ctx.result?.items ?? [];` + sortBlock + `
|
|
700
|
-
// One Scan continuation token per page; encode it (with the item's page index for a
|
|
701
|
-
// unique, opaque Relay cursor). The request side decodes .token back to the raw
|
|
702
|
-
// DynamoDB nextToken (Fix 1).
|
|
703
|
-
const next = ctx.result?.nextToken ?? null;
|
|
704
|
-
const edges = items.map((item, i) => ({
|
|
705
|
-
node: item,
|
|
706
|
-
cursor: util.base64Encode(JSON.stringify({ token: next, index: i })),
|
|
707
|
-
}));
|
|
708
|
-
// A filtered/1MB-capped page can be empty or short while next is still set (limit
|
|
709
|
-
// caps rows scanned, not returned). The token is page-level, so synthesise a
|
|
710
|
-
// boundary cursor from it alone so a client can resume past a fully-filtered-out
|
|
711
|
-
// page instead of restarting from page 1 (Fix 3). The request only reads .token,
|
|
712
|
-
// so index -1 is inert on resume.
|
|
713
|
-
const boundary = next ? util.base64Encode(JSON.stringify({ token: next, index: -1 })) : null;
|
|
714
|
-
return {
|
|
715
|
-
edges,
|
|
716
|
-
pageInfo: {
|
|
717
|
-
hasNextPage: !!next,
|
|
718
|
-
hasPreviousPage: !!ctx.args.after,
|
|
719
|
-
startCursor: edges.length > 0 ? edges[0].cursor : boundary,
|
|
720
|
-
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : boundary,
|
|
721
|
-
},
|
|
722
|
-
};
|
|
764
|
+
let items = ctx.result?.items ?? [];` + responsePathPreamble + sortBlock + cursorDecode("ctx.args") + pageResponse + `
|
|
723
765
|
}
|
|
724
766
|
`;
|
|
725
767
|
}
|
|
@@ -948,12 +990,9 @@ export function request(ctx) {
|
|
|
948
990
|
}
|
|
949
991
|
export function response(ctx) {
|
|
950
992
|
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
951
|
-
// BatchGetItem returns null
|
|
952
|
-
//
|
|
953
|
-
//
|
|
954
|
-
// missing id makes the entire field fail with "Cannot return null for
|
|
955
|
-
// non-nullable type" and the caller sees data=null. Filter the nulls so
|
|
956
|
-
// the field returns just the items that were found.
|
|
993
|
+
// BatchGetItem returns null for keys that don't exist, preserving index
|
|
994
|
+
// correspondence. The SDL declares \`[T!]!\`, so one missing id would null the
|
|
995
|
+
// whole field — drop them and return what was found.
|
|
957
996
|
// The owner and retirement guards the list pushes into a FilterExpression,
|
|
958
997
|
// applied after the read because BatchGetItem has none to push into. A row the
|
|
959
998
|
// caller does not own is dropped rather than refused, for the reason the
|
|
@@ -1249,6 +1288,10 @@ export {
|
|
|
1249
1288
|
queryByIndex,
|
|
1250
1289
|
queryByIndexDeletable,
|
|
1251
1290
|
queryByIndexSort,
|
|
1291
|
+
pageWindowBudget,
|
|
1292
|
+
cursorDecode,
|
|
1293
|
+
cursorPathGuard,
|
|
1294
|
+
connectionPageResponse,
|
|
1252
1295
|
indexConnectionResponseCode,
|
|
1253
1296
|
indexBackwardPagingGuard,
|
|
1254
1297
|
indexCursorPreamble,
|
|
@@ -1285,4 +1328,4 @@ export {
|
|
|
1285
1328
|
resolveIdsResult,
|
|
1286
1329
|
$$null,
|
|
1287
1330
|
}
|
|
1288
|
-
/*
|
|
1331
|
+
/* indexConnectionResponseCode Not a pure module */
|