Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"LogicalResourceId": "MyApi",
"ResourceType": "AWS::ApiGateway::RestApi"
},
{
"LogicalResourceId": "MyApiDeployment",
"ResourceType": "AWS::ApiGateway::Deployment"
},
{
"LogicalResourceId": "MyApiProdStage",
"ResourceType": "AWS::ApiGateway::Stage"
},
{
"LogicalResourceId": "ApiGatewayDomainName",
"ResourceType": "AWS::ApiGateway::DomainName"
},
{
"LogicalResourceId": "MyApiBasePathMapping",
"ResourceType": "AWS::ApiGateway::BasePathMapping"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"LogicalResourceId": "MyApi",
"ResourceType": "AWS::ApiGateway::RestApi"
},
{
"LogicalResourceId": "MyApiDeployment",
"ResourceType": "AWS::ApiGateway::Deployment"
},
{
"LogicalResourceId": "MyApiProdStage",
"ResourceType": "AWS::ApiGateway::Stage"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Parameters:
IpAddressType:
Type: String
Default: dualstack
DomainName:
Type: String
CertificateArn:
Type: String

Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionUri: ${definitionuri}
Domain:
DomainName: !Ref DomainName
CertificateArn: !Ref CertificateArn
EndpointConfiguration: REGIONAL
IpAddressType: !Ref IpAddressType

Metadata:
SamTransformTest: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Parameters:
IpAddressType:
Type: String
Default: ipv4

Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionUri: ${definitionuri}
EndpointConfiguration:
Type: REGIONAL
IpAddressType: !Ref IpAddressType
Metadata:
SamTransformTest: true
35 changes: 35 additions & 0 deletions integration/single/test_api_with_domain_ipaddresstype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from unittest.case import skipIf

from integration.config.service_names import REST_API
from integration.helpers.base_internal_test import BaseInternalTest
from integration.helpers.resource import current_region_does_not_support


@skipIf(
current_region_does_not_support([REST_API]),
"Rest API is not supported in this testing region",
)
class TestApiWithDomainIpAddressType(BaseInternalTest):
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this kind of test uses internal domain / certificate's that are already created I was not able to run it.

"""
Test AWS::Serverless::Api with IpAddressType in Domain configuration
"""

def test_api_with_domain_ipaddresstype(self):
"""
Creates an API with custom domain and IpAddressType set to dualstack
"""
self.create_and_verify_stack("single/api_with_domain_ipaddresstype")

# Verify the domain name resource
domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName")
api_gateway_client = self.client_provider.api_client
result = api_gateway_client.get_domain_name(domainName=domain_name_id)

# Verify endpoint configuration
end_point_config = result["endpointConfiguration"]
end_point_types = end_point_config["types"]
self.assertEqual(1, len(end_point_types))
self.assertEqual("REGIONAL", end_point_types[0])

# Verify IpAddressType is set correctly
self.assertEqual("dualstack", end_point_config["ipAddressType"])
25 changes: 25 additions & 0 deletions integration/single/test_api_with_ipaddresstype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from unittest.case import skipIf

from integration.config.service_names import REST_API
from integration.helpers.base_test import BaseTest
from integration.helpers.resource import current_region_does_not_support


@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region")
class TestApiWithIpAddressType(BaseTest):
"""
Test AWS::Serverless::Api with IpAddressType in EndpointConfiguration
"""

def test_api_with_ipaddresstype(self):
"""
Creates an API with IpAddressType set to ipv4
"""
parameters = [{"ParameterKey": "IpAddressType", "ParameterValue": "ipv4"}]
self.create_and_verify_stack("single/api_with_ipaddresstype", parameters)

rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi")
rest_api = self.client_provider.api_client.get_rest_api(restApiId=rest_api_id)

self.assertEqual(rest_api["endpointConfiguration"]["types"], ["REGIONAL"])
self.assertEqual(rest_api["endpointConfiguration"]["ipAddressType"], "ipv4")
10 changes: 10 additions & 0 deletions samtranslator/internal/schema_source/aws_serverless_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ class Domain(BaseModel):
EndpointConfiguration: Optional[SamIntrinsicable[Literal["REGIONAL", "EDGE", "PRIVATE"]]] = domain(
"EndpointConfiguration"
)
IpAddressType: Optional[PassThroughProp] = passthrough_prop(
DOMAIN_STEM,
"IpAddressType",
["AWS::ApiGateway::DomainName.EndpointConfiguration", "IpAddressType"],
)
MutualTlsAuthentication: Optional[PassThroughProp] = passthrough_prop(
DOMAIN_STEM,
"MutualTlsAuthentication",
Expand Down Expand Up @@ -223,6 +228,11 @@ class EndpointConfiguration(BaseModel):
"VPCEndpointIds",
["AWS::ApiGateway::RestApi.EndpointConfiguration", "VpcEndpointIds"],
)
IpAddressType: Optional[PassThroughProp] = passthrough_prop(
ENDPOINT_CONFIGURATION_STEM,
"IpAddressType",
["AWS::ApiGateway::RestApi.EndpointConfiguration", "IpAddressType"],
)


Name = Optional[PassThroughProp]
Expand Down
8 changes: 5 additions & 3 deletions samtranslator/internal/schema_source/sam-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@
"NormalizeBasePath": "Indicates whether non\\-alphanumeric characters are allowed in basepaths defined by the `BasePath` property\\. When set to `True`, non\\-alphanumeric characters are removed from basepaths\\. \nUse `NormalizeBasePath` with the `BasePath` property\\. \n*Type*: Boolean \n*Required*: No \n*Default*: True \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
"OwnershipVerificationCertificateArn": "The ARN of the public certificate issued by ACM to validate ownership of your custom domain\\. Required only when you configure mutual TLS and you specify an ACM imported or private CA certificate ARN for the `CertificateArn`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`OwnershipVerificationCertificateArn`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-ownershipverificationcertificatearn) property of an `AWS::ApiGateway::DomainName` resource\\.",
"Route53": "Defines an Amazon Route\u00a053 configuration\\. \n*Type*: [Route53Configuration](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-route53configuration.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
"SecurityPolicy": "The TLS version plus cipher suite for this domain name\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-securitypolicy) property of an `AWS::ApiGateway::DomainName` resource\\."
"SecurityPolicy": "The TLS version plus cipher suite for this domain name\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-securitypolicy) property of an `AWS::ApiGateway::DomainName` resource\\.",
"IpAddressType": "The IP address types that can invoke this DomainName. Use `ipv4` to allow only IPv4 addresses to invoke this DomainName, or use `dualstack` to allow both IPv4 and IPv6 addresses to invoke this DomainName. For the `PRIVATE` endpoint type, only `dualstack` is supported. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`IpAddressType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-domainname-endpointconfiguration.html#cfn-apigateway-domainname-endpointconfiguration-ipaddresstype) property of an `AWS::ApiGateway::DomainName.EndpointConfiguration` resource."
},
"sam-property-api-endpointconfiguration": {
"Type": "The endpoint type of a REST API\\. \n*Valid values*: `EDGE` or `REGIONAL` or `PRIVATE` \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`Types`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html#cfn-apigateway-restapi-endpointconfiguration-types) property of the `AWS::ApiGateway::RestApi` `EndpointConfiguration` data type\\.",
"VPCEndpointIds": "A list of VPC endpoint IDs of a REST API against which to create Route53 aliases\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`VpcEndpointIds`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html#cfn-apigateway-restapi-endpointconfiguration-vpcendpointids) property of the `AWS::ApiGateway::RestApi` `EndpointConfiguration` data type\\."
"VPCEndpointIds": "A list of VPC endpoint IDs of a REST API against which to create Route53 aliases\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`VpcEndpointIds`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html#cfn-apigateway-restapi-endpointconfiguration-vpcendpointids) property of the `AWS::ApiGateway::RestApi` `EndpointConfiguration` data type\\.",
"IpAddressType": "The IP address type for the API Gateway endpoint\\. \n*Valid values*: `ipv4` or `dualstack` \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`IpAddressType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html#cfn-apigateway-restapi-endpointconfiguration-ipaddresstype) property of the `AWS::ApiGateway::RestApi` `EndpointConfiguration` data type\\."
},
"sam-property-api-lambdarequestauthorizationidentity": {
"Context": "Converts the given context strings to the mapping expressions of format `context.contextString`\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
Expand Down Expand Up @@ -799,4 +801,4 @@
"UseAliasAsEventTarget": "Indicate whether or not to pass the alias, created by using the `AutoPublishAlias` property, to the events source's target defined with [Events](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/#sam-statemachine-events.html#sam-statemachine-events)\\. \nSpecify `True` to use the alias as the events' target\\. \n*Type*: Boolean \n*Required*: No \n*Default*: `False` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\."
}
}
}
}
18 changes: 16 additions & 2 deletions samtranslator/model/api/api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ def _construct_stage(

return stage

def _construct_api_domain( # noqa: PLR0912, PLR0915
def _construct_api_domain( # noqa: PLR0912, PLR0915 (too many branches/statements)
self, rest_api: ApiGatewayRestApi, route53_record_set_groups: Any
) -> ApiDomainResponse:
"""
Expand Down Expand Up @@ -512,6 +512,11 @@ def _construct_api_domain( # noqa: PLR0912, PLR0915

domain.EndpointConfiguration = {"Types": [endpoint]}

# Handle IpAddressType if present
ip_address_type = self.domain.get("IpAddressType")
if ip_address_type:
domain.EndpointConfiguration["IpAddressType"] = ip_address_type

mutual_tls_auth = self.domain.get("MutualTlsAuthentication", None)
if mutual_tls_auth:
sam_expect(mutual_tls_auth, self.logical_id, "Domain.MutualTlsAuthentication").to_be_a_map()
Expand Down Expand Up @@ -602,7 +607,7 @@ def _construct_api_domain( # noqa: PLR0912, PLR0915

return ApiDomainResponse(domain, basepath_resource_list, record_set_group)

def _construct_api_domain_v2(
def _construct_api_domain_v2( # noqa: PLR0915
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three lines I added put this over the tipping point

self, rest_api: ApiGatewayRestApi, route53_record_set_groups: Any
) -> ApiDomainResponseV2:
"""
Expand Down Expand Up @@ -637,6 +642,11 @@ def _construct_api_domain_v2(

domain.EndpointConfiguration = {"Types": [endpoint]}

# Handle IpAddressType if present
ip_address_type = self.domain.get("IpAddressType")
if ip_address_type:
domain.EndpointConfiguration["IpAddressType"] = ip_address_type

self._set_optional_domain_properties(domain)

basepaths: Optional[List[str]] = self._get_basepaths()
Expand Down Expand Up @@ -1537,6 +1547,10 @@ def _set_endpoint_configuration(self, rest_api: ApiGatewayRestApi, value: Union[
rest_api.EndpointConfiguration["VpcEndpointIds"] = value.get("VPCEndpointIds") or value.get(
"VpcEndpointIds"
)

# Handle IpAddressType if present
if "IpAddressType" in value:
rest_api.EndpointConfiguration["IpAddressType"] = value.get("IpAddressType")
else:
rest_api.EndpointConfiguration = {"Types": [value]}
rest_api.Parameters = {"endpointConfigurationTypes": value}
Expand Down
20 changes: 20 additions & 0 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5622,6 +5622,11 @@
"AWS::ApiGateway::DomainName.EndpointConfiguration": {
"additionalProperties": false,
"properties": {
"IpAddressType": {
"markdownDescription": "The IP address types that can invoke this DomainName. Use `ipv4` to allow only IPv4 addresses to invoke this DomainName, or use `dualstack` to allow both IPv4 and IPv6 addresses to invoke this DomainName. For the `PRIVATE` endpoint type, only `dualstack` is supported.",
"title": "IpAddressType",
"type": "string"
},
"Types": {
"items": {
"type": "string"
Expand Down Expand Up @@ -6478,6 +6483,11 @@
"AWS::ApiGateway::RestApi.EndpointConfiguration": {
"additionalProperties": false,
"properties": {
"IpAddressType": {
"markdownDescription": "The IP address types that can invoke an API (RestApi). Use `ipv4` to allow only IPv4 addresses to invoke an API, or use `dualstack` to allow both IPv4 and IPv6 addresses to invoke an API. For the `PRIVATE` endpoint type, only `dualstack` is supported.",
"title": "IpAddressType",
"type": "string"
},
"Types": {
"items": {
"type": "string"
Expand Down Expand Up @@ -274565,6 +274575,11 @@
"EndpointConfiguration": {
"additionalProperties": false,
"properties": {
"IpAddressType": {
"markdownDescription": "The IP address type for the API Gateway endpoint\\. \n*Valid values*: `ipv4` or `dualstack` \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`IpAddressType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html#cfn-apigateway-restapi-endpointconfiguration-ipaddresstype) property of the `AWS::ApiGateway::RestApi` `EndpointConfiguration` data type\\.",
"title": "IpAddressType",
"type": "string"
},
"Type": {
"items": {
"type": "string"
Expand Down Expand Up @@ -277056,6 +277071,11 @@
"markdownDescription": "Defines the type of API Gateway endpoint to map to the custom domain\\. The value of this property determines how the `CertificateArn` property is mapped in AWS CloudFormation\\. \n*Valid values*: `REGIONAL` or `EDGE` \n*Type*: String \n*Required*: No \n*Default*: `REGIONAL` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
"title": "EndpointConfiguration"
},
"IpAddressType": {
"markdownDescription": "The IP address types that can invoke this DomainName. Use `ipv4` to allow only IPv4 addresses to invoke this DomainName, or use `dualstack` to allow both IPv4 and IPv6 addresses to invoke this DomainName. For the `PRIVATE` endpoint type, only `dualstack` is supported. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`IpAddressType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-domainname-endpointconfiguration.html#cfn-apigateway-domainname-endpointconfiguration-ipaddresstype) property of an `AWS::ApiGateway::DomainName.EndpointConfiguration` resource.",
"title": "IpAddressType",
"type": "string"
},
"MutualTlsAuthentication": {
"$ref": "#/definitions/AWS::ApiGateway::DomainName.MutualTlsAuthentication",
"markdownDescription": "The mutual Transport Layer Security \\(TLS\\) authentication configuration for a custom domain name\\. \n*Type*: [MutualTlsAuthentication](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-mutualtlsauthentication) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MutualTlsAuthentication`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-mutualtlsauthentication) property of an `AWS::ApiGateway::DomainName` resource\\.",
Expand Down
Loading