@aws/nx-plugin 1.0.0-rc.18 → 1.0.0-rc.19
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/py/agent/__snapshots__/generator.spec.ts.snap +15 -6
- package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +201 -0
- package/src/py/agent/a2a-connection/files/agent-connection/app/{__targetAgentSnakeCase___client.py.template → __targetAgentSnakeCase___client_strands.py.template} +7 -5
- package/src/py/agent/a2a-connection/generator.js +6 -5
- package/src/py/agent/a2a-connection/generator.js.map +1 -1
- package/src/py/agent/files/http/main.py.template +2 -2
- package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +197 -0
- package/src/py/agent/gateway-connection/files/agent-connection/app/{__gatewaySnakeCase___client.py.template → __gatewaySnakeCase___client_strands.py.template} +6 -6
- package/src/py/agent/gateway-connection/generator.js +5 -5
- package/src/py/agent/gateway-connection/generator.js.map +1 -1
- package/src/py/agent/generator.js +3 -0
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +157 -79
- package/src/py/agent/mcp-connection/files/agent-connection/app/{__mcpServerSnakeCase___client.py.template → __mcpServerSnakeCase___client_strands.py.template} +7 -5
- package/src/py/agent/mcp-connection/generator.js +5 -5
- package/src/py/agent/mcp-connection/generator.js.map +1 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +37 -3
- package/src/py/fast-api/files/app/__name__/main.py.template +4 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +13 -4
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/smithy/project/files/src/operations/echo.smithy.template +1 -0
- package/src/terraform/project/files/application/src/main.tf.template +6 -1
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +35 -4
- package/src/trpc/backend/files/src/procedures/echo.ts.template +1 -1
- package/src/trpc/backend/files/src/schema/echo.ts.template +2 -2
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +14 -5
- package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +256 -0
- package/src/ts/agent/a2a-connection/files/agent-connection/app/{__targetAgentKebabCase__-client.ts.template → __targetAgentKebabCase__-client-strands.ts.template} +5 -5
- package/src/ts/agent/a2a-connection/generator.js +3 -3
- package/src/ts/agent/a2a-connection/generator.js.map +1 -1
- package/src/ts/agent/files/http/router.ts.template +1 -1
- package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +220 -0
- package/src/ts/agent/gateway-connection/files/agent-connection/app/{__gatewayKebabCase__-client.ts.template → __gatewayKebabCase__-client-strands.ts.template} +5 -5
- package/src/ts/agent/gateway-connection/generator.js +3 -3
- package/src/ts/agent/gateway-connection/generator.js.map +1 -1
- package/src/ts/agent/generator.js +3 -0
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +183 -110
- package/src/ts/agent/mcp-connection/files/agent-connection/app/{__mcpServerKebabCase__-client.ts.template → __mcpServerKebabCase__-client-strands.ts.template} +5 -5
- package/src/ts/agent/mcp-connection/generator.js +3 -3
- package/src/ts/agent/mcp-connection/generator.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +13 -4
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +64 -24
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +13 -4
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +9 -3
- package/src/utils/agent-connection/agent-connection.d.ts +50 -24
- package/src/utils/agent-connection/agent-connection.js +137 -45
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client-config.ts.template +93 -0
- package/src/utils/agent-connection/files/core-auth/agentcore-endpoints.ts.template +37 -0
- package/src/utils/agent-connection/files/core-auth/agentcore-fetch.ts.template +54 -0
- package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-transport.ts.template +41 -0
- package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-transport.ts.template +70 -0
- package/src/utils/agent-connection/files/core-shared/agentcore-transport.ts.template +39 -0
- package/src/utils/agent-connection/files/core-strands/a2a/agentcore-a2a-client-strands.ts.template +61 -0
- package/src/utils/agent-connection/files/core-strands/gateway/agentcore-gateway-mcp-client-strands.ts.template +26 -0
- package/src/utils/agent-connection/files/core-strands/mcp/agentcore-mcp-client-strands.ts.template +27 -0
- package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client_config.py.template +43 -0
- package/src/utils/agent-connection/files/py-core-auth/agentcore_endpoints.py.template +47 -0
- package/src/utils/agent-connection/files/py-core-auth/auth/session.py.template +51 -0
- package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_transport.py.template +28 -0
- package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_transport.py.template +34 -0
- package/src/utils/agent-connection/files/py-core-shared/agentcore_transport.py.template +40 -0
- package/src/utils/agent-connection/files/py-core-strands/a2a/agentcore_a2a_client_strands.py.template +66 -0
- package/src/utils/agent-connection/files/py-core-strands/gateway/agentcore_gateway_mcp_client_strands.py.template +22 -0
- package/src/utils/agent-connection/files/py-core-strands/mcp/agentcore_mcp_client_strands.py.template +30 -0
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +7 -2
- package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +13 -4
- package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +9 -0
- package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +23 -1
- package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +9 -3
- package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +7 -2
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +9 -3
- package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +22 -8
- package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +10 -4
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +13 -4
- package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client.ts.template +0 -123
- package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-client.ts.template +0 -78
- package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-client.ts.template +0 -128
- package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client.py.template +0 -120
- package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_client.py.template +0 -78
- package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_client.py.template +0 -89
- /package/src/utils/agent-connection/files/{core-runtime-config/model-errors.ts.template → core-strands/base/model-errors-strands.ts.template} +0 -0
- /package/src/utils/agent-connection/files/{core-runtime-config/with-session-id.ts.template → core-strands/base/with-session-id-strands.ts.template} +0 -0
- /package/src/utils/agent-connection/files/{py-core-runtime-config/model_errors.py.template → py-core-strands/base/model_errors_strands.py.template} +0 -0
- /package/src/utils/agent-connection/files/{py-core-runtime-config/with_session_id.py.template → py-core-strands/base/with_session_id_strands.py.template} +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
from .agentcore_endpoints import mcp_url_from_arn, region_from_arn
|
|
4
|
+
from .agentcore_transport import (
|
|
5
|
+
TransportFactory,
|
|
6
|
+
jwt_transport,
|
|
7
|
+
no_auth_transport,
|
|
8
|
+
sigv4_transport,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentCoreMCPTransport:
|
|
13
|
+
"""Factory for MCP transport factories that connect to an AgentCore runtime."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def with_iam_auth(agent_runtime_arn: str) -> TransportFactory:
|
|
17
|
+
"""SigV4-authenticated transport for a Bedrock AgentCore runtime."""
|
|
18
|
+
return sigv4_transport(
|
|
19
|
+
mcp_url_from_arn(agent_runtime_arn), region_from_arn(agent_runtime_arn)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def with_jwt_auth(
|
|
24
|
+
agent_runtime_arn: str, access_token_provider: Callable[[], str]
|
|
25
|
+
) -> TransportFactory:
|
|
26
|
+
"""Bearer-authenticated transport for a Bedrock AgentCore runtime."""
|
|
27
|
+
return jwt_transport(
|
|
28
|
+
mcp_url_from_arn(agent_runtime_arn), access_token_provider
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def without_auth(url: str) -> TransportFactory:
|
|
33
|
+
"""Plain-HTTP transport — for local dev."""
|
|
34
|
+
return no_auth_transport(url)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import boto3
|
|
5
|
+
|
|
6
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
7
|
+
|
|
8
|
+
from .auth.session import jwt_auth, plain_auth, sigv4_auth
|
|
9
|
+
|
|
10
|
+
# Agent frameworks wrap a transport *factory* — a no-arg callable returning a
|
|
11
|
+
# streamable-HTTP transport context manager. Typed as ``Any`` so this layer
|
|
12
|
+
# stays free of any framework-specific transport type.
|
|
13
|
+
TransportFactory = Callable[[], Any]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _factory(url: str, auth) -> TransportFactory:
|
|
17
|
+
return lambda: streamablehttp_client(
|
|
18
|
+
url,
|
|
19
|
+
auth=auth,
|
|
20
|
+
timeout=120,
|
|
21
|
+
terminate_on_close=False,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def sigv4_transport(url: str, region: str) -> TransportFactory:
|
|
26
|
+
"""SigV4-signed transport factory for a resolved AgentCore endpoint."""
|
|
27
|
+
credentials = boto3.Session(region_name=region).get_credentials()
|
|
28
|
+
return _factory(url, sigv4_auth(credentials, region))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def jwt_transport(
|
|
32
|
+
url: str, access_token_provider: Callable[[], str]
|
|
33
|
+
) -> TransportFactory:
|
|
34
|
+
"""Bearer-token transport factory for a resolved AgentCore endpoint."""
|
|
35
|
+
return _factory(url, jwt_auth(access_token_provider))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def no_auth_transport(url: str) -> TransportFactory:
|
|
39
|
+
"""Plain-HTTP transport factory — for local dev."""
|
|
40
|
+
return _factory(url, plain_auth())
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
from strands.agent.a2a_agent import A2AAgent
|
|
4
|
+
|
|
5
|
+
from .agentcore_a2a_client_config import AgentCoreA2aClientConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _build(
|
|
9
|
+
config: tuple, *, name: str | None, description: str | None
|
|
10
|
+
) -> A2AAgent:
|
|
11
|
+
url, client_config = config
|
|
12
|
+
kwargs: dict = {"endpoint": url, "client_config": client_config}
|
|
13
|
+
if name:
|
|
14
|
+
kwargs["name"] = name
|
|
15
|
+
if description:
|
|
16
|
+
kwargs["description"] = description
|
|
17
|
+
return A2AAgent(**kwargs)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AgentCoreA2aClientStrands:
|
|
21
|
+
"""Factory for Strands A2A clients that connect to an AgentCore runtime."""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def with_iam_auth(
|
|
25
|
+
agent_runtime_arn: str,
|
|
26
|
+
*,
|
|
27
|
+
name: str | None = None,
|
|
28
|
+
description: str | None = None,
|
|
29
|
+
) -> A2AAgent:
|
|
30
|
+
"""SigV4-authenticated client for a Bedrock AgentCore runtime."""
|
|
31
|
+
return _build(
|
|
32
|
+
AgentCoreA2aClientConfig.with_iam_auth(agent_runtime_arn),
|
|
33
|
+
name=name,
|
|
34
|
+
description=description,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def with_jwt_auth(
|
|
39
|
+
agent_runtime_arn: str,
|
|
40
|
+
access_token_provider: Callable[[], str],
|
|
41
|
+
*,
|
|
42
|
+
name: str | None = None,
|
|
43
|
+
description: str | None = None,
|
|
44
|
+
) -> A2AAgent:
|
|
45
|
+
"""Bearer-authenticated client for a Bedrock AgentCore runtime."""
|
|
46
|
+
return _build(
|
|
47
|
+
AgentCoreA2aClientConfig.with_jwt_auth(
|
|
48
|
+
agent_runtime_arn, access_token_provider
|
|
49
|
+
),
|
|
50
|
+
name=name,
|
|
51
|
+
description=description,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def without_auth(
|
|
56
|
+
url: str,
|
|
57
|
+
*,
|
|
58
|
+
name: str | None = None,
|
|
59
|
+
description: str | None = None,
|
|
60
|
+
) -> A2AAgent:
|
|
61
|
+
"""Plain-HTTP client — for local dev."""
|
|
62
|
+
return _build(
|
|
63
|
+
AgentCoreA2aClientConfig.without_auth(url),
|
|
64
|
+
name=name,
|
|
65
|
+
description=description,
|
|
66
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from strands.tools.mcp.mcp_client import MCPClient
|
|
2
|
+
|
|
3
|
+
from .agentcore_gateway_mcp_transport import AgentCoreGatewayMCPTransport
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AgentCoreGatewayMCPClientStrands:
|
|
7
|
+
"""Factory for Strands MCP clients that connect to an AgentCore Gateway."""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def with_iam_auth(
|
|
11
|
+
gateway_url: str,
|
|
12
|
+
region: str | None = None,
|
|
13
|
+
) -> MCPClient:
|
|
14
|
+
"""Create a gateway MCP client authenticated with IAM SigV4."""
|
|
15
|
+
return MCPClient(
|
|
16
|
+
AgentCoreGatewayMCPTransport.with_iam_auth(gateway_url, region)
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def without_auth(gateway_url: str) -> MCPClient:
|
|
21
|
+
"""Plain-HTTP gateway client, for the local gateway started by serve-local."""
|
|
22
|
+
return MCPClient(AgentCoreGatewayMCPTransport.without_auth(gateway_url))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
from strands.tools.mcp.mcp_client import MCPClient
|
|
4
|
+
|
|
5
|
+
from .agentcore_mcp_transport import AgentCoreMCPTransport
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AgentCoreMCPClientStrands:
|
|
9
|
+
"""Factory for Strands MCP clients that connect to an AgentCore runtime."""
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def with_iam_auth(agent_runtime_arn: str) -> MCPClient:
|
|
13
|
+
"""SigV4-authenticated client for a Bedrock AgentCore runtime."""
|
|
14
|
+
return MCPClient(AgentCoreMCPTransport.with_iam_auth(agent_runtime_arn))
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def with_jwt_auth(
|
|
18
|
+
agent_runtime_arn: str, access_token_provider: Callable[[], str]
|
|
19
|
+
) -> MCPClient:
|
|
20
|
+
"""Bearer-authenticated client for a Bedrock AgentCore runtime."""
|
|
21
|
+
return MCPClient(
|
|
22
|
+
AgentCoreMCPTransport.with_jwt_auth(
|
|
23
|
+
agent_runtime_arn, access_token_provider
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def without_auth(url: str) -> MCPClient:
|
|
29
|
+
"""Plain-HTTP client — for local dev."""
|
|
30
|
+
return MCPClient(AgentCoreMCPTransport.without_auth(url))
|
|
@@ -235,6 +235,7 @@ resource "null_resource" "gateway_ready" {
|
|
|
235
235
|
command = <<-EOT
|
|
236
236
|
uv run --with boto3 --with httpx --with mcp python -c "
|
|
237
237
|
import asyncio
|
|
238
|
+
import os
|
|
238
239
|
import time
|
|
239
240
|
|
|
240
241
|
import boto3
|
|
@@ -244,8 +245,8 @@ from botocore.awsrequest import AWSRequest
|
|
|
244
245
|
from mcp import ClientSession
|
|
245
246
|
from mcp.client.streamable_http import streamablehttp_client
|
|
246
247
|
|
|
247
|
-
gateway_url =
|
|
248
|
-
region =
|
|
248
|
+
gateway_url = os.environ['GATEWAY_URL']
|
|
249
|
+
region = os.environ['REGION']
|
|
249
250
|
credentials = boto3.Session(region_name=region).get_credentials()
|
|
250
251
|
signer = SigV4Auth(credentials, 'bedrock-agentcore', region)
|
|
251
252
|
|
|
@@ -293,6 +294,10 @@ while True:
|
|
|
293
294
|
print(f'Gateway {gateway_url} is serving tools')
|
|
294
295
|
"
|
|
295
296
|
EOT
|
|
297
|
+
environment = {
|
|
298
|
+
GATEWAY_URL = self.triggers.gateway_url
|
|
299
|
+
REGION = self.triggers.region
|
|
300
|
+
}
|
|
296
301
|
}
|
|
297
302
|
}
|
|
298
303
|
|
|
@@ -281,14 +281,19 @@ resource "null_resource" "docker_publish" {
|
|
|
281
281
|
provisioner "local-exec" {
|
|
282
282
|
command = <<-EOT
|
|
283
283
|
# Get ECR login token
|
|
284
|
-
aws ecr get-login-password --region $
|
|
284
|
+
aws ecr get-login-password --region "$AWS_REGION" | <%= containers %> login --username AWS --password-stdin "$REPOSITORY_URL"
|
|
285
285
|
|
|
286
286
|
# Tag the image
|
|
287
|
-
<%= containers %> tag $
|
|
287
|
+
<%= containers %> tag "$DOCKER_IMAGE_TAG" "$REPOSITORY_URL:latest"
|
|
288
288
|
|
|
289
289
|
# Push the image
|
|
290
|
-
<%= containers %> push $
|
|
290
|
+
<%= containers %> push "$REPOSITORY_URL:latest"
|
|
291
291
|
EOT
|
|
292
|
+
environment = {
|
|
293
|
+
AWS_REGION = local.aws_region
|
|
294
|
+
REPOSITORY_URL = self.triggers.repository_url
|
|
295
|
+
DOCKER_IMAGE_TAG = self.triggers.docker_image_tag
|
|
296
|
+
}
|
|
292
297
|
}
|
|
293
298
|
|
|
294
299
|
depends_on = [aws_ecr_repository_policy.agent_core_ecr_policy]
|
|
@@ -350,10 +355,11 @@ resource "null_resource" "runtime_ready" {
|
|
|
350
355
|
uv run --with boto3 python -c "
|
|
351
356
|
import boto3
|
|
352
357
|
import json
|
|
358
|
+
import os
|
|
353
359
|
import time
|
|
354
360
|
import uuid
|
|
355
361
|
|
|
356
|
-
runtime_arn =
|
|
362
|
+
runtime_arn = os.environ['RUNTIME_ARN']
|
|
357
363
|
client = boto3.client('bedrock-agentcore')
|
|
358
364
|
initialize_request = json.dumps({
|
|
359
365
|
'jsonrpc': '2.0',
|
|
@@ -385,6 +391,9 @@ while True:
|
|
|
385
391
|
print(f'Runtime {runtime_arn} is serving MCP')
|
|
386
392
|
"
|
|
387
393
|
EOT
|
|
394
|
+
environment = {
|
|
395
|
+
RUNTIME_ARN = self.triggers.runtime_arn
|
|
396
|
+
}
|
|
388
397
|
}
|
|
389
398
|
}
|
|
390
399
|
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
HttpMethod,
|
|
13
13
|
HttpStage,
|
|
14
14
|
LogGroupLogDestination,
|
|
15
|
+
ThrottleSettings,
|
|
15
16
|
} from 'aws-cdk-lib/aws-apigatewayv2';
|
|
16
17
|
import { LogGroup } from 'aws-cdk-lib/aws-logs';
|
|
17
18
|
import { suppressRules } from '../checkov.js';
|
|
@@ -38,6 +39,12 @@ export interface HttpApiProps<
|
|
|
38
39
|
* Map of operation names to their API Gateway integrations
|
|
39
40
|
*/
|
|
40
41
|
readonly integrations: TIntegrations;
|
|
42
|
+
/**
|
|
43
|
+
* Default throttling limits applied to the API's default stage.
|
|
44
|
+
*
|
|
45
|
+
* @default { rateLimit: 10000, burstLimit: 5000 }
|
|
46
|
+
*/
|
|
47
|
+
readonly throttle?: ThrottleSettings;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
/**
|
|
@@ -71,6 +78,7 @@ export class HttpApi<
|
|
|
71
78
|
apiName,
|
|
72
79
|
operations,
|
|
73
80
|
integrations,
|
|
81
|
+
throttle = { rateLimit: 10000, burstLimit: 5000 },
|
|
74
82
|
...props
|
|
75
83
|
}: HttpApiProps<TIntegrations, TOperation>,
|
|
76
84
|
) {
|
|
@@ -98,6 +106,7 @@ export class HttpApi<
|
|
|
98
106
|
this.defaultStage = new HttpStage(this, 'DefaultStage', {
|
|
99
107
|
httpApi: this.api,
|
|
100
108
|
autoDeploy: true,
|
|
109
|
+
throttle,
|
|
101
110
|
accessLogSettings: {
|
|
102
111
|
destination: new LogGroupLogDestination(accessLogGroup),
|
|
103
112
|
},
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
RestApiProps as _RestApiProps,
|
|
7
7
|
IResource,
|
|
8
8
|
Stage,
|
|
9
|
+
ThrottleSettings,
|
|
9
10
|
} from 'aws-cdk-lib/aws-apigateway';
|
|
10
11
|
import { IAspect, RemovalPolicy } from 'aws-cdk-lib';
|
|
11
12
|
import {
|
|
@@ -52,6 +53,12 @@ export interface RestApiProps<
|
|
|
52
53
|
* @default true
|
|
53
54
|
*/
|
|
54
55
|
readonly enableWaf?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Default throttling limits applied to all methods on the API's default stage.
|
|
58
|
+
*
|
|
59
|
+
* @default { rateLimit: 10000, burstLimit: 5000 }
|
|
60
|
+
*/
|
|
61
|
+
readonly throttle?: ThrottleSettings;
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
/**
|
|
@@ -86,6 +93,7 @@ export class RestApi<
|
|
|
86
93
|
operations,
|
|
87
94
|
integrations,
|
|
88
95
|
enableWaf = true,
|
|
96
|
+
throttle = { rateLimit: 10000, burstLimit: 5000 },
|
|
89
97
|
...props
|
|
90
98
|
}: RestApiProps<TIntegrations, TOperation>,
|
|
91
99
|
) {
|
|
@@ -93,7 +101,21 @@ export class RestApi<
|
|
|
93
101
|
this.integrations = integrations;
|
|
94
102
|
|
|
95
103
|
// Create the API Gateway REST API
|
|
96
|
-
this.api = new _RestApi(this, 'Api',
|
|
104
|
+
this.api = new _RestApi(this, 'Api', {
|
|
105
|
+
...props,
|
|
106
|
+
deployOptions: {
|
|
107
|
+
...props.deployOptions,
|
|
108
|
+
methodOptions: {
|
|
109
|
+
...props.deployOptions?.methodOptions,
|
|
110
|
+
// Default throttling applied to all resource paths and methods
|
|
111
|
+
'/*/*': {
|
|
112
|
+
throttlingRateLimit: throttle.rateLimit,
|
|
113
|
+
throttlingBurstLimit: throttle.burstLimit,
|
|
114
|
+
...props.deployOptions?.methodOptions?.['/*/*'],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
97
119
|
|
|
98
120
|
if (enableWaf) {
|
|
99
121
|
this.webAcl = new CfnWebACL(this, 'WebAcl', {
|
|
@@ -65,12 +65,13 @@ resource "null_resource" "aggregate_namespace" {
|
|
|
65
65
|
command = <<-EOT
|
|
66
66
|
uv run python -c "
|
|
67
67
|
import json
|
|
68
|
+
import os
|
|
68
69
|
import pathlib
|
|
69
70
|
import sys
|
|
70
71
|
|
|
71
|
-
namespace =
|
|
72
|
-
entries_dir = pathlib.Path(
|
|
73
|
-
output_file = pathlib.Path(
|
|
72
|
+
namespace = os.environ['NAMESPACE']
|
|
73
|
+
entries_dir = pathlib.Path(os.environ['ENTRIES_DIR']) / namespace
|
|
74
|
+
output_file = pathlib.Path(os.environ['CONFIG_DIR']) / f'{namespace}.json'
|
|
74
75
|
|
|
75
76
|
def deep_merge(target: dict, source: dict) -> dict:
|
|
76
77
|
for k, v in source.items():
|
|
@@ -103,6 +104,11 @@ output_file.write_text(json.dumps(merged, indent=2))
|
|
|
103
104
|
print(f'Aggregated {namespace}.json with {len(merged)} section(s)')
|
|
104
105
|
"
|
|
105
106
|
EOT
|
|
107
|
+
environment = {
|
|
108
|
+
NAMESPACE = each.key
|
|
109
|
+
ENTRIES_DIR = local.entries_dir
|
|
110
|
+
CONFIG_DIR = local.config_dir
|
|
111
|
+
}
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
|
|
@@ -38,11 +38,12 @@ resource "null_resource" "aggregate_namespace" {
|
|
|
38
38
|
command = <<-EOT
|
|
39
39
|
uv run python -c "
|
|
40
40
|
import json
|
|
41
|
+
import os
|
|
41
42
|
import pathlib
|
|
42
43
|
import sys
|
|
43
44
|
|
|
44
|
-
entries_dir = pathlib.Path(
|
|
45
|
-
output_file = pathlib.Path(
|
|
45
|
+
entries_dir = pathlib.Path(os.environ['ENTRIES_DIR'])
|
|
46
|
+
output_file = pathlib.Path(os.environ['NAMESPACE_PATH'])
|
|
46
47
|
|
|
47
48
|
def deep_merge(target: dict, source: dict) -> dict:
|
|
48
49
|
for k, v in source.items():
|
|
@@ -71,6 +72,10 @@ output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
71
72
|
output_file.write_text(json.dumps(merged, indent=2))
|
|
72
73
|
"
|
|
73
74
|
EOT
|
|
75
|
+
environment = {
|
|
76
|
+
ENTRIES_DIR = local.entries_dir
|
|
77
|
+
NAMESPACE_PATH = local.namespace_path
|
|
78
|
+
}
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
|
|
@@ -61,12 +61,13 @@ resource "null_resource" "add_callback_url" {
|
|
|
61
61
|
command = <<-EOT
|
|
62
62
|
uv run --with boto3 python -c "
|
|
63
63
|
import boto3
|
|
64
|
+
import os
|
|
64
65
|
import sys
|
|
65
66
|
|
|
66
67
|
# Configuration
|
|
67
|
-
user_pool_id =
|
|
68
|
-
client_id =
|
|
69
|
-
new_callback_url =
|
|
68
|
+
user_pool_id = os.environ['USER_POOL_ID']
|
|
69
|
+
client_id = os.environ['CLIENT_ID']
|
|
70
|
+
new_callback_url = os.environ['NEW_CALLBACK_URL']
|
|
70
71
|
|
|
71
72
|
# Initialize Cognito client
|
|
72
73
|
cognito = boto3.client('cognito-idp')
|
|
@@ -118,6 +119,11 @@ except Exception as e:
|
|
|
118
119
|
sys.exit(1)
|
|
119
120
|
"
|
|
120
121
|
EOT
|
|
122
|
+
environment = {
|
|
123
|
+
USER_POOL_ID = local.user_pool_id
|
|
124
|
+
CLIENT_ID = local.user_pool_client_id
|
|
125
|
+
NEW_CALLBACK_URL = var.callback_url
|
|
126
|
+
}
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
depends_on = [terraform_data.validate_cognito_props]
|
|
@@ -287,10 +287,15 @@ resource "null_resource" "docker_publish" {
|
|
|
287
287
|
|
|
288
288
|
provisioner "local-exec" {
|
|
289
289
|
command = <<-EOT
|
|
290
|
-
aws ecr get-login-password --region $
|
|
291
|
-
<%= containerEngine %> tag $
|
|
292
|
-
<%= containerEngine %> push $
|
|
290
|
+
aws ecr get-login-password --region "$AWS_REGION" | <%= containerEngine %> login --username AWS --password-stdin "$REPOSITORY_URL"
|
|
291
|
+
<%= containerEngine %> tag "$DOCKER_IMAGE_TAG" "$REPOSITORY_URL:latest"
|
|
292
|
+
<%= containerEngine %> push "$REPOSITORY_URL:latest"
|
|
293
293
|
EOT
|
|
294
|
+
environment = {
|
|
295
|
+
AWS_REGION = data.aws_region.current.region
|
|
296
|
+
REPOSITORY_URL = self.triggers.repository_url
|
|
297
|
+
DOCKER_IMAGE_TAG = self.triggers.docker_image_tag
|
|
298
|
+
}
|
|
294
299
|
}
|
|
295
300
|
|
|
296
301
|
depends_on = [aws_ecr_repository_policy.migration_handler]
|
|
@@ -574,10 +579,10 @@ resource "null_resource" "create_db_user_trigger" {
|
|
|
574
579
|
command = <<-EOT
|
|
575
580
|
output_file=$(mktemp)
|
|
576
581
|
response=$(aws lambda invoke \
|
|
577
|
-
--region $
|
|
578
|
-
--function-name $
|
|
582
|
+
--region "$AWS_REGION" \
|
|
583
|
+
--function-name "$FUNCTION_NAME" \
|
|
579
584
|
--cli-binary-format raw-in-base64-out \
|
|
580
|
-
--payload
|
|
585
|
+
--payload "{\"RequestType\":\"Create\",\"PhysicalResourceId\":\"db-user:$DB_USER\"}" \
|
|
581
586
|
"$output_file")
|
|
582
587
|
cat "$output_file"
|
|
583
588
|
echo "$response"
|
|
@@ -587,6 +592,11 @@ resource "null_resource" "create_db_user_trigger" {
|
|
|
587
592
|
fi
|
|
588
593
|
rm -f "$output_file"
|
|
589
594
|
EOT
|
|
595
|
+
environment = {
|
|
596
|
+
AWS_REGION = data.aws_region.current.region
|
|
597
|
+
FUNCTION_NAME = self.triggers.function_name
|
|
598
|
+
DB_USER = self.triggers.db_user
|
|
599
|
+
}
|
|
590
600
|
}
|
|
591
601
|
|
|
592
602
|
depends_on = [aws_lambda_function.create_db_user, module.aurora]
|
|
@@ -655,8 +665,8 @@ resource "null_resource" "migration_trigger" {
|
|
|
655
665
|
command = <<-EOT
|
|
656
666
|
output_file=$(mktemp)
|
|
657
667
|
response=$(aws lambda invoke \
|
|
658
|
-
--region $
|
|
659
|
-
--function-name $
|
|
668
|
+
--region "$AWS_REGION" \
|
|
669
|
+
--function-name "$FUNCTION_NAME" \
|
|
660
670
|
--cli-binary-format raw-in-base64-out \
|
|
661
671
|
"$output_file")
|
|
662
672
|
cat "$output_file"
|
|
@@ -667,6 +677,10 @@ resource "null_resource" "migration_trigger" {
|
|
|
667
677
|
fi
|
|
668
678
|
rm -f "$output_file"
|
|
669
679
|
EOT
|
|
680
|
+
environment = {
|
|
681
|
+
AWS_REGION = data.aws_region.current.region
|
|
682
|
+
FUNCTION_NAME = self.triggers.function_name
|
|
683
|
+
}
|
|
670
684
|
}
|
|
671
685
|
|
|
672
686
|
depends_on = [
|
|
@@ -540,9 +540,9 @@ resource "null_resource" "proxy_target_ready" {
|
|
|
540
540
|
deadline=$((start_time + 600))
|
|
541
541
|
while true; do
|
|
542
542
|
target_state=$(aws rds describe-db-proxy-targets \
|
|
543
|
-
--region $
|
|
544
|
-
--db-proxy-name $
|
|
545
|
-
--target-group-name $
|
|
543
|
+
--region "$AWS_REGION" \
|
|
544
|
+
--db-proxy-name "$DB_PROXY_NAME" \
|
|
545
|
+
--target-group-name "$TARGET_GROUP_NAME" \
|
|
546
546
|
--query "Targets[?Type=='RDS_INSTANCE'] | [0].TargetHealth.State" \
|
|
547
547
|
--output text)
|
|
548
548
|
|
|
@@ -552,13 +552,19 @@ resource "null_resource" "proxy_target_ready" {
|
|
|
552
552
|
|
|
553
553
|
current_time=$(date +%s)
|
|
554
554
|
if [ "$current_time" -ge "$deadline" ]; then
|
|
555
|
-
echo "RDS Proxy target $
|
|
555
|
+
echo "RDS Proxy target $DB_CLUSTER_IDENTIFIER did not become AVAILABLE within 10 minutes. Last state: $target_state"
|
|
556
556
|
exit 1
|
|
557
557
|
fi
|
|
558
558
|
|
|
559
559
|
sleep 10
|
|
560
560
|
done
|
|
561
561
|
EOT
|
|
562
|
+
environment = {
|
|
563
|
+
AWS_REGION = data.aws_region.current.region
|
|
564
|
+
DB_PROXY_NAME = self.triggers.db_proxy_name
|
|
565
|
+
TARGET_GROUP_NAME = self.triggers.target_group_name
|
|
566
|
+
DB_CLUSTER_IDENTIFIER = self.triggers.db_cluster_identifier
|
|
567
|
+
}
|
|
562
568
|
}
|
|
563
569
|
|
|
564
570
|
depends_on = [
|
package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template
CHANGED
|
@@ -553,7 +553,7 @@ resource "null_resource" "upload_website_files" {
|
|
|
553
553
|
|
|
554
554
|
provisioner "local-exec" {
|
|
555
555
|
command = <<-EOT
|
|
556
|
-
cd "$
|
|
556
|
+
cd "$ROOT_PATH"
|
|
557
557
|
uv run --with boto3 python3 -c "
|
|
558
558
|
import os
|
|
559
559
|
import sys
|
|
@@ -642,9 +642,14 @@ def sync_to_s3(local_path, bucket_name):
|
|
|
642
642
|
sys.exit(1)
|
|
643
643
|
|
|
644
644
|
# Execute sync
|
|
645
|
-
sync_to_s3(
|
|
645
|
+
sync_to_s3(os.environ['WEBSITE_FILE_PATH'], os.environ['BUCKET_NAME'])
|
|
646
646
|
"
|
|
647
647
|
EOT
|
|
648
|
+
environment = {
|
|
649
|
+
ROOT_PATH = path.root
|
|
650
|
+
WEBSITE_FILE_PATH = var.website_file_path
|
|
651
|
+
BUCKET_NAME = aws_s3_bucket.website.bucket
|
|
652
|
+
}
|
|
648
653
|
}
|
|
649
654
|
|
|
650
655
|
depends_on = [aws_s3_bucket_policy.website_cloudfront_policy]
|
|
@@ -674,6 +679,7 @@ resource "null_resource" "cloudfront_invalidation" {
|
|
|
674
679
|
command = <<-EOT
|
|
675
680
|
uv run --with boto3 python3 -c "
|
|
676
681
|
import boto3
|
|
682
|
+
import os
|
|
677
683
|
import sys
|
|
678
684
|
from botocore.exceptions import ClientError, NoCredentialsError
|
|
679
685
|
|
|
@@ -695,7 +701,7 @@ def create_invalidation(distribution_id):
|
|
|
695
701
|
|
|
696
702
|
invalidation_id = response['Invalidation']['Id']
|
|
697
703
|
print(f'CloudFront cache invalidation created: {invalidation_id}')
|
|
698
|
-
print(f'Distribution:
|
|
704
|
+
print(f'Distribution: {distribution_id}')
|
|
699
705
|
print(f'Status: {response[\"Invalidation\"][\"Status\"]}')
|
|
700
706
|
|
|
701
707
|
except NoCredentialsError:
|
|
@@ -709,9 +715,12 @@ def create_invalidation(distribution_id):
|
|
|
709
715
|
sys.exit(1)
|
|
710
716
|
|
|
711
717
|
# Execute invalidation
|
|
712
|
-
create_invalidation(
|
|
718
|
+
create_invalidation(os.environ['DISTRIBUTION_ID'])
|
|
713
719
|
"
|
|
714
720
|
EOT
|
|
721
|
+
environment = {
|
|
722
|
+
DISTRIBUTION_ID = aws_cloudfront_distribution.website.id
|
|
723
|
+
}
|
|
715
724
|
}
|
|
716
725
|
|
|
717
726
|
depends_on = [
|