@reventlessdev/reventless-aws 3.0.0-alpha.294 → 3.0.0-alpha.295
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 +7 -0
- package/package.json +9 -9
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +8 -1
- package/src/components/Api/AppSync_Adapter.res +19 -23
- package/src/components/Api/AppSync_Adapter.res.mjs +8 -20
- package/src/components/Api/AppSync_SdlDecorate.res +30 -14
- package/src/components/Api/AppSync_SdlDecorate.res.mjs +11 -10
- package/tests/AppSync_AdapterTest.res +35 -20
- package/tests/AppSync_AdapterTest.res.mjs +23 -16
- package/tests/AppSync_SdlDecorateTest.res +4 -4
- package/tests/AppSync_SdlDecorateTest.res.mjs +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.295 (2026-08-14)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** enforce Cognito group gates that AppSync was silently ignoring ([1392638](https://github.com/ReventlessDev/reventless-core/commit/13926388c097500606f75c387630a19ee1a3aa71))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.294 (2026-08-14)
|
|
7
14
|
|
|
8
15
|
### 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.295",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"@aws-sdk/s3-request-presigner": "3.970.0",
|
|
13
13
|
"sury": "11.0.0-alpha.4",
|
|
14
14
|
"uuid": "^13.0.0",
|
|
15
|
-
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.
|
|
16
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
15
|
+
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.10",
|
|
17
16
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
18
|
-
"@reventlessdev/rescript-
|
|
19
|
-
"@reventlessdev/rescript-
|
|
17
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
18
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.6",
|
|
19
|
+
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.75",
|
|
20
20
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
21
21
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
22
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
23
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
24
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.139",
|
|
22
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.233",
|
|
23
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.97",
|
|
25
24
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.31",
|
|
26
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
25
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.113",
|
|
26
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.140"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"rescript": "12.3.0",
|
|
@@ -6,13 +6,20 @@
|
|
|
6
6
|
// ([Auth_ActiveRoleTrigger.res] bundles it and attaches it to the user pool).
|
|
7
7
|
//
|
|
8
8
|
// The narrowing has to happen here rather than in our own enforcement code
|
|
9
|
-
// because `@authorize(AllowGroups([...]))` compiles to
|
|
9
|
+
// because `@authorize(AllowGroups([...]))` compiles to a field directive AppSync
|
|
10
10
|
// evaluates against `cognito:groups` **before any of our code executes**. A
|
|
11
11
|
// request header we invent could scope reads correctly and still leave every
|
|
12
12
|
// group-gated mutation callable — right about the data, wrong about the writes.
|
|
13
13
|
// Overriding the group claim is the one place every enforcement point already
|
|
14
14
|
// looks.
|
|
15
15
|
//
|
|
16
|
+
// That premise was false for as long as the directive was emitted as the
|
|
17
|
+
// single-mode `@aws_auth(...)`, which AppSync ignores on a multi-auth API — the
|
|
18
|
+
// gate was open and this trigger narrowed a claim nothing read for authorization.
|
|
19
|
+
// The directive is now `@aws_cognito_user_pools(...)`, which the service does
|
|
20
|
+
// enforce, so the reasoning above holds again. See
|
|
21
|
+
// `AppSync_SdlDecorate.formatCognitoGroupsDirective`.
|
|
22
|
+
//
|
|
16
23
|
// **Version 1 of the trigger, deliberately.** Group override and added ID-token
|
|
17
24
|
// claims are `V1_0` capabilities; `V2_0`/`V3_0` buy access-token customisation
|
|
18
25
|
// this does not need and are gated behind the Essentials and Plus feature plans.
|
|
@@ -171,9 +171,9 @@ let rec waitForMergeSuccess = async (
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
// ──
|
|
175
|
-
// Injects @
|
|
176
|
-
// based on authorization metadata from schema entries.
|
|
174
|
+
// ── Cognito group directive injection ─────────────────────────────────────
|
|
175
|
+
// Injects @aws_cognito_user_pools(cognito_groups: [...]) directives into SDL
|
|
176
|
+
// field strings based on authorization metadata from schema entries.
|
|
177
177
|
//
|
|
178
178
|
// Two sources are merged:
|
|
179
179
|
// 1. The legacy `authorization?: {tableName, group}` field — single group
|
|
@@ -184,19 +184,22 @@ let rec waitForMergeSuccess = async (
|
|
|
184
184
|
//
|
|
185
185
|
// When both are present on the same field, the spec-level permission wins
|
|
186
186
|
// (it is more specific). `AllowGroups([g1, g2, ...])` emits
|
|
187
|
-
// `@
|
|
188
|
-
// `AllowAnonymous` emit no directive (with Cognito as
|
|
189
|
-
// reaching request is already authenticated). `AllowGroups([])`
|
|
190
|
-
// `DenyAll` emit a sentinel `__deny_all__` group that no Cognito user can
|
|
187
|
+
// `@aws_cognito_user_pools(cognito_groups: ["g1", "g2", ...])`.
|
|
188
|
+
// `AllowAuthenticated` / `AllowAnonymous` emit no directive (with Cognito as
|
|
189
|
+
// primary auth, any reaching request is already authenticated). `AllowGroups([])`
|
|
190
|
+
// and `DenyAll` emit a sentinel `__deny_all__` group that no Cognito user can
|
|
191
191
|
// belong to — effectively blocking the field at the API layer.
|
|
192
192
|
//
|
|
193
|
+
// NOT `@aws_auth(...)`: that is the single-mode form, which AppSync ignores on a
|
|
194
|
+
// multi-auth API — which every API this adapter provisions is. See
|
|
195
|
+
// `AppSync_SdlDecorate.formatCognitoGroupsDirective` for the full rationale and
|
|
196
|
+
// the observation that established it.
|
|
197
|
+
//
|
|
193
198
|
// ── Dual-auth (Cognito + IAM) for deploy-time system callers ────────────────
|
|
194
199
|
// A field flagged `systemCallable` (via `mutationSchemaEntry`/`querySchemaEntry`)
|
|
195
200
|
// must be reachable by BOTH the console UI (Cognito) and a deploy-time system
|
|
196
201
|
// caller signing SigV4 with ambient AWS credentials (`Util_AppSync_Caller`,
|
|
197
|
-
// the AWS_IAM additional auth provider).
|
|
198
|
-
// directive form and does not admit IAM on a multi-auth API. For these fields
|
|
199
|
-
// we emit the multi-auth form instead:
|
|
202
|
+
// the AWS_IAM additional auth provider). For these fields we append the IAM arm:
|
|
200
203
|
// @aws_cognito_user_pools(cognito_groups: [...]) @aws_iam
|
|
201
204
|
// The Cognito arm preserves the field's existing group gating (or, when the
|
|
202
205
|
// field carries no Cognito group restriction, a bare `@aws_cognito_user_pools`
|
|
@@ -215,24 +218,17 @@ let _permissionToCognitoGroups = (
|
|
|
215
218
|
| AllowAuthenticated | AllowAnonymous => None
|
|
216
219
|
}
|
|
217
220
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
// Both directive forms are defined once in the runtime-pure AppSync_SdlDecorate
|
|
222
|
+
// so this deploy path and the bundled AdminEventCollector Lambda's reactive push
|
|
223
|
+
// cannot drift. The Cognito-only arm carries the rationale for why it is
|
|
224
|
+
// `@aws_cognito_user_pools(...)` and never `@aws_auth(...)`.
|
|
225
|
+
let _formatGroupsDirective = AppSync_SdlDecorate.formatCognitoGroupsDirective
|
|
222
226
|
|
|
223
227
|
// Multi-auth directive for a field that must accept BOTH Cognito and IAM.
|
|
224
228
|
// `groups=Some([...])` preserves Cognito group gating; `groups=None` keeps the
|
|
225
229
|
// field open to any authenticated Cognito user. `@aws_iam` admits the
|
|
226
230
|
// deploy-time SigV4 system caller. See the dual-auth note above.
|
|
227
|
-
let _formatDualAuthDirective =
|
|
228
|
-
let cognito = switch groups {
|
|
229
|
-
| Some(g) =>
|
|
230
|
-
let quoted = g->Array.map(x => `"${x}"`)->Array.join(", ")
|
|
231
|
-
`@aws_cognito_user_pools(cognito_groups: [${quoted}])`
|
|
232
|
-
| None => `@aws_cognito_user_pools`
|
|
233
|
-
}
|
|
234
|
-
`${cognito} @aws_iam`
|
|
235
|
-
}
|
|
231
|
+
let _formatDualAuthDirective = AppSync_SdlDecorate.formatDualAuthDirective
|
|
236
232
|
|
|
237
233
|
// ── Type-level dual-auth ─────────────────────────────────────────────────────
|
|
238
234
|
// On a multi-auth API, object TYPES without auth directives are accessible only
|
|
@@ -126,22 +126,6 @@ function _permissionToCognitoGroups(permission) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
function _formatGroupsDirective(groups) {
|
|
130
|
-
let quoted = groups.map(g => `"` + g + `"`).join(", ");
|
|
131
|
-
return `@aws_auth(cognito_groups: [` + quoted + `])`;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function _formatDualAuthDirective(groups) {
|
|
135
|
-
let cognito;
|
|
136
|
-
if (groups !== undefined) {
|
|
137
|
-
let quoted = groups.map(x => `"` + x + `"`).join(", ");
|
|
138
|
-
cognito = `@aws_cognito_user_pools(cognito_groups: [` + quoted + `])`;
|
|
139
|
-
} else {
|
|
140
|
-
cognito = `@aws_cognito_user_pools`;
|
|
141
|
-
}
|
|
142
|
-
return cognito + ` @aws_iam`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
129
|
function _typeDeclName(decl) {
|
|
146
130
|
if (!decl.startsWith("type ")) {
|
|
147
131
|
return;
|
|
@@ -228,9 +212,9 @@ function injectAwsAuth(fragment, mutationEntries, queryEntries) {
|
|
|
228
212
|
let fieldName = GraphQL_Stitcher$ReventlessCore.extractLeadingName(field);
|
|
229
213
|
let groups = mutationAuthMap[fieldName];
|
|
230
214
|
if (Stdlib_Option.getOr(iamFields[fieldName], false)) {
|
|
231
|
-
return field + `\n ` +
|
|
215
|
+
return field + `\n ` + AppSync_SdlDecorate$ReventlessAws.formatDualAuthDirective(groups);
|
|
232
216
|
} else if (groups !== undefined) {
|
|
233
|
-
return field + `\n ` +
|
|
217
|
+
return field + `\n ` + AppSync_SdlDecorate$ReventlessAws.formatCognitoGroupsDirective(groups);
|
|
234
218
|
} else {
|
|
235
219
|
return field;
|
|
236
220
|
}
|
|
@@ -240,9 +224,9 @@ function injectAwsAuth(fragment, mutationEntries, queryEntries) {
|
|
|
240
224
|
let groups = queryAuthMap[fieldName];
|
|
241
225
|
let isIam = Stdlib_Option.getOr(iamFields[fieldName], false) || iamQueryFieldPrefixes.some(p => fieldName.startsWith(p));
|
|
242
226
|
if (isIam) {
|
|
243
|
-
return field + ` ` +
|
|
227
|
+
return field + ` ` + AppSync_SdlDecorate$ReventlessAws.formatDualAuthDirective(groups);
|
|
244
228
|
} else if (groups !== undefined) {
|
|
245
|
-
return field + ` ` +
|
|
229
|
+
return field + ` ` + AppSync_SdlDecorate$ReventlessAws.formatCognitoGroupsDirective(groups);
|
|
246
230
|
} else {
|
|
247
231
|
return field;
|
|
248
232
|
}
|
|
@@ -346,6 +330,10 @@ function generateFragment(mutationEntries, queryEntries) {
|
|
|
346
330
|
return injectAwsAuth(fragment, mutationEntries, queryEntries);
|
|
347
331
|
}
|
|
348
332
|
|
|
333
|
+
let _formatGroupsDirective = AppSync_SdlDecorate$ReventlessAws.formatCognitoGroupsDirective;
|
|
334
|
+
|
|
335
|
+
let _formatDualAuthDirective = AppSync_SdlDecorate$ReventlessAws.formatDualAuthDirective;
|
|
336
|
+
|
|
349
337
|
let stampSharedIamTypes = AppSync_SdlDecorate$ReventlessAws.stampSharedIamTypes;
|
|
350
338
|
|
|
351
339
|
let primaryAuthenticationType = "AMAZON_COGNITO_USER_POOLS";
|
|
@@ -67,26 +67,43 @@ let injectAwsSubscribe = (
|
|
|
67
67
|
// the deploy path without dragging Pulumi into the runtime graph. AppSync_Adapter
|
|
68
68
|
// delegates its `injectAwsAuthAll` / `stampSharedIamTypes` to these definitions.
|
|
69
69
|
|
|
70
|
+
// Cognito group gate. `@aws_cognito_user_pools(cognito_groups: [...])` — NOT the
|
|
71
|
+
// single-mode `@aws_auth(cognito_groups: [...])`.
|
|
72
|
+
//
|
|
73
|
+
// `@aws_auth` is honoured only on an API whose sole authorization mode is
|
|
74
|
+
// AMAZON_COGNITO_USER_POOLS. This adapter always configures AWS_IAM as an
|
|
75
|
+
// additional provider (see AppSync_Adapter's api args — the server-to-server
|
|
76
|
+
// lambdas need it), so every API we provision is multi-auth and the service
|
|
77
|
+
// **silently ignores** `@aws_auth`: the schema deploys, the directive shows up
|
|
78
|
+
// in the deployed SDL, and it gates nothing. What is left is
|
|
79
|
+
// `userPoolConfig.defaultAction: ALLOW`, which admits every authenticated
|
|
80
|
+
// Cognito user — a group gate that fails open.
|
|
81
|
+
//
|
|
82
|
+
// Confirmed by observation against a deployed API: a user in no groups read
|
|
83
|
+
// every `@aws_auth`-gated field and reached a gated mutation's handler. See
|
|
84
|
+
// docs/plans/appsync-group-authorization-unenforced.md.
|
|
85
|
+
let formatCognitoGroupsDirective = (groups: array<string>): string => {
|
|
86
|
+
let quoted = groups->Array.map(g => `"${g}"`)->Array.join(", ")
|
|
87
|
+
`@aws_cognito_user_pools(cognito_groups: [${quoted}])`
|
|
88
|
+
}
|
|
89
|
+
|
|
70
90
|
// Multi-auth directive for a field/type that must accept BOTH Cognito and IAM.
|
|
71
91
|
// `groups=Some([...])` preserves Cognito group gating; `groups=None` keeps it
|
|
72
92
|
// open to any authenticated Cognito user. `@aws_iam` admits the deploy-time
|
|
73
93
|
// SigV4 system caller.
|
|
74
94
|
let formatDualAuthDirective = (groups: option<array<string>>): string => {
|
|
75
95
|
let cognito = switch groups {
|
|
76
|
-
| Some(g) =>
|
|
77
|
-
let quoted = g->Array.map(x => `"${x}"`)->Array.join(", ")
|
|
78
|
-
`@aws_cognito_user_pools(cognito_groups: [${quoted}])`
|
|
96
|
+
| Some(g) => formatCognitoGroupsDirective(g)
|
|
79
97
|
| None => `@aws_cognito_user_pools`
|
|
80
98
|
}
|
|
81
99
|
`${cognito} @aws_iam`
|
|
82
100
|
}
|
|
83
101
|
|
|
84
|
-
// Injects
|
|
85
|
-
//
|
|
86
|
-
// deploy-time IAM
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
// the SigV4 system caller. Subscriptions are never IAM-marked.
|
|
102
|
+
// Injects the Cognito group gate on ALL mutation, query, and subscription fields
|
|
103
|
+
// in a fragment. `~iamFieldNames` opts the named mutation/query fields into
|
|
104
|
+
// deploy-time IAM as well, appending the `@aws_iam` arm. Subscriptions are never
|
|
105
|
+
// IAM-marked (the deploy caller does not subscribe) but still carry the Cognito
|
|
106
|
+
// gate — a subscription is a read.
|
|
90
107
|
let injectAwsAuthAll = (
|
|
91
108
|
fragment: Reventless.Plugin.apiSchemaFragment,
|
|
92
109
|
~group: string,
|
|
@@ -95,19 +112,18 @@ let injectAwsAuthAll = (
|
|
|
95
112
|
let parts = ReventlessCore.GraphQL_Stitcher.decode(fragment)
|
|
96
113
|
let isIam = (field: string): bool =>
|
|
97
114
|
iamFieldNames->Array.includes(ReventlessCore.GraphQL_Stitcher.extractLeadingName(field))
|
|
115
|
+
let cognitoOnly = formatCognitoGroupsDirective([group])
|
|
98
116
|
|
|
99
117
|
let augmentedMutations = parts.mutations->Array.map(field =>
|
|
100
118
|
isIam(field)
|
|
101
119
|
? `${field}\n ${formatDualAuthDirective(Some([group]))}`
|
|
102
|
-
: `${field}\n
|
|
120
|
+
: `${field}\n ${cognitoOnly}`
|
|
103
121
|
)
|
|
104
122
|
let augmentedQueries = parts.queries->Array.map(field =>
|
|
105
|
-
isIam(field)
|
|
106
|
-
? `${field} ${formatDualAuthDirective(Some([group]))}`
|
|
107
|
-
: `${field} @aws_auth(cognito_groups: ["${group}"])`
|
|
123
|
+
isIam(field) ? `${field} ${formatDualAuthDirective(Some([group]))}` : `${field} ${cognitoOnly}`
|
|
108
124
|
)
|
|
109
125
|
let augmentedSubscriptions = parts.subscriptions->Array.map(field =>
|
|
110
|
-
`${field}\n
|
|
126
|
+
`${field}\n ${cognitoOnly}`
|
|
111
127
|
)
|
|
112
128
|
|
|
113
129
|
ReventlessCore.GraphQL_Stitcher.encode({
|
|
@@ -37,35 +37,35 @@ function injectAwsSubscribe(sdl, sources) {
|
|
|
37
37
|
return before + decorated + after;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function formatCognitoGroupsDirective(groups) {
|
|
41
|
+
let quoted = groups.map(g => `"` + g + `"`).join(", ");
|
|
42
|
+
return `@aws_cognito_user_pools(cognito_groups: [` + quoted + `])`;
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
function formatDualAuthDirective(groups) {
|
|
41
|
-
let cognito
|
|
42
|
-
if (groups !== undefined) {
|
|
43
|
-
let quoted = groups.map(x => `"` + x + `"`).join(", ");
|
|
44
|
-
cognito = `@aws_cognito_user_pools(cognito_groups: [` + quoted + `])`;
|
|
45
|
-
} else {
|
|
46
|
-
cognito = `@aws_cognito_user_pools`;
|
|
47
|
-
}
|
|
46
|
+
let cognito = groups !== undefined ? formatCognitoGroupsDirective(groups) : `@aws_cognito_user_pools`;
|
|
48
47
|
return cognito + ` @aws_iam`;
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
function injectAwsAuthAll(fragment, group, iamFieldNamesOpt) {
|
|
52
51
|
let iamFieldNames = iamFieldNamesOpt !== undefined ? iamFieldNamesOpt : [];
|
|
53
52
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(fragment);
|
|
53
|
+
let cognitoOnly = formatCognitoGroupsDirective([group]);
|
|
54
54
|
let augmentedMutations = parts.mutations.map(field => {
|
|
55
55
|
if (iamFieldNames.includes(GraphQL_Stitcher$ReventlessCore.extractLeadingName(field))) {
|
|
56
56
|
return field + `\n ` + formatDualAuthDirective([group]);
|
|
57
57
|
} else {
|
|
58
|
-
return field + `\n
|
|
58
|
+
return field + `\n ` + cognitoOnly;
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
let augmentedQueries = parts.queries.map(field => {
|
|
62
62
|
if (iamFieldNames.includes(GraphQL_Stitcher$ReventlessCore.extractLeadingName(field))) {
|
|
63
63
|
return field + ` ` + formatDualAuthDirective([group]);
|
|
64
64
|
} else {
|
|
65
|
-
return field + `
|
|
65
|
+
return field + ` ` + cognitoOnly;
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
|
-
let augmentedSubscriptions = parts.subscriptions.map(field => field + `\n
|
|
68
|
+
let augmentedSubscriptions = parts.subscriptions.map(field => field + `\n ` + cognitoOnly);
|
|
69
69
|
return GraphQL_Stitcher$ReventlessCore.encode({
|
|
70
70
|
types: parts.types,
|
|
71
71
|
mutations: augmentedMutations,
|
|
@@ -125,6 +125,7 @@ function stampCanonicalTypes(sdl) {
|
|
|
125
125
|
|
|
126
126
|
export {
|
|
127
127
|
injectAwsSubscribe,
|
|
128
|
+
formatCognitoGroupsDirective,
|
|
128
129
|
formatDualAuthDirective,
|
|
129
130
|
injectAwsAuthAll,
|
|
130
131
|
sharedIamTypeNames,
|
|
@@ -25,19 +25,19 @@ let decodeFragment = (fragment: Reventless.Plugin.apiSchemaFragment) =>
|
|
|
25
25
|
describe("AppSync_Adapter.injectAwsAuthAll", () => {
|
|
26
26
|
let baseFragment = ReventlessCore.Platform_AdminApi.baseFragment(~cloner=true)
|
|
27
27
|
|
|
28
|
-
testSync("adds
|
|
28
|
+
testSync("adds the Cognito group directive to all mutation fields", () => {
|
|
29
29
|
let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
|
|
30
30
|
let parts = decodeFragment(augmented)
|
|
31
31
|
parts.mutations->Array.forEach(field => {
|
|
32
|
-
expect(field)->toContain(`@
|
|
32
|
+
expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
33
33
|
})
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
testSync("adds
|
|
36
|
+
testSync("adds the Cognito group directive to all query fields", () => {
|
|
37
37
|
let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
|
|
38
38
|
let parts = decodeFragment(augmented)
|
|
39
39
|
parts.queries->Array.forEach(field => {
|
|
40
|
-
expect(field)->toContain(`@
|
|
40
|
+
expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
41
41
|
})
|
|
42
42
|
})
|
|
43
43
|
|
|
@@ -61,15 +61,15 @@ describe("AppSync_Adapter.injectAwsAuthAll", () => {
|
|
|
61
61
|
// admin base. That caused AppSync to reject the auto-generated resolver for
|
|
62
62
|
// `Subscription.onPlatform_Plugin_Activate` (and Deactivate) with "Type not
|
|
63
63
|
// found" at deploy time. The subscription fields must survive the round-trip
|
|
64
|
-
// and pick up the same
|
|
65
|
-
testSync("preserves subscription fields with
|
|
64
|
+
// and pick up the same Cognito group directive as mutations/queries.
|
|
65
|
+
testSync("preserves subscription fields with the Cognito group directive", () => {
|
|
66
66
|
let original = decodeFragment(baseFragment)
|
|
67
67
|
let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
|
|
68
68
|
let parts = decodeFragment(augmented)
|
|
69
69
|
expect(parts.subscriptions->Array.length)->toBe(original.subscriptions->Array.length)
|
|
70
70
|
expect(parts.subscriptions->Array.length)->toBeGreaterThan(0)
|
|
71
71
|
parts.subscriptions->Array.forEach(field => {
|
|
72
|
-
expect(field)->toContain(`@
|
|
72
|
+
expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
73
73
|
})
|
|
74
74
|
})
|
|
75
75
|
|
|
@@ -80,6 +80,21 @@ describe("AppSync_Adapter.injectAwsAuthAll", () => {
|
|
|
80
80
|
expect(joined)->toContain("onPlatform_Plugin_Activate")
|
|
81
81
|
expect(joined)->toContain("onPlatform_Plugin_Deactivate")
|
|
82
82
|
})
|
|
83
|
+
|
|
84
|
+
// Regression: the admin base was stamped with the single-mode
|
|
85
|
+
// `@aws_auth(cognito_groups: [...])`, which AppSync IGNORES on a multi-auth API
|
|
86
|
+
// — and every API this adapter provisions is multi-auth. A deployed probe found
|
|
87
|
+
// a Cognito user in no groups reading every admin field. The directive must be
|
|
88
|
+
// `@aws_cognito_user_pools(...)`, the form the service actually enforces.
|
|
89
|
+
// See docs/plans/appsync-group-authorization-unenforced.md.
|
|
90
|
+
testSync("never emits the single-mode @aws_auth form on any field", () => {
|
|
91
|
+
let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
|
|
92
|
+
let parts = decodeFragment(augmented)
|
|
93
|
+
let everyField =
|
|
94
|
+
Array.concatMany(parts.mutations, [parts.queries, parts.subscriptions])->Array.join("\n")
|
|
95
|
+
expect(everyField)->not_->toContain("@aws_auth")
|
|
96
|
+
expect(everyField)->toContain("@aws_cognito_user_pools")
|
|
97
|
+
})
|
|
83
98
|
})
|
|
84
99
|
|
|
85
100
|
describe("AppSync_Adapter.injectAwsAuth", () => {
|
|
@@ -97,12 +112,12 @@ describe("AppSync_Adapter.injectAwsAuth", () => {
|
|
|
97
112
|
)
|
|
98
113
|
let parts = decodeFragment(augmented)
|
|
99
114
|
|
|
100
|
-
// Query entries have authorization → should
|
|
115
|
+
// Query entries have authorization → should carry the directive
|
|
101
116
|
let queryWithAuth =
|
|
102
117
|
queryEntries->Array.some(entry => entry.authorization->Option.isSome)
|
|
103
118
|
if queryWithAuth {
|
|
104
119
|
let hasAuthQuery = parts.queries->Array.some(field =>
|
|
105
|
-
field->String.includes("@
|
|
120
|
+
field->String.includes("@aws_cognito_user_pools")
|
|
106
121
|
)
|
|
107
122
|
expect(hasAuthQuery)->toBe(true)
|
|
108
123
|
}
|
|
@@ -124,11 +139,11 @@ describe("AppSync_Adapter.generateFragment", () => {
|
|
|
124
139
|
})
|
|
125
140
|
})
|
|
126
141
|
|
|
127
|
-
// ── Stage E2: spec-level permission →
|
|
142
|
+
// ── Stage E2: spec-level permission → Cognito group directive ───────────────
|
|
128
143
|
//
|
|
129
144
|
// Verifies that `injectAwsAuth` lifts the `Authorization.permission` values
|
|
130
145
|
// (set by Plugin_Builder / Dcb_Builder from `@@reventless.authorize` /
|
|
131
|
-
// `@authorize` PPX annotations) into `@
|
|
146
|
+
// `@authorize` PPX annotations) into `@aws_cognito_user_pools(cognito_groups: [...])`
|
|
132
147
|
// directives on the corresponding SDL fields.
|
|
133
148
|
|
|
134
149
|
describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () => {
|
|
@@ -182,7 +197,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
|
|
|
182
197
|
)
|
|
183
198
|
let parts = decodeFragment(aug)
|
|
184
199
|
let m = parts.mutations->Array.getUnsafe(0)
|
|
185
|
-
expect(m)->toContain(`@
|
|
200
|
+
expect(m)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
186
201
|
})
|
|
187
202
|
|
|
188
203
|
testSync("AllowGroups multi-group emits comma-separated groups", () => {
|
|
@@ -199,7 +214,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
|
|
|
199
214
|
)
|
|
200
215
|
let parts = decodeFragment(aug)
|
|
201
216
|
expect(parts.mutations->Array.getUnsafe(0))->toContain(
|
|
202
|
-
`@
|
|
217
|
+
`@aws_cognito_user_pools(cognito_groups: ["Admin", "Editor"])`,
|
|
203
218
|
)
|
|
204
219
|
})
|
|
205
220
|
|
|
@@ -216,7 +231,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
|
|
|
216
231
|
~queryEntries=[],
|
|
217
232
|
)
|
|
218
233
|
let parts = decodeFragment(aug)
|
|
219
|
-
expect(parts.mutations->Array.getUnsafe(0))->not_->toContain("@
|
|
234
|
+
expect(parts.mutations->Array.getUnsafe(0))->not_->toContain("@aws_cognito_user_pools")
|
|
220
235
|
})
|
|
221
236
|
|
|
222
237
|
testSync("DenyAll emits sentinel __deny_all__ group", () => {
|
|
@@ -258,7 +273,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
|
|
|
258
273
|
| None => JsError.throwWithMessage("missing archive field")
|
|
259
274
|
}
|
|
260
275
|
switch add {
|
|
261
|
-
| Some(f) => expect(f)->not_->toContain("@
|
|
276
|
+
| Some(f) => expect(f)->not_->toContain("@aws_cognito_user_pools")
|
|
262
277
|
| None => JsError.throwWithMessage("missing add field")
|
|
263
278
|
}
|
|
264
279
|
})
|
|
@@ -344,7 +359,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
|
|
|
344
359
|
~queryEntries=[],
|
|
345
360
|
)
|
|
346
361
|
let parts = decodeFragment(aug)
|
|
347
|
-
expect(parts.mutations->Array.getUnsafe(0))->not_->toContain("@
|
|
362
|
+
expect(parts.mutations->Array.getUnsafe(0))->not_->toContain("@aws_cognito_user_pools")
|
|
348
363
|
})
|
|
349
364
|
})
|
|
350
365
|
|
|
@@ -397,7 +412,7 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
|
|
|
397
412
|
expect(m)->not_->toContain("@aws_auth")
|
|
398
413
|
})
|
|
399
414
|
|
|
400
|
-
testSync("a non-systemCallable sibling
|
|
415
|
+
testSync("a non-systemCallable sibling stays Cognito-only (no @aws_iam)", () => {
|
|
401
416
|
let entry: ReventlessInfra.Api.mutationSchemaEntry = {
|
|
402
417
|
fieldNames: ["p_Sync", "p_Other"],
|
|
403
418
|
commandSchema: S.unknown,
|
|
@@ -420,7 +435,7 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
|
|
|
420
435
|
let parts = decodeFragment(aug)
|
|
421
436
|
switch parts.mutations->Array.find(f => f->String.includes("p_Other")) {
|
|
422
437
|
| Some(other) =>
|
|
423
|
-
expect(other)->toContain(`@
|
|
438
|
+
expect(other)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
424
439
|
expect(other)->not_->toContain("@aws_iam")
|
|
425
440
|
| None => JsError.throwWithMessage("missing p_Other field")
|
|
426
441
|
}
|
|
@@ -588,13 +603,13 @@ describe("AppSync_Adapter.injectAwsAuthAll — ~iamFieldNames", () => {
|
|
|
588
603
|
expect(field)->toContain("@aws_iam")
|
|
589
604
|
expect(field)->not_->toContain("@aws_auth")
|
|
590
605
|
} else {
|
|
591
|
-
expect(field)->toContain(`@
|
|
606
|
+
expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
592
607
|
expect(field)->not_->toContain("@aws_iam")
|
|
593
608
|
}
|
|
594
609
|
})
|
|
595
610
|
})
|
|
596
611
|
|
|
597
|
-
testSync("default (no ~iamFieldNames) leaves every field
|
|
612
|
+
testSync("default (no ~iamFieldNames) leaves every field Cognito-only", () => {
|
|
598
613
|
let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
|
|
599
614
|
let parts = decodeFragment(augmented)
|
|
600
615
|
parts.mutations->Array.forEach(field => expect(field)->not_->toContain("@aws_iam"))
|
|
@@ -29,18 +29,18 @@ let decodeFragment = GraphQL_Stitcher$ReventlessCore.decode;
|
|
|
29
29
|
|
|
30
30
|
globalThis.describe("AppSync_Adapter.injectAwsAuthAll", () => {
|
|
31
31
|
let baseFragment = Platform_AdminApi$ReventlessCore.baseFragment(true);
|
|
32
|
-
globalThis.test("adds
|
|
32
|
+
globalThis.test("adds the Cognito group directive to all mutation fields", () => {
|
|
33
33
|
let augmented = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(baseFragment, "Admin", undefined);
|
|
34
34
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(augmented);
|
|
35
35
|
parts.mutations.forEach(field => {
|
|
36
|
-
globalThis.expect(field).toContain(`@
|
|
36
|
+
globalThis.expect(field).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
|
-
globalThis.test("adds
|
|
39
|
+
globalThis.test("adds the Cognito group directive to all query fields", () => {
|
|
40
40
|
let augmented = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(baseFragment, "Admin", undefined);
|
|
41
41
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(augmented);
|
|
42
42
|
parts.queries.forEach(field => {
|
|
43
|
-
globalThis.expect(field).toContain(`@
|
|
43
|
+
globalThis.expect(field).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
globalThis.test("preserves type definitions unchanged", () => {
|
|
@@ -56,14 +56,14 @@ globalThis.describe("AppSync_Adapter.injectAwsAuthAll", () => {
|
|
|
56
56
|
globalThis.expect(field).toContain(`cognito_groups: ["SuperAdmin"]`);
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
|
-
globalThis.test("preserves subscription fields with
|
|
59
|
+
globalThis.test("preserves subscription fields with the Cognito group directive", () => {
|
|
60
60
|
let original = GraphQL_Stitcher$ReventlessCore.decode(baseFragment);
|
|
61
61
|
let augmented = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(baseFragment, "Admin", undefined);
|
|
62
62
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(augmented);
|
|
63
63
|
globalThis.expect(parts.subscriptions.length).toBe(original.subscriptions.length);
|
|
64
64
|
globalThis.expect(parts.subscriptions.length).toBeGreaterThan(0);
|
|
65
65
|
parts.subscriptions.forEach(field => {
|
|
66
|
-
globalThis.expect(field).toContain(`@
|
|
66
|
+
globalThis.expect(field).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
globalThis.test("admin Plugin aggregate subscription fields survive the round-trip", () => {
|
|
@@ -73,6 +73,13 @@ globalThis.describe("AppSync_Adapter.injectAwsAuthAll", () => {
|
|
|
73
73
|
globalThis.expect(joined).toContain("onPlatform_Plugin_Activate");
|
|
74
74
|
globalThis.expect(joined).toContain("onPlatform_Plugin_Deactivate");
|
|
75
75
|
});
|
|
76
|
+
globalThis.test("never emits the single-mode @aws_auth form on any field", () => {
|
|
77
|
+
let augmented = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(baseFragment, "Admin", undefined);
|
|
78
|
+
let parts = GraphQL_Stitcher$ReventlessCore.decode(augmented);
|
|
79
|
+
let everyField = parts.mutations.concat(parts.queries, parts.subscriptions).join("\n");
|
|
80
|
+
globalThis.expect(everyField).not.toContain("@aws_auth");
|
|
81
|
+
globalThis.expect(everyField).toContain("@aws_cognito_user_pools");
|
|
82
|
+
});
|
|
76
83
|
});
|
|
77
84
|
|
|
78
85
|
globalThis.describe("AppSync_Adapter.injectAwsAuth", () => {
|
|
@@ -85,7 +92,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth", () => {
|
|
|
85
92
|
if (!queryWithAuth) {
|
|
86
93
|
return;
|
|
87
94
|
}
|
|
88
|
-
let hasAuthQuery = parts.queries.some(field => field.includes("@
|
|
95
|
+
let hasAuthQuery = parts.queries.some(field => field.includes("@aws_cognito_user_pools"));
|
|
89
96
|
globalThis.expect(hasAuthQuery).toBe(true);
|
|
90
97
|
});
|
|
91
98
|
});
|
|
@@ -135,7 +142,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
|
|
|
135
142
|
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [entry], []);
|
|
136
143
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
137
144
|
let m = parts.mutations[0];
|
|
138
|
-
globalThis.expect(m).toContain(`@
|
|
145
|
+
globalThis.expect(m).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
139
146
|
});
|
|
140
147
|
globalThis.test("AllowGroups multi-group emits comma-separated groups", () => {
|
|
141
148
|
let fp = Object.fromEntries([[
|
|
@@ -152,7 +159,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
|
|
|
152
159
|
let frag = makeFragment(["p_X(id: ID!): String"], []);
|
|
153
160
|
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [entry], []);
|
|
154
161
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
155
|
-
globalThis.expect(parts.mutations[0]).toContain(`@
|
|
162
|
+
globalThis.expect(parts.mutations[0]).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin", "Editor"])`);
|
|
156
163
|
});
|
|
157
164
|
globalThis.test("AllowAuthenticated emits no directive", () => {
|
|
158
165
|
let fp = Object.fromEntries([[
|
|
@@ -163,7 +170,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
|
|
|
163
170
|
let frag = makeFragment(["p_Add(id: ID!): String"], []);
|
|
164
171
|
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [entry], []);
|
|
165
172
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
166
|
-
globalThis.expect(parts.mutations[0]).not.toContain("@
|
|
173
|
+
globalThis.expect(parts.mutations[0]).not.toContain("@aws_cognito_user_pools");
|
|
167
174
|
});
|
|
168
175
|
globalThis.test("DenyAll emits sentinel __deny_all__ group", () => {
|
|
169
176
|
let fp = Object.fromEntries([[
|
|
@@ -208,7 +215,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
|
|
|
208
215
|
Stdlib_JsError.throwWithMessage("missing archive field");
|
|
209
216
|
}
|
|
210
217
|
if (add !== undefined) {
|
|
211
|
-
globalThis.expect(add).not.toContain("@
|
|
218
|
+
globalThis.expect(add).not.toContain("@aws_cognito_user_pools");
|
|
212
219
|
return;
|
|
213
220
|
} else {
|
|
214
221
|
return Stdlib_JsError.throwWithMessage("missing add field");
|
|
@@ -282,7 +289,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
|
|
|
282
289
|
let frag = makeFragment(["p_X(id: ID!): String"], []);
|
|
283
290
|
let aug = AppSync_Adapter$ReventlessAws.injectAwsAuth(frag, [entry], []);
|
|
284
291
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
285
|
-
globalThis.expect(parts.mutations[0]).not.toContain("@
|
|
292
|
+
globalThis.expect(parts.mutations[0]).not.toContain("@aws_cognito_user_pools");
|
|
286
293
|
});
|
|
287
294
|
});
|
|
288
295
|
|
|
@@ -337,7 +344,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
|
|
|
337
344
|
globalThis.expect(m).not.toContain("cognito_groups");
|
|
338
345
|
globalThis.expect(m).not.toContain("@aws_auth");
|
|
339
346
|
});
|
|
340
|
-
globalThis.test("a non-systemCallable sibling
|
|
347
|
+
globalThis.test("a non-systemCallable sibling stays Cognito-only (no @aws_iam)", () => {
|
|
341
348
|
let entry_fieldNames = [
|
|
342
349
|
"p_Sync",
|
|
343
350
|
"p_Other"
|
|
@@ -391,7 +398,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
|
|
|
391
398
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(aug);
|
|
392
399
|
let other = parts.mutations.find(f => f.includes("p_Other"));
|
|
393
400
|
if (other !== undefined) {
|
|
394
|
-
globalThis.expect(other).toContain(`@
|
|
401
|
+
globalThis.expect(other).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
395
402
|
globalThis.expect(other).not.toContain("@aws_iam");
|
|
396
403
|
return;
|
|
397
404
|
} else {
|
|
@@ -548,12 +555,12 @@ globalThis.describe("AppSync_Adapter.injectAwsAuthAll — ~iamFieldNames", () =>
|
|
|
548
555
|
globalThis.expect(field).toContain("@aws_iam");
|
|
549
556
|
globalThis.expect(field).not.toContain("@aws_auth");
|
|
550
557
|
} else {
|
|
551
|
-
globalThis.expect(field).toContain(`@
|
|
558
|
+
globalThis.expect(field).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
552
559
|
globalThis.expect(field).not.toContain("@aws_iam");
|
|
553
560
|
}
|
|
554
561
|
});
|
|
555
562
|
});
|
|
556
|
-
globalThis.test("default (no ~iamFieldNames) leaves every field
|
|
563
|
+
globalThis.test("default (no ~iamFieldNames) leaves every field Cognito-only", () => {
|
|
557
564
|
let augmented = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(baseFragment, "Admin", undefined);
|
|
558
565
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(augmented);
|
|
559
566
|
parts.mutations.forEach(field => {
|
|
@@ -31,7 +31,7 @@ type Mutation {
|
|
|
31
31
|
type Subscription {
|
|
32
32
|
onCatalog_AddProduct(id: ID): CommandResult
|
|
33
33
|
onUIFragmentChange: UIFragmentChangeEvent
|
|
34
|
-
@
|
|
34
|
+
@aws_cognito_user_pools(cognito_groups: ["Admin"])
|
|
35
35
|
onCatalogEventLog_eventAppended: CatalogEventLogEvent
|
|
36
36
|
}`
|
|
37
37
|
|
|
@@ -43,13 +43,13 @@ describe("AppSync_SdlDecorate.injectAwsSubscribe", () => {
|
|
|
43
43
|
)
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
testSync("appends the many-mutations fan-in on a no-arg field, keeping
|
|
46
|
+
testSync("appends the many-mutations fan-in on a no-arg field, keeping the group directive", () => {
|
|
47
47
|
let sdl = AppSync_SdlDecorate.injectAwsSubscribe(stitchedSdl, ~sources)
|
|
48
48
|
expect(sdl)->toContain(
|
|
49
49
|
`onUIFragmentChange: UIFragmentChangeEvent\n @aws_subscribe(mutations: ["Platform_UIFragmentRegistered", "Platform_UIFragmentUpdated", "Platform_UIFragmentDeregistered"])`,
|
|
50
50
|
)
|
|
51
51
|
// The auth directive line appended by injectAwsAuthAll survives.
|
|
52
|
-
expect(sdl)->toContain(`@
|
|
52
|
+
expect(sdl)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
53
53
|
})
|
|
54
54
|
|
|
55
55
|
testSync("leaves unmapped fields (Source A) and non-Subscription blocks untouched", () => {
|
|
@@ -113,7 +113,7 @@ describe("AppSync_SdlDecorate.injectAwsAuthAll", () => {
|
|
|
113
113
|
expect(parts.mutations->Array.getUnsafe(0))->toContain("@aws_iam")
|
|
114
114
|
// A query NOT in iamFieldNames stays Cognito-only.
|
|
115
115
|
let other = queryField("Other_Query")
|
|
116
|
-
expect(other)->toContain(`@
|
|
116
|
+
expect(other)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
|
|
117
117
|
expect(other->String.includes("@aws_iam"))->toBe(false)
|
|
118
118
|
})
|
|
119
119
|
})
|
|
@@ -35,7 +35,7 @@ type Mutation {
|
|
|
35
35
|
type Subscription {
|
|
36
36
|
onCatalog_AddProduct(id: ID): CommandResult
|
|
37
37
|
onUIFragmentChange: UIFragmentChangeEvent
|
|
38
|
-
@
|
|
38
|
+
@aws_cognito_user_pools(cognito_groups: ["Admin"])
|
|
39
39
|
onCatalogEventLog_eventAppended: CatalogEventLogEvent
|
|
40
40
|
}`;
|
|
41
41
|
|
|
@@ -44,10 +44,10 @@ globalThis.describe("AppSync_SdlDecorate.injectAwsSubscribe", () => {
|
|
|
44
44
|
let sdl = AppSync_SdlDecorate$ReventlessAws.injectAwsSubscribe(stitchedSdl, sources);
|
|
45
45
|
globalThis.expect(sdl).toContain(`onCatalog_AddProduct(id: ID): CommandResult\n @aws_subscribe(mutations: ["Catalog_AddProduct"])`);
|
|
46
46
|
});
|
|
47
|
-
globalThis.test("appends the many-mutations fan-in on a no-arg field, keeping
|
|
47
|
+
globalThis.test("appends the many-mutations fan-in on a no-arg field, keeping the group directive", () => {
|
|
48
48
|
let sdl = AppSync_SdlDecorate$ReventlessAws.injectAwsSubscribe(stitchedSdl, sources);
|
|
49
49
|
globalThis.expect(sdl).toContain(`onUIFragmentChange: UIFragmentChangeEvent\n @aws_subscribe(mutations: ["Platform_UIFragmentRegistered", "Platform_UIFragmentUpdated", "Platform_UIFragmentDeregistered"])`);
|
|
50
|
-
globalThis.expect(sdl).toContain(`@
|
|
50
|
+
globalThis.expect(sdl).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
51
51
|
});
|
|
52
52
|
globalThis.test("leaves unmapped fields (Source A) and non-Subscription blocks untouched", () => {
|
|
53
53
|
let sdl = AppSync_SdlDecorate$ReventlessAws.injectAwsSubscribe(stitchedSdl, sources);
|
|
@@ -100,7 +100,7 @@ globalThis.describe("AppSync_SdlDecorate.injectAwsAuthAll", () => {
|
|
|
100
100
|
globalThis.expect(queryField("Platform_ApiFragments")).toContain("@aws_iam");
|
|
101
101
|
globalThis.expect(parts.mutations[0]).toContain("@aws_iam");
|
|
102
102
|
let other = queryField("Other_Query");
|
|
103
|
-
globalThis.expect(other).toContain(`@
|
|
103
|
+
globalThis.expect(other).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`);
|
|
104
104
|
globalThis.expect(other.includes("@aws_iam")).toBe(false);
|
|
105
105
|
});
|
|
106
106
|
});
|