Skip to content

Commit 1437497

Browse files
GavinZZxazhao
andauthored
Don't strip hyphen from BasePathMapping (#2680)
Co-authored-by: Gavin Zhang <yuanhaoz@amazon.com> Co-authored-by: Xia Zhao <78883180+xazhao@users.noreply.github.com>
1 parent 5ecc134 commit 1437497

File tree

7 files changed

+540
-3
lines changed

7 files changed

+540
-3
lines changed

samtranslator/model/api/api_generator.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,9 @@ def _construct_api_domain(self, rest_api, route53_record_set_groups): # type: i
502502
else:
503503
basepaths = None
504504

505+
# Boolean to allow/disallow symbols in BasePath property
506+
normalize_basepath = self.domain.get("NormalizeBasePath", True)
507+
505508
basepath_resource_list = []
506509

507510
if basepaths is None:
@@ -513,16 +516,19 @@ def _construct_api_domain(self, rest_api, route53_record_set_groups): # type: i
513516
basepath_mapping.Stage = ref(rest_api.logical_id + ".Stage")
514517
basepath_resource_list.extend([basepath_mapping])
515518
else:
516-
for path in basepaths:
517-
path = "".join(e for e in path if e.isalnum())
519+
for basepath in basepaths:
520+
# Remove possible leading and trailing '/' because a base path may only
521+
# contain letters, numbers, and one of "$-_.+!*'()"
522+
path = "".join(e for e in basepath if e.isalnum())
523+
basepath = path if normalize_basepath else basepath
518524
logical_id = "{}{}{}".format(self.logical_id, path, "BasePathMapping")
519525
basepath_mapping = ApiGatewayBasePathMapping(
520526
logical_id, attributes=self.passthrough_resource_attributes
521527
)
522528
basepath_mapping.DomainName = ref(self.domain.get("ApiDomainName"))
523529
basepath_mapping.RestApiId = ref(rest_api.logical_id)
524530
basepath_mapping.Stage = ref(rest_api.logical_id + ".Stage")
525-
basepath_mapping.BasePath = path
531+
basepath_mapping.BasePath = basepath
526532
basepath_resource_list.extend([basepath_mapping])
527533

528534
# Create the Route53 RecordSetGroup resource

samtranslator/schema/aws_serverless_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class Route53(BaseModel):
127127

128128
class Domain(BaseModel):
129129
BasePath: Optional[PassThrough] = domain("BasePath")
130+
NormalizeBasePath: Optional[bool] # TODO: Add documentation for this property
130131
CertificateArn: PassThrough = domain("CertificateArn")
131132
DomainName: PassThrough = domain("DomainName")
132133
EndpointConfiguration: Optional[SamIntrinsicable[Literal["REGIONAL", "EDGE"]]] = domain("EndpointConfiguration")

samtranslator/schema/schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,10 @@
11981198
"description": "A list of the basepaths to configure with the Amazon API Gateway domain name\\. \n*Type*: List \n*Required*: No \n*Default*: / \n*AWS CloudFormation compatibility*: This property is similar to the [`BasePath`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-basepathmapping.html#cfn-apigateway-basepathmapping-basepath) property of an `AWS::ApiGateway::BasePathMapping` resource\\. AWS SAM creates multiple `AWS::ApiGateway::BasePathMapping` resources, one per `BasePath` specified in this property\\.",
11991199
"markdownDescription": "A list of the basepaths to configure with the Amazon API Gateway domain name\\. \n*Type*: List \n*Required*: No \n*Default*: / \n*AWS CloudFormation compatibility*: This property is similar to the [`BasePath`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-basepathmapping.html#cfn-apigateway-basepathmapping-basepath) property of an `AWS::ApiGateway::BasePathMapping` resource\\. AWS SAM creates multiple `AWS::ApiGateway::BasePathMapping` resources, one per `BasePath` specified in this property\\."
12001200
},
1201+
"NormalizeBasePath": {
1202+
"title": "Normalizebasepath",
1203+
"type": "boolean"
1204+
},
12011205
"CertificateArn": {
12021206
"title": "CertificateArn",
12031207
"description": "The Amazon Resource Name \\(ARN\\) of an AWS managed certificate this domain name's endpoint\\. AWS Certificate Manager is the only supported source\\. \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is similar to the [`CertificateArn`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-certificatearn) property of an `AWS::ApiGateway::DomainName` resource\\. If `EndpointConfiguration` is set to `REGIONAL` \\(the default value\\), `CertificateArn` maps to [RegionalCertificateArn](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-regionalcertificatearn) in `AWS::ApiGateway::DomainName`\\. If the `EndpointConfiguration` is set to `EDGE`, `CertificateArn` maps to [CertificateArn](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-certificatearn) in `AWS::ApiGateway::DomainName`\\. \n*Additional notes*: For an `EDGE` endpoint, you must create the certificate in the `us-east-1` AWS Region\\.",

tests/translator/input/api_with_custom_base_path.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,35 @@ Resources:
3232
HostedZoneId:
3333
Ref: HostedZoneId
3434
IpV6: true
35+
36+
MyApiWithoutNormalizedBasePath:
37+
Type: AWS::Serverless::Api
38+
Properties:
39+
OpenApiVersion: 3.0.1
40+
StageName: Prod
41+
Domain:
42+
DomainName: MyEdgeDomainName
43+
CertificateArn: MyEdgeDomainCert
44+
EndpointConfiguration: EDGE
45+
NormalizeBasePath: false
46+
Route53:
47+
HostedZoneId:
48+
Ref: HostedZoneId
49+
IpV6: true
50+
51+
MyApiWithoutNormalizedBasePathWithHyphen:
52+
Type: AWS::Serverless::Api
53+
Properties:
54+
OpenApiVersion: 3.0.1
55+
StageName: Prod
56+
Domain:
57+
DomainName: MyEdgeDomainName
58+
CertificateArn: MyEdgeDomainCert
59+
EndpointConfiguration: EDGE
60+
NormalizeBasePath: false
61+
BasePath:
62+
- /foo-too
63+
Route53:
64+
HostedZoneId:
65+
Ref: HostedZoneId
66+
IpV6: true

tests/translator/output/api_with_custom_base_path.json

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,108 @@
9999
},
100100
"Type": "AWS::ApiGateway::Stage"
101101
},
102+
"MyApiWithoutNormalizedBasePath": {
103+
"Properties": {
104+
"Body": {
105+
"info": {
106+
"title": {
107+
"Ref": "AWS::StackName"
108+
},
109+
"version": "1.0"
110+
},
111+
"openapi": "3.0.1",
112+
"paths": {}
113+
}
114+
},
115+
"Type": "AWS::ApiGateway::RestApi"
116+
},
117+
"MyApiWithoutNormalizedBasePathDeployment11d19722bb": {
118+
"Properties": {
119+
"Description": "RestApi deployment id: 11d19722bb8994eaba07f229ab985f06d30c42d6",
120+
"RestApiId": {
121+
"Ref": "MyApiWithoutNormalizedBasePath"
122+
}
123+
},
124+
"Type": "AWS::ApiGateway::Deployment"
125+
},
126+
"MyApiWithoutNormalizedBasePathProdStage": {
127+
"Properties": {
128+
"DeploymentId": {
129+
"Ref": "MyApiWithoutNormalizedBasePathDeployment11d19722bb"
130+
},
131+
"RestApiId": {
132+
"Ref": "MyApiWithoutNormalizedBasePath"
133+
},
134+
"StageName": "Prod"
135+
},
136+
"Type": "AWS::ApiGateway::Stage"
137+
},
138+
"MyApiWithoutNormalizedBasePathWithHyphen": {
139+
"Properties": {
140+
"Body": {
141+
"info": {
142+
"title": {
143+
"Ref": "AWS::StackName"
144+
},
145+
"version": "1.0"
146+
},
147+
"openapi": "3.0.1",
148+
"paths": {}
149+
}
150+
},
151+
"Type": "AWS::ApiGateway::RestApi"
152+
},
153+
"MyApiWithoutNormalizedBasePathWithHyphenDeployment43b3fbff50": {
154+
"Properties": {
155+
"Description": "RestApi deployment id: 43b3fbff506a380a3dd89acf64bd0731b1819dd8",
156+
"RestApiId": {
157+
"Ref": "MyApiWithoutNormalizedBasePathWithHyphen"
158+
}
159+
},
160+
"Type": "AWS::ApiGateway::Deployment"
161+
},
162+
"MyApiWithoutNormalizedBasePathWithHyphenProdStage": {
163+
"Properties": {
164+
"DeploymentId": {
165+
"Ref": "MyApiWithoutNormalizedBasePathWithHyphenDeployment43b3fbff50"
166+
},
167+
"RestApiId": {
168+
"Ref": "MyApiWithoutNormalizedBasePathWithHyphen"
169+
},
170+
"StageName": "Prod"
171+
},
172+
"Type": "AWS::ApiGateway::Stage"
173+
},
174+
"MyApiWithoutNormalizedBasePathWithHyphenfootooBasePathMapping": {
175+
"Properties": {
176+
"BasePath": "/foo-too",
177+
"DomainName": {
178+
"Ref": "ApiGatewayDomainName67a385f467"
179+
},
180+
"RestApiId": {
181+
"Ref": "MyApiWithoutNormalizedBasePathWithHyphen"
182+
},
183+
"Stage": {
184+
"Ref": "MyApiWithoutNormalizedBasePathWithHyphenProdStage"
185+
}
186+
},
187+
"Type": "AWS::ApiGateway::BasePathMapping"
188+
},
189+
"MyApiWithoutNormalizedBasePathfoobarBasePathMapping": {
190+
"Properties": {
191+
"BasePath": "foo-bar",
192+
"DomainName": {
193+
"Ref": "ApiGatewayDomainName67a385f467"
194+
},
195+
"RestApiId": {
196+
"Ref": "MyApiWithoutNormalizedBasePath"
197+
},
198+
"Stage": {
199+
"Ref": "MyApiWithoutNormalizedBasePathProdStage"
200+
}
201+
},
202+
"Type": "AWS::ApiGateway::BasePathMapping"
203+
},
102204
"MyApifootooBasePathMapping": {
103205
"Properties": {
104206
"BasePath": "footoo",
@@ -120,6 +222,58 @@
120222
"Ref": "HostedZoneId"
121223
},
122224
"RecordSets": [
225+
{
226+
"AliasTarget": {
227+
"DNSName": {
228+
"Fn::GetAtt": [
229+
"ApiGatewayDomainName67a385f467",
230+
"DistributionDomainName"
231+
]
232+
},
233+
"HostedZoneId": "Z2FDTNDATAQYW2"
234+
},
235+
"Name": "MyEdgeDomainName",
236+
"Type": "A"
237+
},
238+
{
239+
"AliasTarget": {
240+
"DNSName": {
241+
"Fn::GetAtt": [
242+
"ApiGatewayDomainName67a385f467",
243+
"DistributionDomainName"
244+
]
245+
},
246+
"HostedZoneId": "Z2FDTNDATAQYW2"
247+
},
248+
"Name": "MyEdgeDomainName",
249+
"Type": "AAAA"
250+
},
251+
{
252+
"AliasTarget": {
253+
"DNSName": {
254+
"Fn::GetAtt": [
255+
"ApiGatewayDomainName67a385f467",
256+
"DistributionDomainName"
257+
]
258+
},
259+
"HostedZoneId": "Z2FDTNDATAQYW2"
260+
},
261+
"Name": "MyEdgeDomainName",
262+
"Type": "A"
263+
},
264+
{
265+
"AliasTarget": {
266+
"DNSName": {
267+
"Fn::GetAtt": [
268+
"ApiGatewayDomainName67a385f467",
269+
"DistributionDomainName"
270+
]
271+
},
272+
"HostedZoneId": "Z2FDTNDATAQYW2"
273+
},
274+
"Name": "MyEdgeDomainName",
275+
"Type": "AAAA"
276+
},
123277
{
124278
"AliasTarget": {
125279
"DNSName": {

0 commit comments

Comments
 (0)