diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b23816c3c0..e74c21fa23 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,7 +87,7 @@ Here are some questions that you should answer in your plan: We welcome you to use the GitHub issue tracker to report bugs or suggest features. -When filing an issue, please check [existing open](https://github.com/awslabs/PRIVATE-aws-sam-development/issues), or [recently closed](https://github.com/awslabs/PRIVATE-aws-sam-development/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already +When filing an issue, please check [existing open](https://github.com/awslabs/serverless-application-model/issues), or [recently closed](https://github.com/awslabs/serverless-application-model/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: - A reproducible test case or series of steps diff --git a/docs/cloudformation_compatibility.rst b/docs/cloudformation_compatibility.rst index 02395b39b4..15fab40a45 100644 --- a/docs/cloudformation_compatibility.rst +++ b/docs/cloudformation_compatibility.rst @@ -184,6 +184,7 @@ MinimumCompressionSize All Cors All TracingEnabled All OpenApiVersion None +Domain All ================================== ======================== ======================== diff --git a/docs/globals.rst b/docs/globals.rst index c409da5f40..7c38dcba81 100644 --- a/docs/globals.rst +++ b/docs/globals.rst @@ -89,6 +89,7 @@ Currently, the following resources and properties are being supported: CanarySetting: TracingEnabled: OpenApiVersion: + Domain: SimpleTable: # Properties of AWS::Serverless::SimpleTable diff --git a/examples/2016-10-31/custom_domains_without_route53/README.md b/examples/2016-10-31/custom_domains_without_route53/README.md new file mode 100644 index 0000000000..68315db158 --- /dev/null +++ b/examples/2016-10-31/custom_domains_without_route53/README.md @@ -0,0 +1,19 @@ +# Custom Domains support + +Example SAM template for setting up Api Gateway resources for custom domains. + +## Prerequisites for setting up custom domains +1. A domain name. You can purchase a domain name from a domain name provider. +1. A certificate ARN. Set up or import a valid certificate into AWS Certificate Manager. + +## PostRequisites +After deploying the template, make sure you configure the DNS settings on the domain name provider's website. You will need to add Type A and Type AAAA DNS records that are point to ApiGateway's Hosted Zone Id. Read more [here](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-api-gateway.html) + +## Running the example + +```bash +$ sam deploy \ + --template-file /path_to_template/packaged-template.yaml \ + --stack-name my-new-stack \ + --capabilities CAPABILITY_IAM +``` diff --git a/examples/2016-10-31/custom_domains_without_route53/template.yaml b/examples/2016-10-31/custom_domains_without_route53/template.yaml new file mode 100644 index 0000000000..67c0a16907 --- /dev/null +++ b/examples/2016-10-31/custom_domains_without_route53/template.yaml @@ -0,0 +1,37 @@ +Parameters: + MyDomainName: + Type: String + Default: another-example.com + + MyDomainCert: + Type: String + Default: another-api-arn + +Globals: + Api: + Domain: + DomainName: !Ref MyDomainName + CertificateArn: !Ref MyDomainCert + EndpointConfiguration: 'REGIONAL' + BasePath: ['/get'] + +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: nodejs8.10 + Events: + ImplicitGet: + Type: Api + Properties: + Method: Get + Path: /get diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 65e29d19d7..4320a752f4 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -3,7 +3,8 @@ from samtranslator.model.intrinsics import ref from samtranslator.model.apigateway import (ApiGatewayDeployment, ApiGatewayRestApi, ApiGatewayStage, ApiGatewayAuthorizer, - ApiGatewayResponse) + ApiGatewayResponse, ApiGatewayDomainName, + ApiGatewayBasePathMapping) from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.s3_utils.uri_parser import parse_s3_uri from samtranslator.region_configuration import RegionConfiguration @@ -35,7 +36,7 @@ def __init__(self, logical_id, cache_cluster_enabled, cache_cluster_size, variab method_settings=None, binary_media=None, minimum_compression_size=None, cors=None, auth=None, gateway_responses=None, access_log_setting=None, canary_setting=None, tracing_enabled=None, resource_attributes=None, passthrough_resource_attributes=None, - open_api_version=None, models=None): + open_api_version=None, models=None, domain=None): """Constructs an API Generator class that generates API Gateway resources :param logical_id: Logical id of the SAM API Resource @@ -80,6 +81,7 @@ def __init__(self, logical_id, cache_cluster_enabled, cache_cluster_size, variab self.open_api_version = open_api_version self.remove_extra_stage = open_api_version self.models = models + self.domain = domain def _construct_rest_api(self): """Constructs and returns the ApiGateway RestApi. @@ -204,13 +206,78 @@ def _construct_stage(self, deployment, swagger): stage.TracingEnabled = self.tracing_enabled if swagger is not None: - deployment.make_auto_deployable(stage, self.remove_extra_stage, swagger) + deployment.make_auto_deployable(stage, self.remove_extra_stage, swagger, self.domain) if self.tags is not None: stage.Tags = get_tag_list(self.tags) return stage + def _construct_api_domain(self, rest_api): + """ + Constructs and returns the ApiGateway Domain and BasepathMapping + """ + if self.domain is None: + return 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") + + logical_id = logical_id_generator.LogicalIdGenerator("", self.domain).gen() + + domain = ApiGatewayDomainName('ApiGatewayDomainName' + logical_id, + attributes=self.passthrough_resource_attributes) + domain.DomainName = self.domain.get('DomainName') + endpoint = self.domain.get('EndpointConfiguration') + + if endpoint is None: + endpoint = 'REGIONAL' + elif endpoint not in ['EDGE', 'REGIONAL']: + raise InvalidResourceException(self.logical_id, + "EndpointConfiguration for Custom Domains must be" + " one of {}".format(['EDGE', 'REGIONAL'])) + + if endpoint == 'REGIONAL': + domain.RegionalCertificateArn = self.domain.get('CertificateArn') + else: + domain.CertificateArn = self.domain.get('CertificateArn') + + domain.EndpointConfiguration = {"Types": [endpoint]} + + # 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 = [] + + if basepaths is None: + basepath_mapping = ApiGatewayBasePathMapping(self.logical_id + 'BasePathMapping', + attributes=self.passthrough_resource_attributes) + basepath_mapping.DomainName = self.domain.get('DomainName') + basepath_mapping.RestApiId = ref(rest_api.logical_id) + basepath_mapping.Stage = ref(rest_api.logical_id + '.Stage') + basepath_resource_list.extend([basepath_mapping]) + else: + for path in basepaths: + path = ''.join(e for e in path if e.isalnum()) + logical_id = "{}{}{}".format(self.logical_id, path, 'BasePathMapping') + basepath_mapping = ApiGatewayBasePathMapping(logical_id, + attributes=self.passthrough_resource_attributes) + basepath_mapping.DomainName = self.domain.get('DomainName') + basepath_mapping.RestApiId = ref(rest_api.logical_id) + basepath_mapping.Stage = ref(rest_api.logical_id + '.Stage') + basepath_mapping.BasePath = path + basepath_resource_list.extend([basepath_mapping]) + + return domain, basepath_resource_list + def to_cloudformation(self): """Generates CloudFormation resources from a SAM API resource @@ -218,6 +285,7 @@ def to_cloudformation(self): :rtype: tuple """ rest_api = self._construct_rest_api() + domain, basepath_mapping = self._construct_api_domain(rest_api) deployment = self._construct_deployment(rest_api) swagger = None @@ -229,7 +297,7 @@ def to_cloudformation(self): stage = self._construct_stage(deployment, swagger) permissions = self._construct_authorizer_lambda_permission() - return rest_api, deployment, stage, permissions + return rest_api, deployment, stage, permissions, domain, basepath_mapping def _add_cors(self): """ diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index e2cc90095a..95a063744c 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -1,5 +1,5 @@ +import json from re import match - from samtranslator.model import PropertyType, Resource from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.types import is_type, one_of, is_str, list_of @@ -76,12 +76,14 @@ class ApiGatewayDeployment(Resource): "deployment_id": lambda self: ref(self.logical_id), } - def make_auto_deployable(self, stage, openapi_version=None, swagger=None): + def make_auto_deployable(self, stage, openapi_version=None, swagger=None, domain=None): """ Sets up the resource such that it will trigger a re-deployment when Swagger changes - or the openapi version changes. + or the openapi version changes or a domain resource changes. :param swagger: Dictionary containing the Swagger definition of the API + :param openapi_version: string containing value of OpenApiVersion flag in the template + :param domain: Dictionary containing the custom domain configuration for the API """ if not swagger: return @@ -95,6 +97,9 @@ def make_auto_deployable(self, stage, openapi_version=None, swagger=None): hash_input = [str(swagger)] if openapi_version: hash_input.append(str(openapi_version)) + if domain: + hash_input.append(self._X_HASH_DELIMITER) + hash_input.append(json.dumps(domain)) data = self._X_HASH_DELIMITER.join(hash_input) generator = logical_id_generator.LogicalIdGenerator(self.logical_id, data) @@ -153,6 +158,26 @@ def _status_code_string(self, status_code): return None if status_code is None else str(status_code) +class ApiGatewayDomainName(Resource): + resource_type = 'AWS::ApiGateway::DomainName' + property_types = { + 'RegionalCertificateArn': PropertyType(False, is_str()), + 'DomainName': PropertyType(True, is_str()), + 'EndpointConfiguration': PropertyType(False, is_type(dict)), + 'CertificateArn': PropertyType(False, is_str()) + } + + +class ApiGatewayBasePathMapping(Resource): + resource_type = 'AWS::ApiGateway::BasePathMapping' + property_types = { + 'BasePath': PropertyType(False, is_str()), + 'DomainName': PropertyType(True, is_str()), + 'RestApiId': PropertyType(False, is_str()), + 'Stage': PropertyType(False, is_str()) + } + + class ApiGatewayAuthorizer(object): _VALID_FUNCTION_PAYLOAD_TYPES = [None, 'TOKEN', 'REQUEST'] diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index f2f56e7943..aa583efb88 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -482,7 +482,8 @@ class SamApi(SamResourceMacro): 'CanarySetting': PropertyType(False, is_type(dict)), 'TracingEnabled': PropertyType(False, is_type(bool)), 'OpenApiVersion': PropertyType(False, is_str()), - 'Models': PropertyType(False, is_type(dict)) + 'Models': PropertyType(False, is_type(dict)), + 'Domain': PropertyType(False, is_type(dict)) } referable_properties = { @@ -502,6 +503,7 @@ def to_cloudformation(self, **kwargs): intrinsics_resolver = kwargs["intrinsics_resolver"] self.BinaryMediaTypes = intrinsics_resolver.resolve_parameter_refs(self.BinaryMediaTypes) + self.Domain = intrinsics_resolver.resolve_parameter_refs(self.Domain) api_generator = ApiGenerator(self.logical_id, self.CacheClusterEnabled, @@ -526,13 +528,17 @@ def to_cloudformation(self, **kwargs): resource_attributes=self.resource_attributes, passthrough_resource_attributes=self.get_passthrough_resource_attributes(), open_api_version=self.OpenApiVersion, - models=self.Models) + models=self.Models, + domain=self.Domain) - rest_api, deployment, stage, permissions = api_generator.to_cloudformation() + rest_api, deployment, stage, permissions, domain, basepath_mapping = api_generator.to_cloudformation() resources.extend([rest_api, deployment, stage]) resources.extend(permissions) - + if domain: + resources.extend([domain]) + if basepath_mapping: + resources.extend(basepath_mapping) return resources diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index 41cb9fc265..47cf675eb4 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -59,7 +59,8 @@ class Globals(object): "AccessLogSetting", "CanarySetting", "TracingEnabled", - "OpenApiVersion" + "OpenApiVersion", + "Domain" ], SamResourceType.SimpleTable.value: [ diff --git a/samtranslator/plugins/globals/globals_plugin.py b/samtranslator/plugins/globals/globals_plugin.py index 3c4872773c..17f42e3e75 100644 --- a/samtranslator/plugins/globals/globals_plugin.py +++ b/samtranslator/plugins/globals/globals_plugin.py @@ -26,7 +26,6 @@ def on_before_transform_template(self, template_dict): :param dict template_dict: SAM template as a dictionary """ - try: global_section = Globals(template_dict) except InvalidGlobalsSectionException as ex: diff --git a/samtranslator/translator/verify_logical_id.py b/samtranslator/translator/verify_logical_id.py index e2d5c25e6c..69ae10bf57 100644 --- a/samtranslator/translator/verify_logical_id.py +++ b/samtranslator/translator/verify_logical_id.py @@ -7,7 +7,9 @@ 'AWS::SNS::Topic': 'AWS::SNS::Topic', 'AWS::DynamoDB::Table': 'AWS::Serverless::SimpleTable', 'AWS::CloudFormation::Stack': 'AWS::Serverless::Application', - 'AWS::Cognito::UserPool': 'AWS::Cognito::UserPool' + 'AWS::Cognito::UserPool': 'AWS::Cognito::UserPool', + 'AWS::ApiGateway::DomainName': 'AWS::ApiGateway::DomainName', + 'AWS::ApiGateway::BasePathMapping': 'AWS::ApiGateway::BasePathMapping' } diff --git a/tests/translator/input/api_with_basic_custom_domain.yaml b/tests/translator/input/api_with_basic_custom_domain.yaml new file mode 100644 index 0000000000..a1e53495e5 --- /dev/null +++ b/tests/translator/input/api_with_basic_custom_domain.yaml @@ -0,0 +1,71 @@ +Parameters: + MyDomainName: + Type: String + Default: another-example.com + + MyDomainCert: + Type: String + Default: another-api-arn + +Globals: + Api: + Domain: + DomainName: !Ref MyDomainName + CertificateArn: !Ref MyDomainCert + +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: nodejs8.10 + Events: + Api: + Type: Api + Properties: + RestApiId: !Ref MyApi + Method: Put + Path: /get + Fetch: + Type: Api + Properties: + RestApiId: !Ref MyApi + Method: Post + Path: /fetch + ImplicitGet: + Type: Api + Properties: + Method: Post + Path: /implicit + AnotherGet: + Type: Api + Properties: + RestApiId: !Ref MyAnotherApi + Method: Post + Path: /another/get + + MyApi: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + Domain: + DomainName: 'api-example.com' + CertificateArn: 'my-api-cert-arn' + EndpointConfiguration: 'EDGE' + BasePath: [ "/get", "/fetch" ] + + MyAnotherApi: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + diff --git a/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml b/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml new file mode 100644 index 0000000000..dee4537028 --- /dev/null +++ b/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml @@ -0,0 +1,54 @@ +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: nodejs8.10 + Events: + Api: + Type: Api + Properties: + RestApiId: !Ref MyApi + Method: Put + Path: /get + ImplicitGet: + Type: Api + Properties: + Method: Post + Path: /implicit + + MyApi: + Condition: C1 + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + Domain: + DomainName: !Sub 'example-${AWS::Region}.com' + CertificateArn: !Ref MyDomainCert + EndpointConfiguration: !Ref EndpointConf + BasePath: [ "/get", "/fetch" ] + diff --git a/tests/translator/input/error_api_with_custom_domains_invalid.yaml b/tests/translator/input/error_api_with_custom_domains_invalid.yaml new file mode 100644 index 0000000000..bc58f6f1aa --- /dev/null +++ b/tests/translator/input/error_api_with_custom_domains_invalid.yaml @@ -0,0 +1,57 @@ +Parameters: + MyDomainName: + Type: String + Default: another-example.com + + MyDomainCert: + Type: String + Default: another-api-arn + +Globals: + Api: + Domain: + CertificateArn: !Ref MyDomainCert + +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: nodejs8.10 + Events: + Api: + Type: Api + Properties: + RestApiId: !Ref MyApi + Method: Put + Path: /get + Fetch: + Type: Api + Properties: + RestApiId: !Ref MyApi + Method: Post + Path: /fetch + ImplicitGet: + Type: Api + Properties: + Method: Post + Path: /implicit + + MyApi: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + Domain: + DomainName: 'api-example.com' + CertificateArn: 'my-api-cert-arn' + EndpointConfiguration: 'Invalid' + BasePath: [ "/get", "/fetch" ] diff --git a/tests/translator/output/api_with_basic_custom_domain.json b/tests/translator/output/api_with_basic_custom_domain.json new file mode 100644 index 0000000000..aaea82eea6 --- /dev/null +++ b/tests/translator/output/api_with_basic_custom_domain.json @@ -0,0 +1,378 @@ +{ + "Parameters": { + "MyDomainName": { + "Default": "another-example.com", + "Type": "String" + }, + "MyDomainCert": { + "Default": "another-api-arn", + "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": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionAnotherGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/another/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyAnotherApi" + } + } + ] + } + } + }, + "MyApifetchBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "fetch", + "DomainName": "api-example.com", + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyFunctionImplicitGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyAnotherApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyAnotherApiDeployment298b716713" + }, + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "StageName": "Prod" + } + }, + "MyAnotherApiDeployment298b716713": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "Description": "RestApi deployment id: 298b71671363fe1258648c4e2231d5efb81ee2cb" + } + }, + "MyFunctionFetchPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyAnotherApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/another/get": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "openapi": "3.0.1" + } + } + }, + "ApiGatewayDomainName461285836f": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "CertificateArn": "my-api-cert-arn", + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "DomainName": "api-example.com" + } + }, + "ServerlessRestApiDeployment6f245e96ee": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 6f245e96ee393dcf04d340172a2d8f590cff7bff", + "StageName": "Stage" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "MyApigetBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "get", + "DomainName": "api-example.com", + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/get": { + "put": { + "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" + } + }, + "responses": {} + } + }, + "/fetch": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "openapi": "3.0.1" + } + } + }, + "MyAnotherApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": "another-example.com", + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "Stage": { + "Ref": "MyAnotherApiProdStage" + } + } + }, + "MyApiDeployment345fbac8f2": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: 345fbac8f2a7592c5592018ad06db6de00590d35" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment6f245e96ee" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": "another-example.com", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeployment345fbac8f2" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/implicit": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + } + } + }, + "ApiGatewayDomainName7a5525b8a3": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": "another-api-arn", + "DomainName": "another-example.com" + } + }, + "MyFunctionApiPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_basic_custom_domain_intrinsics.json b/tests/translator/output/api_with_basic_custom_domain_intrinsics.json new file mode 100644 index 0000000000..a41bfa93c5 --- /dev/null +++ b/tests/translator/output/api_with_basic_custom_domain_intrinsics.json @@ -0,0 +1,314 @@ +{ + "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": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "MyFunctionImplicitGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApigetBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "get", + "DomainName": { + "Fn::Sub": "example-ap-southeast-1.com" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment43c34b4e46" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "MyApifetchBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "fetch", + "DomainName": { + "Fn::Sub": "example-ap-southeast-1.com" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "C1" + }, + "ApiGatewayDomainName2bd847e760": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": "another-api-arn", + "DomainName": { + "Fn::Sub": "example-ap-southeast-1.com" + } + }, + "Condition": "C1" + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "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:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1" + } + }, + "Condition": "C1" + }, + "ServerlessRestApiDeployment43c34b4e46": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 43c34b4e46d381101dd94b95856a2cea244411b4", + "StageName": "Stage" + }, + "Condition": "C1" + }, + "MyApiDeploymentb73cd6313c": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: b73cd6313c957bca233f1137e39ffbe99e448848" + }, + "Condition": "C1" + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentb73cd6313c" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "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:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "swagger": "2.0" + } + }, + "Condition": "C1" + }, + "MyFunctionApiPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + }, + "Condition": "C1" + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain.json new file mode 100644 index 0000000000..97a2aae5d8 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain.json @@ -0,0 +1,402 @@ +{ + "Parameters": { + "MyDomainName": { + "Default": "another-example.com", + "Type": "String" + }, + "MyDomainCert": { + "Default": "another-api-arn", + "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": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionAnotherGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/another/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyAnotherApi" + } + } + ] + } + } + }, + "MyFunctionImplicitGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyAnotherApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyAnotherApiDeploymentc61fb61d5c" + }, + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "StageName": "Prod" + } + }, + "MyFunctionFetchPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyAnotherApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/another/get": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "openapi": "3.0.1" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ApiGatewayDomainName461285836f": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "CertificateArn": "my-api-cert-arn", + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "DomainName": "api-example.com" + } + }, + "MyApifetchBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "fetch", + "DomainName": "api-example.com", + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "MyApigetBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "get", + "DomainName": "api-example.com", + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/get": { + "put": { + "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" + } + }, + "responses": {} + } + }, + "/fetch": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "openapi": "3.0.1" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyAnotherApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": "another-example.com", + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "Stage": { + "Ref": "MyAnotherApiProdStage" + } + } + }, + "MyApiDeploymentaf6c57ee88": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: af6c57ee88bdbf0d8ace828d5ac9cc02dba55922" + } + }, + "ServerlessRestApiDeploymentd099245fb5": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: d099245fb560a6117ebb37d418da8821c19f1eac", + "StageName": "Stage" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentd099245fb5" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "MyAnotherApiDeploymentc61fb61d5c": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "Description": "RestApi deployment id: c61fb61d5cfc17e7e2f188dc18c5b11745bb6bc8" + } + }, + "ServerlessRestApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": "another-example.com", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentaf6c57ee88" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/implicit": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ApiGatewayDomainName7a5525b8a3": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": "another-api-arn", + "DomainName": "another-example.com" + } + }, + "MyFunctionApiPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json new file mode 100644 index 0000000000..c4a2c9abc6 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json @@ -0,0 +1,330 @@ +{ + "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": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "MyFunctionImplicitGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApifetchBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "fetch", + "DomainName": { + "Fn::Sub": "example-cn-north-1.com" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment599c0b434d" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "ServerlessRestApiDeployment599c0b434d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 599c0b434d7122049846aedc74269bb6020bd9d3", + "StageName": "Stage" + }, + "Condition": "C1" + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApigetBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "get", + "DomainName": { + "Fn::Sub": "example-cn-north-1.com" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "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-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Condition": "C1" + }, + "MyApiDeployment6cfbc38395": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: 6cfbc383958724f1d2d4d05d6e76f8670e66831f" + }, + "Condition": "C1" + }, + "ApiGatewayDomainName4bfafc5803": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": "another-api-arn", + "DomainName": { + "Fn::Sub": "example-cn-north-1.com" + } + }, + "Condition": "C1" + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeployment6cfbc38395" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "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-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Condition": "C1" + }, + "MyFunctionApiPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + }, + "Condition": "C1" + } + } +} diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json new file mode 100644 index 0000000000..23d1f3ff40 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json @@ -0,0 +1,402 @@ +{ + "Parameters": { + "MyDomainName": { + "Default": "another-example.com", + "Type": "String" + }, + "MyDomainCert": { + "Default": "another-api-arn", + "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": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionAnotherGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/another/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyAnotherApi" + } + } + ] + } + } + }, + "MyFunctionImplicitGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyAnotherApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyAnotherApiDeployment619d82c169" + }, + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "StageName": "Prod" + } + }, + "MyFunctionFetchPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/fetch", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyAnotherApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/another/get": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "openapi": "3.0.1" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ApiGatewayDomainName461285836f": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "CertificateArn": "my-api-cert-arn", + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "DomainName": "api-example.com" + } + }, + "MyApifetchBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "fetch", + "DomainName": "api-example.com", + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "MyApigetBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "get", + "DomainName": "api-example.com", + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + } + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/get": { + "put": { + "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" + } + }, + "responses": {} + } + }, + "/fetch": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "openapi": "3.0.1" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyAnotherApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": "another-example.com", + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "Stage": { + "Ref": "MyAnotherApiProdStage" + } + } + }, + "MyApiDeploymentb70cda6c19": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: b70cda6c195d5d7d6300422874b03fdf3ac8cf16" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment15c5513463" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiDeployment15c5513463": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 15c5513463f336ce7d80f9107134351289f4dc75", + "StageName": "Stage" + } + }, + "MyAnotherApiDeployment619d82c169": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyAnotherApi" + }, + "Description": "RestApi deployment id: 619d82c169072c33e3a62ae66f9bbe723d9f6d71" + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentb70cda6c19" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + } + }, + "ServerlessRestApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": "another-example.com", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/implicit": { + "post": { + "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" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ApiGatewayDomainName7a5525b8a3": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": "another-api-arn", + "DomainName": "another-example.com" + } + }, + "MyFunctionApiPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json new file mode 100644 index 0000000000..1b738ec6ef --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json @@ -0,0 +1,330 @@ +{ + "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": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "C1" + }, + "MyFunctionImplicitGetPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/implicit", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApiDeploymentc15ba09b92": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: c15ba09b92a98c578c133496aa7d330c1666c2b8" + }, + "Condition": "C1" + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment0294e6b48a" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "MyApifetchBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "fetch", + "DomainName": { + "Fn::Sub": "example-us-gov-west-1.com" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "C1" + }, + "MyApigetBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "BasePath": "get", + "DomainName": { + "Fn::Sub": "example-us-gov-west-1.com" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "Stage": { + "Ref": "MyApiProdStage" + } + }, + "Condition": "C1" + }, + "ServerlessRestApiDeployment0294e6b48a": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 0294e6b48a4d409ae8dbd2333cd092c27cbee6fe", + "StageName": "Stage" + }, + "Condition": "C1" + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "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-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "openapi": "3.0.1" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Condition": "C1" + }, + "ApiGatewayDomainNamedf402213e0": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "RegionalCertificateArn": "another-api-arn", + "DomainName": { + "Fn::Sub": "example-us-gov-west-1.com" + } + }, + "Condition": "C1" + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentc15ba09b92" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "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-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "responses": {} + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Condition": "C1" + }, + "MyFunctionApiPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/get", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + }, + "Condition": "C1" + } + } +} \ No newline at end of file diff --git a/tests/translator/output/error_api_with_custom_domains_invalid.json b/tests/translator/output/error_api_with_custom_domains_invalid.json new file mode 100644 index 0000000000..1f60e2664e --- /dev/null +++ b/tests/translator/output/error_api_with_custom_domains_invalid.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [MyApi] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL'] Resource with id [ServerlessRestApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided" + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 2. Resource with id [MyApi] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL'] Resource with id [ServerlessRestApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided" +} diff --git a/tests/translator/output/error_globals_api_with_stage_name.json b/tests/translator/output/error_globals_api_with_stage_name.json index 0b4d867abc..7faf2ba6b9 100644 --- a/tests/translator/output/error_globals_api_with_stage_name.json +++ b/tests/translator/output/error_globals_api_with_stage_name.json @@ -1,8 +1,8 @@ { "errors": [ { - "errorMessage": "'Globals' section is invalid. 'StageName' is not a supported property of 'Api'. Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'OpenApiVersion']" + "errorMessage": "'Globals' section is invalid. 'StageName' is not a supported property of 'Api'. Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'OpenApiVersion', 'Domain']" } ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'StageName' is not a supported property of 'Api'. Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'MinimumCompressionSize', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'TracingEnabled', 'OpenApiVersion']" + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'StageName' is not a supported property of 'Api'. Must be one of the following values - ['Auth', 'Name', 'DefinitionUri', 'CacheClusterEnabled', 'CacheClusterSize', 'Variables', 'EndpointConfiguration', 'MethodSettings', 'BinaryMediaTypes', 'MinimumCompressionSize', 'Cors', 'GatewayResponses', 'AccessLogSetting', 'CanarySetting', 'TracingEnabled', 'OpenApiVersion', 'Domain']" } diff --git a/tests/translator/test_api_resource.py b/tests/translator/test_api_resource.py index 88bea499b8..1c77c07220 100644 --- a/tests/translator/test_api_resource.py +++ b/tests/translator/test_api_resource.py @@ -33,7 +33,6 @@ def test_redeploy_explicit_api(): } } } - original_deployment_ids = translate_and_find_deployment_ids(manifest) # Now update the API specification. This should redeploy the API by creating a new deployment resource diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index b29eb843b4..9b0a5363fc 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -313,7 +313,9 @@ def test_transform_success(self, testcase, partition_with_region): 'api_with_swagger_and_openapi_with_auth', 'api_with_openapi_definition_body_no_flag', 'api_request_model_openapi_3', - 'api_with_apikey_required_openapi_3' + 'api_with_apikey_required_openapi_3', + 'api_with_basic_custom_domain', + 'api_with_basic_custom_domain_intrinsics', ], [ ("aws", "ap-southeast-1"), @@ -542,7 +544,8 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw 'error_function_with_invalid_policy_statement', 'error_function_with_invalid_condition_name', 'error_invalid_document_empty_semantic_version', - 'error_api_with_invalid_open_api_version_type' + 'error_api_with_invalid_open_api_version_type', + 'error_api_with_custom_domains_invalid' ]) @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch('samtranslator.plugins.application.serverless_app_plugin.ServerlessAppPlugin._sar_service_call', mock_sar_service_call) diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index a37cf42d65..f2a9719af9 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -81,6 +81,10 @@ Globals: Api: EndpointConfiguration: REGIONAL Cors: "'www.example.com'" + Domain: + DomainName: www.my-domain.com + CertificateArn: my-valid-cert-arn + EndpointConfiguration: EDGE SimpleTable: SSESpecification: @@ -233,7 +237,7 @@ AccessLogSetting | [CloudFormation AccessLogSetting property](https://docs.aws.a CanarySetting | [CloudFormation CanarySetting property](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-stage-canarysetting.html) | Configure a Canary Setting to a Stage of a regular deployment. This value is passed through to Cloudformation, so any value supported by `CanarySetting` is supported here. TracingEnabled | `boolean` | Indicates whether active tracing with X-Ray is enabled for the stage. Models | `List of JSON or YAML objects` | JSON schemas that describes the models to be used by API methods. - +Domain | [Domain Configuration Object](#domain-configuration-object) | Configuration settings for custom domains on API. Must contain `DomainName` and `CertificateArn` ##### Return values ###### Ref @@ -986,3 +990,13 @@ All properties of a Gateway Response object are optional. API Gateway has knowle [response type]: https://docs.aws.amazon.com/apigateway/api-reference/resource/gateway-response/ [`AWS::ApiGateway::GatewayResponse`]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-gatewayresponse.html + +### Domain Configuration object +Enable custom domains to be configured with your Api. Currently only supports Creating Api gateway resources for custom domains. + +```yaml +Domain: + DomainName: "example.com" # REQUIRED + CertificateARN: "" # REQUIRED | Must be a valid certificate ARN, and for EDGE endpoint configuration the certificate must be in us-east-1 + EndpointConfiguration: "EDGE" # optional | Default value is REGIONAL | Accepted values are EDGE | REGIONAL + BasePath: ["/foo"] # optional | Default value is '/' | List of basepaths to be configured with the ApiGateway Domain Name \ No newline at end of file