Skip to content
This repository was archived by the owner on Jul 21, 2025. It is now read-only.

Commit a1d7b82

Browse files
hrodmngadomski
andauthored
feat: configure custom domain for API (#80)
--------- Co-authored-by: Pete Gadomski <[email protected]>
1 parent f1f3351 commit a1d7b82

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ jobs:
5353
STACK_BUCKET_NAME: stac-fastapi-geoparquet-labs-375
5454
STACK_COLLECTIONS_KEY: collections.json
5555
STACK_RATE_LIMIT: 10
56+
STACK_API_CUSTOM_DOMAIN: ${{ vars.API_CUSTOM_DOMAIN }}
57+
STACK_ACM_CERTIFICATE_ARN: ${{ vars.ACM_CERTIFICATE_ARN }}
5658
environment:
5759
name: development
5860
url: ${{ steps.deploy.outputs.api_url }}

docs/katas/8_planetary_computer.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@
13181318
"YEARS = [2024]\n",
13191319
"number_of_items = 0\n",
13201320
"\n",
1321+
"\n",
13211322
"def clean_dict(item: dict[str, Any]) -> dict[str, Any]:\n",
13221323
" for key, value in item.items():\n",
13231324
" if hasattr(value, \"tolist\"):\n",
@@ -1328,6 +1329,7 @@
13281329
" item[key] = clean_dict(value)\n",
13291330
" return item\n",
13301331
"\n",
1332+
"\n",
13311333
"Path(\"/Users/gadomski/Desktop/sentinel-2-l2a-fixed.parquet\").mkdir(exist_ok=True)\n",
13321334
"\n",
13331335
"for path in Path(\"/Users/gadomski/Desktop/sentinel-2-l2a.parquet\").glob(\"*.parquet\"):\n",
@@ -1337,9 +1339,7 @@
13371339
" items = []\n",
13381340
" for feature in tqdm(feature_collection[\"features\"]):\n",
13391341
" item = clean_dict(feature[\"properties\"])\n",
1340-
" new_item = {\n",
1341-
" \"geometry\": feature[\"geometry\"]\n",
1342-
" }\n",
1342+
" new_item = {\"geometry\": feature[\"geometry\"]}\n",
13431343
" properties = {}\n",
13441344
" for key, value in item.items():\n",
13451345
" if key in TOP_LEVEL_ATTRIBUTES:\n",
@@ -1355,7 +1355,7 @@
13551355
" ),\n",
13561356
" items,\n",
13571357
" )\n",
1358-
" print(number_of_items, \"items\")\n"
1358+
" print(number_of_items, \"items\")"
13591359
]
13601360
},
13611361
{

infrastructure/aws/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class Config(BaseSettings):
3030
"maximum average requests per second over an extended period of time",
3131
] = 10
3232

33+
# custom domain
34+
api_custom_domain: Optional[str] = None
35+
acm_certificate_arn: Optional[str] = None
36+
3337
# vpc
3438
nat_gateway_count: int = 0
3539

infrastructure/aws/stacks/app.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,25 @@ def __init__(
4646
geoparquet_api = GeoparquetApiLambda(
4747
self, "geoparquet-api", config=config, bucket=bucket
4848
)
49-
assert geoparquet_api.url
50-
CfnOutput(self, "GeoparquetApiURL", value=geoparquet_api.url)
49+
50+
if geoparquet_api.domain_name:
51+
CfnOutput(
52+
self,
53+
"ApiGatewayDomainNameTarget",
54+
value=geoparquet_api.domain_name.regional_domain_name,
55+
description="The target for the CNAME/ALIAS record",
56+
)
57+
CfnOutput(
58+
self,
59+
"GeoparquetApiURL",
60+
value=f"https://{geoparquet_api.domain_name.name}",
61+
description="The custom domain URL for the API",
62+
)
63+
else:
64+
assert geoparquet_api.stage.url
65+
CfnOutput(
66+
self,
67+
"GeoparquetApiURL",
68+
value=geoparquet_api.stage.url,
69+
description="The default stage URL for the API",
70+
)

infrastructure/aws/stacks/constructs/geoparquet_api_lambda.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,17 @@
44
from aws_cdk import (
55
Duration,
66
)
7-
from aws_cdk.aws_apigatewayv2 import HttpApi, HttpStage, ThrottleSettings
7+
from aws_cdk.aws_apigatewayv2 import (
8+
DomainMappingOptions,
9+
DomainName,
10+
HttpApi,
11+
HttpStage,
12+
MappingValue,
13+
ParameterMapping,
14+
ThrottleSettings,
15+
)
816
from aws_cdk.aws_apigatewayv2_integrations import HttpLambdaIntegration
17+
from aws_cdk.aws_certificatemanager import Certificate
918
from aws_cdk.aws_lambda import Code, Function, Handler, Runtime
1019
from aws_cdk.aws_logs import RetentionDays
1120
from config import Config
@@ -42,20 +51,46 @@ def __init__(
4251
},
4352
)
4453
bucket.bucket.grant_read(api_lambda)
45-
api = HttpApi(
54+
55+
self.domain_name = (
56+
DomainName(
57+
self,
58+
"api-domain-name",
59+
domain_name=config.api_custom_domain,
60+
certificate=Certificate.from_certificate_arn(
61+
self,
62+
"api-cdn-certificate",
63+
certificate_arn=config.acm_certificate_arn,
64+
),
65+
)
66+
if config.api_custom_domain and config.acm_certificate_arn
67+
else None
68+
)
69+
70+
self.api = HttpApi(
4671
scope=self,
4772
id="api",
4873
default_integration=HttpLambdaIntegration(
4974
"api-integration",
5075
handler=api_lambda,
76+
parameter_mapping=ParameterMapping().overwrite_header(
77+
"host", MappingValue.custom(self.domain_name.name)
78+
)
79+
if self.domain_name
80+
else None,
5181
),
52-
default_domain_mapping=None, # TODO: enable custom domain name
82+
default_domain_mapping=DomainMappingOptions(
83+
domain_name=self.domain_name,
84+
)
85+
if self.domain_name
86+
else None,
5387
create_default_stage=False, # Important: disable default stage creation
5488
)
55-
stage = HttpStage(
89+
90+
self.stage = HttpStage(
5691
self,
5792
"api-stage",
58-
http_api=api,
93+
http_api=self.api,
5994
auto_deploy=True,
6095
stage_name="$default",
6196
throttle=ThrottleSettings(
@@ -65,4 +100,3 @@ def __init__(
65100
if config.rate_limit
66101
else None,
67102
)
68-
self.url = stage.url

0 commit comments

Comments
 (0)