diff --git a/docs/globals.rst b/docs/globals.rst index 54035d722f..fc341f9ad8 100644 --- a/docs/globals.rst +++ b/docs/globals.rst @@ -100,6 +100,7 @@ Currently, the following resources and properties are being supported: AccessLogSettings: Tags: DefaultRouteSettings: + Domain: SimpleTable: # Properties of AWS::Serverless::SimpleTable diff --git a/examples/2016-10-31/custom_domains_with_route53/template.yaml b/examples/2016-10-31/custom_domains_with_route53/template.yaml index e4b560d703..b9b7dbb7d9 100644 --- a/examples/2016-10-31/custom_domains_with_route53/template.yaml +++ b/examples/2016-10-31/custom_domains_with_route53/template.yaml @@ -28,7 +28,7 @@ Resources: Path: /fetch MyApi: - Type: AWS::Serverless::Api + Type: AWS::Serverless::Api # Also works with HTTP API Properties: OpenApiVersion: 3.0.1 StageName: Prod diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index dde3ef3943..a1da483a3e 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -1,13 +1,21 @@ +import re from collections import namedtuple from six import string_types -from samtranslator.model.intrinsics import ref -from samtranslator.model.apigatewayv2 import ApiGatewayV2HttpApi, ApiGatewayV2Stage, ApiGatewayV2Authorizer +from samtranslator.model.intrinsics import ref, fnGetAtt +from samtranslator.model.apigatewayv2 import ( + ApiGatewayV2HttpApi, + ApiGatewayV2Stage, + ApiGatewayV2Authorizer, + ApiGatewayV2DomainName, + ApiGatewayV2ApiMapping, +) from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.s3_utils.uri_parser import parse_s3_uri from samtranslator.open_api.open_api import OpenApiEditor from samtranslator.translator import logical_id_generator from samtranslator.model.tags.resource_tagging import get_tag_list from samtranslator.model.intrinsics import is_intrinsic +from samtranslator.model.route53 import Route53RecordSetGroup _CORS_WILDCARD = "*" CorsProperties = namedtuple( @@ -37,6 +45,7 @@ def __init__( default_route_settings=None, resource_attributes=None, passthrough_resource_attributes=None, + domain=None, ): """Constructs an API Generator class that generates API Gateway resources @@ -67,6 +76,7 @@ def __init__( self.default_route_settings = default_route_settings self.resource_attributes = resource_attributes self.passthrough_resource_attributes = passthrough_resource_attributes + self.domain = domain def _construct_http_api(self): """Constructs and returns the ApiGatewayV2 HttpApi. @@ -164,6 +174,147 @@ def _add_cors(self): # Assign the OpenApi back to template self.definition_body = editor.openapi + def _construct_api_domain(self, http_api): + """ + Constructs and returns the ApiGateway Domain and BasepathMapping + """ + if self.domain is None: + return None, None, None + + if self.domain.get("DomainName") is None or self.domain.get("CertificateArn") is None: + raise InvalidResourceException( + self.logical_id, "Custom Domains only works if both DomainName and CertificateArn" " are provided." + ) + + self.domain["ApiDomainName"] = "{}{}".format( + "ApiGatewayDomainNameV2", logical_id_generator.LogicalIdGenerator("", self.domain.get("DomainName")).gen() + ) + + domain = ApiGatewayV2DomainName( + self.domain.get("ApiDomainName"), attributes=self.passthrough_resource_attributes + ) + domain_config = dict() + domain.DomainName = self.domain.get("DomainName") + endpoint = self.domain.get("EndpointConfiguration") + + if endpoint is None: + endpoint = "REGIONAL" + # to make sure that default is always REGIONAL + self.domain["EndpointConfiguration"] = "REGIONAL" + elif endpoint not in ["EDGE", "REGIONAL"]: + raise InvalidResourceException( + self.logical_id, + "EndpointConfiguration for Custom Domains must be one of {}.".format(["EDGE", "REGIONAL"]), + ) + domain_config["EndpointType"] = endpoint + domain_config["CertificateArn"] = self.domain.get("CertificateArn") + + domain.DomainNameConfigurations = [domain_config] + + # Create BasepathMappings + if self.domain.get("BasePath") and isinstance(self.domain.get("BasePath"), string_types): + basepaths = [self.domain.get("BasePath")] + elif self.domain.get("BasePath") and isinstance(self.domain.get("BasePath"), list): + basepaths = self.domain.get("BasePath") + else: + basepaths = None + basepath_resource_list = self._construct_basepath_mappings(basepaths, http_api) + + # Create the Route53 RecordSetGroup resource + record_set_group = self._construct_route53_recordsetgroup() + + return domain, basepath_resource_list, record_set_group + + def _construct_route53_recordsetgroup(self): + record_set_group = None + if self.domain.get("Route53") is not None: + route53 = self.domain.get("Route53") + if route53.get("HostedZoneId") is None and route53.get("HostedZoneName") is None: + raise InvalidResourceException( + self.logical_id, + "HostedZoneId or HostedZoneName is required to enable Route53 support on Custom Domains.", + ) + logical_id = logical_id_generator.LogicalIdGenerator( + "", route53.get("HostedZoneId") or route53.get("HostedZoneName") + ).gen() + record_set_group = Route53RecordSetGroup( + "RecordSetGroup" + logical_id, attributes=self.passthrough_resource_attributes + ) + if "HostedZoneId" in route53: + record_set_group.HostedZoneId = route53.get("HostedZoneId") + elif "HostedZoneName" in route53: + record_set_group.HostedZoneName = route53.get("HostedZoneName") + record_set_group.RecordSets = self._construct_record_sets_for_domain(self.domain) + + return record_set_group + + def _construct_basepath_mappings(self, basepaths, http_api): + basepath_resource_list = [] + + if basepaths is None: + basepath_mapping = ApiGatewayV2ApiMapping( + self.logical_id + "ApiMapping", attributes=self.passthrough_resource_attributes + ) + basepath_mapping.DomainName = ref(self.domain.get("ApiDomainName")) + basepath_mapping.ApiId = ref(http_api.logical_id) + basepath_mapping.Stage = ref(http_api.logical_id + ".Stage") + basepath_resource_list.extend([basepath_mapping]) + else: + for path in basepaths: + # search for invalid characters in the path and raise error if there are + invalid_regex = r"[^0-9a-zA-Z\/\-\_]+" + if re.search(invalid_regex, path) is not None: + raise InvalidResourceException(self.logical_id, "Invalid Basepath name provided.") + # ignore leading and trailing `/` in the path name + m = re.search(r"[a-zA-Z0-9]+[\-\_]?[a-zA-Z0-9]+", path) + path = m.string[m.start(0) : m.end(0)] + if path is None: + raise InvalidResourceException(self.logical_id, "Invalid Basepath name provided.") + logical_id = "{}{}{}".format(self.logical_id, re.sub(r"[\-\_]+", "", path), "ApiMapping") + basepath_mapping = ApiGatewayV2ApiMapping(logical_id, attributes=self.passthrough_resource_attributes) + basepath_mapping.DomainName = ref(self.domain.get("ApiDomainName")) + basepath_mapping.ApiId = ref(http_api.logical_id) + basepath_mapping.Stage = ref(http_api.logical_id + ".Stage") + basepath_mapping.ApiMappingKey = path + basepath_resource_list.extend([basepath_mapping]) + return basepath_resource_list + + def _construct_record_sets_for_domain(self, domain): + recordset_list = [] + recordset = {} + route53 = domain.get("Route53") + + recordset["Name"] = domain.get("DomainName") + recordset["Type"] = "A" + recordset["AliasTarget"] = self._construct_alias_target(self.domain) + recordset_list.extend([recordset]) + + recordset_ipv6 = {} + if route53.get("IpV6"): + recordset_ipv6["Name"] = domain.get("DomainName") + recordset_ipv6["Type"] = "AAAA" + recordset_ipv6["AliasTarget"] = self._construct_alias_target(self.domain) + recordset_list.extend([recordset_ipv6]) + + return recordset_list + + def _construct_alias_target(self, domain): + alias_target = {} + route53 = domain.get("Route53") + target_health = route53.get("EvaluateTargetHealth") + + if target_health is not None: + alias_target["EvaluateTargetHealth"] = target_health + if domain.get("EndpointConfiguration") == "REGIONAL": + alias_target["HostedZoneId"] = fnGetAtt(self.domain.get("ApiDomainName"), "RegionalHostedZoneId") + alias_target["DNSName"] = fnGetAtt(self.domain.get("ApiDomainName"), "RegionalDomainName") + else: + if route53.get("DistributionDomainName") is None: + route53["DistributionDomainName"] = fnGetAtt(self.domain.get("ApiDomainName"), "DistributionDomainName") + alias_target["HostedZoneId"] = "Z2FDTNDATAQYW2" + alias_target["DNSName"] = route53.get("DistributionDomainName") + return alias_target + def _add_auth(self): """ Add Auth configuration to the OAS file, if necessary @@ -349,7 +500,7 @@ def to_cloudformation(self): :rtype: tuple """ http_api = self._construct_http_api() - + domain, basepath_mapping, route53 = self._construct_api_domain(http_api) stage = self._construct_stage() - return http_api, stage + return http_api, stage, domain, basepath_mapping, route53 diff --git a/samtranslator/model/apigatewayv2.py b/samtranslator/model/apigatewayv2.py index 93f0fdec6d..695cec4a2d 100644 --- a/samtranslator/model/apigatewayv2.py +++ b/samtranslator/model/apigatewayv2.py @@ -36,6 +36,25 @@ class ApiGatewayV2Stage(Resource): runtime_attrs = {"stage_name": lambda self: ref(self.logical_id)} +class ApiGatewayV2DomainName(Resource): + resource_type = "AWS::ApiGatewayV2::DomainName" + property_types = { + "DomainName": PropertyType(True, is_str()), + "DomainNameConfigurations": PropertyType(False, list_of(is_type(dict))), + "Tags": PropertyType(False, list_of(is_type(dict))), + } + + +class ApiGatewayV2ApiMapping(Resource): + resource_type = "AWS::ApiGatewayV2::ApiMapping" + property_types = { + "ApiId": PropertyType(True, is_str()), + "ApiMappingKey": PropertyType(False, is_str()), + "DomainName": PropertyType(True, is_str()), + "Stage": PropertyType(True, is_str()), + } + + class ApiGatewayV2Authorizer(object): def __init__( self, diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index cdab194775..d6fcad40a1 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -18,7 +18,7 @@ ApiGatewayUsagePlanKey, ApiGatewayApiKey, ) -from samtranslator.model.apigatewayv2 import ApiGatewayV2Stage +from samtranslator.model.apigatewayv2 import ApiGatewayV2Stage, ApiGatewayV2DomainName from samtranslator.model.cloudformation import NestedStack from samtranslator.model.dynamodb import DynamoDBTable from samtranslator.model.exceptions import InvalidEventException, InvalidResourceException @@ -876,9 +876,13 @@ class SamHttpApi(SamResourceMacro): "AccessLogSettings": PropertyType(False, is_type(dict)), "DefaultRouteSettings": PropertyType(False, is_type(dict)), "Auth": PropertyType(False, is_type(dict)), + "Domain": PropertyType(False, is_type(dict)), } - referable_properties = {"Stage": ApiGatewayV2Stage.resource_type} + referable_properties = { + "Stage": ApiGatewayV2Stage.resource_type, + "DomainName": ApiGatewayV2DomainName.resource_type, + } def to_cloudformation(self, **kwargs): """Returns the API GatewayV2 Api, Deployment, and Stage to which this SAM Api corresponds. @@ -892,6 +896,9 @@ def to_cloudformation(self, **kwargs): intrinsics_resolver = kwargs["intrinsics_resolver"] self.CorsConfiguration = intrinsics_resolver.resolve_parameter_refs(self.CorsConfiguration) + intrinsics_resolver = kwargs["intrinsics_resolver"] + self.Domain = intrinsics_resolver.resolve_parameter_refs(self.Domain) + api_generator = HttpApiGenerator( self.logical_id, self.StageVariables, @@ -906,11 +913,18 @@ def to_cloudformation(self, **kwargs): default_route_settings=self.DefaultRouteSettings, resource_attributes=self.resource_attributes, passthrough_resource_attributes=self.get_passthrough_resource_attributes(), + domain=self.Domain, ) - http_api, stage = api_generator.to_cloudformation() + (http_api, stage, domain, basepath_mapping, route53,) = api_generator.to_cloudformation() resources.append(http_api) + if domain: + resources.append(domain) + if basepath_mapping: + resources.extend(basepath_mapping) + if route53: + resources.append(route53) # Stage is now optional. Only add it if one is created. if stage: diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index c02e1f19b5..e3cb4683b5 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -71,6 +71,7 @@ class Globals(object): "Tags", "CorsConfiguration", "DefaultRouteSettings", + "Domain", ], SamResourceType.SimpleTable.value: ["SSESpecification"], } diff --git a/tests/model/api/test_http_api_generator.py b/tests/model/api/test_http_api_generator.py new file mode 100644 index 0000000000..e1d01070a9 --- /dev/null +++ b/tests/model/api/test_http_api_generator.py @@ -0,0 +1,251 @@ +from unittest import TestCase +from mock import patch +import pytest +from functools import reduce + +from samtranslator.model import InvalidResourceException +from samtranslator.model.api.http_api_generator import HttpApiGenerator +from samtranslator.open_api.open_api import OpenApiEditor + + +class TestHttpApiGenerator(TestCase): + kwargs = { + "logical_id": "HttpApiId", + "stage_variables": None, + "depends_on": None, + "definition_body": None, + "definition_uri": None, + "stage_name": None, + "tags": None, + "auth": None, + "access_log_settings": None, + "resource_attributes": None, + "passthrough_resource_attributes": None, + } + authorizers = { + "Authorizers": { + "OAuth2": { + "AuthorizationScopes": ["scope"], + "JwtConfiguration": {"config": "value"}, + "IdentitySource": "https://example.com", + } + } + } + + def test_auth_no_def_body(self): + self.kwargs["auth"] = {"Authorizers": "configuration"} + self.kwargs["definition_body"] = None + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_auth_wrong_properties(self): + self.kwargs["auth"] = {"Invalid": "auth"} + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_auth_invalid_def_body(self): + self.kwargs["auth"] = {"Authorizers": "auth"} + self.kwargs["definition_body"] = {"Invalid": "open_api"} + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_auth_invalid_auth_dict(self): + self.kwargs["auth"] = {"Authorizers": "auth"} + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_auth_invalid_auth_strategy(self): + self.kwargs["auth"] = {"Authorizers": {"Auth1": "invalid"}} + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_auth_missing_default_auth(self): + self.kwargs["auth"] = self.authorizers + self.kwargs["auth"]["DefaultAuthorizer"] = "DNE" + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_def_uri_invalid_dict(self): + self.kwargs["auth"] = None + self.kwargs["definition_body"] = None + self.kwargs["definition_uri"] = {"Invalid": "key"} + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_def_uri_invalid_uri(self): + self.kwargs["auth"] = None + self.kwargs["definition_body"] = None + self.kwargs["definition_uri"] = "invalid_uri" + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + def test_no_def_body_or_uri(self): + self.kwargs["auth"] = None + self.kwargs["definition_body"] = None + self.kwargs["definition_uri"] = None + with pytest.raises(InvalidResourceException): + HttpApiGenerator(**self.kwargs)._construct_http_api() + + +class TestCustomDomains(TestCase): + kwargs = { + "logical_id": "HttpApiId", + "stage_variables": None, + "depends_on": None, + "definition_body": None, + "definition_uri": "s3://bucket/key", + "stage_name": None, + "tags": None, + "auth": None, + "access_log_settings": None, + "resource_attributes": None, + "passthrough_resource_attributes": None, + "domain": None, + } + + def test_no_domain(self): + self.kwargs["domain"] = None + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNone(domain) + self.assertIsNone(basepath) + self.assertIsNone(route) + + def test_no_domain_name(self): + self.kwargs["domain"] = {"CertificateArn": "someurl"} + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + with pytest.raises(InvalidResourceException) as e: + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertEquals( + e.value.message, + "Resource with id [HttpApiId] is invalid. " + + "Custom Domains only works if both DomainName and CertificateArn are provided.", + ) + + def test_no_cert_arn(self): + self.kwargs["domain"] = {"DomainName": "example.com"} + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + with pytest.raises(InvalidResourceException) as e: + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertEquals( + e.value.message, + "Resource with id [HttpApiId] is invalid. " + + "Custom Domains only works if both DomainName and CertificateArn are provided.", + ) + + def test_basic_domain_default_endpoint(self): + self.kwargs["domain"] = {"DomainName": "example.com", "CertificateArn": "some-url"} + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNotNone(domain, None) + self.assertIsNotNone(basepath, None) + self.assertEqual(len(basepath), 1) + self.assertIsNone(route, None) + self.assertEquals(domain.DomainNameConfigurations[0].get("EndpointType"), "REGIONAL") + + def test_basic_domain_regional_endpoint(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "EndpointConfiguration": "REGIONAL", + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNotNone(domain, None) + self.assertIsNotNone(basepath, None) + self.assertEqual(len(basepath), 1) + self.assertIsNone(route, None) + self.assertEquals(domain.DomainNameConfigurations[0].get("EndpointType"), "REGIONAL") + + def test_basic_domain_edge_endpoint(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "EndpointConfiguration": "EDGE", + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNotNone(domain, None) + self.assertIsNotNone(basepath, None) + self.assertEqual(len(basepath), 1) + self.assertIsNone(route, None) + self.assertEquals(domain.DomainNameConfigurations[0].get("EndpointType"), "EDGE") + + def test_bad_endpoint(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "EndpointConfiguration": "INVALID", + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + with pytest.raises(InvalidResourceException) as e: + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertEquals( + e.value.message, + "Resource with id [HttpApiId] is invalid. " + + "EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL'].", + ) + + def test_basic_route53(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "Route53": {"HostedZoneId": "xyz"}, + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNotNone(domain, None) + self.assertIsNotNone(basepath, None) + self.assertEqual(len(basepath), 1) + self.assertIsNotNone(route, None) + self.assertEquals(domain.DomainNameConfigurations[0].get("EndpointType"), "REGIONAL") + + def test_basepaths(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "BasePath": ["one", "two", "three"], + "Route53": {"HostedZoneId": "xyz"}, + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNotNone(domain, None) + self.assertIsNotNone(basepath, None) + self.assertEqual(len(basepath), 3) + self.assertIsNotNone(route, None) + self.assertEquals(domain.DomainNameConfigurations[0].get("EndpointType"), "REGIONAL") + + def test_invalid_basepaths(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "BasePath": ["inv*alid"], + "Route53": {"HostedZoneId": "xyz"}, + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + with pytest.raises(InvalidResourceException) as e: + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertEquals( + e.value.message, "Resource with id [HttpApiId] is invalid. " + "Invalid Basepath name provided." + ) + + def test_basepaths(self): + self.kwargs["domain"] = { + "DomainName": "example.com", + "CertificateArn": "some-url", + "BasePath": ["one-1", "two_2", "three"], + "Route53": {"HostedZoneId": "xyz", "HostedZoneName": "abc", "IpV6": True}, + } + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + self.assertIsNotNone(domain, None) + self.assertIsNotNone(basepath, None) + self.assertEqual(len(basepath), 3) + self.assertIsNotNone(route, None) + self.assertEquals(route.HostedZoneName, None) + self.assertEquals(route.HostedZoneId, "xyz") + self.assertEquals(len(route.RecordSets), 2) diff --git a/tests/model/api/test_htttp_api_generator.py b/tests/model/api/test_htttp_api_generator.py deleted file mode 100644 index cee1621610..0000000000 --- a/tests/model/api/test_htttp_api_generator.py +++ /dev/null @@ -1,90 +0,0 @@ -from unittest import TestCase -from mock import patch -import pytest - -from samtranslator.model import InvalidResourceException -from samtranslator.model.api.http_api_generator import HttpApiGenerator -from samtranslator.open_api.open_api import OpenApiEditor - - -class TestHttpApiGenerator(TestCase): - kwargs = { - "logical_id": "HttpApiId", - "stage_variables": None, - "depends_on": None, - "definition_body": None, - "definition_uri": None, - "stage_name": None, - "tags": None, - "auth": None, - "access_log_settings": None, - "resource_attributes": None, - "passthrough_resource_attributes": None, - } - authorizers = { - "Authorizers": { - "OAuth2": { - "AuthorizationScopes": ["scope"], - "JwtConfiguration": {"config": "value"}, - "IdentitySource": "https://example.com", - } - } - } - - def test_auth_no_def_body(self): - self.kwargs["auth"] = {"Authorizers": "configuration"} - self.kwargs["definition_body"] = None - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_auth_wrong_properties(self): - self.kwargs["auth"] = {"Invalid": "auth"} - self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_auth_invalid_def_body(self): - self.kwargs["auth"] = {"Authorizers": "auth"} - self.kwargs["definition_body"] = {"Invalid": "open_api"} - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_auth_invalid_auth_dict(self): - self.kwargs["auth"] = {"Authorizers": "auth"} - self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_auth_invalid_auth_strategy(self): - self.kwargs["auth"] = {"Authorizers": {"Auth1": "invalid"}} - self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_auth_missing_default_auth(self): - self.kwargs["auth"] = self.authorizers - self.kwargs["auth"]["DefaultAuthorizer"] = "DNE" - self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_def_uri_invalid_dict(self): - self.kwargs["auth"] = None - self.kwargs["definition_body"] = None - self.kwargs["definition_uri"] = {"Invalid": "key"} - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_def_uri_invalid_uri(self): - self.kwargs["auth"] = None - self.kwargs["definition_body"] = None - self.kwargs["definition_uri"] = "invalid_uri" - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() - - def test_no_def_body_or_uri(self): - self.kwargs["auth"] = None - self.kwargs["definition_body"] = None - self.kwargs["definition_uri"] = None - with pytest.raises(InvalidResourceException): - HttpApiGenerator(**self.kwargs)._construct_http_api() diff --git a/tests/translator/input/api_with_basic_custom_domain_http.yaml b/tests/translator/input/api_with_basic_custom_domain_http.yaml new file mode 100644 index 0000000000..009190a198 --- /dev/null +++ b/tests/translator/input/api_with_basic_custom_domain_http.yaml @@ -0,0 +1,50 @@ +Parameters: + MyDomainName: + Type: String + Default: sam-v2-regional-test.com + + MyDomainCert: + Type: String + Default: arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3 + +Globals: + HttpApi: + Domain: + DomainName: !Ref MyDomainName + CertificateArn: !Ref MyDomainCert + EndpointConfiguration: REGIONAL + BasePath: ["/basic", "/begin-here"] + Route53: + HostedZoneName: sam-v2-regional-test.com. + + +Resources: + HttpApiFunction: + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('Hello from Lambda!'), + }; + return response; + }; + Handler: index.handler + Runtime: nodejs12.x + Events: + Basic: + Type: HttpApi + Properties: + Path: /basic + Method: post + ApiId: !Ref MyApi + SimpleCase: + Type: HttpApi + Properties: + ApiId: !Ref MyApi + + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + StageName: Prod \ No newline at end of file diff --git a/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml b/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml new file mode 100644 index 0000000000..03cb1323cd --- /dev/null +++ b/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml @@ -0,0 +1,53 @@ +Conditions: + C1: + Fn::Equals: + - true + - true +Parameters: + MyDomainCert: + Type: String + Default: another-api-arn + + EndpointConf: + Type: String + Default: REGIONAL + +Resources: + MyFunction: + Condition: C1 + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('Hello from Lambda!'), + }; + return response; + }; + Handler: index.handler + Runtime: nodejs12.x + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref MyApi + Method: Put + Path: /get + ImplicitGet: + Type: HttpApi + Properties: + Method: Post + Path: /implicit + + MyApi: + Condition: C1 + Type: AWS::Serverless::HttpApi + Properties: + StageName: Prod + Domain: + DomainName: !Sub 'example-${AWS::Region}.com' + CertificateArn: !Ref MyDomainCert + EndpointConfiguration: !Ref EndpointConf + BasePath: [ "/get", "/fetch" ] + diff --git a/tests/translator/input/api_with_custom_domain_route53_hosted_zone_name_http.yaml b/tests/translator/input/api_with_custom_domain_route53_hosted_zone_name_http.yaml new file mode 100644 index 0000000000..f7de53992f --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_route53_hosted_zone_name_http.yaml @@ -0,0 +1,44 @@ +Parameters: + DomainName: + Type: String + Default: 'example.com' + ACMCertificateArn: + Type: String + Default: 'cert-arn-in-us-east-1' +Globals: + HttpApi: + Domain: + DomainName: !Ref DomainName + CertificateArn: !Ref ACMCertificateArn + EndpointConfiguration: EDGE + BasePath: + - /one + Route53: + HostedZoneName: www.my-domain.com. + IpV6: false +Resources: + MyFunction: + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('Hello from Lambda!'), + }; + return response; + }; + Handler: index.handler + Runtime: nodejs12.x + Events: + Fetch: + Type: HttpApi + Properties: + ApiId: !Ref MyApi + Method: Post + Path: /fetch + + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + StageName: Prod diff --git a/tests/translator/input/api_with_custom_domain_route53_http.yaml b/tests/translator/input/api_with_custom_domain_route53_http.yaml new file mode 100644 index 0000000000..87c84c543c --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_route53_http.yaml @@ -0,0 +1,42 @@ +Parameters: + DomainName: + Type: String + Default: 'example.com' + ACMCertificateArn: + Type: String + Default: 'cert-arn-in-us-east-1' +Resources: + MyFunction: + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('Hello from Lambda!'), + }; + return response; + }; + Handler: index.handler + Runtime: nodejs12.x + Events: + Fetch: + Type: HttpApi + Properties: + ApiId: !Ref MyApi + Method: Post + Path: /fetch + + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + StageName: Prod + Domain: + DomainName: !Ref DomainName + CertificateArn: !Ref ACMCertificateArn + EndpointConfiguration: EDGE + BasePath: + - /one + Route53: + HostedZoneId: ZQ1UAL4EFZVME + IpV6: true \ No newline at end of file diff --git a/tests/translator/output/api_with_basic_custom_domain_http.json b/tests/translator/output/api_with_basic_custom_domain_http.json new file mode 100644 index 0000000000..c37e4a826e --- /dev/null +++ b/tests/translator/output/api_with_basic_custom_domain_http.json @@ -0,0 +1,214 @@ +{ + "Parameters": { + "MyDomainName": { + "Default": "sam-v2-regional-test.com", + "Type": "String" + }, + "MyDomainCert": { + "Default": "arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", + "Type": "String" + } + }, + "Resources": { + "MyApibasicApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2ab8e63947d" + }, + "ApiMappingKey": "basic", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "HttpApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "HttpApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "HttpApiFunctionSimpleCasePermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "HttpApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApibeginhereApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2ab8e63947d" + }, + "ApiMappingKey": "begin-here", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "RecordSetGroup9616db1511": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneName": "sam-v2-regional-test.com.", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV2ab8e63947d", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV2ab8e63947d", + "RegionalDomainName" + ] + } + }, + "Type": "A", + "Name": "sam-v2-regional-test.com" + } + ] + } + }, + "HttpApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiGatewayDomainNameV2ab8e63947d": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", + "EndpointType": "REGIONAL" + } + ], + "DomainName": "sam-v2-regional-test.com" + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/basic": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + }, + "$default": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "isDefaultRoute": true, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json b/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json new file mode 100644 index 0000000000..6a85c1dd7e --- /dev/null +++ b/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json @@ -0,0 +1,309 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "MyDomainCert": { + "Default": "another-api-arn", + "Type": "String" + }, + "EndpointConf": { + "Default": "REGIONAL", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "ServerlessHttpApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/implicit": { + "Fn::If": [ + "C1", + { + "post": { + "Fn::If": [ + "C1", + { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::If": [ + "C1", + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionImplicitGetPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessHttpApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/get": { + "Fn::If": [ + "C1", + { + "put": { + "Fn::If": [ + "C1", + { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::If": [ + "C1", + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionApiPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApigetApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2c0ed6fae34" + }, + "ApiMappingKey": "get", + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "ApiGatewayDomainNameV2c0ed6fae34": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "another-api-arn", + "EndpointType": "REGIONAL" + } + ], + "DomainName": { + "Fn::Sub": "example-ap-southeast-1.com" + } + }, + "Condition": "C1" + }, + "ServerlessHttpApiApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "ServerlessHttpApi" + }, + "AutoDeploy": true, + "StageName": "$default" + }, + "Condition": "C1" + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "MyApifetchApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2c0ed6fae34" + }, + "ApiMappingKey": "fetch", + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_custom_domain_route53_hosted_zone_name_http.json b/tests/translator/output/api_with_custom_domain_route53_hosted_zone_name_http.json new file mode 100644 index 0000000000..5c1096fd72 --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_route53_hosted_zone_name_http.json @@ -0,0 +1,180 @@ +{ + "Parameters": { + "ACMCertificateArn": { + "Default": "cert-arn-in-us-east-1", + "Type": "String" + }, + "DomainName": { + "Default": "example.com", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "MyApioneApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV20caaf24ab1" + }, + "ApiMappingKey": "one", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "ApiGatewayDomainNameV20caaf24ab1": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "cert-arn-in-us-east-1", + "EndpointType": "EDGE" + } + ], + "DomainName": "example.com" + } + }, + "RecordSetGroup456ebaf280": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneName": "www.my-domain.com.", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "A", + "Name": "example.com" + } + ] + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionFetchPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/fetch": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_custom_domain_route53_http.json b/tests/translator/output/api_with_custom_domain_route53_http.json new file mode 100644 index 0000000000..0a24aca116 --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_route53_http.json @@ -0,0 +1,193 @@ +{ + "Parameters": { + "ACMCertificateArn": { + "Default": "cert-arn-in-us-east-1", + "Type": "String" + }, + "DomainName": { + "Default": "example.com", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApioneApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV20caaf24ab1" + }, + "ApiMappingKey": "one", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "RecordSetGroupbd00d962a4": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "ZQ1UAL4EFZVME", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "A", + "Name": "example.com" + }, + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "AAAA", + "Name": "example.com" + } + ] + } + }, + "ApiGatewayDomainNameV20caaf24ab1": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "cert-arn-in-us-east-1", + "EndpointType": "EDGE" + } + ], + "DomainName": "example.com" + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionFetchPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/fetch": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json new file mode 100644 index 0000000000..744faa7444 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json @@ -0,0 +1,214 @@ +{ + "Parameters": { + "MyDomainName": { + "Default": "sam-v2-regional-test.com", + "Type": "String" + }, + "MyDomainCert": { + "Default": "arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", + "Type": "String" + } + }, + "Resources": { + "MyApibasicApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2ab8e63947d" + }, + "ApiMappingKey": "basic", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "HttpApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "HttpApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "HttpApiFunctionSimpleCasePermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "HttpApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApibeginhereApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2ab8e63947d" + }, + "ApiMappingKey": "begin-here", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "RecordSetGroup9616db1511": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneName": "sam-v2-regional-test.com.", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV2ab8e63947d", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV2ab8e63947d", + "RegionalDomainName" + ] + } + }, + "Type": "A", + "Name": "sam-v2-regional-test.com" + } + ] + } + }, + "HttpApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiGatewayDomainNameV2ab8e63947d": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", + "EndpointType": "REGIONAL" + } + ], + "DomainName": "sam-v2-regional-test.com" + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/basic": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + }, + "$default": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "isDefaultRoute": true, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json new file mode 100644 index 0000000000..55846f7ad9 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json @@ -0,0 +1,309 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "MyDomainCert": { + "Default": "another-api-arn", + "Type": "String" + }, + "EndpointConf": { + "Default": "REGIONAL", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "ServerlessHttpApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/implicit": { + "Fn::If": [ + "C1", + { + "post": { + "Fn::If": [ + "C1", + { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::If": [ + "C1", + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionImplicitGetPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessHttpApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "ApiGatewayDomainNameV2c0cd2d9dfc": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "another-api-arn", + "EndpointType": "REGIONAL" + } + ], + "DomainName": { + "Fn::Sub": "example-cn-north-1.com" + } + }, + "Condition": "C1" + }, + "MyFunctionApiPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApigetApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2c0cd2d9dfc" + }, + "ApiMappingKey": "get", + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "ServerlessHttpApiApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "ServerlessHttpApi" + }, + "AutoDeploy": true, + "StageName": "$default" + }, + "Condition": "C1" + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/get": { + "Fn::If": [ + "C1", + { + "put": { + "Fn::If": [ + "C1", + { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::If": [ + "C1", + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Condition": "C1" + }, + "MyApifetchApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2c0cd2d9dfc" + }, + "ApiMappingKey": "fetch", + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_route53_hosted_zone_name_http.json b/tests/translator/output/aws-cn/api_with_custom_domain_route53_hosted_zone_name_http.json new file mode 100644 index 0000000000..73714c6cdf --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_route53_hosted_zone_name_http.json @@ -0,0 +1,180 @@ +{ + "Parameters": { + "ACMCertificateArn": { + "Default": "cert-arn-in-us-east-1", + "Type": "String" + }, + "DomainName": { + "Default": "example.com", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "MyApioneApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV20caaf24ab1" + }, + "ApiMappingKey": "one", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "ApiGatewayDomainNameV20caaf24ab1": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "cert-arn-in-us-east-1", + "EndpointType": "EDGE" + } + ], + "DomainName": "example.com" + } + }, + "RecordSetGroup456ebaf280": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneName": "www.my-domain.com.", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "A", + "Name": "example.com" + } + ] + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionFetchPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/fetch": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_route53_http.json b/tests/translator/output/aws-cn/api_with_custom_domain_route53_http.json new file mode 100644 index 0000000000..82bb133608 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_route53_http.json @@ -0,0 +1,193 @@ +{ + "Parameters": { + "ACMCertificateArn": { + "Default": "cert-arn-in-us-east-1", + "Type": "String" + }, + "DomainName": { + "Default": "example.com", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApioneApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV20caaf24ab1" + }, + "ApiMappingKey": "one", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "RecordSetGroupbd00d962a4": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "ZQ1UAL4EFZVME", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "A", + "Name": "example.com" + }, + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "AAAA", + "Name": "example.com" + } + ] + } + }, + "ApiGatewayDomainNameV20caaf24ab1": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "cert-arn-in-us-east-1", + "EndpointType": "EDGE" + } + ], + "DomainName": "example.com" + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionFetchPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/fetch": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json new file mode 100644 index 0000000000..589b610a23 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json @@ -0,0 +1,214 @@ +{ + "Parameters": { + "MyDomainName": { + "Default": "sam-v2-regional-test.com", + "Type": "String" + }, + "MyDomainCert": { + "Default": "arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", + "Type": "String" + } + }, + "Resources": { + "MyApibasicApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2ab8e63947d" + }, + "ApiMappingKey": "basic", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "HttpApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "HttpApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "HttpApiFunctionSimpleCasePermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "HttpApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApibeginhereApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV2ab8e63947d" + }, + "ApiMappingKey": "begin-here", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "RecordSetGroup9616db1511": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneName": "sam-v2-regional-test.com.", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV2ab8e63947d", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV2ab8e63947d", + "RegionalDomainName" + ] + } + }, + "Type": "A", + "Name": "sam-v2-regional-test.com" + } + ] + } + }, + "HttpApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiGatewayDomainNameV2ab8e63947d": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "arn:aws:acm:us-east-1:551213647843:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", + "EndpointType": "REGIONAL" + } + ], + "DomainName": "sam-v2-regional-test.com" + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/basic": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + }, + "$default": { + "x-amazon-apigateway-any-method": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "isDefaultRoute": true, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json new file mode 100644 index 0000000000..f786841828 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json @@ -0,0 +1,309 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "MyDomainCert": { + "Default": "another-api-arn", + "Type": "String" + }, + "EndpointConf": { + "Default": "REGIONAL", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "ServerlessHttpApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/implicit": { + "Fn::If": [ + "C1", + { + "post": { + "Fn::If": [ + "C1", + { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::If": [ + "C1", + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionImplicitGetPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessHttpApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "ApiGatewayDomainNameV29c93aac102": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "another-api-arn", + "EndpointType": "REGIONAL" + } + ], + "DomainName": { + "Fn::Sub": "example-us-gov-west-1.com" + } + }, + "Condition": "C1" + }, + "MyFunctionApiPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApigetApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV29c93aac102" + }, + "ApiMappingKey": "get", + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "ServerlessHttpApiApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "ServerlessHttpApi" + }, + "AutoDeploy": true, + "StageName": "$default" + }, + "Condition": "C1" + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/get": { + "Fn::If": [ + "C1", + { + "put": { + "Fn::If": [ + "C1", + { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::If": [ + "C1", + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Condition": "C1" + }, + "MyApifetchApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV29c93aac102" + }, + "ApiMappingKey": "fetch", + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_hosted_zone_name_http.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_hosted_zone_name_http.json new file mode 100644 index 0000000000..821e6bc248 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_hosted_zone_name_http.json @@ -0,0 +1,180 @@ +{ + "Parameters": { + "ACMCertificateArn": { + "Default": "cert-arn-in-us-east-1", + "Type": "String" + }, + "DomainName": { + "Default": "example.com", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "MyApioneApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV20caaf24ab1" + }, + "ApiMappingKey": "one", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "ApiGatewayDomainNameV20caaf24ab1": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "cert-arn-in-us-east-1", + "EndpointType": "EDGE" + } + ], + "DomainName": "example.com" + } + }, + "RecordSetGroup456ebaf280": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneName": "www.my-domain.com.", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "A", + "Name": "example.com" + } + ] + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionFetchPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/fetch": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_http.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_http.json new file mode 100644 index 0000000000..956e7ac727 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_http.json @@ -0,0 +1,193 @@ +{ + "Parameters": { + "ACMCertificateArn": { + "Default": "cert-arn-in-us-east-1", + "Type": "String" + }, + "DomainName": { + "Default": "example.com", + "Type": "String" + } + }, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n const response = {\n statusCode: 200,\n body: JSON.stringify('Hello from Lambda!'),\n };\n return response;\n};\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApioneApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV20caaf24ab1" + }, + "ApiMappingKey": "one", + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "RecordSetGroupbd00d962a4": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "ZQ1UAL4EFZVME", + "RecordSets": [ + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "A", + "Name": "example.com" + }, + { + "AliasTarget": { + "HostedZoneId": "Z2FDTNDATAQYW2", + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV20caaf24ab1", + "DistributionDomainName" + ] + } + }, + "Type": "AAAA", + "Name": "example.com" + } + ] + } + }, + "ApiGatewayDomainNameV20caaf24ab1": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainNameConfigurations": [ + { + "CertificateArn": "cert-arn-in-us-east-1", + "EndpointType": "EDGE" + } + ], + "DomainName": "example.com" + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi" + }, + "AutoDeploy": true, + "StageName": "Prod" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionFetchPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/fetch": { + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + "payloadFormatVersion": "1.0" + }, + "responses": {} + } + } + }, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 69cefbeb96..304d50f2cf 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -339,6 +339,10 @@ def test_transform_success(self, testcase, partition_with_region): "api_with_basic_custom_domain_intrinsics", "api_with_custom_domain_route53", "api_with_custom_domain_route53_hosted_zone_name", + "api_with_basic_custom_domain_http", + "api_with_basic_custom_domain_intrinsics_http", + "api_with_custom_domain_route53_http", + "api_with_custom_domain_route53_hosted_zone_name_http", "implicit_http_api", "explicit_http_api_minimum", "implicit_http_api_auth_and_simple_case", diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index a8a6a138e8..4b868ee675 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -16,6 +16,7 @@ The AWS Serverless Application Model (SAM) is licensed under [The Apache License * [Event source types](#event-source-types) * [Property types](#property-types) * [Data types](#data-types) + * [Referable properties of SAM resources](#referable-properties-of-sam-resources) ## Introduction @@ -282,6 +283,7 @@ Tags | Map of `string` to `string` | A map (string to string) that specifies the AccessLogSettings | [AccessLogSettings](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-stage-accesslogsettings.html) | Settings for logging access in a stage. CorsConfiguration | `boolean` or [CorsConfiguration Object](#cors-configuration-object) | Enable CORS for all your Http APIs. Specify `true` for adding Cors with domain '*' to your Http APIs or specify a dictionary with additional [CorsConfiguration-Object](#cors-configuration-object). SAM adds `x-amazon-apigateway-cors` header in open api definition for your Http API when this property is defined. NOTE: Cors requires SAM to modify your OpenAPI definition. Hence it works only inline OpenAPI defined with `DefinitionBody`. DefaultRouteSettings | [RouteSettings](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-stage-routesettings.html) | The default route settings for this HTTP API. +Domain | [Domain Configuration Object](#domain-configuration-object) | Configuration settings for custom domains on API. Must contain `DomainName` and `CertificateArn` ##### Return values @@ -1273,3 +1275,34 @@ CorsConfiguration: ExposeHeaders: - "*" ``` + +### Referable properties of SAM resources +- [AWS::Serverless::Function](#referable-properties-of-serverless-function) +- [AWS::Serverless::Api](#referable-properties-of-serverless-RestApi) +- [AWS::Serverless::HttpApi](#referable-properties-of-serverless-HttpApi) + +#### Referable properties of Serverless Function +Property Name | Reference | LogicalId | Description +---|:---:|---|--- +Alias | `function-logical-id`.Alias | `function-logical-id`Alias`alias-name` | SAM generates an `AWS::Lambda::Alias` resource when `AutoPublishAlias` property is set. This resource can be referenced in intrinsic functions by using the resource logical ID or `function-logical-id`.Alias +Version | `function-logical-id`.Version | `function-logical-id`Version`sha` | SAM generates an `AWS::Lambda::Version` resource when `AutoPublishAlias` property is set. This resource can be referenced in intrinsic functions by using the resource logical ID or `function-logical-id`.Version +DestinationTopic | `function-logical-id`.DestinationTopic |`function-logical-id`EventInvokeConfig`OnSuccess/OnFailure`Topic| SAM auto creates an `AWS::SNS::Topic` resource when `Destination` property of `DestinationConfig` property in `EventInvokeConfig` property is not specified. This generated resource can be referenced by using `function-logical-id`.DestinationTopic +DestinationQueue | `function-logical-id`.DestinationQueue |`function-logical-id`EventInvokeConfig`OnSuccess/OnFailure`Queue | SAM auto creates an `AWS::SQS::Queue` resource when `Destination` property of `DestinationConfig` property in `EventInvokeConfig` property is not specified. This generated resource can be referenced by using `function-logical-id`.DestinationQueue + +#### Referable properties of Serverless RestApi + +Property Name | Reference | LogicalId | Description +---|:---:|---|--- +Stage | `restapi-logical-id`.Stage | `restapi-logical-id` `StageName`Stage | SAM generates `AWS::ApiGateway::Stage` resource when `AWS::Serverless::Api` resource is defined. This resource can be referenced in intrinsic function using the resource logical id or `restapi-logical-id`.Stage +Deployment | `restapi-logical-id`.Deployment | `restapi-logical-id`Deployment`sha` | SAM generates `AWS::ApiGateway::Deployment` resource when `AWS::Serverless::Api` resource is defined. This resource can be referenced in intrinsic function using the resource logical id or `restapi-logical-id`.Deployment +DomainName | `restapi-logical-id`.DomainName | `domain-logical-id` | `AWS::ApiGateway::DomainName` resource can be referenced by using the resource logical id or `restapi-logical-id`.DomainName when `DomainName` resource is defined in `Domain` property of `AWS::Serverless::Api` +UsagePlan | `restapi-logical-id`.UsagePlan | `restapi-logical-id`UsagePlan | SAM generates UsagePlan, UsagePlanKey and ApiKey resources when `UsagePlan` property is set. UsagePlan resource can be referenced in intrinsic function using the resource logical id or `restapi-logical-id`.UsagePlan +UsagePlanKey | `restapi-logical-id`.UsagePlanKey |`restapi-logical-id`UsagePlanKey | SAM generates UsagePlan, UsagePlanKey and ApiKey resources when `UsagePlan` property is set. UsagePlanKey resource can be referenced in intrinsic function using the resource logical id or `restapi-logical-id`.UsagePlanKey +ApiKey | `restapi-logical-id`.ApiKey |`restapi-logical-id`ApiKey | SAM generates UsagePlan, UsagePlanKey and ApiKey resources when `UsagePlan` property is set. ApiKey resource can be referenced in intrinsic function using the resource logical id or `restapi-logical-id`.ApiKey + +#### Referable properties of Serverless HttpApi + +Property Name | Reference | LogicalId | Description +---|:---:|---|--- +Stage | `httpapi-logical-id`.Stage | `httpapi-logical-id`ApiGatewayDefaultStage or `httpapi-logical-id` `StageName`Stage | SAM generates `AWS::ApiGatewayV2::Stage` resource with `httpapi-logical-id`ApiGatewayDefaultStage logical id if `StageName` property is not defined. If an explicit `StageName` property is defined them SAM generates `AWS::ApiGatewayV2::Stage` resource with `httpapi-logical-id` `StageName`Stage logicalId. This resource can be referenced in intrinsic functions using `httpapi-logical-id`.Stage +DomainName | `httpapi-logical-id`.DomainName | `domain-logical-id` | `AWS::ApiGatewayV2::DomainName` resource can be referenced by using the resource logical id or `restapi-logical-id`.DomainName when `DomainName` resource is defined in `Domain` property of `AWS::Serverless::Api` \ No newline at end of file