Skip to content

Merge branch 'develop' into main #3756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion samtranslator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.96.0"
__version__ = "1.97.0"
8 changes: 8 additions & 0 deletions samtranslator/internal/schema_source/aws_serverless_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ class Route53(BaseModel):
SetIdentifier: Optional[PassThroughProp] # TODO: add docs
Region: Optional[PassThroughProp] # TODO: add docs
SeparateRecordSetGroup: Optional[bool] # TODO: add docs
VpcEndpointDomainName: Optional[PassThroughProp] # TODO: add docs
VpcEndpointHostedZoneId: Optional[PassThroughProp] # TODO: add docs


class AccessAssociation(BaseModel):
VpcEndpointId: PassThroughProp # TODO: add docs


class Domain(BaseModel):
Expand Down Expand Up @@ -185,6 +191,7 @@ class Domain(BaseModel):
"SecurityPolicy",
["AWS::ApiGateway::DomainName", "Properties", "SecurityPolicy"],
)
AccessAssociation: Optional[AccessAssociation]


class DefinitionUri(BaseModel):
Expand Down Expand Up @@ -307,6 +314,7 @@ class Properties(BaseModel):
OpenApiVersion: Optional[OpenApiVersion] = properties("OpenApiVersion")
StageName: SamIntrinsicable[str] = properties("StageName")
Tags: Optional[DictStrAny] = properties("Tags")
Policy: Optional[PassThroughProp] # TODO: add docs
PropagateTags: Optional[bool] # TODO: add docs
TracingEnabled: Optional[TracingEnabled] = passthrough_prop(
PROPERTIES_STEM,
Expand Down
56 changes: 52 additions & 4 deletions samtranslator/model/api/api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
ApiGatewayBasePathMappingV2,
ApiGatewayDeployment,
ApiGatewayDomainName,
ApiGatewayDomainNameAccessAssociation,
ApiGatewayDomainNameV2,
ApiGatewayResponse,
ApiGatewayRestApi,
Expand Down Expand Up @@ -86,6 +87,7 @@ class ApiDomainResponseV2:
domain: Optional[ApiGatewayDomainNameV2]
apigw_basepath_mapping_list: Optional[List[ApiGatewayBasePathMappingV2]]
recordset_group: Any
domain_access_association: Any


class SharedApiUsagePlan:
Expand Down Expand Up @@ -218,6 +220,7 @@ def __init__( # noqa: PLR0913
api_key_source_type: Optional[Intrinsicable[str]] = None,
always_deploy: Optional[bool] = False,
feature_toggle: Optional[FeatureToggle] = None,
policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]] = None,
):
"""Constructs an API Generator class that generates API Gateway resources

Expand Down Expand Up @@ -275,6 +278,7 @@ def __init__( # noqa: PLR0913
self.api_key_source_type = api_key_source_type
self.always_deploy = always_deploy
self.feature_toggle = feature_toggle
self.policy = policy

def _construct_rest_api(self) -> ApiGatewayRestApi:
"""Constructs and returns the ApiGateway RestApi.
Expand Down Expand Up @@ -328,6 +332,9 @@ def _construct_rest_api(self) -> ApiGatewayRestApi:
if self.api_key_source_type:
rest_api.ApiKeySourceType = self.api_key_source_type

if self.policy:
rest_api.Policy = self.policy

return rest_api

def _validate_properties(self) -> None:
Expand Down Expand Up @@ -602,7 +609,7 @@ def _construct_api_domain_v2(
Constructs and returns the ApiGateway Domain V2 and BasepathMapping V2
"""
if self.domain is None:
return ApiDomainResponseV2(None, None, None)
return ApiDomainResponseV2(None, None, None, None)

sam_expect(self.domain, self.logical_id, "Domain").to_be_a_map()
domain_name: PassThrough = sam_expect(
Expand Down Expand Up @@ -657,6 +664,14 @@ def _construct_api_domain_v2(
basepath_mapping.BasePath = path if normalize_basepath else basepath
basepath_resource_list.extend([basepath_mapping])

# Create the DomainNameAccessAssociation
domain_access_association = self.domain.get("AccessAssociation")
domain_access_association_resource = None
if domain_access_association is not None:
domain_access_association_resource = self._generate_domain_access_association(
domain_access_association, domain_name_arn, api_domain_name
)

# Create the Route53 RecordSetGroup resource
record_set_group = None
route53 = self.domain.get("Route53")
Expand All @@ -683,6 +698,7 @@ def _construct_api_domain_v2(
domain,
basepath_resource_list,
self._construct_single_record_set_group(self.domain, domain_name, route53),
domain_access_association_resource,
)

if not record_set_group:
Expand All @@ -691,7 +707,7 @@ def _construct_api_domain_v2(

record_set_group.RecordSets += self._construct_record_sets_for_domain(self.domain, domain_name, route53)

return ApiDomainResponseV2(domain, basepath_resource_list, record_set_group)
return ApiDomainResponseV2(domain, basepath_resource_list, record_set_group, domain_access_association_resource)

def _get_basepaths(self) -> Optional[List[str]]:
if self.domain is None:
Expand Down Expand Up @@ -779,11 +795,14 @@ def _construct_alias_target(self, domain: Dict[str, Any], api_domain_name: str,
if domain.get("EndpointConfiguration") == "REGIONAL":
alias_target["HostedZoneId"] = fnGetAtt(api_domain_name, "RegionalHostedZoneId")
alias_target["DNSName"] = fnGetAtt(api_domain_name, "RegionalDomainName")
else:
elif domain.get("EndpointConfiguration") == "EDGE":
if route53.get("DistributionDomainName") is None:
route53["DistributionDomainName"] = fnGetAtt(api_domain_name, "DistributionDomainName")
alias_target["HostedZoneId"] = "Z2FDTNDATAQYW2"
alias_target["DNSName"] = route53.get("DistributionDomainName")
else:
alias_target["HostedZoneId"] = route53.get("VpcEndpointHostedZoneId")
alias_target["DNSName"] = route53.get("VpcEndpointDomainName")
return alias_target

def _create_basepath_mapping(
Expand Down Expand Up @@ -833,12 +852,17 @@ def to_cloudformation(
domain: Union[Resource, None]
basepath_mapping: Union[List[ApiGatewayBasePathMapping], List[ApiGatewayBasePathMappingV2], None]
rest_api = self._construct_rest_api()
is_private_domain = isinstance(self.domain, dict) and self.domain.get("EndpointConfiguration") == "PRIVATE"
api_domain_response = (
self._construct_api_domain_v2(rest_api, route53_record_set_groups)
if isinstance(self.domain, dict) and self.domain.get("EndpointConfiguration") == "PRIVATE"
if is_private_domain
else self._construct_api_domain(rest_api, route53_record_set_groups)
)

domain_access_association = None
if is_private_domain:
domain_access_association = cast(ApiDomainResponseV2, api_domain_response).domain_access_association

domain = api_domain_response.domain
basepath_mapping = api_domain_response.apigw_basepath_mapping_list

Expand Down Expand Up @@ -882,6 +906,9 @@ def to_cloudformation(
]
)

if domain_access_association is not None:
generated_resources.append(domain_access_association)

# Make a list of single resources
generated_resources_list: List[Resource] = []
for resource in generated_resources:
Expand Down Expand Up @@ -1513,3 +1540,24 @@ def _set_endpoint_configuration(self, rest_api: ApiGatewayRestApi, value: Union[
else:
rest_api.EndpointConfiguration = {"Types": [value]}
rest_api.Parameters = {"endpointConfigurationTypes": value}

def _generate_domain_access_association(
self,
domain_access_association: Dict[str, Any],
domain_name_arn: Dict[str, str],
domain_logical_id: str,
) -> ApiGatewayDomainNameAccessAssociation:
"""
Generate domain access association resource
"""
vpcEndpointId = domain_access_association.get("VpcEndpointId")
logical_id = LogicalIdGenerator("DomainNameAccessAssociation", [vpcEndpointId, domain_logical_id]).gen()

domain_access_association_resource = ApiGatewayDomainNameAccessAssociation(
logical_id, attributes=self.passthrough_resource_attributes
)
domain_access_association_resource.DomainNameArn = domain_name_arn
domain_access_association_resource.AccessAssociationSourceType = "VPCE"
domain_access_association_resource.AccessAssociationSource = vpcEndpointId

return domain_access_association_resource
12 changes: 12 additions & 0 deletions samtranslator/model/apigateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ApiGatewayRestApi(Resource):
"Mode": GeneratedProperty(),
"ApiKeySourceType": GeneratedProperty(),
"Tags": GeneratedProperty(),
"Policy": GeneratedProperty(),
}

Body: Optional[Dict[str, Any]]
Expand All @@ -44,6 +45,7 @@ class ApiGatewayRestApi(Resource):
Mode: Optional[PassThrough]
ApiKeySourceType: Optional[PassThrough]
Tags: Optional[PassThrough]
Policy: Optional[PassThrough]

runtime_attrs = {"rest_api_id": lambda self: ref(self.logical_id)}

Expand Down Expand Up @@ -307,6 +309,16 @@ class ApiGatewayApiKey(Resource):
runtime_attrs = {"api_key_id": lambda self: ref(self.logical_id)}


class ApiGatewayDomainNameAccessAssociation(Resource):
resource_type = "AWS::ApiGateway::DomainNameAccessAssociation"
property_types = {
"AccessAssociationSource": GeneratedProperty(),
"AccessAssociationSourceType": GeneratedProperty(),
"DomainNameArn": GeneratedProperty(),
"Tags": GeneratedProperty(),
}


class ApiGatewayAuthorizer:
_VALID_FUNCTION_PAYLOAD_TYPES = [None, "TOKEN", "REQUEST"]

Expand Down
5 changes: 4 additions & 1 deletion samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" SAM macro definitions """
""" SAM macro definitions """

import copy
from contextlib import suppress
Expand Down Expand Up @@ -1275,6 +1275,7 @@ class SamApi(SamResourceMacro):
"DisableExecuteApiEndpoint": PropertyType(False, IS_BOOL),
"ApiKeySourceType": PropertyType(False, IS_STR),
"AlwaysDeploy": Property(False, IS_BOOL),
"Policy": PropertyType(False, one_of(IS_STR, IS_DICT)),
}

Name: Optional[Intrinsicable[str]]
Expand Down Expand Up @@ -1306,6 +1307,7 @@ class SamApi(SamResourceMacro):
DisableExecuteApiEndpoint: Optional[Intrinsicable[bool]]
ApiKeySourceType: Optional[Intrinsicable[str]]
AlwaysDeploy: Optional[bool]
Policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]]

referable_properties = {
"Stage": ApiGatewayStage.resource_type,
Expand Down Expand Up @@ -1373,6 +1375,7 @@ def to_cloudformation(self, **kwargs) -> List[Resource]: # type: ignore[no-unty
api_key_source_type=self.ApiKeySourceType,
always_deploy=self.AlwaysDeploy,
feature_toggle=feature_toggle,
policy=self.Policy,
)

generated_resources = api_generator.to_cloudformation(redeploy_restapi_parameters, route53_record_set_groups)
Expand Down
Loading