@aws/nx-plugin 0.46.0 → 0.47.0
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/package.json +1 -1
- package/src/infra/app/generator.js +4 -1
- package/src/infra/app/generator.js.map +1 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +60 -0
- package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +310 -0
- package/src/py/lambda-function/generator.js +15 -25
- package/src/py/lambda-function/generator.js.map +1 -1
- package/src/py/lambda-function/schema.d.ts +1 -0
- package/src/py/lambda-function/schema.json +8 -0
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +60 -0
- package/src/trpc/react/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +310 -0
- package/src/ts/lambda-function/generator.js +15 -27
- package/src/ts/lambda-function/generator.js.map +1 -1
- package/src/ts/lambda-function/schema.d.ts +1 -0
- package/src/ts/lambda-function/schema.json +8 -0
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +756 -0
- package/src/ts/react-website/app/generator.js +42 -43
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/app/schema.d.ts +1 -0
- package/src/ts/react-website/app/schema.json +8 -0
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +259 -0
- package/src/ts/react-website/cognito-auth/generator.js +10 -13
- package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/schema.d.ts +1 -0
- package/src/ts/react-website/cognito-auth/schema.json +8 -0
- package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +0 -40
- package/src/ts/react-website/runtime-config/generator.js +0 -2
- package/src/ts/react-website/runtime-config/generator.js.map +1 -1
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +10 -0
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +10 -0
- package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +119 -0
- package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +28 -0
- package/src/{py/lambda-function/files/common/constructs/src/app/lambda-functions/__constructFunctionKebabCase__.ts.template → utils/function-constructs/files/cdk/app/lambda-functions/__functionNameKebabCase__.ts.template} +5 -5
- package/src/utils/function-constructs/files/terraform/app/lambda-functions/__functionNameKebabCase__/__functionNameKebabCase__.tf.template +150 -0
- package/src/utils/function-constructs/function-constructs.d.ts +19 -0
- package/src/utils/function-constructs/function-constructs.js +57 -0
- package/src/utils/function-constructs/function-constructs.js.map +1 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +123 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +421 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +47 -0
- package/src/utils/identity-constructs/identity-constructs.d.ts +15 -0
- package/src/utils/identity-constructs/identity-constructs.js +84 -0
- package/src/utils/identity-constructs/identity-constructs.js.map +1 -0
- package/src/utils/metrics.js +1 -1
- package/src/utils/metrics.js.map +1 -1
- package/src/utils/shared-constructs.js +24 -0
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +42 -0
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +709 -0
- package/src/utils/website-constructs/website-constructs.d.ts +18 -0
- package/src/utils/website-constructs/website-constructs.js +61 -0
- package/src/utils/website-constructs/website-constructs.js.map +1 -0
- package/src/ts/lambda-function/files/common/constructs/src/app/lambda-functions/__constructFunctionNameKebabCase__.ts.template +0 -24
- /package/src/{ts/react-website/cognito-auth/files/common/constructs/src → utils/identity-constructs/files/cdk}/core/user-identity.ts.template +0 -0
- /package/src/{ts/react-website/app/files/common/constructs/src → utils/website-constructs/files/cdk}/app/static-websites/__websiteNameKebabCase__.ts.template +0 -0
- /package/src/{ts/react-website/app/files/common/constructs/src → utils/website-constructs/files/cdk}/core/static-website.ts.template +0 -0
|
@@ -258,6 +258,16 @@ resource "aws_apigatewayv2_route" "proxy_routes" {
|
|
|
258
258
|
depends_on = [aws_apigatewayv2_integration.lambda_integration<% if (auth === 'Cognito') { %>, aws_apigatewayv2_authorizer.cognito_authorizer<% } %>]
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
# Add API url to runtime config
|
|
262
|
+
module "add_url_to_runtime_config" {
|
|
263
|
+
source = "../../../core/runtime-config/entry"
|
|
264
|
+
|
|
265
|
+
key_path = "apis.<%- apiNameClassName %>"
|
|
266
|
+
value = module.http_api.stage_invoke_url
|
|
267
|
+
|
|
268
|
+
depends_on = [module.http_api]
|
|
269
|
+
}
|
|
270
|
+
|
|
261
271
|
# Lambda permission for API Gateway to invoke the function
|
|
262
272
|
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
263
273
|
statement_id = "AllowExecutionFromAPIGateway"
|
|
@@ -385,6 +385,16 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
|
385
385
|
depends_on = [module.rest_api, aws_lambda_function.api_lambda]
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
# Add API url to runtime config
|
|
389
|
+
module "add_url_to_runtime_config" {
|
|
390
|
+
source = "../../../core/runtime-config/entry"
|
|
391
|
+
|
|
392
|
+
key_path = "apis.<%- apiNameClassName %>"
|
|
393
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
394
|
+
|
|
395
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
396
|
+
}
|
|
397
|
+
|
|
388
398
|
# Outputs
|
|
389
399
|
|
|
390
400
|
# API Gateway Outputs (from core module)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
local = {
|
|
4
|
+
source = "hashicorp/local"
|
|
5
|
+
version = "~> 2.0"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# Variables
|
|
11
|
+
variable "key_path" {
|
|
12
|
+
description = "Dot-separated path for the configuration key (e.g., 'apis.FooApi', 'cognito.userPoolId')"
|
|
13
|
+
type = string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
variable "value" {
|
|
17
|
+
description = "Value to set at the key path"
|
|
18
|
+
type = any
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
locals {
|
|
22
|
+
config_file_path = "${path.module}/../../../../../../../dist/packages/common/terraform/runtime-config.json"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# This module writes an entry to runtime-config.json which is provided to any static websites
|
|
26
|
+
# as a way to pass deploy-time values to the UI such as API urls
|
|
27
|
+
|
|
28
|
+
data "external" "updated_config" {
|
|
29
|
+
program = ["uv", "run", "python", "-c", <<-EOT
|
|
30
|
+
import json
|
|
31
|
+
import sys
|
|
32
|
+
import os
|
|
33
|
+
import time
|
|
34
|
+
import fcntl
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
# Read input from Terraform
|
|
38
|
+
input_data = json.load(sys.stdin)
|
|
39
|
+
config_file = input_data['config_file']
|
|
40
|
+
key_path = input_data['key_path']
|
|
41
|
+
value = json.loads(input_data['value'])
|
|
42
|
+
|
|
43
|
+
# Create lock file path
|
|
44
|
+
lock_file = config_file + '.lock'
|
|
45
|
+
|
|
46
|
+
# Ensure directory exists
|
|
47
|
+
Path(config_file).parent.mkdir(parents=True, exist_ok=True)
|
|
48
|
+
|
|
49
|
+
# Acquire exclusive lock with retry mechanism
|
|
50
|
+
max_retries = 30
|
|
51
|
+
retry_delay = 1.0
|
|
52
|
+
|
|
53
|
+
for attempt in range(max_retries):
|
|
54
|
+
try:
|
|
55
|
+
# Open lock file for exclusive access
|
|
56
|
+
with open(lock_file, 'w') as lock_fd:
|
|
57
|
+
# Try to acquire exclusive lock (non-blocking)
|
|
58
|
+
fcntl.flock(lock_fd.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
59
|
+
|
|
60
|
+
# Critical section - file operations under lock
|
|
61
|
+
try:
|
|
62
|
+
# Create empty base config if file doesn't exist
|
|
63
|
+
if not os.path.exists(config_file):
|
|
64
|
+
base_config = {}
|
|
65
|
+
with open(config_file, 'w') as f:
|
|
66
|
+
json.dump(base_config, f, indent=2)
|
|
67
|
+
|
|
68
|
+
# Read current config
|
|
69
|
+
with open(config_file, 'r') as f:
|
|
70
|
+
config = json.load(f)
|
|
71
|
+
|
|
72
|
+
# Set the nested key using dot notation
|
|
73
|
+
keys = key_path.split('.')
|
|
74
|
+
current = config
|
|
75
|
+
for key in keys[:-1]:
|
|
76
|
+
if key not in current:
|
|
77
|
+
current[key] = {}
|
|
78
|
+
current = current[key]
|
|
79
|
+
current[keys[-1]] = value
|
|
80
|
+
|
|
81
|
+
# Write updated config back to file atomically
|
|
82
|
+
temp_file = config_file + '.tmp'
|
|
83
|
+
with open(temp_file, 'w') as f:
|
|
84
|
+
json.dump(config, f, indent=2)
|
|
85
|
+
os.rename(temp_file, config_file)
|
|
86
|
+
|
|
87
|
+
# Output the updated config for Terraform
|
|
88
|
+
print(json.dumps({"updated_json": json.dumps(config)}))
|
|
89
|
+
|
|
90
|
+
# Success - break out of retry loop
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
finally:
|
|
94
|
+
# Lock is automatically released when file is closed
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
except (IOError, OSError) as e:
|
|
98
|
+
if attempt < max_retries - 1:
|
|
99
|
+
# Wait before retrying
|
|
100
|
+
time.sleep(retry_delay)
|
|
101
|
+
continue
|
|
102
|
+
else:
|
|
103
|
+
# Final attempt failed
|
|
104
|
+
raise Exception(f"Failed to acquire lock after {max_retries} attempts: {e}")
|
|
105
|
+
|
|
106
|
+
# Clean up lock file if it exists
|
|
107
|
+
try:
|
|
108
|
+
os.remove(lock_file)
|
|
109
|
+
except OSError:
|
|
110
|
+
pass
|
|
111
|
+
EOT
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
query = {
|
|
115
|
+
config_file = local.config_file_path
|
|
116
|
+
key_path = var.key_path
|
|
117
|
+
value = jsonencode(var.value)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
local = {
|
|
4
|
+
source = "hashicorp/local"
|
|
5
|
+
version = "~> 2.0"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
locals {
|
|
11
|
+
config_file_path = "${path.module}/../../../../../../../dist/packages/common/terraform/runtime-config.json"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Read the runtime config file - will fail gracefully if file doesn't exist
|
|
15
|
+
data "local_file" "runtime_config" {
|
|
16
|
+
filename = local.config_file_path
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Outputs
|
|
20
|
+
output "config" {
|
|
21
|
+
description = "Runtime configuration object"
|
|
22
|
+
value = jsondecode(data.local_file.runtime_config.content)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
output "config_json" {
|
|
26
|
+
description = "Runtime configuration as JSON string"
|
|
27
|
+
value = data.local_file.runtime_config.content
|
|
28
|
+
}
|
|
@@ -3,15 +3,15 @@ import * as url from 'url';
|
|
|
3
3
|
import { Code, Function, Runtime, Tracing } from 'aws-cdk-lib/aws-lambda';
|
|
4
4
|
import { Duration } from 'aws-cdk-lib';
|
|
5
5
|
|
|
6
|
-
export class <%=
|
|
6
|
+
export class <%= functionNameClassName %> extends Function {
|
|
7
7
|
constructor(scope: Construct, id: string) {
|
|
8
8
|
super(scope, id, {
|
|
9
9
|
timeout: Duration.seconds(30),
|
|
10
|
-
runtime:
|
|
11
|
-
handler: '<%=
|
|
10
|
+
runtime: <%= runtime %>,
|
|
11
|
+
handler: '<%= handler %>',
|
|
12
12
|
code: Code.fromAsset(url.fileURLToPath(
|
|
13
13
|
new URL(
|
|
14
|
-
'
|
|
14
|
+
'../../../../../../<%- bundlePathFromRoot %>',
|
|
15
15
|
import.meta.url
|
|
16
16
|
)
|
|
17
17
|
)),
|
|
@@ -21,4 +21,4 @@ export class <%= constructFunctionClassName %> extends Function {
|
|
|
21
21
|
},
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
variable "tags" {
|
|
2
|
+
description = "Tags to apply to resources"
|
|
3
|
+
type = map(string)
|
|
4
|
+
default = {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
variable "env" {
|
|
8
|
+
description = "Additional environment variables for the Lambda function"
|
|
9
|
+
type = map(string)
|
|
10
|
+
default = {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
variable "additional_iam_policy_statements" {
|
|
14
|
+
description = "Additional IAM policy statements to attach to the Lambda role"
|
|
15
|
+
type = list(object({
|
|
16
|
+
Effect = string
|
|
17
|
+
Action = list(string)
|
|
18
|
+
Resource = list(string)
|
|
19
|
+
}))
|
|
20
|
+
default = []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
data "aws_caller_identity" "current" {}
|
|
24
|
+
data "aws_region" "current" {}
|
|
25
|
+
|
|
26
|
+
locals {
|
|
27
|
+
aws_account_id = data.aws_caller_identity.current.account_id
|
|
28
|
+
aws_region = data.aws_region.current.name
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
resource "random_string" "suffix" {
|
|
32
|
+
length = 8
|
|
33
|
+
special = false
|
|
34
|
+
upper = false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
resource "aws_iam_role" "lambda_role" {
|
|
38
|
+
name = "<%- functionNameKebabCase %>-role-${random_string.suffix.result}"
|
|
39
|
+
|
|
40
|
+
assume_role_policy = jsonencode({
|
|
41
|
+
Version = "2012-10-17"
|
|
42
|
+
Statement = [
|
|
43
|
+
{
|
|
44
|
+
Sid = "LambdaAssumeRolePolicy"
|
|
45
|
+
Effect = "Allow"
|
|
46
|
+
Principal = {
|
|
47
|
+
Service = "lambda.amazonaws.com"
|
|
48
|
+
}
|
|
49
|
+
Action = "sts:AssumeRole"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
tags = var.tags
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resource "aws_iam_policy" "lambda_policy" {
|
|
58
|
+
name = "<%- functionNameKebabCase %>-policy-${random_string.suffix.result}"
|
|
59
|
+
description = "Policy for <%- functionNameKebabCase %> Lambda function"
|
|
60
|
+
|
|
61
|
+
policy = jsonencode({
|
|
62
|
+
Version = "2012-10-17"
|
|
63
|
+
Statement = concat([
|
|
64
|
+
{
|
|
65
|
+
Sid = "CloudWatchLogsAccess"
|
|
66
|
+
Effect = "Allow"
|
|
67
|
+
Action = [
|
|
68
|
+
"logs:CreateLogGroup",
|
|
69
|
+
"logs:CreateLogStream",
|
|
70
|
+
"logs:PutLogEvents"
|
|
71
|
+
]
|
|
72
|
+
Resource = [
|
|
73
|
+
"arn:aws:logs:${local.aws_region}:${local.aws_account_id}:log-group:/aws/lambda/<%- functionNameKebabCase %>-${random_string.suffix.result}:*"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
Sid = "XRayAccess"
|
|
78
|
+
Effect = "Allow"
|
|
79
|
+
Action = [
|
|
80
|
+
"xray:PutTraceSegments",
|
|
81
|
+
"xray:PutTelemetryRecords"
|
|
82
|
+
]
|
|
83
|
+
Resource = ["*"]
|
|
84
|
+
}
|
|
85
|
+
], var.additional_iam_policy_statements)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
tags = var.tags
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Attach the policy to the role
|
|
92
|
+
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment" {
|
|
93
|
+
role = aws_iam_role.lambda_role.name
|
|
94
|
+
policy_arn = aws_iam_policy.lambda_policy.arn
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
data "archive_file" "lambda_zip" {
|
|
98
|
+
type = "zip"
|
|
99
|
+
source_dir = "${path.module}/../../../../../../../<%- bundlePathFromRoot %>"
|
|
100
|
+
output_path = "${path.module}/../../../../../../../dist/packages/common/terraform/lambda-functions/<%- functionNameKebabCase %>/lambda.zip"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
resource "aws_lambda_function" "lambda_function" {
|
|
104
|
+
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
105
|
+
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple use case
|
|
106
|
+
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
107
|
+
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
108
|
+
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
109
|
+
filename = data.archive_file.lambda_zip.output_path
|
|
110
|
+
function_name = "<%- functionNameKebabCase %>-${random_string.suffix.result}"
|
|
111
|
+
role = aws_iam_role.lambda_role.arn
|
|
112
|
+
handler = "<%- handler %>"
|
|
113
|
+
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
114
|
+
runtime = "<%- runtime %>"
|
|
115
|
+
timeout = 30
|
|
116
|
+
|
|
117
|
+
tracing_config {
|
|
118
|
+
mode = "Active"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
environment {
|
|
122
|
+
variables = merge({
|
|
123
|
+
AWS_CONNECTION_REUSE_ENABLED = "1"
|
|
124
|
+
}, var.env)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
tags = var.tags
|
|
128
|
+
|
|
129
|
+
depends_on = [aws_iam_role_policy_attachment.lambda_policy_attachment]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
output "function_name" {
|
|
133
|
+
description = "Name of the Lambda function"
|
|
134
|
+
value = aws_lambda_function.lambda_function.function_name
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
output "function_arn" {
|
|
138
|
+
description = "ARN of the Lambda function"
|
|
139
|
+
value = aws_lambda_function.lambda_function.arn
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
output "role_arn" {
|
|
143
|
+
description = "ARN of the Lambda execution role"
|
|
144
|
+
value = aws_iam_role.lambda_role.arn
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
output "role_name" {
|
|
148
|
+
description = "Name of the Lambda execution role"
|
|
149
|
+
value = aws_iam_role.lambda_role.name
|
|
150
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { Tree } from '@nx/devkit';
|
|
6
|
+
export interface AddLambdaFunctionConstructOptions {
|
|
7
|
+
functionProjectName: string;
|
|
8
|
+
functionNameClassName: string;
|
|
9
|
+
functionNameKebabCase: string;
|
|
10
|
+
bundlePathFromRoot: string;
|
|
11
|
+
handler: string;
|
|
12
|
+
runtime: 'node' | 'python';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Add infrastructure for a lambda function
|
|
16
|
+
*/
|
|
17
|
+
export declare const addLambdaFunctionInfra: (tree: Tree, options: AddLambdaFunctionConstructOptions & {
|
|
18
|
+
iacProvider: "CDK" | "Terraform";
|
|
19
|
+
}) => void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addLambdaFunctionInfra = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
const devkit_1 = require("@nx/devkit");
|
|
9
|
+
const shared_constructs_constants_1 = require("../shared-constructs-constants");
|
|
10
|
+
const ast_1 = require("../ast");
|
|
11
|
+
/**
|
|
12
|
+
* Add infrastructure for a lambda function
|
|
13
|
+
*/
|
|
14
|
+
const addLambdaFunctionInfra = (tree, options) => {
|
|
15
|
+
if (options.iacProvider === 'CDK') {
|
|
16
|
+
addLambdaFunctionCdkConstructs(tree, options);
|
|
17
|
+
}
|
|
18
|
+
else if (options.iacProvider === 'Terraform') {
|
|
19
|
+
addLambdaFunctionTerraformModules(tree, options);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
throw new Error(`Unsupported iacProvider ${options.iacProvider}`);
|
|
23
|
+
}
|
|
24
|
+
(0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, options.iacProvider === 'CDK'
|
|
25
|
+
? shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR
|
|
26
|
+
: shared_constructs_constants_1.SHARED_TERRAFORM_DIR, 'project.json'), (config) => {
|
|
27
|
+
var _a;
|
|
28
|
+
if (!config.targets) {
|
|
29
|
+
config.targets = {};
|
|
30
|
+
}
|
|
31
|
+
if (!config.targets.build) {
|
|
32
|
+
config.targets.build = {};
|
|
33
|
+
}
|
|
34
|
+
config.targets.build.dependsOn = [
|
|
35
|
+
...((_a = config.targets.build.dependsOn) !== null && _a !== void 0 ? _a : []),
|
|
36
|
+
`${options.functionProjectName}:build`,
|
|
37
|
+
];
|
|
38
|
+
return config;
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
exports.addLambdaFunctionInfra = addLambdaFunctionInfra;
|
|
42
|
+
const addLambdaFunctionCdkConstructs = (tree, options) => {
|
|
43
|
+
// Generate app specific CDK construct
|
|
44
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'cdk', 'app', 'lambda-functions'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app', 'lambda-functions'), Object.assign(Object.assign({}, options), { runtime: `Runtime.${options.runtime === 'python' ? 'PYTHON_3_12' : 'NODEJS_LATEST'}` }), {
|
|
45
|
+
overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
|
|
46
|
+
});
|
|
47
|
+
// Export app specific CDK construct
|
|
48
|
+
(0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app', 'lambda-functions', 'index.ts'), `./${options.functionNameKebabCase}.js`);
|
|
49
|
+
(0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app', 'index.ts'), './lambda-functions/index.js');
|
|
50
|
+
};
|
|
51
|
+
const addLambdaFunctionTerraformModules = (tree, options) => {
|
|
52
|
+
// Generate app specific terraform module
|
|
53
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'terraform', 'app', 'lambda-functions'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_TERRAFORM_DIR, 'src', 'app', 'lambda-functions'), Object.assign(Object.assign({}, options), { runtime: options.runtime === 'python' ? 'python3.12' : 'nodejs22.x' }), {
|
|
54
|
+
overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=function-constructs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"function-constructs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/function-constructs/function-constructs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
aws = {
|
|
4
|
+
source = "hashicorp/aws"
|
|
5
|
+
version = "~> 6.0"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# Variables
|
|
11
|
+
variable "callback_url" {
|
|
12
|
+
description = "Callback URL to add (e.g., https://d123456789.cloudfront.net)"
|
|
13
|
+
type = string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# Read runtime config to get user pool client details
|
|
17
|
+
module "runtime_config_reader" {
|
|
18
|
+
source = "../../runtime-config/read"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Extract cognito details from runtime config
|
|
22
|
+
locals {
|
|
23
|
+
cognito_props = try(module.runtime_config_reader.config.cognitoProps, null)
|
|
24
|
+
|
|
25
|
+
user_pool_id = local.cognito_props != null ? local.cognito_props.userPoolId : null
|
|
26
|
+
user_pool_client_id = local.cognito_props != null ? local.cognito_props.userPoolWebClientId : null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Validation: Ensure cognito props exist in runtime config
|
|
30
|
+
resource "terraform_data" "validate_cognito_props" {
|
|
31
|
+
lifecycle {
|
|
32
|
+
precondition {
|
|
33
|
+
condition = local.cognito_props != null
|
|
34
|
+
error_message = "ERROR: cognitoProps not found in runtime config. Ensure user-identity module has been deployed first and has added cognitoProps to the runtime configuration."
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
precondition {
|
|
38
|
+
condition = local.user_pool_id != null && local.user_pool_id != ""
|
|
39
|
+
error_message = "ERROR: cognitoProps.userPoolId is missing or empty in runtime config. Check that user-identity module completed successfully."
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
precondition {
|
|
43
|
+
condition = local.user_pool_client_id != null && local.user_pool_client_id != ""
|
|
44
|
+
error_message = "ERROR: cognitoProps.userPoolWebClientId is missing or empty in runtime config. Check that user-identity module completed successfully."
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Update the user pool client with additional callback URL
|
|
51
|
+
resource "null_resource" "add_callback_url" {
|
|
52
|
+
triggers = {
|
|
53
|
+
callback_url = var.callback_url
|
|
54
|
+
user_pool_id = local.user_pool_id
|
|
55
|
+
user_pool_client_id = local.user_pool_client_id
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
provisioner "local-exec" {
|
|
59
|
+
command = <<-EOT
|
|
60
|
+
uv run --with boto3 python -c "
|
|
61
|
+
import boto3
|
|
62
|
+
import sys
|
|
63
|
+
|
|
64
|
+
# Configuration
|
|
65
|
+
user_pool_id = '${local.user_pool_id}'
|
|
66
|
+
client_id = '${local.user_pool_client_id}'
|
|
67
|
+
new_callback_url = '${var.callback_url}'
|
|
68
|
+
|
|
69
|
+
# Initialize Cognito client
|
|
70
|
+
cognito = boto3.client('cognito-idp')
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
# Get current user pool client configuration
|
|
74
|
+
response = cognito.describe_user_pool_client(
|
|
75
|
+
UserPoolId=user_pool_id,
|
|
76
|
+
ClientId=client_id
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
client_config = response['UserPoolClient']
|
|
80
|
+
current_callback_urls = client_config.get('CallbackURLs', [])
|
|
81
|
+
current_logout_urls = client_config.get('LogoutURLs', [])
|
|
82
|
+
|
|
83
|
+
# Check if URL already exists
|
|
84
|
+
if new_callback_url in current_callback_urls:
|
|
85
|
+
print(f'Callback URL {new_callback_url} already exists')
|
|
86
|
+
else:
|
|
87
|
+
# Add new URL to both callback and logout URLs
|
|
88
|
+
updated_callback_urls = current_callback_urls + [new_callback_url]
|
|
89
|
+
updated_logout_urls = current_logout_urls + [new_callback_url]
|
|
90
|
+
|
|
91
|
+
# Update the user pool client
|
|
92
|
+
# Only include valid update parameters (exclude read-only fields and ones we're setting)
|
|
93
|
+
valid_update_params = [
|
|
94
|
+
'ClientName', 'RefreshTokenValidity', 'AccessTokenValidity', 'IdTokenValidity',
|
|
95
|
+
'TokenValidityUnits', 'ReadAttributes', 'WriteAttributes', 'ExplicitAuthFlows',
|
|
96
|
+
'SupportedIdentityProviders', 'DefaultRedirectURI', 'AllowedOAuthFlows',
|
|
97
|
+
'AllowedOAuthScopes', 'AllowedOAuthFlowsUserPoolClient', 'AnalyticsConfiguration',
|
|
98
|
+
'PreventUserExistenceErrors', 'EnableTokenRevocation',
|
|
99
|
+
'EnablePropagateAdditionalUserContextData', 'AuthSessionValidity', 'RefreshTokenRotation'
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
update_config = {k: v for k, v in client_config.items() if k in valid_update_params}
|
|
103
|
+
|
|
104
|
+
cognito.update_user_pool_client(
|
|
105
|
+
UserPoolId=user_pool_id,
|
|
106
|
+
ClientId=client_id,
|
|
107
|
+
CallbackURLs=updated_callback_urls,
|
|
108
|
+
LogoutURLs=updated_logout_urls,
|
|
109
|
+
**update_config
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
print(f'Successfully added callback URL: {new_callback_url}')
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f'Error updating callback URLs: {e}')
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
"
|
|
118
|
+
EOT
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
depends_on = [terraform_data.validate_cognito_props]
|
|
122
|
+
}
|
|
123
|
+
|