@reventlessdev/reventless-aws 3.0.0-alpha.178 → 3.0.0-alpha.180
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 +14 -0
- package/package.json +4 -4
- package/src/Platform.res +3 -3
- package/src/Platform.res.mjs +6 -6
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Postgres_Runtime.res +4 -2
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Postgres_Runtime.res.mjs +2 -2
- package/src/adapter/EventLog/EventLogStorage_Postgres_Runtime.res +3 -1
- package/src/adapter/EventLog/EventLogStorage_Postgres_Runtime.res.mjs +2 -2
- package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +3 -1
- package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +2 -2
- package/src/components/Api/AppSync_Adapter.res +83 -4
- package/src/components/Api/AppSync_Adapter.res.mjs +53 -3
- package/tests/AppSync_AdapterTest.res +112 -0
- package/tests/AppSync_AdapterTest.res.mjs +101 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.180 (2026-07-07)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **reventless-aws:** type-level dual-auth + derived query fields for system callers ([91e01f8](https://github.com/ReventlessDev/reventless-core/commit/91e01f82f43f64f07e7eb0a4aaa8cf5dc77a916e))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.179 (2026-07-07)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **reventless-postgres:** keep [@pulumi](https://github.com/pulumi) out of the deployed Lambda runtime graph ([0f363cd](https://github.com/ReventlessDev/reventless-core/commit/0f363cd273590591c8f9353fa38ac8b6072e6c49))
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
# 3.0.0-alpha.178 (2026-07-06)
|
|
7
21
|
|
|
8
22
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.180",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.46",
|
|
16
16
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
|
|
17
17
|
"@reventlessdev/reventless-core": "3.0.0-alpha.143",
|
|
18
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.24",
|
|
19
18
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.89",
|
|
20
|
-
"@reventlessdev/reventless-
|
|
21
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.67"
|
|
19
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.24",
|
|
20
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.67",
|
|
21
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.7"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"rescript": "^12.3.0",
|
package/src/Platform.res
CHANGED
|
@@ -700,7 +700,7 @@ module MakeWithConfig = (
|
|
|
700
700
|
let sdl = ReventlessCore.GraphQL_Stitcher.stitch(
|
|
701
701
|
~baseFragment=adminBaseFragment,
|
|
702
702
|
~pluginFragments=[],
|
|
703
|
-
)
|
|
703
|
+
)->AppSync_Adapter.stampSharedIamTypes
|
|
704
704
|
(targetApi, adminBarrier)
|
|
705
705
|
->Pulumi.Output.all2
|
|
706
706
|
->Pulumi.Output.flatMap(((api, _)) =>
|
|
@@ -928,7 +928,7 @@ module MakeWithConfig = (
|
|
|
928
928
|
let sdl = ReventlessCore.GraphQL_Stitcher.stitch(
|
|
929
929
|
~baseFragment,
|
|
930
930
|
~pluginFragments=allPluginFragments,
|
|
931
|
-
)
|
|
931
|
+
)->AppSync_Adapter.stampSharedIamTypes
|
|
932
932
|
let currentHash = AppSync_Adapter.sha256Hex(sdl)
|
|
933
933
|
let storedHash = switch tableNameOpt {
|
|
934
934
|
| Some(tn) => await readSchemaHash(~tableName=tn, ~apiId)
|
|
@@ -1628,7 +1628,7 @@ module MakeWithConfig = (
|
|
|
1628
1628
|
let sdl = ReventlessCore.GraphQL_Stitcher.stitch(
|
|
1629
1629
|
~baseFragment,
|
|
1630
1630
|
~pluginFragments=fragments,
|
|
1631
|
-
)
|
|
1631
|
+
)->AppSync_Adapter.stampSharedIamTypes
|
|
1632
1632
|
await AppSync_Adapter.getClient()->AppSync_Adapter.startSchemaCreationRetrying({
|
|
1633
1633
|
apiId,
|
|
1634
1634
|
definition: sdl,
|
package/src/Platform.res.mjs
CHANGED
|
@@ -639,7 +639,7 @@ function MakeWithConfig(Config) {
|
|
|
639
639
|
let runSchemaPush = () => writeAndScanFragments().then(async allPluginFragments => {
|
|
640
640
|
let baseFragment;
|
|
641
641
|
baseFragment = capturedDeployTarget === "Domain" && Config.splitApi ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin", undefined);
|
|
642
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, allPluginFragments);
|
|
642
|
+
let sdl = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, allPluginFragments));
|
|
643
643
|
let currentHash = AppSync_Adapter$ReventlessAws.sha256Hex(sdl);
|
|
644
644
|
let storedHash = tableNameOpt !== undefined ? await readSchemaHash(tableNameOpt, apiId) : undefined;
|
|
645
645
|
let client = AppSync_Adapter$ReventlessAws.getClient();
|
|
@@ -694,7 +694,7 @@ function MakeWithConfig(Config) {
|
|
|
694
694
|
let match = splitApiOutputsRef.contents;
|
|
695
695
|
let targetApi = match !== undefined ? match.platformApi : domainApi;
|
|
696
696
|
let adminBaseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin", undefined);
|
|
697
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(adminBaseFragment, []);
|
|
697
|
+
let sdl = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(adminBaseFragment, []));
|
|
698
698
|
return Output$Pulumi.flatMap(Pulumi.all([
|
|
699
699
|
targetApi,
|
|
700
700
|
adminBarrier
|
|
@@ -1066,7 +1066,7 @@ function MakeWithConfig(Config) {
|
|
|
1066
1066
|
}
|
|
1067
1067
|
});
|
|
1068
1068
|
let baseFragment = Config.splitApi ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin", undefined);
|
|
1069
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, fragments);
|
|
1069
|
+
let sdl = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, fragments));
|
|
1070
1070
|
return await AppSync_Adapter$ReventlessAws.startSchemaCreationRetrying(AppSync_Adapter$ReventlessAws.getClient(), {
|
|
1071
1071
|
apiId: apiId,
|
|
1072
1072
|
definition: sdl
|
|
@@ -1870,7 +1870,7 @@ function Make($star) {
|
|
|
1870
1870
|
let runSchemaPush = () => writeAndScanFragments().then(async allPluginFragments => {
|
|
1871
1871
|
let baseFragment;
|
|
1872
1872
|
baseFragment = capturedDeployTarget === "Domain" ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(false), "Admin", undefined);
|
|
1873
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, allPluginFragments);
|
|
1873
|
+
let sdl = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, allPluginFragments));
|
|
1874
1874
|
let currentHash = AppSync_Adapter$ReventlessAws.sha256Hex(sdl);
|
|
1875
1875
|
let storedHash = tableNameOpt !== undefined ? await readSchemaHash(tableNameOpt, apiId) : undefined;
|
|
1876
1876
|
let client = AppSync_Adapter$ReventlessAws.getClient();
|
|
@@ -1925,7 +1925,7 @@ function Make($star) {
|
|
|
1925
1925
|
let match = splitApiOutputsRef.contents;
|
|
1926
1926
|
let targetApi = match !== undefined ? match.platformApi : domainApi;
|
|
1927
1927
|
let adminBaseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(false), "Admin", undefined);
|
|
1928
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(adminBaseFragment, []);
|
|
1928
|
+
let sdl = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(adminBaseFragment, []));
|
|
1929
1929
|
return Output$Pulumi.flatMap(Pulumi.all([
|
|
1930
1930
|
targetApi,
|
|
1931
1931
|
adminBarrier
|
|
@@ -2280,7 +2280,7 @@ function Make($star) {
|
|
|
2280
2280
|
return;
|
|
2281
2281
|
}
|
|
2282
2282
|
});
|
|
2283
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(emptyBaseFragment, fragments);
|
|
2283
|
+
let sdl = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(emptyBaseFragment, fragments));
|
|
2284
2284
|
return await AppSync_Adapter$ReventlessAws.startSchemaCreationRetrying(AppSync_Adapter$ReventlessAws.getClient(), {
|
|
2285
2285
|
apiId: apiId,
|
|
2286
2286
|
definition: sdl
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
let opsFor = (
|
|
12
12
|
config: PgConnection.connectionConfig,
|
|
13
13
|
~logName: string,
|
|
14
|
-
~lockStrategy: ReventlessPostgres.
|
|
14
|
+
~lockStrategy: ReventlessPostgres.DcbEventLogStorage_Postgres_Ops.lockStrategy=#AdvisoryLocks,
|
|
15
15
|
): ReventlessCore.DcbEventLog_Adapter.operations => {
|
|
16
16
|
let pool = PgRuntime.poolFor(config)
|
|
17
|
-
|
|
17
|
+
// Import the runtime-pure ops module (no @pulumi/pulumi) so this deployed-Lambda
|
|
18
|
+
// path never drags a deploy-time dep into its ESM import graph.
|
|
19
|
+
let (_name, ops) = ReventlessPostgres.DcbEventLogStorage_Postgres_Ops.makeOps(
|
|
18
20
|
~pool,
|
|
19
21
|
~name=logName,
|
|
20
22
|
~indexes=[],
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
|
|
4
|
-
import * as
|
|
4
|
+
import * as DcbEventLogStorage_Postgres_Ops$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/DcbEventLogStorage_Postgres_Ops.res.mjs";
|
|
5
5
|
|
|
6
6
|
function opsFor(config, logName, lockStrategyOpt) {
|
|
7
7
|
let lockStrategy = lockStrategyOpt !== undefined ? lockStrategyOpt : "AdvisoryLocks";
|
|
8
8
|
let pool = PgRuntime$ReventlessAws.poolFor(config);
|
|
9
|
-
return
|
|
9
|
+
return DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.makeOps(pool, logName, [], undefined, undefined, undefined, lockStrategy, undefined)[1];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export {
|
|
@@ -14,7 +14,9 @@ let opsFor = (
|
|
|
14
14
|
~logName: string,
|
|
15
15
|
): ReventlessCore.EventLog_Adapter.operations => {
|
|
16
16
|
let pool = PgRuntime.poolFor(config)
|
|
17
|
-
|
|
17
|
+
// Import the runtime-pure ops module (no @pulumi/pulumi) so this deployed-Lambda
|
|
18
|
+
// path never drags a deploy-time dep into its ESM import graph.
|
|
19
|
+
let (_name, ops) = ReventlessPostgres.EventLogStorage_Postgres_Ops.makeOps(
|
|
18
20
|
~pool,
|
|
19
21
|
~name=logName,
|
|
20
22
|
~opts=(),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
|
|
4
|
-
import * as
|
|
4
|
+
import * as EventLogStorage_Postgres_Ops$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/EventLogStorage_Postgres_Ops.res.mjs";
|
|
5
5
|
|
|
6
6
|
function opsFor(config, logName) {
|
|
7
7
|
let pool = PgRuntime$ReventlessAws.poolFor(config);
|
|
8
|
-
return
|
|
8
|
+
return EventLogStorage_Postgres_Ops$ReventlessPostgres.makeOps(pool, logName, undefined, undefined)[1];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export {
|
|
@@ -15,5 +15,7 @@ let opsFor = (
|
|
|
15
15
|
~subIdField: option<string>=?,
|
|
16
16
|
): ReventlessCore.QueryDb_Adapter.operations => {
|
|
17
17
|
let pool = PgRuntime.poolFor(config)
|
|
18
|
-
|
|
18
|
+
// Import the runtime-pure ops module (no @pulumi/pulumi) so this deployed-Lambda
|
|
19
|
+
// path never drags a deploy-time dep into its ESM import graph.
|
|
20
|
+
ReventlessPostgres.QueryDbStorage_Postgres_Ops.makeOperations(~pool, ~name, ~indexes, ~subIdField)
|
|
19
21
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
|
|
4
|
-
import * as
|
|
4
|
+
import * as QueryDbStorage_Postgres_Ops$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/QueryDbStorage_Postgres_Ops.res.mjs";
|
|
5
5
|
|
|
6
6
|
function opsFor(config, name, indexesOpt, subIdField) {
|
|
7
7
|
let indexes = indexesOpt !== undefined ? indexesOpt : [];
|
|
8
8
|
let pool = PgRuntime$ReventlessAws.poolFor(config);
|
|
9
|
-
return
|
|
9
|
+
return QueryDbStorage_Postgres_Ops$ReventlessPostgres.makeOperations(pool, name, indexes, subIdField);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export {
|
|
@@ -311,6 +311,55 @@ let _formatDualAuthDirective = (groups: option<array<string>>): string => {
|
|
|
311
311
|
`${cognito} @aws_iam`
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
// ── Type-level dual-auth ─────────────────────────────────────────────────────
|
|
315
|
+
// On a multi-auth API, object TYPES without auth directives are accessible only
|
|
316
|
+
// via the default auth mode — a field-level `@aws_iam` admits the system caller
|
|
317
|
+
// to the top-level field, but response shaping then walks the return types
|
|
318
|
+
// (`…Connection` → `…Edge` → node type → nested state types) and dies with
|
|
319
|
+
// "Not Authorized to access <field> on type <T>" one level in. Types reachable
|
|
320
|
+
// from a systemCallable field therefore carry the bare multi-auth pair: the
|
|
321
|
+
// group-less Cognito arm matches the pre-existing accessibility of an
|
|
322
|
+
// undirectived type (any authenticated user; entry gating stays on the fields),
|
|
323
|
+
// the IAM arm admits the traversal. Only `type` declarations take auth
|
|
324
|
+
// directives (inputs/enums/interfaces do not).
|
|
325
|
+
|
|
326
|
+
// The declared name of a `type …` SDL declaration; None for input/enum/union/etc.
|
|
327
|
+
let _typeDeclName = (decl: string): option<string> =>
|
|
328
|
+
if decl->String.startsWith("type ") {
|
|
329
|
+
let rest = decl->String.slice(~start=5, ~end=decl->String.length)
|
|
330
|
+
let end = switch rest->String.search(%re("/[\s{]/")) {
|
|
331
|
+
| -1 => rest->String.length
|
|
332
|
+
| i => i
|
|
333
|
+
}
|
|
334
|
+
Some(rest->String.slice(~start=0, ~end))
|
|
335
|
+
} else {
|
|
336
|
+
None
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Insert the multi-auth pair into a type declaration header (before its `{`).
|
|
340
|
+
let _stampTypeDualAuth = (decl: string): string =>
|
|
341
|
+
switch decl->String.indexOfOpt("{") {
|
|
342
|
+
| Some(i) =>
|
|
343
|
+
decl->String.slice(~start=0, ~end=i) ++
|
|
344
|
+
"@aws_cognito_user_pools @aws_iam " ++
|
|
345
|
+
decl->String.slice(~start=i, ~end=decl->String.length)
|
|
346
|
+
| None => decl
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Shared traversal types every callable surface reaches — `PageInfo` (relay
|
|
350
|
+
// connections, injected by the stitcher) and the `CommandResult` members
|
|
351
|
+
// (mutation returns, deduped across fragments by the stitcher). They must be
|
|
352
|
+
// stamped exactly once on the ASSEMBLED SDL: stamping per-fragment would race
|
|
353
|
+
// the stitcher's first-wins dedupe against unstamped copies from sibling
|
|
354
|
+
// fragments. Unconditional — the API always configures AWS_IAM as an
|
|
355
|
+
// additional provider, so the directive is always valid.
|
|
356
|
+
let sharedIamTypeNames = ["PageInfo", "CommandAccepted", "CommandRejected", "CommandPending"]
|
|
357
|
+
|
|
358
|
+
let stampSharedIamTypes = (sdl: string): string =>
|
|
359
|
+
sharedIamTypeNames->Array.reduce(sdl, (acc, name) =>
|
|
360
|
+
acc->String.replace(`type ${name} {`, `type ${name} @aws_cognito_user_pools @aws_iam {`)
|
|
361
|
+
)
|
|
362
|
+
|
|
314
363
|
let injectAwsAuth = (
|
|
315
364
|
fragment: Reventless.Plugin.apiSchemaFragment,
|
|
316
365
|
~mutationEntries: array<ReventlessInfra.Api.mutationSchemaEntry>,
|
|
@@ -323,8 +372,15 @@ let injectAwsAuth = (
|
|
|
323
372
|
// `authorization.group` per-entry single-group form.
|
|
324
373
|
// Fields opted into deploy-time IAM invocation (dual-auth). Membership here
|
|
325
374
|
// switches a field from the single-mode `@aws_auth` form to the multi-auth
|
|
326
|
-
// `@aws_cognito_user_pools(...) @aws_iam` form.
|
|
375
|
+
// `@aws_cognito_user_pools(...) @aws_iam` form. Query entries mark by PREFIX
|
|
376
|
+
// (single/list field name) because the generator derives further fields from
|
|
377
|
+
// them — `…Items` (sub-id), `…ByIds`, `…By<Index>` (GSI) — which a system
|
|
378
|
+
// caller (reconcile reads) uses too. Type declarations reachable from a
|
|
379
|
+
// callable entry are stamped by returnTypeName prefix (see the type-level
|
|
380
|
+
// dual-auth note above).
|
|
327
381
|
let iamFields: Dict.t<bool> = Dict.make()
|
|
382
|
+
let iamQueryFieldPrefixes: array<string> = []
|
|
383
|
+
let iamTypePrefixes: array<string> = []
|
|
328
384
|
|
|
329
385
|
let mutationAuthMap: Dict.t<array<string>> = Dict.make()
|
|
330
386
|
mutationEntries->Array.forEach(entry => {
|
|
@@ -359,6 +415,9 @@ let injectAwsAuth = (
|
|
|
359
415
|
if entry.systemCallable->Option.getOr(false) {
|
|
360
416
|
iamFields->Dict.set(entry.singleFieldName, true)
|
|
361
417
|
iamFields->Dict.set(entry.listFieldName, true)
|
|
418
|
+
iamQueryFieldPrefixes->Array.push(entry.singleFieldName)
|
|
419
|
+
iamQueryFieldPrefixes->Array.push(entry.listFieldName)
|
|
420
|
+
iamTypePrefixes->Array.push(entry.returnTypeName)
|
|
362
421
|
}
|
|
363
422
|
switch entry.authorization {
|
|
364
423
|
| Some({group}) =>
|
|
@@ -396,7 +455,10 @@ let injectAwsAuth = (
|
|
|
396
455
|
let augmentedQueries = parts.queries->Array.map(field => {
|
|
397
456
|
let fieldName = ReventlessCore.GraphQL_Stitcher.extractLeadingName(field)
|
|
398
457
|
let groups = queryAuthMap->Dict.get(fieldName)
|
|
399
|
-
|
|
458
|
+
let isIam =
|
|
459
|
+
iamFields->Dict.get(fieldName)->Option.getOr(false) ||
|
|
460
|
+
iamQueryFieldPrefixes->Array.some(p => fieldName->String.startsWith(p))
|
|
461
|
+
if isIam {
|
|
400
462
|
`${field} ${_formatDualAuthDirective(groups)}`
|
|
401
463
|
} else {
|
|
402
464
|
switch groups {
|
|
@@ -406,10 +468,21 @@ let injectAwsAuth = (
|
|
|
406
468
|
}
|
|
407
469
|
})
|
|
408
470
|
|
|
471
|
+
// Type-level dual-auth for every type the callable entries' responses reach —
|
|
472
|
+
// the node type, its Connection/Edge wrappers, and nested state types all
|
|
473
|
+
// share the entry's returnTypeName prefix.
|
|
474
|
+
let augmentedTypes = parts.types->Array.map(decl =>
|
|
475
|
+
switch _typeDeclName(decl) {
|
|
476
|
+
| Some(name) if iamTypePrefixes->Array.some(p => name->String.startsWith(p)) =>
|
|
477
|
+
_stampTypeDualAuth(decl)
|
|
478
|
+
| _ => decl
|
|
479
|
+
}
|
|
480
|
+
)
|
|
481
|
+
|
|
409
482
|
let encoded =
|
|
410
483
|
JSON.Encode.object(
|
|
411
484
|
Dict.fromArray([
|
|
412
|
-
("types", JSON.Encode.array(
|
|
485
|
+
("types", JSON.Encode.array(augmentedTypes->Array.map(JSON.Encode.string))),
|
|
413
486
|
("mutations", JSON.Encode.array(augmentedMutations->Array.map(JSON.Encode.string))),
|
|
414
487
|
("queries", JSON.Encode.array(augmentedQueries->Array.map(JSON.Encode.string))),
|
|
415
488
|
(
|
|
@@ -546,7 +619,13 @@ let updateSchema = (
|
|
|
546
619
|
// The base fragment contains core Plugin aggregate queries/mutations — all Admin-only.
|
|
547
620
|
// Plugin fragments already have @aws_auth injected via generateFragment.
|
|
548
621
|
let augmentedBaseFragment = injectAwsAuthAll(baseFragment, ~group="Admin")
|
|
549
|
-
|
|
622
|
+
// Shared traversal types are stamped once on the assembled SDL (post-stitch,
|
|
623
|
+
// post-dedupe) — see stampSharedIamTypes.
|
|
624
|
+
let sdl =
|
|
625
|
+
ReventlessCore.GraphQL_Stitcher.stitch(
|
|
626
|
+
~baseFragment=augmentedBaseFragment,
|
|
627
|
+
~pluginFragments,
|
|
628
|
+
)->stampSharedIamTypes
|
|
550
629
|
// Resolve the API ID from the Output chain. In mock mode (tests) and in Lambda runtime
|
|
551
630
|
// (where the Output is backed by already-known values), this completes synchronously.
|
|
552
631
|
// The resulting promise wraps the AppSync SDK call.
|
|
@@ -4,8 +4,10 @@ import * as Effect from "@reventlessdev/rescript-effect/src/Effect.res.mjs";
|
|
|
4
4
|
import * as Aws from "@pulumi/aws";
|
|
5
5
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
6
6
|
import * as Nodecrypto from "node:crypto";
|
|
7
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
7
8
|
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
8
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
|
+
import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
|
|
9
11
|
import * as Effect$1 from "effect/Effect";
|
|
10
12
|
import * as Pulumi from "@pulumi/pulumi";
|
|
11
13
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
@@ -215,9 +217,41 @@ function _formatDualAuthDirective(groups) {
|
|
|
215
217
|
return cognito + ` @aws_iam`;
|
|
216
218
|
}
|
|
217
219
|
|
|
220
|
+
function _typeDeclName(decl) {
|
|
221
|
+
if (!decl.startsWith("type ")) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
let rest = decl.slice(5, decl.length);
|
|
225
|
+
let i = rest.search(/[\s{]/);
|
|
226
|
+
let end = i !== -1 ? i : rest.length;
|
|
227
|
+
return rest.slice(0, end);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function _stampTypeDualAuth(decl) {
|
|
231
|
+
let i = Stdlib_String.indexOfOpt(decl, "{");
|
|
232
|
+
if (i !== undefined) {
|
|
233
|
+
return decl.slice(0, i) + "@aws_cognito_user_pools @aws_iam " + decl.slice(i, decl.length);
|
|
234
|
+
} else {
|
|
235
|
+
return decl;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let sharedIamTypeNames = [
|
|
240
|
+
"PageInfo",
|
|
241
|
+
"CommandAccepted",
|
|
242
|
+
"CommandRejected",
|
|
243
|
+
"CommandPending"
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
function stampSharedIamTypes(sdl) {
|
|
247
|
+
return Stdlib_Array.reduce(sharedIamTypeNames, sdl, (acc, name) => acc.replace(`type ` + name + ` {`, `type ` + name + ` @aws_cognito_user_pools @aws_iam {`));
|
|
248
|
+
}
|
|
249
|
+
|
|
218
250
|
function injectAwsAuth(fragment, mutationEntries, queryEntries) {
|
|
219
251
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(fragment);
|
|
220
252
|
let iamFields = {};
|
|
253
|
+
let iamQueryFieldPrefixes = [];
|
|
254
|
+
let iamTypePrefixes = [];
|
|
221
255
|
let mutationAuthMap = {};
|
|
222
256
|
mutationEntries.forEach(entry => {
|
|
223
257
|
if (Stdlib_Option.getOr(entry.systemCallable, false)) {
|
|
@@ -252,6 +286,9 @@ function injectAwsAuth(fragment, mutationEntries, queryEntries) {
|
|
|
252
286
|
if (Stdlib_Option.getOr(entry.systemCallable, false)) {
|
|
253
287
|
iamFields[entry.singleFieldName] = true;
|
|
254
288
|
iamFields[entry.listFieldName] = true;
|
|
289
|
+
iamQueryFieldPrefixes.push(entry.singleFieldName);
|
|
290
|
+
iamQueryFieldPrefixes.push(entry.listFieldName);
|
|
291
|
+
iamTypePrefixes.push(entry.returnTypeName);
|
|
255
292
|
}
|
|
256
293
|
let match = entry.authorization;
|
|
257
294
|
if (match !== undefined) {
|
|
@@ -287,7 +324,8 @@ function injectAwsAuth(fragment, mutationEntries, queryEntries) {
|
|
|
287
324
|
let augmentedQueries = parts.queries.map(field => {
|
|
288
325
|
let fieldName = GraphQL_Stitcher$ReventlessCore.extractLeadingName(field);
|
|
289
326
|
let groups = queryAuthMap[fieldName];
|
|
290
|
-
|
|
327
|
+
let isIam = Stdlib_Option.getOr(iamFields[fieldName], false) || iamQueryFieldPrefixes.some(p => fieldName.startsWith(p));
|
|
328
|
+
if (isIam) {
|
|
291
329
|
return field + ` ` + _formatDualAuthDirective(groups);
|
|
292
330
|
} else if (groups !== undefined) {
|
|
293
331
|
return field + ` ` + _formatGroupsDirective(groups);
|
|
@@ -295,10 +333,18 @@ function injectAwsAuth(fragment, mutationEntries, queryEntries) {
|
|
|
295
333
|
return field;
|
|
296
334
|
}
|
|
297
335
|
});
|
|
336
|
+
let augmentedTypes = parts.types.map(decl => {
|
|
337
|
+
let name = _typeDeclName(decl);
|
|
338
|
+
if (name !== undefined && iamTypePrefixes.some(p => name.startsWith(p))) {
|
|
339
|
+
return _stampTypeDualAuth(decl);
|
|
340
|
+
} else {
|
|
341
|
+
return decl;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
298
344
|
let encoded = JSON.stringify(Object.fromEntries([
|
|
299
345
|
[
|
|
300
346
|
"types",
|
|
301
|
-
|
|
347
|
+
augmentedTypes.map(prim => prim)
|
|
302
348
|
],
|
|
303
349
|
[
|
|
304
350
|
"mutations",
|
|
@@ -398,7 +444,7 @@ function generateFragment(mutationEntries, queryEntries) {
|
|
|
398
444
|
|
|
399
445
|
function updateSchema(api, baseFragment, pluginFragments) {
|
|
400
446
|
let augmentedBaseFragment = injectAwsAuthAll(baseFragment, "Admin", undefined);
|
|
401
|
-
let sdl = GraphQL_Stitcher$ReventlessCore.stitch(augmentedBaseFragment, pluginFragments);
|
|
447
|
+
let sdl = stampSharedIamTypes(GraphQL_Stitcher$ReventlessCore.stitch(augmentedBaseFragment, pluginFragments));
|
|
402
448
|
let resultPromise = {
|
|
403
449
|
contents: Promise.resolve()
|
|
404
450
|
};
|
|
@@ -426,6 +472,10 @@ export {
|
|
|
426
472
|
_permissionToCognitoGroups,
|
|
427
473
|
_formatGroupsDirective,
|
|
428
474
|
_formatDualAuthDirective,
|
|
475
|
+
_typeDeclName,
|
|
476
|
+
_stampTypeDualAuth,
|
|
477
|
+
sharedIamTypeNames,
|
|
478
|
+
stampSharedIamTypes,
|
|
429
479
|
injectAwsAuth,
|
|
430
480
|
injectAwsAuthAll,
|
|
431
481
|
makeApiResource,
|
|
@@ -452,6 +452,118 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
|
|
|
452
452
|
| _ => JsError.throwWithMessage("missing query fields")
|
|
453
453
|
}
|
|
454
454
|
})
|
|
455
|
+
|
|
456
|
+
testSync("systemCallable dual-auth extends to derived query fields (Items/ByIds/By<Index>)", () => {
|
|
457
|
+
let entry: ReventlessInfra.Api.querySchemaEntry = {
|
|
458
|
+
singleFieldName: "p_Item",
|
|
459
|
+
listFieldName: "p_Items",
|
|
460
|
+
returnTypeName: "p_Item",
|
|
461
|
+
stateSchema: S.unknown,
|
|
462
|
+
authorization: None,
|
|
463
|
+
permission: Reventless.Authorization.AllowGroups(["Admin"]),
|
|
464
|
+
systemCallable: true,
|
|
465
|
+
}
|
|
466
|
+
let frag = makeFragment(
|
|
467
|
+
[],
|
|
468
|
+
[
|
|
469
|
+
"p_ItemItems(id: ID!, first: Int): p_ItemConnection!",
|
|
470
|
+
"p_ItemsByIds(ids: [ID!]!): [p_Item]!",
|
|
471
|
+
"p_ItemByOwner(id: ID!, first: Int): p_ItemConnection!",
|
|
472
|
+
"q_Unrelated(id: ID!): String",
|
|
473
|
+
],
|
|
474
|
+
)
|
|
475
|
+
let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[entry])
|
|
476
|
+
let parts = decodeFragment(aug)
|
|
477
|
+
parts.queries->Array.forEach(field =>
|
|
478
|
+
if field->String.includes("q_Unrelated") {
|
|
479
|
+
expect(field)->not_->toContain("@aws_iam")
|
|
480
|
+
} else {
|
|
481
|
+
expect(field)->toContain("@aws_iam")
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
})
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
// ── Type-level dual-auth ─────────────────────────────────────────────────────
|
|
488
|
+
//
|
|
489
|
+
// On a multi-auth API a field directive alone is not enough: response shaping
|
|
490
|
+
// walks the return TYPES (Connection → Edge → node → nested state types), each
|
|
491
|
+
// of which is default-mode-only without its own directive. Types prefixed by a
|
|
492
|
+
// systemCallable entry's returnTypeName get the bare multi-auth pair; shared
|
|
493
|
+
// traversal types (PageInfo / CommandResult members) are stamped once on the
|
|
494
|
+
// assembled SDL.
|
|
495
|
+
|
|
496
|
+
describe("AppSync_Adapter — type-level dual-auth", () => {
|
|
497
|
+
let makeFragmentWithTypes = (types, queryFields) =>
|
|
498
|
+
ReventlessCore.GraphQL_Stitcher.encode({
|
|
499
|
+
types,
|
|
500
|
+
mutations: [],
|
|
501
|
+
queries: queryFields,
|
|
502
|
+
subscriptions: [],
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
let callableEntry: ReventlessInfra.Api.querySchemaEntry = {
|
|
506
|
+
singleFieldName: "p_Item",
|
|
507
|
+
listFieldName: "p_Items",
|
|
508
|
+
returnTypeName: "p_Item",
|
|
509
|
+
stateSchema: S.unknown,
|
|
510
|
+
authorization: None,
|
|
511
|
+
permission: Reventless.Authorization.AllowGroups(["Admin"]),
|
|
512
|
+
systemCallable: true,
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let types = [
|
|
516
|
+
"type p_Item implements Node {\n id: ID!\n}",
|
|
517
|
+
"type p_ItemConnection {\n edges: [p_ItemEdge!]!\n}",
|
|
518
|
+
"type p_ItemEdge {\n node: p_Item!\n}",
|
|
519
|
+
"type p_ItemNested {\n x: String\n}",
|
|
520
|
+
"type q_Sibling {\n id: ID!\n}",
|
|
521
|
+
"input p_ItemItemsFilter {\n eq: String\n}",
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
testSync("types prefixed by a callable entry's returnTypeName carry the multi-auth pair", () => {
|
|
525
|
+
let frag = makeFragmentWithTypes(types, ["p_Item(id: ID!): p_Item"])
|
|
526
|
+
let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[callableEntry])
|
|
527
|
+
let parts = decodeFragment(aug)
|
|
528
|
+
parts.types->Array.forEach(decl =>
|
|
529
|
+
if decl->String.startsWith("type p_Item") {
|
|
530
|
+
expect(decl)->toContain("@aws_cognito_user_pools @aws_iam {")
|
|
531
|
+
} else {
|
|
532
|
+
// The sibling view's type and the (non-authorizable) input stay untouched.
|
|
533
|
+
expect(decl)->not_->toContain("@aws_iam")
|
|
534
|
+
}
|
|
535
|
+
)
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
testSync("without a systemCallable entry no type is stamped", () => {
|
|
539
|
+
let plainEntry: ReventlessInfra.Api.querySchemaEntry = {
|
|
540
|
+
...callableEntry,
|
|
541
|
+
systemCallable: false,
|
|
542
|
+
}
|
|
543
|
+
let frag = makeFragmentWithTypes(types, ["p_Item(id: ID!): p_Item"])
|
|
544
|
+
let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[plainEntry])
|
|
545
|
+
decodeFragment(aug).types->Array.forEach(decl => expect(decl)->not_->toContain("@aws_iam"))
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
testSync("stampSharedIamTypes marks PageInfo + CommandResult members on the assembled SDL", () => {
|
|
549
|
+
let sdl = [
|
|
550
|
+
"interface Node {\n id: ID!\n}",
|
|
551
|
+
"type PageInfo {\n hasNextPage: Boolean!\n}",
|
|
552
|
+
"union CommandResult = CommandAccepted | CommandRejected | CommandPending",
|
|
553
|
+
"type CommandAccepted {\n msgId: ID!\n}",
|
|
554
|
+
"type CommandRejected {\n msgId: ID!\n}",
|
|
555
|
+
"type CommandPending {\n msgId: ID!\n}",
|
|
556
|
+
"type Untouched {\n id: ID!\n}",
|
|
557
|
+
]->Array.join("\n\n")
|
|
558
|
+
let stamped = AppSync_Adapter.stampSharedIamTypes(sdl)
|
|
559
|
+
expect(stamped)->toContain("type PageInfo @aws_cognito_user_pools @aws_iam {")
|
|
560
|
+
expect(stamped)->toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {")
|
|
561
|
+
expect(stamped)->toContain("type CommandRejected @aws_cognito_user_pools @aws_iam {")
|
|
562
|
+
expect(stamped)->toContain("type CommandPending @aws_cognito_user_pools @aws_iam {")
|
|
563
|
+
expect(stamped)->toContain("type Untouched {")
|
|
564
|
+
// The union declaration itself takes no directive.
|
|
565
|
+
expect(stamped)->toContain("union CommandResult = CommandAccepted | CommandRejected | CommandPending")
|
|
566
|
+
})
|
|
455
567
|
})
|
|
456
568
|
|
|
457
569
|
describe("AppSync_Adapter.injectAwsAuthAll — ~iamFieldNames", () => {
|
|
@@ -426,6 +426,107 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
|
|
|
426
426
|
return Stdlib_JsError.throwWithMessage("missing query fields");
|
|
427
427
|
}
|
|
428
428
|
});
|
|
429
|
+
globalThis.test("systemCallable dual-auth extends to derived query fields (Items/ByIds/By<Index>)", () => {
|
|
430
|
+
let entry_permission = {
|
|
431
|
+
TAG: "AllowGroups",
|
|
432
|
+
_0: ["Admin"]
|
|
433
|
+
};
|
|
434
|
+
let entry_systemCallable = true;
|
|
435
|
+
let entry = {
|
|
436
|
+
singleFieldName: "p_Item",
|
|
437
|
+
listFieldName: "p_Items",
|
|
438
|
+
returnTypeName: "p_Item",
|
|
439
|
+
stateSchema: S.unknown,
|
|
440
|
+
authorization: undefined,
|
|
441
|
+
permission: entry_permission,
|
|
442
|
+
systemCallable: entry_systemCallable
|
|
443
|
+
};
|
|
444
|
+
let frag = makeFragment([], [
|
|
445
|
+
"p_ItemItems(id: ID!, first: Int): p_ItemConnection!",
|
|
446
|
+
"p_ItemsByIds(ids: [ID!]!): [p_Item]!",
|
|
447
|
+
"p_ItemByOwner(id: ID!, first: Int): p_ItemConnection!",
|
|
448
|
+
"q_Unrelated(id: ID!): String"
|
|
449
|
+
]);
|
|
450
|
+
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [], [entry]);
|
|
451
|
+
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
452
|
+
parts.queries.forEach(field => {
|
|
453
|
+
if (field.includes("q_Unrelated")) {
|
|
454
|
+
globalThis.expect(field).not.toContain("@aws_iam");
|
|
455
|
+
} else {
|
|
456
|
+
globalThis.expect(field).toContain("@aws_iam");
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
globalThis.describe("AppSync_Adapter — type-level dual-auth", () => {
|
|
463
|
+
let makeFragmentWithTypes = (types, queryFields) => GraphQL_Stitcher$ReventlessCore.encode({
|
|
464
|
+
types: types,
|
|
465
|
+
mutations: [],
|
|
466
|
+
queries: queryFields,
|
|
467
|
+
subscriptions: []
|
|
468
|
+
});
|
|
469
|
+
let callableEntry_permission = {
|
|
470
|
+
TAG: "AllowGroups",
|
|
471
|
+
_0: ["Admin"]
|
|
472
|
+
};
|
|
473
|
+
let callableEntry_systemCallable = true;
|
|
474
|
+
let callableEntry = {
|
|
475
|
+
singleFieldName: "p_Item",
|
|
476
|
+
listFieldName: "p_Items",
|
|
477
|
+
returnTypeName: "p_Item",
|
|
478
|
+
stateSchema: S.unknown,
|
|
479
|
+
authorization: undefined,
|
|
480
|
+
permission: callableEntry_permission,
|
|
481
|
+
systemCallable: callableEntry_systemCallable
|
|
482
|
+
};
|
|
483
|
+
let types = [
|
|
484
|
+
"type p_Item implements Node {\n id: ID!\n}",
|
|
485
|
+
"type p_ItemConnection {\n edges: [p_ItemEdge!]!\n}",
|
|
486
|
+
"type p_ItemEdge {\n node: p_Item!\n}",
|
|
487
|
+
"type p_ItemNested {\n x: String\n}",
|
|
488
|
+
"type q_Sibling {\n id: ID!\n}",
|
|
489
|
+
"input p_ItemItemsFilter {\n eq: String\n}"
|
|
490
|
+
];
|
|
491
|
+
globalThis.test("types prefixed by a callable entry's returnTypeName carry the multi-auth pair", () => {
|
|
492
|
+
let frag = makeFragmentWithTypes(types, ["p_Item(id: ID!): p_Item"]);
|
|
493
|
+
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [], [callableEntry]);
|
|
494
|
+
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
495
|
+
parts.types.forEach(decl => {
|
|
496
|
+
if (decl.startsWith("type p_Item")) {
|
|
497
|
+
globalThis.expect(decl).toContain("@aws_cognito_user_pools @aws_iam {");
|
|
498
|
+
} else {
|
|
499
|
+
globalThis.expect(decl).not.toContain("@aws_iam");
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
globalThis.test("without a systemCallable entry no type is stamped", () => {
|
|
504
|
+
let newrecord = {...callableEntry};
|
|
505
|
+
newrecord.systemCallable = false;
|
|
506
|
+
let frag = makeFragmentWithTypes(types, ["p_Item(id: ID!): p_Item"]);
|
|
507
|
+
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [], [newrecord]);
|
|
508
|
+
GraphQL_Stitcher$ReventlessCore.decode(aug).types.forEach(decl => {
|
|
509
|
+
globalThis.expect(decl).not.toContain("@aws_iam");
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
globalThis.test("stampSharedIamTypes marks PageInfo + CommandResult members on the assembled SDL", () => {
|
|
513
|
+
let sdl = [
|
|
514
|
+
"interface Node {\n id: ID!\n}",
|
|
515
|
+
"type PageInfo {\n hasNextPage: Boolean!\n}",
|
|
516
|
+
"union CommandResult = CommandAccepted | CommandRejected | CommandPending",
|
|
517
|
+
"type CommandAccepted {\n msgId: ID!\n}",
|
|
518
|
+
"type CommandRejected {\n msgId: ID!\n}",
|
|
519
|
+
"type CommandPending {\n msgId: ID!\n}",
|
|
520
|
+
"type Untouched {\n id: ID!\n}"
|
|
521
|
+
].join("\n\n");
|
|
522
|
+
let stamped = AppSync_Adapter$ReventlessAws.stampSharedIamTypes(sdl);
|
|
523
|
+
globalThis.expect(stamped).toContain("type PageInfo @aws_cognito_user_pools @aws_iam {");
|
|
524
|
+
globalThis.expect(stamped).toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {");
|
|
525
|
+
globalThis.expect(stamped).toContain("type CommandRejected @aws_cognito_user_pools @aws_iam {");
|
|
526
|
+
globalThis.expect(stamped).toContain("type CommandPending @aws_cognito_user_pools @aws_iam {");
|
|
527
|
+
globalThis.expect(stamped).toContain("type Untouched {");
|
|
528
|
+
globalThis.expect(stamped).toContain("union CommandResult = CommandAccepted | CommandRejected | CommandPending");
|
|
529
|
+
});
|
|
429
530
|
});
|
|
430
531
|
|
|
431
532
|
globalThis.describe("AppSync_Adapter.injectAwsAuthAll — ~iamFieldNames", () => {
|