diff --git a/examples/2016-10-31/usage_plan/README.md b/examples/2016-10-31/usage_plan/README.md deleted file mode 100644 index c91d63b60..000000000 --- a/examples/2016-10-31/usage_plan/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Usage Plans Support for Auth - -Example SAM template for creating Usage Plans. -## Running the example - -```bash -$ sam deploy \ - --template-file /path_to_template/packaged-template.yaml \ - --stack-name my-stack \ - --capabilities CAPABILITY_IAM -``` diff --git a/examples/2016-10-31/usage_plan/template.yaml b/examples/2016-10-31/usage_plan/template.yaml index 3b4e3b48b..4150b2f9b 100644 --- a/examples/2016-10-31/usage_plan/template.yaml +++ b/examples/2016-10-31/usage_plan/template.yaml @@ -7,7 +7,7 @@ Conditions: Parameters: UsagePlanType: Type: String - Default: SINGLE + Default: PER_API Globals: Api: diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index c794d412e..043772197 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -44,8 +44,10 @@ ], ) AuthProperties.__new__.__defaults__ = (None, None, None, True, None, None, None) -UsagePlanProperties = namedtuple("_UsagePlanProperties", ["CreateUsagePlan", "UsagePlanId"]) -UsagePlanProperties.__new__.__defaults__ = (None, None) +UsagePlanProperties = namedtuple( + "_UsagePlanProperties", ["CreateUsagePlan", "Description", "Quota", "Tags", "Throttle", "UsagePlanName"] +) +UsagePlanProperties.__new__.__defaults__ = (None, None, None, None, None, None) GatewayResponseProperties = ["ResponseParameters", "ResponseTemplates", "StatusCode"] @@ -534,7 +536,7 @@ def _construct_usage_plan(self, rest_api_stage=None): """Constructs and returns the ApiGateway UsagePlan, ApiGateway UsagePlanKey, ApiGateway ApiKey for Auth. :param model.apigateway.ApiGatewayStage stage: the stage of rest api - :returns: the UsagePlan, UsagePlanKey, ApiKey for this SAM Api + :returns: UsagePlan, UsagePlanKey, ApiKey for this rest Api :rtype: model.apigateway.ApiGatewayUsagePlan, model.apigateway.ApiGatewayUsagePlanKey, model.apigateway.ApiGatewayApiKey """ @@ -544,7 +546,6 @@ def _construct_usage_plan(self, rest_api_stage=None): if auth_properties.UsagePlan is None: return [] usage_plan_properties = auth_properties.UsagePlan - # throws error if the property invalid/ unsupported for UsagePlan if not all(key in UsagePlanProperties._fields for key in usage_plan_properties.keys()): raise InvalidResourceException(self.logical_id, "Invalid property for 'UsagePlan'") @@ -553,42 +554,33 @@ def _construct_usage_plan(self, rest_api_stage=None): usage_plan = None api_key = None usage_plan_key = None - # doesn't allow giving UsagePlanId when CreateUsagePlan is SINGLE or SHARED - if create_usage_plan not in ["SHARED", "SINGLE", "NONE"]: + + if create_usage_plan is None: + raise InvalidResourceException(self.logical_id, "'CreateUsagePlan' is a required field for UsagePlan") + if create_usage_plan not in ["SHARED", "PER_API", "NONE"]: raise InvalidResourceException( - self.logical_id, "'CreateUsagePlan' accepts only " "NONE, SINGLE and SHARED values" + self.logical_id, "'CreateUsagePlan' accepts only NONE, PER_API and SHARED values" ) - if usage_plan_properties.get("CreateUsagePlan") == "SINGLE": - if usage_plan_properties.get("UsagePlanId") is not None: - usage_plan_logical_id = usage_plan_properties.get("UsagePlanId") - if type(usage_plan_logical_id) == dict: - usage_plan_logical_id = usage_plan_logical_id.get("Ref") - - api_key = self._construct_api_key(usage_plan_logical_id, create_usage_plan, rest_api_stage) - usage_plan_key = self._construct_usage_plan_key(usage_plan_logical_id, create_usage_plan, api_key) - return api_key, usage_plan_key - else: - usage_plan_logical_id = self.logical_id + "UsagePlan" - # create a usage plan for this Api - usage_plan = ApiGatewayUsagePlan(logical_id=usage_plan_logical_id, depends_on=[self.logical_id]) - api_stages = list() - api_stage = dict() - api_stage["ApiId"] = ref(self.logical_id) - api_stage["Stage"] = ref(rest_api_stage.logical_id) - api_stages.append(api_stage) - usage_plan.ApiStages = api_stages - - api_key = self._construct_api_key(usage_plan_logical_id, create_usage_plan, rest_api_stage) - usage_plan_key = self._construct_usage_plan_key(usage_plan_logical_id, create_usage_plan, api_key) + if create_usage_plan == "NONE": + return [] + + # create usage plan for this api only + elif usage_plan_properties.get("CreateUsagePlan") == "PER_API": + usage_plan_logical_id = self.logical_id + "UsagePlan" + usage_plan = ApiGatewayUsagePlan(logical_id=usage_plan_logical_id, depends_on=[self.logical_id]) + api_stages = list() + api_stage = dict() + api_stage["ApiId"] = ref(self.logical_id) + api_stage["Stage"] = ref(rest_api_stage.logical_id) + api_stages.append(api_stage) + usage_plan.ApiStages = api_stages + + api_key = self._construct_api_key(usage_plan_logical_id, create_usage_plan, rest_api_stage) + usage_plan_key = self._construct_usage_plan_key(usage_plan_logical_id, create_usage_plan, api_key) + # create a usage plan for all the Apis elif create_usage_plan == "SHARED": - if usage_plan_properties.get("UsagePlanId") is not None: - raise InvalidResourceException( - self.logical_id, - 'Invalid property "UsagePlanId". This field ' 'can be given only when "CreateUsagePlan" is SINGLE', - ) - # create a usage plan for all the apis usage_plan_logical_id = "ServerlessUsagePlan" ApiGenerator.depends_on_shared.append(self.logical_id) usage_plan = ApiGatewayUsagePlan( @@ -602,17 +594,26 @@ def _construct_usage_plan(self, rest_api_stage=None): api_key = self._construct_api_key(usage_plan_logical_id, create_usage_plan, rest_api_stage) usage_plan_key = self._construct_usage_plan_key(usage_plan_logical_id, create_usage_plan, api_key) - elif create_usage_plan == "NONE": - if usage_plan_properties.get("UsagePlanId") is not None: - raise InvalidResourceException( - self.logical_id, - 'Invalid property "UsagePlanId". This field ' 'can be given only when "CreateUsagePlan" is SINGLE', - ) - return [] + if usage_plan_properties.get("UsagePlanName"): + usage_plan.UsagePlanName = usage_plan_properties.get("UsagePlanName") + if usage_plan_properties.get("Description"): + usage_plan.Description = usage_plan_properties.get("Description") + if usage_plan_properties.get("Quota"): + usage_plan.Quota = usage_plan_properties.get("Quota") + if usage_plan_properties.get("Tags"): + usage_plan.Tags = usage_plan_properties.get("Tags") + if usage_plan_properties.get("Throttle"): + usage_plan.Throttle = usage_plan_properties.get("Throttle") return usage_plan, api_key, usage_plan_key def _construct_api_key(self, usage_plan_logical_id, create_usage_plan, rest_api_stage): + """ + :param usage_plan_logical_id: String + :param create_usage_plan: String + :param rest_api_stage: model.apigateway.ApiGatewayStage stage: the stage of rest api + :return: api_key model.apigateway.ApiGatewayApiKey resource which is created for the given usage plan + """ if create_usage_plan == "SHARED": # create an api key resource for all the apis api_key_logical_id = "ServerlessApiKey" @@ -623,6 +624,7 @@ def _construct_api_key(self, usage_plan_logical_id, create_usage_plan, rest_api_ stage_key["StageName"] = ref(rest_api_stage.logical_id) ApiGenerator.stage_keys_shared.append(stage_key) api_key.StageKeys = ApiGenerator.stage_keys_shared + # for create_usage_plan = "PER_API" else: # create an api key resource for this api api_key_logical_id = self.logical_id + "ApiKey" @@ -637,9 +639,16 @@ def _construct_api_key(self, usage_plan_logical_id, create_usage_plan, rest_api_ return api_key def _construct_usage_plan_key(self, usage_plan_logical_id, create_usage_plan, api_key): + """ + :param usage_plan_logical_id: String + :param create_usage_plan: String + :param api_key: model.apigateway.ApiGatewayApiKey resource + :return: model.apigateway.ApiGatewayUsagePlanKey resource that contains the mapping between usage plan and api key + """ if create_usage_plan == "SHARED": # create a mapping between api key and the usage plan usage_plan_key_logical_id = "ServerlessUsagePlanKey" + # 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" diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index 02e6a646a..e1bfaa282 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -181,7 +181,14 @@ class ApiGatewayBasePathMapping(Resource): class ApiGatewayUsagePlan(Resource): resource_type = "AWS::ApiGateway::UsagePlan" - property_types = {"ApiStages": PropertyType(False, is_type(list))} + property_types = { + "ApiStages": PropertyType(False, is_type(list)), + "Description": PropertyType(False, is_str()), + "Quota": PropertyType(False, is_type(dict)), + "Tags": PropertyType(False, list_of(dict)), + "Throttle": PropertyType(False, is_type(dict)), + "UsagePlanName": PropertyType(False, is_str()), + } runtime_attrs = {"usage_plan_id": lambda self: ref(self.logical_id)} diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 6f3da8780..27744a737 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -811,6 +811,7 @@ def to_cloudformation(self, **kwargs): resources.extend(basepath_mapping) if route53: resources.extend([route53]) + # contains usage plan, api key and usageplan key resources if usage_plan_resources: resources.extend(usage_plan_resources) return resources diff --git a/tests/translator/input/api_with_usageplans.yaml b/tests/translator/input/api_with_usageplans.yaml index eda689c2c..36aa0ba0a 100644 --- a/tests/translator/input/api_with_usageplans.yaml +++ b/tests/translator/input/api_with_usageplans.yaml @@ -20,31 +20,28 @@ Resources: StageName: Prod Auth: UsagePlan: - CreateUsagePlan: SINGLE - UsagePlanId: !Ref MyApiTwoUsagePlan + CreateUsagePlan: PER_API + UsagePlanName: SomeRandomName + Description: "Description for usage plan" + Throttle: + BurstLimit: 1000 + RateLimit: 1000 + Tags: + - Key: key1 + Value: value1 + - Key: key2 + Value: value2 + Quota: + Limit: 10 + Offset: 10 + Period: MONTH - MyApiThree: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - Auth: - UsagePlan: - CreateUsagePlan: SINGLE - MyApiFour: + MyApiThree: Type: AWS::Serverless::Api Properties: StageName: Prod - MyApiTwoUsagePlan: - Type: AWS::ApiGateway::UsagePlan - DependsOn: - - MyApiTwo - Properties: - ApiStages: - - ApiId: !Ref MyApiTwo - Stage: !Ref MyApiTwo.Stage - MyFunctionOne: Type: AWS::Serverless::Function Properties: @@ -88,30 +85,13 @@ Resources: 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: + ImplicitApiEvent: Type: Api Properties: - RestApiId: - Ref: MyApiThree Method: get - Path: /path/three + Path: /path/event - MyFunctionFour: + MyFunctionThree: Type: AWS::Serverless::Function Properties: Handler: index.handler @@ -129,9 +109,9 @@ Resources: Type: Api Properties: RestApiId: - Ref: MyApiFour + Ref: MyApiThree Method: get - Path: /path/four + Path: /path/three Outputs: ApiOneUrl: @@ -146,7 +126,3 @@ Outputs: Description: "API endpoint URL for Prod environment" Value: Fn::Sub: 'https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/' - ApiFourUrl: - Description: "API endpoint URL for Prod environment" - Value: - Fn::Sub: 'https://${MyApiFour}.execute-api.${AWS::Region}.amazonaws.com/Prod/' diff --git a/tests/translator/input/api_with_usageplans_intrinsics.yaml b/tests/translator/input/api_with_usageplans_intrinsics.yaml index 136edc095..7a386b556 100644 --- a/tests/translator/input/api_with_usageplans_intrinsics.yaml +++ b/tests/translator/input/api_with_usageplans_intrinsics.yaml @@ -7,7 +7,7 @@ Conditions: Parameters: UsagePlanType: Type: String - Default: SINGLE + Default: PER_API Globals: Api: diff --git a/tests/translator/input/api_with_usageplans_intrinsics.yaml._sam_packaged_.yaml b/tests/translator/input/api_with_usageplans_intrinsics.yaml._sam_packaged_.yaml deleted file mode 100644 index 2ecfaead7..000000000 --- a/tests/translator/input/api_with_usageplans_intrinsics.yaml._sam_packaged_.yaml +++ /dev/null @@ -1,68 +0,0 @@ -Conditions: - C1: - Fn::Equals: - - true - - true -Parameters: - UsagePlanType: - Type: String - Default: SINGLE -Globals: - Api: - OpenApiVersion: 3.0.0 - Auth: - ApiKeyRequired: true - UsagePlan: - CreateUsagePlan: - Fn::Sub: UsagePlanType -Resources: - MyApiOne: - Condition: C1 - Type: AWS::Serverless::Api - Properties: - StageName: Prod - MyApiTwo: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - MyFunctionOne: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs8.10 - InlineCode: "exports.handler = async (event) => {\n return {\n statusCode:\ - \ 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" - Events: - ApiKey: - Type: Api - Properties: - RestApiId: - Ref: MyApiOne - Method: get - Path: /path/one - CodeUri: s3://shreygan-test-west-2/7c97ede99c55014c6908d8771300dcb8 - MyFunctionTwo: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs8.10 - InlineCode: "exports.handler = async (event) => {\n return {\n statusCode:\ - \ 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" - Events: - ApiKey: - Type: Api - Properties: - RestApiId: - Ref: MyApiTwo - Method: get - Path: /path/two - CodeUri: s3://shreygan-test-west-2/7c97ede99c55014c6908d8771300dcb8 -Outputs: - ApiOneUrl: - Description: API endpoint URL for Prod environment - Value: - Fn::Sub: https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/ - ApiTwoUrl: - Description: API endpoint URL for Prod environment - Value: - Fn::Sub: https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/ diff --git a/tests/translator/input/error_api_with_usage_plan_create_usage_plan_none.yaml b/tests/translator/input/error_api_with_usage_plan_create_usage_plan_none.yaml deleted file mode 100644 index 17228aa09..000000000 --- a/tests/translator/input/error_api_with_usage_plan_create_usage_plan_none.yaml +++ /dev/null @@ -1,42 +0,0 @@ -Globals: - Api: - OpenApiVersion: 3.0.0 - Auth: - ApiKeyRequired: true - UsagePlan: - CreateUsagePlan: NONE - UsagePlanId: !Ref usageplanid - -Resources: - MyApiOne: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - - MyFunctionOne: - Type: AWS::Serverless::Function - Properties: - Handler: index.handler - Runtime: nodejs8.10 - 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 - -Outputs: - ApiOneUrl: - Description: "API endpoint URL for Prod environment" - Value: - Fn::Sub: 'https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/' \ No newline at end of file diff --git a/tests/translator/output/api_with_usageplans.json b/tests/translator/output/api_with_usageplans.json index bba1e80a3..6e11a953a 100644 --- a/tests/translator/output/api_with_usageplans.json +++ b/tests/translator/output/api_with_usageplans.json @@ -12,12 +12,6 @@ "Fn::Sub": "https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/" } }, - "ApiFourUrl": { - "Description": "API endpoint URL for Prod environment", - "Value": { - "Fn::Sub": "https://${MyApiFour}.execute-api.${AWS::Region}.amazonaws.com/Prod/" - } - }, "ApiOneUrl": { "Description": "API endpoint URL for Prod environment", "Value": { @@ -29,9 +23,6 @@ "MyFunctionThreeRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -48,47 +39,28 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } }, - "MyApiThreeUsagePlanKey": { - "Type": "AWS::ApiGateway::UsagePlanKey", + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", "Properties": { - "KeyType": "API_KEY", - "KeyId": { - "Ref": "MyApiThreeApiKey" + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment7a26848ac9" }, - "UsagePlanId": { - "Ref": "MyApiThreeUsagePlan" - } - }, - "DependsOn": [ - "MyApiThreeApiKey" - ] - }, - "MyApiThreeApiKey": { - "Type": "AWS::ApiGateway::ApiKey", - "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiThree" - }, - "StageName": { - "Ref": "MyApiThreeProdStage" - } - } - ] - }, - "DependsOn": [ - "MyApiThreeUsagePlan" - ] + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } }, "MyApiTwoUsagePlanKey": { "Type": "AWS::ApiGateway::UsagePlanKey", @@ -145,34 +117,14 @@ } } }, - "MyFunctionFourRole": { - "Type": "AWS::IAM::Role", + "ServerlessRestApiDeployment7a26848ac9": { + "Type": "AWS::ApiGateway::Deployment", "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" - ] - } - } - ] + "RestApiId": { + "Ref": "ServerlessRestApi" }, - "Tags": [ - { - "Key": "lambda:createdBy", - "Value": "SAM" - } - ] + "Description": "RestApi deployment id: 7a26848ac97d678aaf266a8a883d0abd463b3bbc", + "StageName": "Stage" } }, "MyFunctionThreeApiKeyPermissionProd": { @@ -196,20 +148,27 @@ } } }, - "ServerlessUsagePlanKey": { - "Type": "AWS::ApiGateway::UsagePlanKey", + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", "Properties": { - "KeyType": "API_KEY", - "KeyId": { - "Ref": "ServerlessApiKey" + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" }, - "UsagePlanId": { - "Ref": "ServerlessUsagePlan" - } - }, - "DependsOn": [ - "ServerlessApiKey" - ] + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } }, "MyFunctionOneApiKeyPermissionProd": { "Type": "AWS::Lambda::Permission", @@ -266,15 +225,6 @@ "MyFunctionOneRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "Tags": [ - { - "Key": "lambda:createdBy", - "Value": "SAM" - } - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -290,7 +240,16 @@ } } ] - } + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] } }, "MyApiTwo": { @@ -336,9 +295,6 @@ "MyFunctionTwoRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -355,10 +311,13 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } @@ -391,39 +350,27 @@ "ApiStages": [ { "ApiId": { - "Ref": "MyApiFour" + "Ref": "MyApiThree" }, "Stage": { - "Ref": "MyApiFourProdStage" + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" } } ] }, "DependsOn": [ - "MyApiFour" + "MyApiThree", + "ServerlessRestApi" ] }, - "MyFunctionFourApiKeyPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "MyFunctionFour" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/four", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "MyApiFour" - } - } - ] - } - } - }, "MyApiTwoUsagePlan": { "Type": "AWS::ApiGateway::UsagePlan", "Properties": { @@ -436,7 +383,28 @@ "Ref": "MyApiTwoProdStage" } } - ] + ], + "Description": "Description for usage plan", + "Tags": [ + { + "Value": "value1", + "Key": "key1" + }, + { + "Value": "value2", + "Key": "key2" + } + ], + "Quota": { + "Limit": 10, + "Period": "MONTH", + "Offset": 10 + }, + "Throttle": { + "RateLimit": 1000, + "BurstLimit": 1000 + }, + "UsagePlanName": "SomeRandomName" }, "DependsOn": [ "MyApiTwo" @@ -452,26 +420,25 @@ "StageName": "Stage" } }, - "MyFunctionFour": { - "Type": "AWS::Lambda::Function", + "MyFunctionTwoImplicitApiEventPermissionProd": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Handler": "index.handler", - "Code": { - "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" }, - "Role": { - "Fn::GetAtt": [ - "MyFunctionFourRole", - "Arn" + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/event", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } ] - }, - "Runtime": "nodejs12.x", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + } } }, "MyApiTwoProdStage": { @@ -498,65 +465,6 @@ "StageName": "Prod" } }, - "MyApiFourDeploymente7cfed53d1": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "MyApiFour" - }, - "Description": "RestApi deployment id: e7cfed53d17a05b03ea5e2606faa4f13707b32f8", - "StageName": "Stage" - } - }, - "MyApiFourProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "MyApiFourDeploymente7cfed53d1" - }, - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": "Prod" - } - }, - "MyApiThreeUsagePlan": { - "Type": "AWS::ApiGateway::UsagePlan", - "Properties": { - "ApiStages": [ - { - "ApiId": { - "Ref": "MyApiThree" - }, - "Stage": { - "Ref": "MyApiThreeProdStage" - } - } - ] - }, - "DependsOn": [ - "MyApiThree" - ] - }, - "ServerlessApiKey": { - "Type": "AWS::ApiGateway::ApiKey", - "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": { - "Ref": "MyApiFourProdStage" - } - } - ] - }, - "DependsOn": [ - "ServerlessUsagePlan" - ] - }, "MyApiTwoDeploymente9d97923b9": { "Type": "AWS::ApiGateway::Deployment", "Properties": { @@ -601,27 +509,20 @@ "StageName": "Prod" } }, - "MyFunctionTwo": { - "Type": "AWS::Lambda::Function", + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", "Properties": { - "Handler": "index.handler", - "Code": { - "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" - }, - "Role": { - "Fn::GetAtt": [ - "MyFunctionTwoRole", - "Arn" - ] + "KeyType": "API_KEY", + "KeyId": { + "Ref": "ServerlessApiKey" }, - "Runtime": "nodejs12.x", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] - } + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + }, + "DependsOn": [ + "ServerlessApiKey" + ] }, "MyApiOne": { "Type": "AWS::ApiGateway::RestApi", @@ -663,7 +564,34 @@ } } }, - "MyApiFour": { + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "ServerlessUsagePlan" + ] + }, + "ServerlessRestApi": { "Type": "AWS::ApiGateway::RestApi", "Properties": { "Body": { @@ -674,13 +602,13 @@ } }, "paths": { - "/path/four": { + "/path/event": { "get": { "x-amazon-apigateway-integration": { "httpMethod": "POST", "type": "aws_proxy", "uri": { - "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionFour.Arn}/invocations" + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" } }, "security": [ diff --git a/tests/translator/output/api_with_usageplans_intrinsics.json b/tests/translator/output/api_with_usageplans_intrinsics.json index c2f728bae..3c43187e7 100644 --- a/tests/translator/output/api_with_usageplans_intrinsics.json +++ b/tests/translator/output/api_with_usageplans_intrinsics.json @@ -23,7 +23,7 @@ }, "Parameters": { "UsagePlanType": { - "Default": "SINGLE", + "Default": "PER_API", "Type": "String" } }, diff --git a/tests/translator/output/aws-cn/api_with_usageplans.json b/tests/translator/output/aws-cn/api_with_usageplans.json index 3b9e9e2d8..3f38b0ce5 100644 --- a/tests/translator/output/aws-cn/api_with_usageplans.json +++ b/tests/translator/output/aws-cn/api_with_usageplans.json @@ -12,12 +12,6 @@ "Fn::Sub": "https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/" } }, - "ApiFourUrl": { - "Description": "API endpoint URL for Prod environment", - "Value": { - "Fn::Sub": "https://${MyApiFour}.execute-api.${AWS::Region}.amazonaws.com/Prod/" - } - }, "ApiOneUrl": { "Description": "API endpoint URL for Prod environment", "Value": { @@ -29,9 +23,6 @@ "MyFunctionThreeRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -48,10 +39,13 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } @@ -66,39 +60,17 @@ "StageName": "Stage" } }, - "MyApiThreeUsagePlanKey": { - "Type": "AWS::ApiGateway::UsagePlanKey", + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", "Properties": { - "KeyType": "API_KEY", - "KeyId": { - "Ref": "MyApiThreeApiKey" + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment40b50dc688" }, - "UsagePlanId": { - "Ref": "MyApiThreeUsagePlan" - } - }, - "DependsOn": [ - "MyApiThreeApiKey" - ] - }, - "MyApiThreeApiKey": { - "Type": "AWS::ApiGateway::ApiKey", - "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiThree" - }, - "StageName": { - "Ref": "MyApiThreeProdStage" - } - } - ] - }, - "DependsOn": [ - "MyApiThreeUsagePlan" - ] + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } }, "MyApiTwoUsagePlanKey": { "Type": "AWS::ApiGateway::UsagePlanKey", @@ -163,36 +135,6 @@ } } }, - "MyFunctionFourRole": { - "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" - ] - } - } - ] - }, - "Tags": [ - { - "Key": "lambda:createdBy", - "Value": "SAM" - } - ] - } - }, "MyFunctionThreeApiKeyPermissionProd": { "Type": "AWS::Lambda::Permission", "Properties": { @@ -214,39 +156,27 @@ } } }, - "MyApiTwoApiKey": { - "Type": "AWS::ApiGateway::ApiKey", + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", "Properties": { - "Enabled": true, - "StageKeys": [ + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ { - "RestApiId": { - "Ref": "MyApiTwo" - }, - "StageName": { - "Ref": "MyApiTwoProdStage" - } + "Value": "SAM", + "Key": "lambda:createdBy" } ] - }, - "DependsOn": [ - "MyApiTwoUsagePlan" - ] - }, - "ServerlessUsagePlanKey": { - "Type": "AWS::ApiGateway::UsagePlanKey", - "Properties": { - "KeyType": "API_KEY", - "KeyId": { - "Ref": "ServerlessApiKey" - }, - "UsagePlanId": { - "Ref": "ServerlessUsagePlan" - } - }, - "DependsOn": [ - "ServerlessApiKey" - ] + } }, "MyFunctionOneApiKeyPermissionProd": { "Type": "AWS::Lambda::Permission", @@ -293,9 +223,6 @@ "MyFunctionOneRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -312,10 +239,13 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } @@ -371,9 +301,6 @@ "MyFunctionTwoRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -390,10 +317,13 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } @@ -420,54 +350,61 @@ ] } }, + "ServerlessRestApiDeployment40b50dc688": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 40b50dc6888e5b556b1073ba4304ec9b550659db", + "StageName": "Stage" + } + }, "ServerlessUsagePlan": { "Type": "AWS::ApiGateway::UsagePlan", "Properties": { "ApiStages": [ { "ApiId": { - "Ref": "MyApiFour" + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" }, "Stage": { - "Ref": "MyApiFourProdStage" + "Ref": "MyApiThreeProdStage" } }, { "ApiId": { - "Ref": "MyApiFour" + "Ref": "ServerlessRestApi" }, "Stage": { - "Ref": "MyApiFourProdStage" + "Ref": "ServerlessRestApiProdStage" } } ] }, "DependsOn": [ - "MyApiFour", - "MyApiFour" + "MyApiThree", + "ServerlessRestApi", + "MyApiThree", + "ServerlessRestApi" ] }, - "MyFunctionFourApiKeyPermissionProd": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "MyFunctionFour" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/four", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "MyApiFour" - } - } - ] - } - } - }, "MyApiTwoUsagePlan": { "Type": "AWS::ApiGateway::UsagePlan", "Properties": { @@ -480,32 +417,52 @@ "Ref": "MyApiTwoProdStage" } } - ] + ], + "Description": "Description for usage plan", + "Tags": [ + { + "Value": "value1", + "Key": "key1" + }, + { + "Value": "value2", + "Key": "key2" + } + ], + "Quota": { + "Limit": 10, + "Period": "MONTH", + "Offset": 10 + }, + "Throttle": { + "RateLimit": 1000, + "BurstLimit": 1000 + }, + "UsagePlanName": "SomeRandomName" }, "DependsOn": [ "MyApiTwo" ] }, - "MyFunctionFour": { - "Type": "AWS::Lambda::Function", + "MyFunctionTwoImplicitApiEventPermissionProd": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Handler": "index.handler", - "Code": { - "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" }, - "Role": { - "Fn::GetAtt": [ - "MyFunctionFourRole", - "Arn" + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/event", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } ] - }, - "Runtime": "nodejs12.x", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + } } }, "MyApiTwoProdStage": { @@ -542,63 +499,6 @@ "StageName": "Stage" } }, - "MyApiFourProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "MyApiFourDeploymentf92b67251a" - }, - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": "Prod" - } - }, - "MyApiThreeUsagePlan": { - "Type": "AWS::ApiGateway::UsagePlan", - "Properties": { - "ApiStages": [ - { - "ApiId": { - "Ref": "MyApiThree" - }, - "Stage": { - "Ref": "MyApiThreeProdStage" - } - } - ] - }, - "DependsOn": [ - "MyApiThree" - ] - }, - "ServerlessApiKey": { - "Type": "AWS::ApiGateway::ApiKey", - "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": { - "Ref": "MyApiFourProdStage" - } - }, - { - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": { - "Ref": "MyApiFourProdStage" - } - } - ] - }, - "DependsOn": [ - "ServerlessUsagePlan" - ] - }, "MyFunctionThree": { "Type": "AWS::Lambda::Function", "Properties": { @@ -633,27 +533,20 @@ "StageName": "Prod" } }, - "MyFunctionTwo": { - "Type": "AWS::Lambda::Function", + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", "Properties": { - "Handler": "index.handler", - "Code": { - "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" - }, - "Role": { - "Fn::GetAtt": [ - "MyFunctionTwoRole", - "Arn" - ] + "KeyType": "API_KEY", + "KeyId": { + "Ref": "ServerlessApiKey" }, - "Runtime": "nodejs12.x", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] - } + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + }, + "DependsOn": [ + "ServerlessApiKey" + ] }, "MyApiOne": { "Type": "AWS::ApiGateway::RestApi", @@ -703,7 +596,50 @@ } } }, - "MyApiFour": { + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "ServerlessUsagePlan" + ] + }, + "ServerlessRestApi": { "Type": "AWS::ApiGateway::RestApi", "Properties": { "Body": { @@ -714,13 +650,13 @@ } }, "paths": { - "/path/four": { + "/path/event": { "get": { "x-amazon-apigateway-integration": { "httpMethod": "POST", "type": "aws_proxy", "uri": { - "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionFour.Arn}/invocations" + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" } }, "security": [ @@ -761,15 +697,24 @@ "StageName": "Stage" } }, - "MyApiFourDeploymentf92b67251a": { - "Type": "AWS::ApiGateway::Deployment", + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", "Properties": { - "RestApiId": { - "Ref": "MyApiFour" - }, - "Description": "RestApi deployment id: f92b67251a066bec5ee160eeff9c5ab747d9f417", - "StageName": "Stage" - } + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwoUsagePlan" + ] } } } \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_usageplans_intrinsics.json b/tests/translator/output/aws-cn/api_with_usageplans_intrinsics.json index b2b8cfbca..a4d484013 100644 --- a/tests/translator/output/aws-cn/api_with_usageplans_intrinsics.json +++ b/tests/translator/output/aws-cn/api_with_usageplans_intrinsics.json @@ -23,7 +23,7 @@ }, "Parameters": { "UsagePlanType": { - "Default": "SINGLE", + "Default": "PER_API", "Type": "String" } }, diff --git a/tests/translator/output/aws-us-gov/api_with_usageplans.json b/tests/translator/output/aws-us-gov/api_with_usageplans.json index b4805937a..7c0d0eaad 100644 --- a/tests/translator/output/aws-us-gov/api_with_usageplans.json +++ b/tests/translator/output/aws-us-gov/api_with_usageplans.json @@ -12,12 +12,6 @@ "Fn::Sub": "https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/" } }, - "ApiFourUrl": { - "Description": "API endpoint URL for Prod environment", - "Value": { - "Fn::Sub": "https://${MyApiFour}.execute-api.${AWS::Region}.amazonaws.com/Prod/" - } - }, "ApiOneUrl": { "Description": "API endpoint URL for Prod environment", "Value": { @@ -29,9 +23,6 @@ "MyFunctionThreeRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -48,47 +39,28 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } }, - "MyApiThreeUsagePlanKey": { - "Type": "AWS::ApiGateway::UsagePlanKey", + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", "Properties": { - "KeyType": "API_KEY", - "KeyId": { - "Ref": "MyApiThreeApiKey" + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentd197b03bdf" }, - "UsagePlanId": { - "Ref": "MyApiThreeUsagePlan" - } - }, - "DependsOn": [ - "MyApiThreeApiKey" - ] - }, - "MyApiThreeApiKey": { - "Type": "AWS::ApiGateway::ApiKey", - "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiThree" - }, - "StageName": { - "Ref": "MyApiThreeProdStage" - } - } - ] - }, - "DependsOn": [ - "MyApiThreeUsagePlan" - ] + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } }, "MyApiTwoUsagePlanKey": { "Type": "AWS::ApiGateway::UsagePlanKey", @@ -153,36 +125,6 @@ } } }, - "MyFunctionFourRole": { - "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" - ] - } - } - ] - }, - "Tags": [ - { - "Key": "lambda:createdBy", - "Value": "SAM" - } - ] - } - }, "MyFunctionThreeApiKeyPermissionProd": { "Type": "AWS::Lambda::Permission", "Properties": { @@ -204,31 +146,47 @@ } } }, - "MyApiOneDeploymentdccbc5fda1": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "MyApiOne" - }, - "Description": "RestApi deployment id: dccbc5fda163e1abe712073ffacdcc47776a5a09", - "StageName": "Stage" - } - }, - "ServerlessUsagePlanKey": { - "Type": "AWS::ApiGateway::UsagePlanKey", + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", "Properties": { - "KeyType": "API_KEY", - "KeyId": { - "Ref": "ServerlessApiKey" - }, - "UsagePlanId": { - "Ref": "ServerlessUsagePlan" - } + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] }, "DependsOn": [ - "ServerlessApiKey" + "MyApiTwoUsagePlan" ] }, + "MyFunctionTwo": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionTwoRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, "MyFunctionOneApiKeyPermissionProd": { "Type": "AWS::Lambda::Permission", "Properties": { @@ -274,9 +232,6 @@ "MyFunctionOneRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -293,10 +248,13 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } @@ -352,9 +310,6 @@ "MyFunctionTwoRole": { "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ @@ -371,10 +326,13 @@ } ] }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], "Tags": [ { - "Key": "lambda:createdBy", - "Value": "SAM" + "Value": "SAM", + "Key": "lambda:createdBy" } ] } @@ -407,56 +365,121 @@ "ApiStages": [ { "ApiId": { - "Ref": "MyApiFour" + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" }, "Stage": { - "Ref": "MyApiFourProdStage" + "Ref": "ServerlessRestApiProdStage" } }, { "ApiId": { - "Ref": "MyApiFour" + "Ref": "MyApiThree" }, "Stage": { - "Ref": "MyApiFourProdStage" + "Ref": "MyApiThreeProdStage" } }, { "ApiId": { - "Ref": "MyApiFour" + "Ref": "ServerlessRestApi" }, "Stage": { - "Ref": "MyApiFourProdStage" + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" } } ] }, "DependsOn": [ - "MyApiFour", - "MyApiFour", - "MyApiFour" + "MyApiThree", + "ServerlessRestApi", + "MyApiThree", + "ServerlessRestApi", + "MyApiThree", + "ServerlessRestApi" ] }, - "MyFunctionFourApiKeyPermissionProd": { - "Type": "AWS::Lambda::Permission", + "ServerlessApiKey": { + "Type": "AWS::ApiGateway::ApiKey", "Properties": { - "Action": "lambda:InvokeFunction", - "Principal": "apigateway.amazonaws.com", - "FunctionName": { - "Ref": "MyFunctionFour" - }, - "SourceArn": { - "Fn::Sub": [ - "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/four", - { - "__Stage__": "*", - "__ApiId__": { - "Ref": "MyApiFour" - } + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" } - ] - } - } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "ServerlessUsagePlan" + ] }, "MyApiTwoUsagePlan": { "Type": "AWS::ApiGateway::UsagePlan", @@ -470,12 +493,43 @@ "Ref": "MyApiTwoProdStage" } } - ] + ], + "Description": "Description for usage plan", + "Tags": [ + { + "Value": "value1", + "Key": "key1" + }, + { + "Value": "value2", + "Key": "key2" + } + ], + "Quota": { + "Limit": 10, + "Period": "MONTH", + "Offset": 10 + }, + "Throttle": { + "RateLimit": 1000, + "BurstLimit": 1000 + }, + "UsagePlanName": "SomeRandomName" }, "DependsOn": [ "MyApiTwo" ] }, + "ServerlessRestApiDeploymentd197b03bdf": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: d197b03bdfab8dddfb221e8c7f1becff9a9b1d9d", + "StageName": "Stage" + } + }, "MyApiTwoDeployment0e45b81469": { "Type": "AWS::ApiGateway::Deployment", "Properties": { @@ -486,26 +540,25 @@ "StageName": "Stage" } }, - "MyFunctionFour": { - "Type": "AWS::Lambda::Function", + "MyFunctionTwoImplicitApiEventPermissionProd": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Handler": "index.handler", - "Code": { - "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" }, - "Role": { - "Fn::GetAtt": [ - "MyFunctionFourRole", - "Arn" + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/event", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } ] - }, - "Runtime": "nodejs12.x", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + } } }, "MyApiTwoProdStage": { @@ -532,71 +585,6 @@ "StageName": "Prod" } }, - "MyApiFourProdStage": { - "Type": "AWS::ApiGateway::Stage", - "Properties": { - "DeploymentId": { - "Ref": "MyApiFourDeploymentb6dc37f7cc" - }, - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": "Prod" - } - }, - "MyApiThreeUsagePlan": { - "Type": "AWS::ApiGateway::UsagePlan", - "Properties": { - "ApiStages": [ - { - "ApiId": { - "Ref": "MyApiThree" - }, - "Stage": { - "Ref": "MyApiThreeProdStage" - } - } - ] - }, - "DependsOn": [ - "MyApiThree" - ] - }, - "ServerlessApiKey": { - "Type": "AWS::ApiGateway::ApiKey", - "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": { - "Ref": "MyApiFourProdStage" - } - }, - { - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": { - "Ref": "MyApiFourProdStage" - } - }, - { - "RestApiId": { - "Ref": "MyApiFour" - }, - "StageName": { - "Ref": "MyApiFourProdStage" - } - } - ] - }, - "DependsOn": [ - "ServerlessUsagePlan" - ] - }, "MyApiThreeDeployment5206882d23": { "Type": "AWS::ApiGateway::Deployment", "Properties": { @@ -619,57 +607,21 @@ "StageName": "Prod" } }, - "MyApiTwoApiKey": { - "Type": "AWS::ApiGateway::ApiKey", + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", "Properties": { - "Enabled": true, - "StageKeys": [ - { - "RestApiId": { - "Ref": "MyApiTwo" - }, - "StageName": { - "Ref": "MyApiTwoProdStage" - } - } - ] + "KeyType": "API_KEY", + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } }, "DependsOn": [ - "MyApiTwoUsagePlan" + "ServerlessApiKey" ] }, - "MyApiFourDeploymentb6dc37f7cc": { - "Type": "AWS::ApiGateway::Deployment", - "Properties": { - "RestApiId": { - "Ref": "MyApiFour" - }, - "Description": "RestApi deployment id: b6dc37f7cc5eef500a43cb766cd9336e4956b0d2", - "StageName": "Stage" - } - }, - "MyFunctionTwo": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Handler": "index.handler", - "Code": { - "ZipFile": "exports.handler = async (event) => {\n return {\n statusCode: 200,\n body: JSON.stringify(event),\n headers: {}\n }\n}\n" - }, - "Role": { - "Fn::GetAtt": [ - "MyFunctionTwoRole", - "Arn" - ] - }, - "Runtime": "nodejs12.x", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] - } - }, "MyApiOne": { "Type": "AWS::ApiGateway::RestApi", "Properties": { @@ -718,7 +670,17 @@ } } }, - "MyApiFour": { + "MyApiOneDeploymentdccbc5fda1": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: dccbc5fda163e1abe712073ffacdcc47776a5a09", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { "Type": "AWS::ApiGateway::RestApi", "Properties": { "Body": { @@ -729,13 +691,13 @@ } }, "paths": { - "/path/four": { + "/path/event": { "get": { "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/${MyFunctionFour.Arn}/invocations" + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" } }, "security": [ diff --git a/tests/translator/output/aws-us-gov/api_with_usageplans_intrinsics.json b/tests/translator/output/aws-us-gov/api_with_usageplans_intrinsics.json index fffaa3647..0a60e70b8 100644 --- a/tests/translator/output/aws-us-gov/api_with_usageplans_intrinsics.json +++ b/tests/translator/output/aws-us-gov/api_with_usageplans_intrinsics.json @@ -23,7 +23,7 @@ }, "Parameters": { "UsagePlanType": { - "Default": "SINGLE", + "Default": "PER_API", "Type": "String" } }, diff --git a/tests/translator/output/error_api_with_usage_plan_create_usage_plan_none.json b/tests/translator/output/error_api_with_usage_plan_create_usage_plan_none.json deleted file mode 100644 index 6faaf7fd6..000000000 --- a/tests/translator/output/error_api_with_usage_plan_create_usage_plan_none.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "errors": [ - { - "errorMessage": "'Resource with id [MyApiOne] is invalid. Invalid property \"UsagePlanId\". This field can be given only when \"CreateUsagePlan\" is SINGLE'" - } - ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MyApiOne] is invalid. Invalid property \"UsagePlanId\". This field can be given only when \"CreateUsagePlan\" is SINGLE" -} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 6511796d4..54373f86a 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -606,7 +606,6 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw "error_function_with_event_dest_type", "error_function_with_api_key_false", "error_api_with_usage_plan_invalid_parameter", - "error_api_with_usage_plan_create_usage_plan_none", ], ) @patch("boto3.session.Session.region_name", "ap-southeast-1")