diff --git a/integration/combination/test_http_api_with_fail_on_warnings.py b/integration/combination/test_http_api_with_fail_on_warnings.py new file mode 100644 index 000000000..7c82224e0 --- /dev/null +++ b/integration/combination/test_http_api_with_fail_on_warnings.py @@ -0,0 +1,33 @@ +from unittest.case import skipIf +from parameterized import parameterized + +from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import HTTP_API + + +@skipIf(current_region_does_not_support([HTTP_API]), "HttpApi is not supported in this testing region") +class TestHttpApiWithFailOnWarnings(BaseTest): + @parameterized.expand( + [ + ("combination/http_api_with_fail_on_warnings_and_default_stage_name", True), + ("combination/http_api_with_fail_on_warnings_and_default_stage_name", False), + ] + ) + def test_http_api_with_fail_on_warnings(self, file_name, disable_value): + parameters = [ + { + "ParameterKey": "FailOnWarningsValue", + "ParameterValue": "true" if disable_value else "false", + "UsePreviousValue": False, + "ResolvedValue": "string", + } + ] + + self.create_and_verify_stack(file_name, parameters) + + http_api_id = self.get_physical_id_by_type("AWS::ApiGatewayV2::Api") + apigw_v2_client = self.client_provider.api_v2_client + + api_result = apigw_v2_client.get_api(ApiId=http_api_id) + self.assertEqual(api_result["ResponseMetadata"]["HTTPStatusCode"], 200) diff --git a/integration/resources/expected/combination/http_api_with_fail_on_warnings_and_default_stage_name.json b/integration/resources/expected/combination/http_api_with_fail_on_warnings_and_default_stage_name.json new file mode 100644 index 000000000..fc4622bab --- /dev/null +++ b/integration/resources/expected/combination/http_api_with_fail_on_warnings_and_default_stage_name.json @@ -0,0 +1,22 @@ +[ + { + "LogicalResourceId": "AppFunctionAppHandlerPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "AppApi", + "ResourceType": "AWS::ApiGatewayV2::Api" + }, + { + "LogicalResourceId": "AppApiApiGatewayDefaultStage", + "ResourceType": "AWS::ApiGatewayV2::Stage" + }, + { + "LogicalResourceId": "AppFunction", + "ResourceType": "AWS::Lambda::Function" + }, + { + "LogicalResourceId": "AppFunctionRole", + "ResourceType": "AWS::IAM::Role" + } +] diff --git a/integration/resources/templates/combination/http_api_with_fail_on_warnings_and_default_stage_name.yaml b/integration/resources/templates/combination/http_api_with_fail_on_warnings_and_default_stage_name.yaml new file mode 100644 index 000000000..a9b1800a0 --- /dev/null +++ b/integration/resources/templates/combination/http_api_with_fail_on_warnings_and_default_stage_name.yaml @@ -0,0 +1,29 @@ +Parameters: + FailOnWarningsValue: + Type: String + AllowedValues: [true, false] + +Resources: + AppApi: + Type: AWS::Serverless::HttpApi + Properties: + FailOnWarnings: !Ref FailOnWarningsValue + StageName: $default + AppFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + InlineCode: | + def handler(event, context): + print("Hello, world!") + Runtime: python3.8 + Architectures: + - x86_64 + Events: + AppHandler: + Type: HttpApi + Properties: + ApiId: !Ref AppApi + +Metadata: + SamTransformTest: true diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index 54cf31ed2..e332e40d0 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -120,6 +120,7 @@ def _construct_http_api(self) -> ApiGatewayV2HttpApi: self._add_title() self._add_description() + self._update_default_path() if self.definition_uri: http_api.BodyS3Location = self._construct_body_s3_dict(self.definition_uri) @@ -224,6 +225,19 @@ def _add_cors(self) -> None: # Assign the OpenApi back to template self.definition_body = editor.openapi + def _update_default_path(self) -> None: + # Only do the following if FailOnWarnings is enabled for backward compatibility. + if not self.fail_on_warnings or not self.definition_body: + return + + # Using default stage name generate warning during deployment + # Warnings found during import: Parse issue: attribute paths. + # Resource $default should start with / (Service: AmazonApiGatewayV2; Status Code: 400; + # Deployment fails when FailOnWarnings is true: https://github.com/aws/serverless-application-model/issues/2297 + paths: Dict[str, Any] = self.definition_body.get("paths", {}) + if DefaultStageName in paths: + paths[f"/{DefaultStageName}"] = paths.pop(DefaultStageName) + def _construct_api_domain( self, http_api: ApiGatewayV2HttpApi, route53_record_set_groups: Dict[str, Route53RecordSetGroup] ) -> Tuple[ diff --git a/tests/translator/output/aws-cn/explicit_http_api.json b/tests/translator/output/aws-cn/explicit_http_api.json index 31d0cee88..7773386cb 100644 --- a/tests/translator/output/aws-cn/explicit_http_api.json +++ b/tests/translator/output/aws-cn/explicit_http_api.json @@ -123,7 +123,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/aws-cn/http_api_explicit_stage.json b/tests/translator/output/aws-cn/http_api_explicit_stage.json index 4cf02a5c4..849313249 100644 --- a/tests/translator/output/aws-cn/http_api_explicit_stage.json +++ b/tests/translator/output/aws-cn/http_api_explicit_stage.json @@ -95,7 +95,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/aws-cn/http_api_with_default_stage_name_and_fail_on_warnings.json b/tests/translator/output/aws-cn/http_api_with_default_stage_name_and_fail_on_warnings.json index 15c1990aa..3c536e79a 100644 --- a/tests/translator/output/aws-cn/http_api_with_default_stage_name_and_fail_on_warnings.json +++ b/tests/translator/output/aws-cn/http_api_with_default_stage_name_and_fail_on_warnings.json @@ -11,7 +11,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/aws-us-gov/explicit_http_api.json b/tests/translator/output/aws-us-gov/explicit_http_api.json index d99657176..be307679a 100644 --- a/tests/translator/output/aws-us-gov/explicit_http_api.json +++ b/tests/translator/output/aws-us-gov/explicit_http_api.json @@ -123,7 +123,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/aws-us-gov/http_api_explicit_stage.json b/tests/translator/output/aws-us-gov/http_api_explicit_stage.json index 1b07f1df8..78bebc9c5 100644 --- a/tests/translator/output/aws-us-gov/http_api_explicit_stage.json +++ b/tests/translator/output/aws-us-gov/http_api_explicit_stage.json @@ -95,7 +95,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/aws-us-gov/http_api_with_default_stage_name_and_fail_on_warnings.json b/tests/translator/output/aws-us-gov/http_api_with_default_stage_name_and_fail_on_warnings.json index afd8071cd..b93fb4f4f 100644 --- a/tests/translator/output/aws-us-gov/http_api_with_default_stage_name_and_fail_on_warnings.json +++ b/tests/translator/output/aws-us-gov/http_api_with_default_stage_name_and_fail_on_warnings.json @@ -11,7 +11,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/explicit_http_api.json b/tests/translator/output/explicit_http_api.json index 852b31e08..dc3077feb 100644 --- a/tests/translator/output/explicit_http_api.json +++ b/tests/translator/output/explicit_http_api.json @@ -123,7 +123,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/http_api_explicit_stage.json b/tests/translator/output/http_api_explicit_stage.json index ee59ef6ea..380ca0937 100644 --- a/tests/translator/output/http_api_explicit_stage.json +++ b/tests/translator/output/http_api_explicit_stage.json @@ -95,7 +95,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {}, diff --git a/tests/translator/output/http_api_with_default_stage_name_and_fail_on_warnings.json b/tests/translator/output/http_api_with_default_stage_name_and_fail_on_warnings.json index 7c3c6241d..97b131db2 100644 --- a/tests/translator/output/http_api_with_default_stage_name_and_fail_on_warnings.json +++ b/tests/translator/output/http_api_with_default_stage_name_and_fail_on_warnings.json @@ -11,7 +11,7 @@ }, "openapi": "3.0.1", "paths": { - "$default": { + "/$default": { "x-amazon-apigateway-any-method": { "isDefaultRoute": true, "responses": {},