Skip to content

Commit 152fb52

Browse files
committed
Add CDK stack
1 parent f9e8d9e commit 152fb52

8 files changed

Lines changed: 558 additions & 0 deletions

File tree

cdk.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"app": "python3 infrastructure/app.py",
3+
"context": {
4+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
5+
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
6+
"@aws-cdk/core:stackRelativeExports": true,
7+
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
8+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
9+
"@aws-cdk/core:checkSecretUsage": true,
10+
"@aws-cdk/aws-iam:minimizePolicies": true,
11+
"@aws-cdk/core:enablePartitionLiterals": true,
12+
"@aws-cdk/core:target-partitions": [
13+
"aws",
14+
"aws-cn"
15+
]
16+
}
17+
}

infrastructure/app.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env python3
2+
import aws_cdk as cdk
3+
4+
from stack.stack import ServerlessRustStack
5+
6+
app = cdk.App()
7+
ServerlessRustStack(app, "ServerlessRustStack")
8+
9+
app.synth()

infrastructure/stack/__init__.py

Whitespace-only changes.

infrastructure/stack/api.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from aws_cdk.aws_apigatewayv2 import (
2+
CorsHttpMethod,
3+
CorsPreflightOptions,
4+
HttpApi,
5+
HttpMethod,
6+
)
7+
from aws_cdk.aws_apigatewayv2_authorizers import HttpJwtAuthorizer
8+
from aws_cdk.aws_apigatewayv2_integrations import HttpLambdaIntegration
9+
from aws_cdk.aws_cognito import UserPool, UserPoolClient
10+
from aws_cdk.aws_lambda import IFunction
11+
from constructs import Construct
12+
13+
ALLOWED_HEADERS = ["Authorization", "Content-Type"]
14+
ALLOWED_METHODS = [
15+
CorsHttpMethod.GET,
16+
CorsHttpMethod.OPTIONS,
17+
CorsHttpMethod.POST,
18+
]
19+
20+
21+
class ServerlessApi(Construct):
22+
def __init__(
23+
self,
24+
scope: Construct,
25+
construct_id: str,
26+
*,
27+
handler: IFunction,
28+
user_pool: UserPool,
29+
user_pool_client: UserPoolClient,
30+
) -> None:
31+
super().__init__(scope, construct_id)
32+
33+
self._api = self.build_http_api()
34+
authorizer = self.build_authorizer(user_pool, user_pool_client)
35+
self.setup_lambda_integration(self._api, handler, authorizer)
36+
37+
@property
38+
def endpoint_url(self) -> str:
39+
return self._api.api_endpoint
40+
41+
def build_http_api(self) -> HttpApi:
42+
cors_options = CorsPreflightOptions(
43+
allow_headers=ALLOWED_HEADERS, allow_methods=ALLOWED_METHODS
44+
)
45+
return HttpApi(self, "ServerlessRustApi", cors_preflight=cors_options)
46+
47+
@staticmethod
48+
def build_authorizer(
49+
user_pool: UserPool, user_pool_client: UserPoolClient
50+
) -> HttpJwtAuthorizer:
51+
issuer = f"https://cognito-idp.{user_pool.env.region}.amazonaws.com/{user_pool.user_pool_id}"
52+
return HttpJwtAuthorizer(
53+
"JwtAuthorizer", issuer, jwt_audience=[user_pool_client.user_pool_client_id]
54+
)
55+
56+
@staticmethod
57+
def setup_lambda_integration(
58+
api: HttpApi, handler: IFunction, authorizer: HttpJwtAuthorizer
59+
) -> None:
60+
integration = HttpLambdaIntegration("LambdaIntegration", handler)
61+
api.add_routes(
62+
path="/{proxy+}",
63+
methods=[HttpMethod.GET, HttpMethod.POST],
64+
authorizer=authorizer,
65+
integration=integration,
66+
)

infrastructure/stack/stack.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import pathlib
2+
3+
from aws_cdk import CfnOutput, Stack
4+
from aws_cdk.aws_lambda import Architecture, Code, Function, Runtime
5+
from aws_cdk.aws_cognito import (
6+
UserPool,
7+
StandardAttributes,
8+
StandardAttribute,
9+
AuthFlow,
10+
UserPoolClient,
11+
)
12+
from aws_cdk.aws_dynamodb import Table, Attribute, AttributeType
13+
from constructs import Construct
14+
15+
from stack.api import ServerlessApi
16+
17+
18+
class ServerlessRustStack(Stack):
19+
def __init__(self, scope: Construct, construct_id: str) -> None:
20+
super().__init__(scope, construct_id)
21+
22+
database_table = self.create_database_table()
23+
handler = self.create_function(
24+
"serverless", table_name=database_table.table_name
25+
)
26+
database_table.grant_read_write_data(handler)
27+
28+
user_pool, user_pool_client = self.create_user_pool()
29+
api = ServerlessApi(
30+
self,
31+
"ServerlessApi",
32+
handler=handler,
33+
user_pool=user_pool,
34+
user_pool_client=user_pool_client,
35+
)
36+
37+
CfnOutput(
38+
self,
39+
"ApiEndpointUrl",
40+
value=api.endpoint_url,
41+
description="The URL of the API endpoint.",
42+
)
43+
44+
def create_database_table(self) -> Table:
45+
partition_key = Attribute(name="pk", type=AttributeType.STRING)
46+
return Table(
47+
self,
48+
"DatabaseTable",
49+
table_name="currencies",
50+
partition_key=partition_key,
51+
)
52+
53+
def create_function(self, bin_name: str, *, table_name: str) -> Function:
54+
code_path = pathlib.Path.cwd() / "target" / "lambda" / bin_name
55+
code = Code.from_asset(code_path.as_posix())
56+
57+
return Function(
58+
self,
59+
"ApiHandler",
60+
code=code,
61+
architecture=Architecture.ARM_64,
62+
runtime=Runtime.PROVIDED_AL2023,
63+
memory_size=256,
64+
handler="does-not-matter",
65+
environment={
66+
"TABLE_NAME": table_name,
67+
},
68+
)
69+
70+
def create_user_pool(self) -> tuple[UserPool, UserPoolClient]:
71+
standard_attributes = StandardAttributes(
72+
given_name=StandardAttribute(required=True, mutable=True),
73+
family_name=StandardAttribute(required=True, mutable=True),
74+
)
75+
user_pool = UserPool(
76+
self,
77+
"UserPool",
78+
user_pool_name="rust-demo",
79+
self_sign_up_enabled=False,
80+
standard_attributes=standard_attributes,
81+
)
82+
auth_flows = AuthFlow(admin_user_password=True, user_srp=True)
83+
user_pool_client = user_pool.add_client("app-client", auth_flows=auth_flows)
84+
85+
return user_pool, user_pool_client

0 commit comments

Comments
 (0)