@reventlessdev/reventless-aws 3.0.0-alpha.292 → 3.0.0-alpha.294

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.
@@ -0,0 +1,341 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
4
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
+ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
9
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
10
+ import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
11
+ import * as IndexJs from "@pulumi/pulumi/dynamic/index.js";
12
+
13
+ let log = Logger$ReventlessCore.fromEnv();
14
+
15
+ let readOnlyKeys = [
16
+ "Id",
17
+ "Name",
18
+ "Arn",
19
+ "Status",
20
+ "CreationDate",
21
+ "LastModifiedDate",
22
+ "SchemaAttributes",
23
+ "AliasAttributes",
24
+ "UsernameAttributes",
25
+ "UsernameConfiguration",
26
+ "EstimatedNumberOfUsers",
27
+ "Domain",
28
+ "CustomDomain",
29
+ "SmsConfigurationFailure",
30
+ "EmailConfigurationFailure"
31
+ ];
32
+
33
+ function mergedUpdateInput(described, userPoolId, preTokenGenerationArn) {
34
+ let out = {};
35
+ Stdlib_Dict.forEachWithKey(described, (value, key) => {
36
+ if (!readOnlyKeys.includes(key)) {
37
+ out[key] = value;
38
+ return;
39
+ }
40
+ });
41
+ Stdlib_Option.forEach(described["Name"], name => {
42
+ out["PoolName"] = name;
43
+ });
44
+ out["UserPoolId"] = userPoolId;
45
+ let lambdaConfig = Stdlib_Option.mapOr(Stdlib_Option.flatMap(described["LambdaConfig"], Stdlib_JSON.Decode.object), {}, existing => Object.fromEntries(Object.entries(existing)));
46
+ if (preTokenGenerationArn !== undefined) {
47
+ lambdaConfig["PreTokenGeneration"] = preTokenGenerationArn;
48
+ } else {
49
+ Stdlib_Dict.$$delete(lambdaConfig, "PreTokenGeneration");
50
+ }
51
+ out["LambdaConfig"] = lambdaConfig;
52
+ return out;
53
+ }
54
+
55
+ function attachedTrigger(described) {
56
+ return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(described["LambdaConfig"], Stdlib_JSON.Decode.object), c => c["PreTokenGeneration"]), Stdlib_JSON.Decode.string);
57
+ }
58
+
59
+ let newOf1 = ((C, x) => new C(x));
60
+
61
+ let newOf0 = ((C) => new C());
62
+
63
+ let _sdk = {
64
+ contents: undefined
65
+ };
66
+
67
+ let _client = {
68
+ contents: undefined
69
+ };
70
+
71
+ async function getSdk() {
72
+ let m = _sdk.contents;
73
+ if (m !== undefined) {
74
+ return m;
75
+ }
76
+ let m$1 = await import("@aws-sdk/client-cognito-identity-provider");
77
+ _sdk.contents = m$1;
78
+ return m$1;
79
+ }
80
+
81
+ async function getClient() {
82
+ let c = _client.contents;
83
+ if (c !== undefined) {
84
+ return Primitive_option.valFromOption(c);
85
+ }
86
+ let sdk = await getSdk();
87
+ let c$1 = newOf0(sdk.CognitoIdentityProviderClient);
88
+ _client.contents = Primitive_option.some(c$1);
89
+ return c$1;
90
+ }
91
+
92
+ let _lambdaSdk = {
93
+ contents: undefined
94
+ };
95
+
96
+ let _lambdaClient = {
97
+ contents: undefined
98
+ };
99
+
100
+ async function getLambdaSdk() {
101
+ let m = _lambdaSdk.contents;
102
+ if (m !== undefined) {
103
+ return m;
104
+ }
105
+ let m$1 = await import("@aws-sdk/client-lambda");
106
+ _lambdaSdk.contents = m$1;
107
+ return m$1;
108
+ }
109
+
110
+ async function getLambdaClient() {
111
+ let c = _lambdaClient.contents;
112
+ if (c !== undefined) {
113
+ return Primitive_option.valFromOption(c);
114
+ }
115
+ let sdk = await getLambdaSdk();
116
+ let c$1 = newOf0(sdk.LambdaClient);
117
+ _lambdaClient.contents = Primitive_option.some(c$1);
118
+ return c$1;
119
+ }
120
+
121
+ let decodePayload = ((p) => {
122
+ if (p == null) return "";
123
+ if (typeof p === "string") return p;
124
+ try { return new TextDecoder().decode(p); } catch { return String(p); }
125
+ });
126
+
127
+ function isPoolGoneError(jsErr) {
128
+ let match = jsErr.name;
129
+ let match$1 = Stdlib_JsExn.message(jsErr);
130
+ if (match == null) {
131
+ if (match$1 !== undefined) {
132
+ return match$1.includes("ResourceNotFoundException");
133
+ } else {
134
+ return false;
135
+ }
136
+ } else if (match === "ResourceNotFoundException") {
137
+ return true;
138
+ } else if (match$1 !== undefined) {
139
+ return match$1.includes("ResourceNotFoundException");
140
+ } else {
141
+ return false;
142
+ }
143
+ }
144
+
145
+ let probeSubject = "reventless-attachment-probe";
146
+
147
+ function probeEvent(userPoolId) {
148
+ let groupConfiguration = {};
149
+ groupConfiguration["groupsToOverride"] = [];
150
+ groupConfiguration["iamRolesToOverride"] = [];
151
+ let userAttributes = {};
152
+ userAttributes["sub"] = probeSubject;
153
+ let request = {};
154
+ request["userAttributes"] = userAttributes;
155
+ request["groupConfiguration"] = groupConfiguration;
156
+ let event = {};
157
+ event["version"] = "1";
158
+ event["triggerSource"] = "TokenGeneration_Authentication";
159
+ event["userPoolId"] = userPoolId;
160
+ event["userName"] = probeSubject;
161
+ event["request"] = request;
162
+ event["response"] = {};
163
+ return event;
164
+ }
165
+
166
+ function probeVerdict(functionError, payload) {
167
+ if (functionError !== undefined) {
168
+ return {
169
+ TAG: "Crashed",
170
+ _0: functionError
171
+ };
172
+ }
173
+ let hasRequest;
174
+ try {
175
+ hasRequest = Stdlib_Option.isSome(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(payload)), o => o["request"]));
176
+ } catch (exn) {
177
+ hasRequest = false;
178
+ }
179
+ if (hasRequest) {
180
+ return "Healthy";
181
+ } else {
182
+ return "NotAnEvent";
183
+ }
184
+ }
185
+
186
+ async function verifyTrigger(userPoolId, preTokenGenerationArn) {
187
+ let sdk = await getLambdaSdk();
188
+ let client = await getLambdaClient();
189
+ let input = {};
190
+ input["FunctionName"] = preTokenGenerationArn;
191
+ input["InvocationType"] = "RequestResponse";
192
+ input["Payload"] = JSON.stringify(probeEvent(userPoolId));
193
+ let result = await client.send(newOf1(sdk.InvokeCommand, input));
194
+ let payload = Stdlib_Option.mapOr(result.Payload, "", decodePayload);
195
+ let kind = probeVerdict(result.FunctionError, payload);
196
+ if (typeof kind !== "object") {
197
+ if (kind === "Healthy") {
198
+ return log.info("Auth_ActiveRolePoolAttachment", undefined, `trigger ` + preTokenGenerationArn + ` answered the pre-attach check; attaching to ` + userPoolId);
199
+ } else {
200
+ return Stdlib_JsError.throwWithMessage(`active-role trigger ` + preTokenGenerationArn + ` answered its pre-attach check with something that is not a pre-token-generation event, so it was NOT attached to user pool ` + userPoolId + `. Cognito would have failed every sign-in. It returned: ` + payload);
201
+ }
202
+ } else {
203
+ return Stdlib_JsError.throwWithMessage(`active-role trigger ` + preTokenGenerationArn + ` failed its pre-attach check (` + kind._0 + `) and was NOT attached to user pool ` + userPoolId + `. Attaching it would have failed every sign-in on that pool. The function reported: ` + payload);
204
+ }
205
+ }
206
+
207
+ async function describePool(userPoolId) {
208
+ let sdk = await getSdk();
209
+ let client = await getClient();
210
+ let result = await client.send(newOf1(sdk.DescribeUserPoolCommand, {
211
+ UserPoolId: userPoolId
212
+ }));
213
+ return Stdlib_Option.flatMap(result.UserPool, Stdlib_JSON.Decode.object);
214
+ }
215
+
216
+ async function applyTrigger(userPoolId, preTokenGenerationArn) {
217
+ let sdk = await getSdk();
218
+ let client = await getClient();
219
+ let described = await describePool(userPoolId);
220
+ if (described === undefined) {
221
+ return Stdlib_JsError.throwWithMessage(`DescribeUserPool returned no pool for "` + userPoolId + `"; refusing to send an UpdateUserPool that would reset it`);
222
+ }
223
+ let input = mergedUpdateInput(described, userPoolId, preTokenGenerationArn);
224
+ return await client.send(newOf1(sdk.UpdateUserPoolCommand, input));
225
+ }
226
+
227
+ async function create(inputs) {
228
+ await verifyTrigger(inputs.userPoolId, inputs.preTokenGenerationArn);
229
+ await applyTrigger(inputs.userPoolId, inputs.preTokenGenerationArn);
230
+ return {
231
+ id: inputs.userPoolId,
232
+ outs: {
233
+ userPoolId: inputs.userPoolId,
234
+ preTokenGenerationArn: inputs.preTokenGenerationArn,
235
+ codeHash: inputs.codeHash
236
+ }
237
+ };
238
+ }
239
+
240
+ async function update(_id, _olds, news) {
241
+ await verifyTrigger(news.userPoolId, news.preTokenGenerationArn);
242
+ await applyTrigger(news.userPoolId, news.preTokenGenerationArn);
243
+ return {
244
+ outs: {
245
+ userPoolId: news.userPoolId,
246
+ preTokenGenerationArn: news.preTokenGenerationArn,
247
+ codeHash: news.codeHash
248
+ }
249
+ };
250
+ }
251
+
252
+ async function delete_(_id, props) {
253
+ try {
254
+ return await applyTrigger(props.userPoolId, undefined);
255
+ } catch (raw_exn) {
256
+ let exn = Primitive_exceptions.internalToException(raw_exn);
257
+ if (Stdlib_Option.mapOr(Stdlib_JsExn.fromException(exn), false, isPoolGoneError)) {
258
+ return log.info("Auth_ActiveRolePoolAttachment", undefined, `user pool ` + props.userPoolId + ` is gone; nothing to detach`);
259
+ }
260
+ throw exn;
261
+ }
262
+ }
263
+
264
+ function diff_(_id, olds, news) {
265
+ let poolChanged = olds.userPoolId !== news.userPoolId;
266
+ return {
267
+ changes: poolChanged || olds.preTokenGenerationArn !== news.preTokenGenerationArn || olds.codeHash !== news.codeHash,
268
+ replaces: poolChanged ? ["userPoolId"] : [],
269
+ deleteBeforeReplace: true
270
+ };
271
+ }
272
+
273
+ async function read_(id, props) {
274
+ try {
275
+ let described = await describePool(props.userPoolId);
276
+ if (described !== undefined) {
277
+ return {
278
+ id: id,
279
+ props: {
280
+ userPoolId: props.userPoolId,
281
+ preTokenGenerationArn: Stdlib_Option.getOr(attachedTrigger(described), ""),
282
+ codeHash: props.codeHash
283
+ }
284
+ };
285
+ } else {
286
+ return {};
287
+ }
288
+ } catch (raw_exn) {
289
+ let exn = Primitive_exceptions.internalToException(raw_exn);
290
+ if (Stdlib_Option.mapOr(Stdlib_JsExn.fromException(exn), false, isPoolGoneError)) {
291
+ return {};
292
+ }
293
+ throw exn;
294
+ }
295
+ }
296
+
297
+ let provider = {
298
+ create: create,
299
+ update: update,
300
+ delete: delete_,
301
+ diff: diff_,
302
+ read: read_
303
+ };
304
+
305
+ function make(nameOpt, props, opts) {
306
+ let name = nameOpt !== undefined ? nameOpt : "ActiveRolePoolAttachment";
307
+ return new IndexJs.Resource(provider, name, props, opts);
308
+ }
309
+
310
+ export {
311
+ log,
312
+ readOnlyKeys,
313
+ mergedUpdateInput,
314
+ attachedTrigger,
315
+ newOf1,
316
+ newOf0,
317
+ _sdk,
318
+ _client,
319
+ getSdk,
320
+ getClient,
321
+ _lambdaSdk,
322
+ _lambdaClient,
323
+ getLambdaSdk,
324
+ getLambdaClient,
325
+ decodePayload,
326
+ isPoolGoneError,
327
+ probeSubject,
328
+ probeEvent,
329
+ probeVerdict,
330
+ verifyTrigger,
331
+ describePool,
332
+ applyTrigger,
333
+ create,
334
+ update,
335
+ delete_,
336
+ diff_,
337
+ read_,
338
+ provider,
339
+ make,
340
+ }
341
+ /* log Not a pure module */
@@ -0,0 +1,280 @@
1
+ // The role a caller chose to act as, as server-side state — and the one door
2
+ // that writes it. See [docs/plans/active-role-narrows-the-token.md] §6.
3
+ //
4
+ // Why state rather than a parameter on the token request: Cognito "doesn't
5
+ // include data from the ClientMetadata parameter in AdminInitiateAuth and
6
+ // InitiateAuth API operations in the request that it passes to the pre token
7
+ // generation function", and a role switch is a `REFRESH_TOKEN_AUTH` flow — an
8
+ // `InitiateAuth` one. The channel that looks purpose-built for carrying the
9
+ // choice is closed on exactly the path that needs it, so the choice is stored
10
+ // here and the trigger reads it on the next refresh.
11
+ //
12
+ // Why not a `custom:activeRole` attribute on the user, which was the first
13
+ // design: a Cognito custom attribute is a one-way door — it can never be removed
14
+ // or retyped once added to a pool. A table avoids permanently amending a
15
+ // customer's user pool to hold a preference.
16
+ //
17
+ // Split in two because the pieces are needed at different points in the deploy
18
+ // graph, and the order is forced rather than chosen:
19
+ //
20
+ // makeTable the rows. Depends on nothing.
21
+ // ↓
22
+ // trigger needs the table to read from ([Auth_ActiveRoleTrigger.res])
23
+ // ↓
24
+ // user pool needs the trigger's ARN as `lambdaConfig.preTokenGeneration`
25
+ // ↓
26
+ // GraphQL API needs the pool as its authorizer
27
+ // ↓
28
+ // makeWriteDoor needs the API to hang a resolver on, and the table to write
29
+ //
30
+ // Provisioning the table with the write door instead would close that chain into
31
+ // a cycle, so the table is hoisted to where the pool is resolved.
32
+
33
+ open PulumiAws
34
+
35
+ type storeTable = {
36
+ name: Pulumi.Output.t<string>,
37
+ arn: Pulumi.Output.t<string>,
38
+ }
39
+
40
+ // JS resolver code (APPSYNC_JS runtime): forward the caller's arguments and the
41
+ // authorizer-verified identity. `sub` is what keys the row, and it comes from
42
+ // here — the mutation has no `sub` argument for a client to supply.
43
+ let invokeCode: Pulumi.Input.t<string> = `import { util } from '@aws-appsync/utils';
44
+ export function request(ctx) {
45
+ const id = ctx.identity;
46
+ return {
47
+ operation: 'Invoke',
48
+ payload: {
49
+ arguments: ctx.args,
50
+ identity: id != null && id.sub != null ? { sub: id.sub } : null
51
+ }
52
+ };
53
+ }
54
+ export function response(ctx) {
55
+ if (ctx.error) util.error(ctx.error.message, ctx.error.type);
56
+ return ctx.result;
57
+ }
58
+ `->Pulumi.Input.make
59
+
60
+ /** One row per subject: `id` is the caller's Cognito `sub`, `activeRole` the
61
+ group they chose, `updatedAt` an operational breadcrumb. No sort key — a caller
62
+ acts as exactly one role at a time, so the row is the whole state. */
63
+ let makeTable = (
64
+ ~name: string="ActiveRoleStore",
65
+ ~opts: Pulumi.ComponentResource.options,
66
+ ): storeTable => {
67
+ let opts = opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
68
+ let table = Util_DynamoDb.makeTable(
69
+ ~attributes=[{name: "id", type_: "S"}],
70
+ ~tags=AWS.Tags.make(
71
+ ~name=name ++ "Table",
72
+ ~kind=ReventlessCore.ComponentType.Platform,
73
+ ~role=Auth,
74
+ ~scope=Platform,
75
+ ),
76
+ ~opts,
77
+ name,
78
+ )
79
+ {name: table.name, arn: table.arn}
80
+ }
81
+
82
+ /** The Lambda behind `Mutation.Platform_SetActiveRole` and its resolver: an
83
+ execution role scoped to Logs + the one table + `AdminListGroupsForUser` on the
84
+ one pool, an AppSync Lambda data source, and the resolver itself. */
85
+ let makeWriteDoor = (
86
+ ~api: Pulumi.Output.t<AppSync.GraphQLApi.t>,
87
+ ~table: storeTable,
88
+ ~cognitoUserPoolId: Pulumi.Input.t<string>,
89
+ ~cognitoUserPoolArn: Pulumi.Input.t<string>,
90
+ ~name: string="ActiveRoleStore",
91
+ ~opts: Pulumi.ComponentResource.options,
92
+ ): unit => {
93
+ let opts = opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
94
+
95
+ let lambdaRole = IAM.Role.makeWithDefaultPolicy(
96
+ ~name=name ++ "Lambda",
97
+ ~servicePrincipal=AWS.Lambda.principal->Pulumi.Output.make,
98
+ ~tags=AWS.Tags.make(
99
+ ~name=name ++ "Lambda",
100
+ ~kind=ReventlessCore.ComponentType.Platform,
101
+ ~role=Identity,
102
+ ~scope=Platform,
103
+ ),
104
+ ~opts,
105
+ )
106
+
107
+ // Logs, the one table, and group membership on the one pool.
108
+ //
109
+ // `AdminListGroupsForUser` is what makes the refusal in the handler real: a
110
+ // narrowed token carries a single group, so membership has to come from
111
+ // Cognito rather than from the token, or a caller who narrowed once could
112
+ // never widen back. Scoped to this pool's ARN, and it is a read — this role
113
+ // cannot add anyone to a group, only observe who is in one.
114
+ let _policy =
115
+ (table.arn, cognitoUserPoolArn->Pulumi.Output.fromInput)
116
+ ->Pulumi.Output.all2
117
+ ->Pulumi.Output.apply(((tableArn, poolArn)) => {
118
+ let _ = IAM.RolePolicy.make(
119
+ ~name=name ++ "LambdaPolicy",
120
+ ~args={
121
+ policy: PolicyDocument.make(
122
+ ~id=name ++ "LambdaPolicy",
123
+ ~statements=[
124
+ {
125
+ sid: "AllowLambdaLogging",
126
+ effect: Allow,
127
+ actions: Actions([
128
+ "logs:CreateLogGroup",
129
+ "logs:CreateLogStream",
130
+ "logs:PutLogEvents",
131
+ ]),
132
+ resources: Resource("arn:aws:logs:*:*:*"),
133
+ },
134
+ {
135
+ sid: "AllowActiveRoleRowAccess",
136
+ effect: Allow,
137
+ actions: Actions(["dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:GetItem"]),
138
+ resources: Resource(tableArn),
139
+ },
140
+ {
141
+ sid: "AllowReadingOwnPoolMembership",
142
+ effect: Allow,
143
+ actions: Action("cognito-idp:AdminListGroupsForUser"),
144
+ resources: Resource(poolArn),
145
+ },
146
+ ],
147
+ )
148
+ ->PolicyDocument.toJsonString
149
+ ->Pulumi.Input.make,
150
+ role: lambdaRole.id->Pulumi.Output.asInput,
151
+ },
152
+ ~opts,
153
+ )
154
+ })
155
+
156
+ let packageDirs = Dict.fromArray([
157
+ (
158
+ "@reventlessdev/reventless-aws",
159
+ Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws"),
160
+ ),
161
+ ])
162
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
163
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res.mjs",
164
+ ~packageDirs,
165
+ ~bundleRuntimeExtensions=false,
166
+ )
167
+
168
+ let layers =
169
+ Lambda.reventlessLayerArn
170
+ ->Option.map(arn => [arn->Pulumi.Input.make])
171
+ ->Option.getOr([])
172
+ ->Pulumi.Input.make
173
+
174
+ let logGroup = Util_LambdaLogging.makeManagedLogGroup(
175
+ ~name=name ++ "Lambda",
176
+ ~tags=AWS.Tags.make(
177
+ ~name=name ++ "LambdaLogGroup",
178
+ ~kind=ReventlessCore.ComponentType.Platform,
179
+ ~role=Logs,
180
+ ~scope=Platform,
181
+ ),
182
+ ~opts,
183
+ (),
184
+ )
185
+
186
+ let lambda = Lambda.Function.make(
187
+ ~name=name ++ "Lambda",
188
+ ~args={
189
+ handler: "index.handler"->Pulumi.Input.make,
190
+ runtime: "nodejs22.x"->Pulumi.Input.make,
191
+ code: code->Pulumi.Input.make,
192
+ sourceCodeHash: sourceCodeHash->Pulumi.Input.make,
193
+ role: lambdaRole.arn->Pulumi.Output.asInput,
194
+ memorySize: 256->Pulumi.Input.make,
195
+ timeout: 30->Pulumi.Input.make,
196
+ layers,
197
+ tags: AWS.Tags.make(
198
+ ~name=name ++ "Lambda",
199
+ ~kind=ReventlessCore.ComponentType.Platform,
200
+ ~role=Runtime,
201
+ ~scope=Platform,
202
+ ),
203
+ environment: (
204
+ {
205
+ Lambda.Function.variables: Dict.fromArray([
206
+ ("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
207
+ ("ACTIVE_ROLE_TABLE", table.name->Pulumi.Output.asInput),
208
+ ("COGNITO_USER_POOL_ID", cognitoUserPoolId),
209
+ ("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make),
210
+ ("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make),
211
+ Util_LambdaLogging.logLevelEntry(),
212
+ ]),
213
+ }: Lambda.Function.functionEnvironment
214
+ )->Pulumi.Input.make,
215
+ loggingConfig: ?Util_LambdaLogging.loggingConfigFor(logGroup),
216
+ },
217
+ ~opts,
218
+ )
219
+
220
+ let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
221
+ ~name=name ++ "DataSource",
222
+ ~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
223
+ ~tags=AWS.Tags.make(
224
+ ~name=name ++ "DataSource",
225
+ ~kind=ReventlessCore.ComponentType.Platform,
226
+ ~role=Identity,
227
+ ~scope=Platform,
228
+ ),
229
+ ~opts,
230
+ )
231
+
232
+ let _ =
233
+ (lambda.arn, dataSourceRole.id)
234
+ ->Pulumi.Output.all2
235
+ ->Pulumi.Output.apply(((lambdaArn, dataSourceRoleId)) => {
236
+ let _attach = IAM.RolePolicy.make(
237
+ ~name=name ++ "DataSource",
238
+ ~args={
239
+ policy: PolicyDocument.make(
240
+ ~id=name ++ "DataSourcePolicy",
241
+ ~statements=[
242
+ {
243
+ sid: "AllowDataSourceInvokeLambda",
244
+ effect: Allow,
245
+ actions: Action("lambda:InvokeFunction"),
246
+ resources: Resource(lambdaArn),
247
+ },
248
+ ],
249
+ )
250
+ ->PolicyDocument.toJsonString
251
+ ->Pulumi.Input.make,
252
+ role: dataSourceRoleId->Pulumi.Input.make,
253
+ },
254
+ ~opts,
255
+ )
256
+ })
257
+
258
+ let dataSource = AppSync.DataSource.make(
259
+ ~name=name ++ "DataSource",
260
+ ~args={
261
+ type_: AWS_LAMBDA,
262
+ apiId: api->Pulumi.Output.flatMap(api => api.id)->Pulumi.Output.asInput,
263
+ lambdaConfig: {
264
+ AppSync.DataSource.functionArn: lambda.arn->Pulumi.Output.asInput,
265
+ }->Pulumi.Input.make,
266
+ serviceRoleArn: dataSourceRole.arn->Pulumi.Output.asInput,
267
+ },
268
+ ~opts=Some(opts),
269
+ )
270
+
271
+ let _resolver = AppSync_Resolver_Native.makeUnitJsResolver(
272
+ ~name=name ++ "SetActiveRole",
273
+ ~api,
274
+ ~dataSourceName=dataSource.name->Pulumi.Output.asInput,
275
+ ~type_="Mutation"->Pulumi.Input.make,
276
+ ~field="Platform_SetActiveRole"->Pulumi.Input.make,
277
+ ~code=invokeCode,
278
+ ~opts,
279
+ )
280
+ }