From be9b9d9ba841ea3146ab33b8ee279433b241353c Mon Sep 17 00:00:00 2001 From: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Date: Thu, 20 May 2021 10:07:30 -0700 Subject: [PATCH] fix: Shared Usage Plan scenarios for Resource Level Attribute Support (#2040) * do not propagate attributes to CognitoUserPool * shared usage plan with propagated resource level attributes * add unit tests * Added test templates for shared usage plan with resource attributes * Black reformatting * Removing unused import * fix python2 hashes Co-authored-by: Qingchuan Ma --- samtranslator/model/api/api_generator.py | 103 +++- samtranslator/model/eventsources/push.py | 3 - samtranslator/model/sam_resources.py | 2 + ...th_usageplans_shared_attributes_three.yaml | 102 ++++ ...with_usageplans_shared_attributes_two.yaml | 75 +++ ...th_usageplans_shared_attributes_three.json | 524 +++++++++++++++++ ...with_usageplans_shared_attributes_two.json | 385 ++++++++++++ ...th_usageplans_shared_attributes_three.json | 548 ++++++++++++++++++ ...with_usageplans_shared_attributes_two.json | 401 +++++++++++++ ...th_usageplans_shared_attributes_three.json | 548 ++++++++++++++++++ ...with_usageplans_shared_attributes_two.json | 401 +++++++++++++ tests/translator/test_translator.py | 2 + .../unit/model/api/TestSharedApiUsagePlan.py | 119 ++++ 13 files changed, 3203 insertions(+), 10 deletions(-) create mode 100644 tests/translator/input/api_with_usageplans_shared_attributes_three.yaml create mode 100644 tests/translator/input/api_with_usageplans_shared_attributes_two.yaml create mode 100644 tests/translator/output/api_with_usageplans_shared_attributes_three.json create mode 100644 tests/translator/output/api_with_usageplans_shared_attributes_two.json create mode 100644 tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_three.json create mode 100644 tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_two.json create mode 100644 tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_three.json create mode 100644 tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_two.json create mode 100644 tests/unit/model/api/TestSharedApiUsagePlan.py diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index eef1d3d27..3cd9ec082 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -1,7 +1,8 @@ import logging from collections import namedtuple + from six import string_types -from samtranslator.model.intrinsics import ref, fnGetAtt +from samtranslator.model.intrinsics import ref, fnGetAtt, make_or_condition from samtranslator.model.apigateway import ( ApiGatewayDeployment, ApiGatewayRestApi, @@ -15,7 +16,7 @@ ApiGatewayApiKey, ) from samtranslator.model.route53 import Route53RecordSetGroup -from samtranslator.model.exceptions import InvalidResourceException +from samtranslator.model.exceptions import InvalidResourceException, InvalidTemplateException from samtranslator.model.s3_utils.uri_parser import parse_s3_uri from samtranslator.region_configuration import RegionConfiguration from samtranslator.swagger.swagger import SwaggerEditor @@ -61,12 +62,92 @@ class SharedApiUsagePlan(object): so that these information can be used in the shared usage plan """ + SHARED_USAGE_PLAN_CONDITION_NAME = "SharedUsagePlanCondition" + def __init__(self): self.usage_plan_shared = False self.stage_keys_shared = list() self.api_stages_shared = list() self.depends_on_shared = list() + # shared resource level attributes + self.conditions = set() + self.any_api_without_condition = False + self.deletion_policy = None + self.update_replace_policy = None + + def get_combined_resource_attributes(self, resource_attributes, conditions): + """ + This method returns a dictionary which combines 'DeletionPolicy', 'UpdateReplacePolicy' and 'Condition' + values of API definitions that could be used in Shared Usage Plan resources. + + Parameters + ---------- + resource_attributes: Dict[str] + A dictionary of resource level attributes of the API resource + conditions: Dict[str] + Conditions section of the template + """ + self._set_deletion_policy(resource_attributes.get("DeletionPolicy")) + self._set_update_replace_policy(resource_attributes.get("UpdateReplacePolicy")) + self._set_condition(resource_attributes.get("Condition"), conditions) + + combined_resource_attributes = dict() + if self.deletion_policy: + combined_resource_attributes["DeletionPolicy"] = self.deletion_policy + if self.update_replace_policy: + combined_resource_attributes["UpdateReplacePolicy"] = self.update_replace_policy + # do not set Condition if any of the API resource does not have Condition in it + if self.conditions and not self.any_api_without_condition: + combined_resource_attributes["Condition"] = SharedApiUsagePlan.SHARED_USAGE_PLAN_CONDITION_NAME + + return combined_resource_attributes + + def _set_deletion_policy(self, deletion_policy): + if deletion_policy: + if self.deletion_policy: + # update only if new deletion policy is Retain + if deletion_policy == "Retain": + self.deletion_policy = deletion_policy + else: + self.deletion_policy = deletion_policy + + def _set_update_replace_policy(self, update_replace_policy): + if update_replace_policy: + if self.update_replace_policy: + # if new value is Retain or + # new value is retain and current value is Delete then update its value + if (update_replace_policy == "Retain") or ( + update_replace_policy == "Snapshot" and self.update_replace_policy == "Delete" + ): + self.update_replace_policy = update_replace_policy + else: + self.update_replace_policy = update_replace_policy + + def _set_condition(self, condition, template_conditions): + # if there are any API without condition, then skip + if self.any_api_without_condition: + return + + if condition and condition not in self.conditions: + + if template_conditions is None: + raise InvalidTemplateException( + "Can't have condition without having 'Conditions' section in the template" + ) + + if self.conditions: + self.conditions.add(condition) + or_condition = make_or_condition(self.conditions) + template_conditions[SharedApiUsagePlan.SHARED_USAGE_PLAN_CONDITION_NAME] = or_condition + else: + self.conditions.add(condition) + template_conditions[SharedApiUsagePlan.SHARED_USAGE_PLAN_CONDITION_NAME] = condition + elif condition is None: + self.any_api_without_condition = True + if template_conditions and SharedApiUsagePlan.SHARED_USAGE_PLAN_CONDITION_NAME in template_conditions: + del template_conditions[SharedApiUsagePlan.SHARED_USAGE_PLAN_CONDITION_NAME] + class ApiGenerator(object): def __init__( @@ -81,6 +162,7 @@ def __init__( name, stage_name, shared_api_usage_plan, + template_conditions, tags=None, endpoint_configuration=None, method_settings=None, @@ -147,6 +229,7 @@ def __init__( self.domain = domain self.description = description self.shared_api_usage_plan = shared_api_usage_plan + self.template_conditions = template_conditions def _construct_rest_api(self): """Constructs and returns the ApiGateway RestApi. @@ -654,7 +737,9 @@ def _construct_usage_plan(self, rest_api_stage=None): usage_plan = ApiGatewayUsagePlan( logical_id=usage_plan_logical_id, depends_on=self.shared_api_usage_plan.depends_on_shared, - attributes=self.passthrough_resource_attributes, + attributes=self.shared_api_usage_plan.get_combined_resource_attributes( + self.passthrough_resource_attributes, self.template_conditions + ), ) api_stage = dict() api_stage["ApiId"] = ref(self.logical_id) @@ -692,7 +777,9 @@ def _construct_api_key(self, usage_plan_logical_id, create_usage_plan, rest_api_ api_key = ApiGatewayApiKey( logical_id=api_key_logical_id, depends_on=[usage_plan_logical_id], - attributes=self.passthrough_resource_attributes, + attributes=self.shared_api_usage_plan.get_combined_resource_attributes( + self.passthrough_resource_attributes, self.template_conditions + ), ) api_key.Enabled = True stage_key = dict() @@ -710,7 +797,6 @@ def _construct_api_key(self, usage_plan_logical_id, create_usage_plan, rest_api_ depends_on=[usage_plan_logical_id], attributes=self.passthrough_resource_attributes, ) - # api_key = ApiGatewayApiKey(logical_id=api_key_logical_id, depends_on=[usage_plan_logical_id]) api_key.Enabled = True stage_keys = list() stage_key = dict() @@ -730,17 +816,20 @@ def _construct_usage_plan_key(self, usage_plan_logical_id, create_usage_plan, ap if create_usage_plan == "SHARED": # create a mapping between api key and the usage plan usage_plan_key_logical_id = "ServerlessUsagePlanKey" + resource_attributes = self.shared_api_usage_plan.get_combined_resource_attributes( + self.passthrough_resource_attributes, self.template_conditions + ) # for create_usage_plan = "PER_API" else: # create a mapping between api key and the usage plan usage_plan_key_logical_id = self.logical_id + "UsagePlanKey" + resource_attributes = self.passthrough_resource_attributes usage_plan_key = ApiGatewayUsagePlanKey( logical_id=usage_plan_key_logical_id, depends_on=[api_key.logical_id], - attributes=self.passthrough_resource_attributes, + attributes=resource_attributes, ) - # usage_plan_key = ApiGatewayUsagePlanKey(logical_id=usage_plan_key_logical_id, depends_on=[api_key.logical_id]) usage_plan_key.KeyId = ref(api_key.logical_id) usage_plan_key.KeyType = "API_KEY" usage_plan_key.UsagePlanId = ref(usage_plan_logical_id) diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 39792c211..de0f99637 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -960,9 +960,6 @@ def to_cloudformation(self, **kwargs): resources.append(lambda_permission) self._inject_lambda_config(function, userpool) - userpool_resource = CognitoUserPool.from_dict(userpool_id, userpool) - for attribute, value in function.get_passthrough_resource_attributes().items(): - userpool_resource.set_resource_attribute(attribute, value) resources.append(CognitoUserPool.from_dict(userpool_id, userpool)) return resources diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index b0d179c2f..516d5b3de 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -862,6 +862,7 @@ def to_cloudformation(self, **kwargs): self.Auth = intrinsics_resolver.resolve_parameter_refs(self.Auth) redeploy_restapi_parameters = kwargs.get("redeploy_restapi_parameters") shared_api_usage_plan = kwargs.get("shared_api_usage_plan") + template_conditions = kwargs.get("conditions") api_generator = ApiGenerator( self.logical_id, @@ -874,6 +875,7 @@ def to_cloudformation(self, **kwargs): self.Name, self.StageName, shared_api_usage_plan, + template_conditions, tags=self.Tags, endpoint_configuration=self.EndpointConfiguration, method_settings=self.MethodSettings, diff --git a/tests/translator/input/api_with_usageplans_shared_attributes_three.yaml b/tests/translator/input/api_with_usageplans_shared_attributes_three.yaml new file mode 100644 index 000000000..aed811ca0 --- /dev/null +++ b/tests/translator/input/api_with_usageplans_shared_attributes_three.yaml @@ -0,0 +1,102 @@ +Globals: + Api: + Auth: + ApiKeyRequired: true + UsagePlan: + CreateUsagePlan: SHARED + +Conditions: + C1: + Fn::Equals: + - test + - test + C2: + Fn::Equals: + - test + - test + +Resources: + MyApiOne: + Type: AWS::Serverless::Api + Condition: C1 + UpdateReplacePolicy: Delete + Properties: + StageName: Prod + + MyApiTwo: + Type: AWS::Serverless::Api + Condition: C2 + UpdateReplacePolicy: Snapshot + Properties: + StageName: Prod + + MyApiThree: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + + MyFunctionOne: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } + } + Events: + ApiKey: + Type: Api + Properties: + RestApiId: + Ref: MyApiOne + Method: get + Path: /path/one + + MyFunctionTwo: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } + } + Events: + ApiKey: + Type: Api + Properties: + RestApiId: + Ref: MyApiTwo + Method: get + Path: /path/two + + MyFunctionThree: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } + } + Events: + ApiKey: + Type: Api + Properties: + RestApiId: + Ref: MyApiThree + Method: get + Path: /path/three \ No newline at end of file diff --git a/tests/translator/input/api_with_usageplans_shared_attributes_two.yaml b/tests/translator/input/api_with_usageplans_shared_attributes_two.yaml new file mode 100644 index 000000000..36c5bab65 --- /dev/null +++ b/tests/translator/input/api_with_usageplans_shared_attributes_two.yaml @@ -0,0 +1,75 @@ +Globals: + Api: + Auth: + ApiKeyRequired: true + UsagePlan: + CreateUsagePlan: SHARED + +Conditions: + C1: + Fn::Equals: + - test + - test + C2: + Fn::Equals: + - test + - test + +Resources: + MyApiOne: + Type: AWS::Serverless::Api + DeletionPolicy: Delete + Condition: C1 + Properties: + StageName: Prod + + MyApiTwo: + Type: AWS::Serverless::Api + DeletionPolicy: Retain + Condition: C2 + Properties: + StageName: Prod + + MyFunctionOne: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } + } + Events: + ApiKey: + Type: Api + Properties: + RestApiId: + Ref: MyApiOne + Method: get + Path: /path/one + + MyFunctionTwo: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } + } + Events: + ApiKey: + Type: Api + Properties: + RestApiId: + Ref: MyApiTwo + Method: get + Path: /path/two \ No newline at end of file diff --git a/tests/translator/output/api_with_usageplans_shared_attributes_three.json b/tests/translator/output/api_with_usageplans_shared_attributes_three.json new file mode 100644 index 000000000..7d6b336e1 --- /dev/null +++ b/tests/translator/output/api_with_usageplans_shared_attributes_three.json @@ -0,0 +1,524 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "C2": { + "Fn::Equals": [ + "test", + "test" + ] + } + }, + "Resources": { + "MyFunctionOne": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__ApiId__": { + "Ref": "MyApiOne" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__ApiId__": { + "Ref": "MyApiTwo" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionThree": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionThreeRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionThreeRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionThreeApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionThree" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/three", + { + "__ApiId__": { + "Ref": "MyApiThree" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "MyApiOneDeployment46fb22a429": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "Description": "RestApi deployment id: 46fb22a42926db6f64e09966936d074ec6bb9392", + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Stage" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment46fb22a429" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "DependsOn": [ + "MyApiOne", + "MyApiTwo", + "MyApiThree" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + } + ] + } + }, + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "DependsOn": [ + "ServerlessUsagePlan" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + } + ] + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "DependsOn": [ + "ServerlessApiKey" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "MyApiTwoDeploymente9d97923b9": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Description": "RestApi deployment id: e9d97923b94d0801cd85a8970b3c3f84aa274003", + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Stage" + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeploymente9d97923b9" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/three": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionThree.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "MyApiThreeDeployment1d9cff47dc": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 1d9cff47dc9b822750c668c73b4534022483de6d", + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Stage" + } + }, + "MyApiThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiThreeDeployment1d9cff47dc" + }, + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_usageplans_shared_attributes_two.json b/tests/translator/output/api_with_usageplans_shared_attributes_two.json new file mode 100644 index 000000000..3412bb041 --- /dev/null +++ b/tests/translator/output/api_with_usageplans_shared_attributes_two.json @@ -0,0 +1,385 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "C2": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "SharedUsagePlanCondition": { + "Fn::Or": [ + { + "Condition": "C2" + }, + { + "Condition": "C1" + } + ] + } + }, + "Resources": { + "MyFunctionOne": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__ApiId__": { + "Ref": "MyApiOne" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__ApiId__": { + "Ref": "MyApiTwo" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "DeletionPolicy": "Delete", + "Condition": "C1", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "MyApiOneDeployment46fb22a429": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C1", + "DeletionPolicy": "Delete", + "Properties": { + "Description": "RestApi deployment id: 46fb22a42926db6f64e09966936d074ec6bb9392", + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Stage" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C1", + "DeletionPolicy": "Delete", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment46fb22a429" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "DependsOn": [ + "MyApiOne", + "MyApiTwo" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + } + ] + } + }, + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "DependsOn": [ + "ServerlessUsagePlan" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "DependsOn": [ + "ServerlessApiKey" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "DeletionPolicy": "Retain", + "Condition": "C2", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "MyApiTwoDeploymente9d97923b9": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C2", + "DeletionPolicy": "Retain", + "Properties": { + "Description": "RestApi deployment id: e9d97923b94d0801cd85a8970b3c3f84aa274003", + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Stage" + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C2", + "DeletionPolicy": "Retain", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeploymente9d97923b9" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_three.json b/tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_three.json new file mode 100644 index 000000000..3f37e5bf0 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_three.json @@ -0,0 +1,548 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "C2": { + "Fn::Equals": [ + "test", + "test" + ] + } + }, + "Resources": { + "MyFunctionOne": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__ApiId__": { + "Ref": "MyApiOne" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__ApiId__": { + "Ref": "MyApiTwo" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionThree": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionThreeRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionThreeRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionThreeApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionThree" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/three", + { + "__ApiId__": { + "Ref": "MyApiThree" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiOneDeployment7997029260": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "Description": "RestApi deployment id: 79970292604071da8105ffd8503f82af32b30550", + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Stage" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment7997029260" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "DependsOn": [ + "MyApiOne", + "MyApiTwo", + "MyApiThree" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + } + ] + } + }, + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "DependsOn": [ + "ServerlessUsagePlan" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + } + ] + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "DependsOn": [ + "ServerlessApiKey" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiTwoDeployment03730b64c4": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Description": "RestApi deployment id: 03730b64c486cc490deefb3b8225244b0fe85d34", + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Stage" + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment03730b64c4" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/three": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionThree.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiThreeDeploymentfa9f73f027": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: fa9f73f0272017527c24cc93cc4440dd4476b9f4", + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Stage" + } + }, + "MyApiThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiThreeDeploymentfa9f73f027" + }, + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_two.json b/tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_two.json new file mode 100644 index 000000000..bf555befa --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_usageplans_shared_attributes_two.json @@ -0,0 +1,401 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "C2": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "SharedUsagePlanCondition": { + "Fn::Or": [ + { + "Condition": "C1" + }, + { + "Condition": "C2" + } + ] + } + }, + "Resources": { + "MyFunctionOne": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__ApiId__": { + "Ref": "MyApiOne" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__ApiId__": { + "Ref": "MyApiTwo" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "DeletionPolicy": "Delete", + "Condition": "C1", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiOneDeployment7997029260": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C1", + "DeletionPolicy": "Delete", + "Properties": { + "Description": "RestApi deployment id: 79970292604071da8105ffd8503f82af32b30550", + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Stage" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C1", + "DeletionPolicy": "Delete", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment7997029260" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "DependsOn": [ + "MyApiOne", + "MyApiTwo" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + } + ] + } + }, + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "DependsOn": [ + "ServerlessUsagePlan" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "DependsOn": [ + "ServerlessApiKey" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "DeletionPolicy": "Retain", + "Condition": "C2", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiTwoDeployment03730b64c4": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C2", + "DeletionPolicy": "Retain", + "Properties": { + "Description": "RestApi deployment id: 03730b64c486cc490deefb3b8225244b0fe85d34", + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Stage" + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C2", + "DeletionPolicy": "Retain", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment03730b64c4" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_three.json b/tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_three.json new file mode 100644 index 000000000..26b69c39b --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_three.json @@ -0,0 +1,548 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "C2": { + "Fn::Equals": [ + "test", + "test" + ] + } + }, + "Resources": { + "MyFunctionOne": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__ApiId__": { + "Ref": "MyApiOne" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__ApiId__": { + "Ref": "MyApiTwo" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionThree": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionThreeRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionThreeRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionThreeApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionThree" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/three", + { + "__ApiId__": { + "Ref": "MyApiThree" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiOneDeploymentdccbc5fda1": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "Description": "RestApi deployment id: dccbc5fda163e1abe712073ffacdcc47776a5a09", + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Stage" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C1", + "UpdateReplacePolicy": "Delete", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeploymentdccbc5fda1" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "DependsOn": [ + "MyApiOne", + "MyApiTwo", + "MyApiThree" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + } + ] + } + }, + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "DependsOn": [ + "ServerlessUsagePlan" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + } + ] + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "DependsOn": [ + "ServerlessApiKey" + ], + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiTwoDeployment0e45b81469": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "Description": "RestApi deployment id: 0e45b814691166a59217a088512ee30710a12369", + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Stage" + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C2", + "UpdateReplacePolicy": "Snapshot", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment0e45b81469" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/three": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionThree.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiThreeDeployment5206882d23": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 5206882d23d2cf7913f0fffea98644f959b433f2", + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Stage" + } + }, + "MyApiThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiThreeDeployment5206882d23" + }, + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_two.json b/tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_two.json new file mode 100644 index 000000000..346ef6a12 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_usageplans_shared_attributes_two.json @@ -0,0 +1,401 @@ +{ + "Conditions": { + "C1": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "C2": { + "Fn::Equals": [ + "test", + "test" + ] + }, + "SharedUsagePlanCondition": { + "Fn::Or": [ + { + "Condition": "C1" + }, + { + "Condition": "C2" + } + ] + } + }, + "Resources": { + "MyFunctionOne": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__ApiId__": { + "Ref": "MyApiOne" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__ApiId__": { + "Ref": "MyApiTwo" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "DeletionPolicy": "Delete", + "Condition": "C1", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiOneDeploymentdccbc5fda1": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C1", + "DeletionPolicy": "Delete", + "Properties": { + "Description": "RestApi deployment id: dccbc5fda163e1abe712073ffacdcc47776a5a09", + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Stage" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C1", + "DeletionPolicy": "Delete", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeploymentdccbc5fda1" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "DependsOn": [ + "MyApiOne", + "MyApiTwo" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + } + ] + } + }, + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "DependsOn": [ + "ServerlessUsagePlan" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "DependsOn": [ + "ServerlessApiKey" + ], + "Condition": "SharedUsagePlanCondition", + "DeletionPolicy": "Retain", + "Properties": { + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "DeletionPolicy": "Retain", + "Condition": "C2", + "Properties": { + "Body": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "api_key": [] + } + ] + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyApiTwoDeployment0e45b81469": { + "Type": "AWS::ApiGateway::Deployment", + "Condition": "C2", + "DeletionPolicy": "Retain", + "Properties": { + "Description": "RestApi deployment id: 0e45b814691166a59217a088512ee30710a12369", + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Stage" + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Condition": "C2", + "DeletionPolicy": "Retain", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment0e45b81469" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 5a100fe38..3d93ab571 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -400,6 +400,8 @@ class TestTranslatorEndToEnd(AbstractTestTranslator): "function_with_event_dest_basic", "function_with_event_dest_conditional", "api_with_usageplans", + "api_with_usageplans_shared_attributes_two", + "api_with_usageplans_shared_attributes_three", "api_with_usageplans_intrinsics", "state_machine_with_inline_definition", "state_machine_with_tags", diff --git a/tests/unit/model/api/TestSharedApiUsagePlan.py b/tests/unit/model/api/TestSharedApiUsagePlan.py new file mode 100644 index 000000000..7c9e84699 --- /dev/null +++ b/tests/unit/model/api/TestSharedApiUsagePlan.py @@ -0,0 +1,119 @@ +from unittest import TestCase + +from parameterized import parameterized, param + +from samtranslator.model.api.api_generator import SharedApiUsagePlan +from samtranslator.model.exceptions import InvalidTemplateException + + +class TestSharedApiUsagePlan(TestCase): + def setUp(self): + self.shared_usage_plan = SharedApiUsagePlan() + + def test_values_should_be_propagated(self): + conditions = {} + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + { + "Condition": "C1", + "DeletionPolicy": "Delete", + "UpdateReplacePolicy": "Delete", + }, + conditions, + ) + + self.assertEqual( + { + "Condition": SharedApiUsagePlan.SHARED_USAGE_PLAN_CONDITION_NAME, + "DeletionPolicy": "Delete", + "UpdateReplacePolicy": "Delete", + }, + actual_attributes, + ) + + self.assertEqual(conditions, {"SharedUsagePlanCondition": "C1"}) + + @parameterized.expand( + [ + ([None],), + ([None, "C1"],), + (["C1", None],), + (["C1", "C2"],), + (["C1", "C2", "C3"],), + ] + ) + def test_multiple_apis_with_conditions(self, api_conditions): + template_conditions = dict() + result = {} + for api_condition in api_conditions: + result = self.shared_usage_plan.get_combined_resource_attributes( + {"Condition": api_condition}, template_conditions + ) + print(f"Calling with {api_condition} result {result}") + + if None in api_conditions: + self.assertEqual({}, result) + self.assertEqual({}, template_conditions) + else: + print(template_conditions) + self.assertTrue("SharedUsagePlanCondition" in template_conditions) + self.assertEqual({"Condition": "SharedUsagePlanCondition"}, result) + combined_conditions = [ + condition.get("Condition") + for condition in template_conditions.get("SharedUsagePlanCondition", {}).get("Fn::Or") + ] + for combined_condition in combined_conditions: + self.assertTrue(combined_condition in api_conditions) + + def test_should_raise_invalid_template_when_no_conditions_section(self): + with self.assertRaises(InvalidTemplateException): + self.shared_usage_plan.get_combined_resource_attributes({"Condition": "C1"}, None) + + def test_deletion_policy_priority(self): + # first api sets it to delete + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes({"DeletionPolicy": "Delete"}, {}) + self.assertEqual(actual_attributes["DeletionPolicy"], "Delete") + + # then second api sets it to Retain + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes({"DeletionPolicy": "Retain"}, {}) + self.assertEqual(actual_attributes["DeletionPolicy"], "Retain") + + # if third api sets it to delete, it should keep retain value + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes({"DeletionPolicy": "Delete"}, {}) + self.assertEqual(actual_attributes["DeletionPolicy"], "Retain") + + def test_update_replace_policy_priority(self): + # first api sets it to delete + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + {"UpdateReplacePolicy": "Delete"}, {} + ) + self.assertEqual(actual_attributes["UpdateReplacePolicy"], "Delete") + + # then second api sets it to Retain + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + {"UpdateReplacePolicy": "Snapshot"}, {} + ) + self.assertEqual(actual_attributes["UpdateReplacePolicy"], "Snapshot") + + # if third api sets it to delete, it should keep retain value + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + {"UpdateReplacePolicy": "Delete"}, {} + ) + self.assertEqual(actual_attributes["UpdateReplacePolicy"], "Snapshot") + + # if third api sets it to delete, it should keep retain value + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + {"UpdateReplacePolicy": "Retain"}, {} + ) + self.assertEqual(actual_attributes["UpdateReplacePolicy"], "Retain") + + # if third api sets it to delete, it should keep retain value + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + {"UpdateReplacePolicy": "Snapshot"}, {} + ) + self.assertEqual(actual_attributes["UpdateReplacePolicy"], "Retain") + + # if third api sets it to delete, it should keep retain value + actual_attributes = self.shared_usage_plan.get_combined_resource_attributes( + {"UpdateReplacePolicy": "Delete"}, {} + ) + self.assertEqual(actual_attributes["UpdateReplacePolicy"], "Retain")