From 047b96db896209ef928720365ca0b165b81c5a31 Mon Sep 17 00:00:00 2001 From: Vidit Shah Date: Mon, 5 Oct 2026 00:08:27 +0530 Subject: [PATCH] fix: preserve ANY routes when merging API definitions --- samtranslator/model/eventsources/push.py | 14 +- .../eventsources/test_api_event_source.py | 55 +++++ ..._definitions_with_existing_any_method.yaml | 47 ++++ ...api_merge_definitions_with_any_method.json | 20 +- ..._definitions_with_existing_any_method.json | 216 +++++++++++++++++ ...api_with_merge_definitions_null_paths.json | 21 +- ...api_merge_definitions_with_any_method.json | 20 +- ..._definitions_with_existing_any_method.json | 224 ++++++++++++++++++ ...api_with_merge_definitions_null_paths.json | 21 +- ...api_merge_definitions_with_any_method.json | 20 +- ..._definitions_with_existing_any_method.json | 224 ++++++++++++++++++ ...api_with_merge_definitions_null_paths.json | 21 +- 12 files changed, 876 insertions(+), 27 deletions(-) create mode 100644 tests/translator/input/api_merge_definitions_with_existing_any_method.yaml create mode 100644 tests/translator/output/api_merge_definitions_with_existing_any_method.json create mode 100644 tests/translator/output/aws-cn/api_merge_definitions_with_existing_any_method.json create mode 100644 tests/translator/output/aws-us-gov/api_merge_definitions_with_existing_any_method.json diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 425b2af745..162d1fc753 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -1041,24 +1041,26 @@ def _get_merged_definitions( """ merged_definition_body = source_definition_body.copy() source_body_paths = merged_definition_body.get("paths") or {} + merged_definition_body["paths"] = source_body_paths + + # Normalize API Gateway specific methods such as ANY for both lookup and write-back. + method = editor._normalize_method_name(self.Method) try: - path_method_body = dict_deep_get(source_body_paths, [self.Path, self.Method]) or {} + path_method_body = dict_deep_get(source_body_paths, [self.Path, method]) or {} except InvalidValueType as e: raise InvalidResourceException(api_id, f"Property 'DefinitionBody' is invalid: {e!s}") from e - sam_expect(path_method_body, api_id, f"DefinitionBody.paths.{self.Path}.{self.Method}").to_be_a_map() + sam_expect(path_method_body, api_id, f"DefinitionBody.paths.{self.Path}.{method}").to_be_a_map() - # Normalized version of HTTP Method. It also handle API Gateway specific methods like "ANY" - method = editor._normalize_method_name(self.Method) dest_definition_body = editor.swagger generated_path_method_body = dest_definition_body["paths"][self.Path][method] # this guarantees that the merged definition use SAM generated value for a conflicting key merged_path_method_body = {**path_method_body, **generated_path_method_body} if self.Path not in source_body_paths: - source_body_paths[self.Path] = {self.Method: merged_path_method_body} - source_body_paths[self.Path][self.Method] = merged_path_method_body + source_body_paths[self.Path] = {} + source_body_paths[self.Path][method] = merged_path_method_body return merged_definition_body diff --git a/tests/model/eventsources/test_api_event_source.py b/tests/model/eventsources/test_api_event_source.py index 1b3e9e2667..918a1fd032 100644 --- a/tests/model/eventsources/test_api_event_source.py +++ b/tests/model/eventsources/test_api_event_source.py @@ -1,9 +1,12 @@ from unittest import TestCase from unittest.mock import Mock, patch +from parameterized import parameterized from samtranslator.intrinsics.resolver import IntrinsicsResolver from samtranslator.model.eventsources.push import Api +from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.lambda_ import LambdaFunction, LambdaPermission +from samtranslator.swagger.swagger import SwaggerEditor class ApiEventSource(TestCase): @@ -124,6 +127,58 @@ def test_get_permission_with_just_slash(self): self.assertEqual(arn, "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/") + @parameterized.expand( + [(method, paths) for method in ("get", "any") for paths in ({}, {"paths": None}, {"paths": {}})] + ) + def test_merge_definitions_keeps_generated_path(self, method, paths): + self.api_event_source.Method = method + editor = SwaggerEditor(SwaggerEditor.gen_skeleton()) + editor.add_lambda_integration("/foo", method, "lambda-uri", {}, {}) + + merged = self.api_event_source._get_merged_definitions("RestApi", {"swagger": "2.0", **paths}, editor) + + self.assertEqual(merged["paths"], editor.swagger["paths"]) + + @parameterized.expand([("get", "get"), ("any", "x-amazon-apigateway-any-method")]) + def test_merge_definitions_preserves_inline_method_fields(self, method, method_key): + self.api_event_source.Method = method + editor = SwaggerEditor(SwaggerEditor.gen_skeleton()) + editor.add_lambda_integration("/foo", method, "lambda-uri", {}, {}) + source = { + "swagger": "2.0", + "paths": { + "/foo": { + method_key: { + "summary": "Inline operation", + "x-amazon-apigateway-integration": {"type": "http_proxy", "uri": "https://example.com"}, + }, + "post": {"summary": "Other method"}, + }, + "/other": {"get": {"summary": "Other path"}}, + }, + } + + merged = self.api_event_source._get_merged_definitions("RestApi", source, editor) + + self.assertEqual(set(merged["paths"]["/foo"]), {method_key, "post"}) + self.assertEqual( + merged["paths"]["/foo"][method_key], + {"summary": "Inline operation", **editor.swagger["paths"]["/foo"][method_key]}, + ) + self.assertEqual(merged["paths"]["/foo"]["post"], {"summary": "Other method"}) + self.assertEqual(merged["paths"]["/other"], {"get": {"summary": "Other path"}}) + + def test_merge_definitions_rejects_invalid_inline_any_method(self): + self.api_event_source.Method = "any" + editor = SwaggerEditor(SwaggerEditor.gen_skeleton()) + editor.add_lambda_integration("/foo", "any", "lambda-uri", {}, {}) + source = {"swagger": "2.0", "paths": {"/foo": {"x-amazon-apigateway-any-method": "invalid"}}} + + with self.assertRaisesRegex( + InvalidResourceException, r"DefinitionBody.paths./foo.x-amazon-apigateway-any-method" + ): + self.api_event_source._get_merged_definitions("RestApi", source, editor) + def _extract_path_from_arn(self, logical_id, perm): arn = perm.to_dict().get(logical_id, {}).get("Properties", {}).get("SourceArn", {}).get("Fn::Sub", [])[0] diff --git a/tests/translator/input/api_merge_definitions_with_existing_any_method.yaml b/tests/translator/input/api_merge_definitions_with_existing_any_method.yaml new file mode 100644 index 0000000000..6c7a3438e1 --- /dev/null +++ b/tests/translator/input/api_merge_definitions_with_existing_any_method.yaml @@ -0,0 +1,47 @@ +Transform: AWS::Serverless-2016-10-31 + +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + MergeDefinitions: true + Cors: "'*'" + DefinitionBody: + openapi: 3.0.1 + info: + title: Example + version: '1' + paths: + /test: + x-amazon-apigateway-any-method: + summary: Inline operation + x-amazon-apigateway-integration: + httpMethod: ANY + type: http_proxy + uri: https://example.com + /other: + get: + responses: + '200': + description: Success + x-amazon-apigateway-integration: + httpMethod: GET + type: http_proxy + uri: https://example.com + + MyFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: python3.11 + InlineCode: | + def handler(event, context): + return {"statusCode": 200, "body": "Success"} + Events: + AnyEvent: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /test + Method: ANY diff --git a/tests/translator/output/api_merge_definitions_with_any_method.json b/tests/translator/output/api_merge_definitions_with_any_method.json index c6b65e2bb2..ea5b7490d6 100644 --- a/tests/translator/output/api_merge_definitions_with_any_method.json +++ b/tests/translator/output/api_merge_definitions_with_any_method.json @@ -3,6 +3,20 @@ "WebhooksApi": { "Properties": { "Body": { + "paths": { + "/proxy": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebhooksReceiver.Arn}/invocations" + } + } + } + } + }, "swagger": 2, "x-amazon-apigateway-policy": { "Version": "2012-10-17" @@ -11,9 +25,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "WebhooksApiDeployment472b94a845": { + "WebhooksApiDeploymenta4764457ec": { "Properties": { - "Description": "RestApi deployment id: 472b94a845dac11081ca2890e848230a4e30a2aa", + "Description": "RestApi deployment id: a4764457ec88823a60b2b3741f89cbdca314b6b1", "RestApiId": { "Ref": "WebhooksApi" }, @@ -24,7 +38,7 @@ "WebhooksApiliveStage": { "Properties": { "DeploymentId": { - "Ref": "WebhooksApiDeployment472b94a845" + "Ref": "WebhooksApiDeploymenta4764457ec" }, "RestApiId": { "Ref": "WebhooksApi" diff --git a/tests/translator/output/api_merge_definitions_with_existing_any_method.json b/tests/translator/output/api_merge_definitions_with_existing_any_method.json new file mode 100644 index 0000000000..0e05304c82 --- /dev/null +++ b/tests/translator/output/api_merge_definitions_with_existing_any_method.json @@ -0,0 +1,216 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "info": { + "title": "Example", + "version": "1" + }, + "openapi": "3.0.1", + "paths": { + "/other": { + "get": { + "responses": { + "200": { + "description": "Success" + } + }, + "x-amazon-apigateway-integration": { + "httpMethod": "GET", + "type": "http_proxy", + "uri": "https://example.com" + } + }, + "options": { + "responses": { + "200": { + "description": "Default response for CORS method", + "headers": { + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "CORS support", + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "responseTemplates": { + "application/json": "{}\n" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + }, + "/test": { + "options": { + "responses": { + "200": { + "description": "Default response for CORS method", + "headers": { + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "CORS support", + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "responseTemplates": { + "application/json": "{}\n" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + }, + "x-amazon-apigateway-any-method": { + "responses": {}, + "summary": "Inline operation", + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + } + } + } + } + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "MyApiDeployment9f12f1d50a": { + "Properties": { + "Description": "RestApi deployment id: 9f12f1d50a3b352abe66ca3d7233c381189ead36", + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "MyApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeployment9f12f1d50a" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "MyFunction": { + "Properties": { + "Code": { + "ZipFile": "def handler(event, context):\n return {\"statusCode\": 200, \"body\": \"Success\"}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.11", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyFunctionAnyEventPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/test", + { + "__ApiId__": { + "Ref": "MyApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "MyFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/api_with_merge_definitions_null_paths.json b/tests/translator/output/api_with_merge_definitions_null_paths.json index 91b39e6e81..901a8e8be3 100644 --- a/tests/translator/output/api_with_merge_definitions_null_paths.json +++ b/tests/translator/output/api_with_merge_definitions_null_paths.json @@ -3,7 +3,20 @@ "WebhooksApi": { "Properties": { "Body": { - "paths": null, + "paths": { + "/proxy": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebhooksReceiver.Arn}/invocations" + } + } + } + } + }, "swagger": 2, "x-amazon-apigateway-policy": { "Version": "2012-10-17" @@ -12,9 +25,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "WebhooksApiDeployment5ce813cc52": { + "WebhooksApiDeploymentfdb350257c": { "Properties": { - "Description": "RestApi deployment id: 5ce813cc52a4e5d017317029559e0c18939085eb", + "Description": "RestApi deployment id: fdb350257c141d86794961cdf12770c079158386", "RestApiId": { "Ref": "WebhooksApi" }, @@ -25,7 +38,7 @@ "WebhooksApiliveStage": { "Properties": { "DeploymentId": { - "Ref": "WebhooksApiDeployment5ce813cc52" + "Ref": "WebhooksApiDeploymentfdb350257c" }, "RestApiId": { "Ref": "WebhooksApi" diff --git a/tests/translator/output/aws-cn/api_merge_definitions_with_any_method.json b/tests/translator/output/aws-cn/api_merge_definitions_with_any_method.json index c31894edcd..165eafe15c 100644 --- a/tests/translator/output/aws-cn/api_merge_definitions_with_any_method.json +++ b/tests/translator/output/aws-cn/api_merge_definitions_with_any_method.json @@ -3,6 +3,20 @@ "WebhooksApi": { "Properties": { "Body": { + "paths": { + "/proxy": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebhooksReceiver.Arn}/invocations" + } + } + } + } + }, "swagger": 2, "x-amazon-apigateway-policy": { "Version": "2012-10-17" @@ -19,9 +33,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "WebhooksApiDeployment472b94a845": { + "WebhooksApiDeployment821adeed02": { "Properties": { - "Description": "RestApi deployment id: 472b94a845dac11081ca2890e848230a4e30a2aa", + "Description": "RestApi deployment id: 821adeed02971c88b1e91cee6f3056c64f70c79c", "RestApiId": { "Ref": "WebhooksApi" }, @@ -32,7 +46,7 @@ "WebhooksApiliveStage": { "Properties": { "DeploymentId": { - "Ref": "WebhooksApiDeployment472b94a845" + "Ref": "WebhooksApiDeployment821adeed02" }, "RestApiId": { "Ref": "WebhooksApi" diff --git a/tests/translator/output/aws-cn/api_merge_definitions_with_existing_any_method.json b/tests/translator/output/aws-cn/api_merge_definitions_with_existing_any_method.json new file mode 100644 index 0000000000..301575e48b --- /dev/null +++ b/tests/translator/output/aws-cn/api_merge_definitions_with_existing_any_method.json @@ -0,0 +1,224 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "info": { + "title": "Example", + "version": "1" + }, + "openapi": "3.0.1", + "paths": { + "/other": { + "get": { + "responses": { + "200": { + "description": "Success" + } + }, + "x-amazon-apigateway-integration": { + "httpMethod": "GET", + "type": "http_proxy", + "uri": "https://example.com" + } + }, + "options": { + "responses": { + "200": { + "description": "Default response for CORS method", + "headers": { + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "CORS support", + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "responseTemplates": { + "application/json": "{}\n" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + }, + "/test": { + "options": { + "responses": { + "200": { + "description": "Default response for CORS method", + "headers": { + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "CORS support", + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "responseTemplates": { + "application/json": "{}\n" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + }, + "x-amazon-apigateway-any-method": { + "responses": {}, + "summary": "Inline operation", + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + } + } + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "MyApiDeploymentb2dc69e78a": { + "Properties": { + "Description": "RestApi deployment id: b2dc69e78a679b8fa542197cb0a9eb7aa18cbfec", + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "MyApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentb2dc69e78a" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "MyFunction": { + "Properties": { + "Code": { + "ZipFile": "def handler(event, context):\n return {\"statusCode\": 200, \"body\": \"Success\"}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.11", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyFunctionAnyEventPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/test", + { + "__ApiId__": { + "Ref": "MyApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "MyFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/api_with_merge_definitions_null_paths.json b/tests/translator/output/aws-cn/api_with_merge_definitions_null_paths.json index 1720309cfc..3ea80641e5 100644 --- a/tests/translator/output/aws-cn/api_with_merge_definitions_null_paths.json +++ b/tests/translator/output/aws-cn/api_with_merge_definitions_null_paths.json @@ -3,7 +3,20 @@ "WebhooksApi": { "Properties": { "Body": { - "paths": null, + "paths": { + "/proxy": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebhooksReceiver.Arn}/invocations" + } + } + } + } + }, "swagger": 2, "x-amazon-apigateway-policy": { "Version": "2012-10-17" @@ -20,9 +33,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "WebhooksApiDeployment5ce813cc52": { + "WebhooksApiDeployment45596d488e": { "Properties": { - "Description": "RestApi deployment id: 5ce813cc52a4e5d017317029559e0c18939085eb", + "Description": "RestApi deployment id: 45596d488eb3f794a1e2d9ace553c728638d03eb", "RestApiId": { "Ref": "WebhooksApi" }, @@ -33,7 +46,7 @@ "WebhooksApiliveStage": { "Properties": { "DeploymentId": { - "Ref": "WebhooksApiDeployment5ce813cc52" + "Ref": "WebhooksApiDeployment45596d488e" }, "RestApiId": { "Ref": "WebhooksApi" diff --git a/tests/translator/output/aws-us-gov/api_merge_definitions_with_any_method.json b/tests/translator/output/aws-us-gov/api_merge_definitions_with_any_method.json index 1ee01d89e6..637007dbc5 100644 --- a/tests/translator/output/aws-us-gov/api_merge_definitions_with_any_method.json +++ b/tests/translator/output/aws-us-gov/api_merge_definitions_with_any_method.json @@ -3,6 +3,20 @@ "WebhooksApi": { "Properties": { "Body": { + "paths": { + "/proxy": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebhooksReceiver.Arn}/invocations" + } + } + } + } + }, "swagger": 2, "x-amazon-apigateway-policy": { "Version": "2012-10-17" @@ -19,9 +33,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "WebhooksApiDeployment472b94a845": { + "WebhooksApiDeployment1fe94aeaec": { "Properties": { - "Description": "RestApi deployment id: 472b94a845dac11081ca2890e848230a4e30a2aa", + "Description": "RestApi deployment id: 1fe94aeaec889d076be1cd2f2e0ff973c30567ba", "RestApiId": { "Ref": "WebhooksApi" }, @@ -32,7 +46,7 @@ "WebhooksApiliveStage": { "Properties": { "DeploymentId": { - "Ref": "WebhooksApiDeployment472b94a845" + "Ref": "WebhooksApiDeployment1fe94aeaec" }, "RestApiId": { "Ref": "WebhooksApi" diff --git a/tests/translator/output/aws-us-gov/api_merge_definitions_with_existing_any_method.json b/tests/translator/output/aws-us-gov/api_merge_definitions_with_existing_any_method.json new file mode 100644 index 0000000000..0b908ac21d --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_merge_definitions_with_existing_any_method.json @@ -0,0 +1,224 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "info": { + "title": "Example", + "version": "1" + }, + "openapi": "3.0.1", + "paths": { + "/other": { + "get": { + "responses": { + "200": { + "description": "Success" + } + }, + "x-amazon-apigateway-integration": { + "httpMethod": "GET", + "type": "http_proxy", + "uri": "https://example.com" + } + }, + "options": { + "responses": { + "200": { + "description": "Default response for CORS method", + "headers": { + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "CORS support", + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "responseTemplates": { + "application/json": "{}\n" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + } + }, + "/test": { + "options": { + "responses": { + "200": { + "description": "Default response for CORS method", + "headers": { + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "CORS support", + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\n \"statusCode\" : 200\n}\n" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "responseTemplates": { + "application/json": "{}\n" + }, + "statusCode": "200" + } + }, + "type": "mock" + } + }, + "x-amazon-apigateway-any-method": { + "responses": {}, + "summary": "Inline operation", + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + } + } + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "MyApiDeploymentc941bfeb35": { + "Properties": { + "Description": "RestApi deployment id: c941bfeb3528b2768c86edebeb5050be182eb462", + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "MyApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentc941bfeb35" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, + "MyFunction": { + "Properties": { + "Code": { + "ZipFile": "def handler(event, context):\n return {\"statusCode\": 200, \"body\": \"Success\"}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.11", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyFunctionAnyEventPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/test", + { + "__ApiId__": { + "Ref": "MyApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "MyFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_merge_definitions_null_paths.json b/tests/translator/output/aws-us-gov/api_with_merge_definitions_null_paths.json index 1a4a149648..05d4bd5115 100644 --- a/tests/translator/output/aws-us-gov/api_with_merge_definitions_null_paths.json +++ b/tests/translator/output/aws-us-gov/api_with_merge_definitions_null_paths.json @@ -3,7 +3,20 @@ "WebhooksApi": { "Properties": { "Body": { - "paths": null, + "paths": { + "/proxy": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WebhooksReceiver.Arn}/invocations" + } + } + } + } + }, "swagger": 2, "x-amazon-apigateway-policy": { "Version": "2012-10-17" @@ -20,9 +33,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "WebhooksApiDeployment5ce813cc52": { + "WebhooksApiDeployment36d160a15b": { "Properties": { - "Description": "RestApi deployment id: 5ce813cc52a4e5d017317029559e0c18939085eb", + "Description": "RestApi deployment id: 36d160a15b54432ba5cccf81fb6aaf2e0f7f1dca", "RestApiId": { "Ref": "WebhooksApi" }, @@ -33,7 +46,7 @@ "WebhooksApiliveStage": { "Properties": { "DeploymentId": { - "Ref": "WebhooksApiDeployment5ce813cc52" + "Ref": "WebhooksApiDeployment36d160a15b" }, "RestApiId": { "Ref": "WebhooksApi"