From 5be0b07efb5265671e6e9848b2397918673520b5 Mon Sep 17 00:00:00 2001 From: Shreya Date: Mon, 20 Jan 2020 22:43:45 -0800 Subject: [PATCH] feat: usage plans support for Api Auth (#1179) --- examples/2016-10-31/usage_plan/template.yaml | 80 ++ samtranslator/model/api/api_generator.py | 147 +++- samtranslator/model/apigateway.py | 37 + samtranslator/model/sam_resources.py | 7 +- .../translator/input/api_with_usageplans.yaml | 128 +++ .../input/api_with_usageplans_intrinsics.yaml | 85 ++ ...api_with_usage_plan_invalid_parameter.yaml | 41 + .../output/api_with_usageplans.json | 654 +++++++++++++++ .../api_with_usageplans_intrinsics.json | 411 ++++++++++ .../output/aws-cn/api_with_usageplans.json | 720 +++++++++++++++++ .../api_with_usageplans_intrinsics.json | 427 ++++++++++ .../aws-us-gov/api_with_usageplans.json | 754 ++++++++++++++++++ .../api_with_usageplans_intrinsics.json | 427 ++++++++++ ...api_with_usage_plan_invalid_parameter.json | 8 + tests/translator/test_translator.py | 3 + versions/2016-10-31.md | 11 + 16 files changed, 3937 insertions(+), 3 deletions(-) create mode 100644 examples/2016-10-31/usage_plan/template.yaml create mode 100644 tests/translator/input/api_with_usageplans.yaml create mode 100644 tests/translator/input/api_with_usageplans_intrinsics.yaml create mode 100644 tests/translator/input/error_api_with_usage_plan_invalid_parameter.yaml create mode 100644 tests/translator/output/api_with_usageplans.json create mode 100644 tests/translator/output/api_with_usageplans_intrinsics.json create mode 100644 tests/translator/output/aws-cn/api_with_usageplans.json create mode 100644 tests/translator/output/aws-cn/api_with_usageplans_intrinsics.json create mode 100644 tests/translator/output/aws-us-gov/api_with_usageplans.json create mode 100644 tests/translator/output/aws-us-gov/api_with_usageplans_intrinsics.json create mode 100644 tests/translator/output/error_api_with_usage_plan_invalid_parameter.json diff --git a/examples/2016-10-31/usage_plan/template.yaml b/examples/2016-10-31/usage_plan/template.yaml new file mode 100644 index 000000000..bd79b6cb7 --- /dev/null +++ b/examples/2016-10-31/usage_plan/template.yaml @@ -0,0 +1,80 @@ +Parameters: + UsagePlanType: + Type: String + Default: PER_API + +Globals: + Api: + OpenApiVersion: 3.0.0 + Auth: + ApiKeyRequired: true + UsagePlan: + CreateUsagePlan: !Ref UsagePlanType + +Resources: + MyApiOne: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + + MyApiTwo: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + UsagePlan: + CreateUsagePlan: SHARED + + 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 +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/' \ No newline at end of file diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 773e61617..5aedec4d1 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -9,6 +9,9 @@ ApiGatewayResponse, ApiGatewayDomainName, ApiGatewayBasePathMapping, + ApiGatewayUsagePlan, + ApiGatewayUsagePlanKey, + ApiGatewayApiKey, ) from samtranslator.model.route53 import Route53RecordSetGroup from samtranslator.model.exceptions import InvalidResourceException @@ -37,14 +40,24 @@ "AddDefaultAuthorizerToCorsPreflight", "ApiKeyRequired", "ResourcePolicy", + "UsagePlan", ], ) -AuthProperties.__new__.__defaults__ = (None, None, None, True, None, None) +AuthProperties.__new__.__defaults__ = (None, None, None, True, None, None, None) +UsagePlanProperties = namedtuple( + "_UsagePlanProperties", ["CreateUsagePlan", "Description", "Quota", "Tags", "Throttle", "UsagePlanName"] +) +UsagePlanProperties.__new__.__defaults__ = (None, None, None, None, None, None) GatewayResponseProperties = ["ResponseParameters", "ResponseTemplates", "StatusCode"] class ApiGenerator(object): + usage_plan_shared = False + stage_keys_shared = list() + api_stages_shared = list() + depends_on_shared = list() + def __init__( self, logical_id, @@ -392,8 +405,9 @@ def to_cloudformation(self, redeploy_restapi_parameters): stage = self._construct_stage(deployment, swagger, redeploy_restapi_parameters) permissions = self._construct_authorizer_lambda_permission() + usage_plan = self._construct_usage_plan(rest_api_stage=stage) - return rest_api, deployment, stage, permissions, domain, basepath_mapping, route53 + return rest_api, deployment, stage, permissions, domain, basepath_mapping, route53, usage_plan def _add_cors(self): """ @@ -518,6 +532,135 @@ def _add_auth(self): self.definition_body = self._openapi_postprocess(swagger_editor.swagger) + 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: UsagePlan, UsagePlanKey, ApiKey for this rest Api + :rtype: model.apigateway.ApiGatewayUsagePlan, model.apigateway.ApiGatewayUsagePlanKey, + model.apigateway.ApiGatewayApiKey + """ + create_usage_plans_accepted_values = ["SHARED", "PER_API", "NONE"] + if not self.auth: + return [] + auth_properties = AuthProperties(**self.auth) + 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'") + + create_usage_plan = usage_plan_properties.get("CreateUsagePlan") + usage_plan = None + api_key = None + usage_plan_key = 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 create_usage_plans_accepted_values: + raise InvalidResourceException( + self.logical_id, "'CreateUsagePlan' accepts one of {}".format(create_usage_plans_accepted_values) + ) + + 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": + usage_plan_logical_id = "ServerlessUsagePlan" + ApiGenerator.depends_on_shared.append(self.logical_id) + usage_plan = ApiGatewayUsagePlan( + logical_id=usage_plan_logical_id, depends_on=ApiGenerator.depends_on_shared + ) + api_stage = dict() + api_stage["ApiId"] = ref(self.logical_id) + api_stage["Stage"] = ref(rest_api_stage.logical_id) + ApiGenerator.api_stages_shared.append(api_stage) + usage_plan.ApiStages = ApiGenerator.api_stages_shared + + 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 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" + api_key = ApiGatewayApiKey(logical_id=api_key_logical_id, depends_on=[usage_plan_logical_id]) + api_key.Enabled = True + stage_key = dict() + stage_key["RestApiId"] = ref(self.logical_id) + 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" + 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() + stage_key["RestApiId"] = ref(self.logical_id) + stage_key["StageName"] = ref(rest_api_stage.logical_id) + stage_keys.append(stage_key) + api_key.StageKeys = stage_keys + 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" + + 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) + + return usage_plan_key + def _add_gateway_responses(self): """ Add Gateway Response configuration to the Swagger file, if necessary diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index aeb9f37ae..e1bfaa282 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -179,6 +179,43 @@ class ApiGatewayBasePathMapping(Resource): } +class ApiGatewayUsagePlan(Resource): + resource_type = "AWS::ApiGateway::UsagePlan" + 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)} + + +class ApiGatewayUsagePlanKey(Resource): + resource_type = "AWS::ApiGateway::UsagePlanKey" + property_types = { + "KeyId": PropertyType(True, is_str()), + "KeyType": PropertyType(True, is_str()), + "UsagePlanId": PropertyType(True, is_str()), + } + + +class ApiGatewayApiKey(Resource): + resource_type = "AWS::ApiGateway::ApiKey" + property_types = { + "CustomerId": PropertyType(False, is_str()), + "Description": PropertyType(False, is_str()), + "Enabled": PropertyType(False, is_type(bool)), + "GenerateDistinctId": PropertyType(False, is_type(bool)), + "Name": PropertyType(False, is_str()), + "StageKeys": PropertyType(False, is_type(list)), + "Value": PropertyType(False, is_str()), + } + + runtime_attrs = {"api_key_id": lambda self: ref(self.logical_id)} + + class ApiGatewayAuthorizer(object): _VALID_FUNCTION_PAYLOAD_TYPES = [None, "TOKEN", "REQUEST"] diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index dd3a97313..27744a737 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -767,6 +767,8 @@ def to_cloudformation(self, **kwargs): intrinsics_resolver = kwargs["intrinsics_resolver"] self.BinaryMediaTypes = intrinsics_resolver.resolve_parameter_refs(self.BinaryMediaTypes) self.Domain = intrinsics_resolver.resolve_parameter_refs(self.Domain) + self.Auth = intrinsics_resolver.resolve_parameter_refs(self.Auth) + redeploy_restapi_parameters = kwargs.get("redeploy_restapi_parameters") api_generator = ApiGenerator( @@ -797,7 +799,7 @@ def to_cloudformation(self, **kwargs): domain=self.Domain, ) - rest_api, deployment, stage, permissions, domain, basepath_mapping, route53 = api_generator.to_cloudformation( + rest_api, deployment, stage, permissions, domain, basepath_mapping, route53, usage_plan_resources = api_generator.to_cloudformation( redeploy_restapi_parameters ) @@ -809,6 +811,9 @@ 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 new file mode 100644 index 000000000..36aa0ba0a --- /dev/null +++ b/tests/translator/input/api_with_usageplans.yaml @@ -0,0 +1,128 @@ +Globals: + Api: + Auth: + ApiKeyRequired: true + UsagePlan: + CreateUsagePlan: SHARED + +Resources: + MyApiOne: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + UsagePlan: + CreateUsagePlan: NONE + + MyApiTwo: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + UsagePlan: + 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 + + 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 + ImplicitApiEvent: + Type: Api + Properties: + Method: get + Path: /path/event + + 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 + +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/' + ApiThreeUrl: + Description: "API endpoint URL for Prod environment" + Value: + Fn::Sub: 'https://${MyApiThree}.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 new file mode 100644 index 000000000..7a386b556 --- /dev/null +++ b/tests/translator/input/api_with_usageplans_intrinsics.yaml @@ -0,0 +1,85 @@ +Conditions: + C1: + Fn::Equals: + - true + - true + +Parameters: + UsagePlanType: + Type: String + Default: PER_API + +Globals: + Api: + OpenApiVersion: 3.0.0 + Auth: + ApiKeyRequired: true + UsagePlan: + CreateUsagePlan: !Ref 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: 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 +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/' \ No newline at end of file diff --git a/tests/translator/input/error_api_with_usage_plan_invalid_parameter.yaml b/tests/translator/input/error_api_with_usage_plan_invalid_parameter.yaml new file mode 100644 index 000000000..a347c5621 --- /dev/null +++ b/tests/translator/input/error_api_with_usage_plan_invalid_parameter.yaml @@ -0,0 +1,41 @@ +Globals: + Api: + OpenApiVersion: 3.0.0 + Auth: + ApiKeyRequired: true + UsagePlan: + CreateUP: NONE + +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 new file mode 100644 index 000000000..6e11a953a --- /dev/null +++ b/tests/translator/output/api_with_usageplans.json @@ -0,0 +1,654 @@ +{ + "Outputs": { + "ApiTwoUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiThreeUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiOneUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + }, + "Resources": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment7a26848ac9" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "MyApiTwoUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiTwoApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiTwoUsagePlan" + } + }, + "DependsOn": [ + "MyApiTwoApiKey" + ] + }, + "MyApiThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/three": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionThree.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "ServerlessRestApiDeployment7a26848ac9": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: 7a26848ac97d678aaf266a8a883d0abd463b3bbc", + "StageName": "Stage" + } + }, + "MyFunctionThreeApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionThree" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/three", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiThree" + } + } + ] + } + } + }, + "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": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiOne" + } + } + ] + } + } + }, + "MyApiOneDeployment46fb22a429": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: 46fb22a42926db6f64e09966936d074ec6bb9392", + "StageName": "Stage" + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiTwo" + } + } + ] + } + } + }, + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionOne": { + "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": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiThree", + "ServerlessRestApi" + ] + }, + "MyApiTwoUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "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" + ] + }, + "MyApiThreeDeployment1d9cff47dc": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiThree" + }, + "Description": "RestApi deployment id: 1d9cff47dc9b822750c668c73b4534022483de6d", + "StageName": "Stage" + } + }, + "MyFunctionTwoImplicitApiEventPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/event", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeploymente9d97923b9" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment46fb22a429" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "MyApiTwoDeploymente9d97923b9": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "Description": "RestApi deployment id: e9d97923b94d0801cd85a8970b3c3f84aa274003", + "StageName": "Stage" + } + }, + "MyFunctionThree": { + "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": [ + "MyFunctionThreeRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiThreeDeployment1d9cff47dc" + }, + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + }, + "DependsOn": [ + "ServerlessApiKey" + ] + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "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": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwoUsagePlan" + ] + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_usageplans_intrinsics.json b/tests/translator/output/api_with_usageplans_intrinsics.json new file mode 100644 index 000000000..3c43187e7 --- /dev/null +++ b/tests/translator/output/api_with_usageplans_intrinsics.json @@ -0,0 +1,411 @@ +{ + "Outputs": { + "ApiTwoUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiOneUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + }, + "Conditions": { + "C1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "UsagePlanType": { + "Default": "PER_API", + "Type": "String" + } + }, + "Resources": { + "MyApiTwoUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiTwoApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiTwoUsagePlan" + } + }, + "DependsOn": [ + "MyApiTwoApiKey" + ] + }, + "MyApiTwoDeploymenta78b9db9dd": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "Description": "RestApi deployment id: a78b9db9ddd80dec31c4b3c3e2b6e037345252ce" + } + }, + "MyApiOneUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiOne" + ] + }, + "MyApiTwoUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwo" + ] + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeploymenta78b9db9dd" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiOneDeployment37a3a51a0f": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: 37a3a51a0fd9d750b0bd43bdab40046fdc167e8d" + }, + "Condition": "C1" + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment37a3a51a0f" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "MyApiOneApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiOneUsagePlan" + ] + }, + "MyFunctionTwoRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyApiOneUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiOneApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiOneUsagePlan" + } + }, + "DependsOn": [ + "MyApiOneApiKey" + ] + }, + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwoUsagePlan" + ] + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiOne" + } + } + ] + } + } + }, + "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" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiTwo" + } + } + ] + } + } + }, + "MyFunctionOneRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "openapi": "3.0.0", + "components": { + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionOne.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "openapi": "3.0.0", + "components": { + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + }, + "Condition": "C1" + }, + "MyFunctionOne": { + "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": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_usageplans.json b/tests/translator/output/aws-cn/api_with_usageplans.json new file mode 100644 index 000000000..3f38b0ce5 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_usageplans.json @@ -0,0 +1,720 @@ +{ + "Outputs": { + "ApiTwoUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiThreeUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiOneUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + }, + "Resources": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiOneDeployment7997029260": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: 79970292604071da8105ffd8503f82af32b30550", + "StageName": "Stage" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment40b50dc688" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "MyApiTwoUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiTwoApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiTwoUsagePlan" + } + }, + "DependsOn": [ + "MyApiTwoApiKey" + ] + }, + "MyApiThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/three": { + "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/${MyFunctionThree.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyFunctionThreeApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionThree" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/three", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiThree" + } + } + ] + } + } + }, + "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": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiOne" + } + } + ] + } + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiTwo" + } + } + ] + } + } + }, + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionOne": { + "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": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "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": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiThree", + "ServerlessRestApi", + "MyApiThree", + "ServerlessRestApi" + ] + }, + "MyApiTwoUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "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" + ] + }, + "MyFunctionTwoImplicitApiEventPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/event", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment03730b64c4" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment7997029260" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "MyApiTwoDeployment03730b64c4": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "Description": "RestApi deployment id: 03730b64c486cc490deefb3b8225244b0fe85d34", + "StageName": "Stage" + } + }, + "MyFunctionThree": { + "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": [ + "MyFunctionThreeRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiThreeDeploymentfa9f73f027" + }, + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + }, + "DependsOn": [ + "ServerlessApiKey" + ] + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "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/${MyFunctionOne.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "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": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiThreeDeploymentfa9f73f027": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiThree" + }, + "Description": "RestApi deployment id: fa9f73f0272017527c24cc93cc4440dd4476b9f4", + "StageName": "Stage" + } + }, + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "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 new file mode 100644 index 000000000..a4d484013 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_usageplans_intrinsics.json @@ -0,0 +1,427 @@ +{ + "Outputs": { + "ApiTwoUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiOneUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + }, + "Conditions": { + "C1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "UsagePlanType": { + "Default": "PER_API", + "Type": "String" + } + }, + "Resources": { + "MyApiTwoUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiTwoApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiTwoUsagePlan" + } + }, + "DependsOn": [ + "MyApiTwoApiKey" + ] + }, + "MyApiOneUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiOne" + ] + }, + "MyApiTwoUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwo" + ] + }, + "MyApiTwoDeploymenta997b9e562": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "Description": "RestApi deployment id: a997b9e562a05ea9cb87782748d6df042f6a82d4" + } + }, + "MyApiOneUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiOneApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiOneUsagePlan" + } + }, + "DependsOn": [ + "MyApiOneApiKey" + ] + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeploymenta997b9e562" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeploymentd8cca8c82b" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "MyApiOneApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiOneUsagePlan" + ] + }, + "MyFunctionTwoRole": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiOneDeploymentd8cca8c82b": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: d8cca8c82bb70492b08f8ad3e95b75b30fb6ce14" + }, + "Condition": "C1" + }, + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwoUsagePlan" + ] + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiOne" + } + } + ] + } + } + }, + "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" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiTwo" + } + } + ] + } + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "openapi": "3.0.0", + "components": { + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "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/${MyFunctionOne.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "openapi": "3.0.0", + "components": { + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Condition": "C1" + }, + "MyFunctionOne": { + "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": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_usageplans.json b/tests/translator/output/aws-us-gov/api_with_usageplans.json new file mode 100644 index 000000000..7c0d0eaad --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_usageplans.json @@ -0,0 +1,754 @@ +{ + "Outputs": { + "ApiTwoUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiThreeUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiThree}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiOneUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + }, + "Resources": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentd197b03bdf" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + } + }, + "MyApiTwoUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiTwoApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiTwoUsagePlan" + } + }, + "DependsOn": [ + "MyApiTwoApiKey" + ] + }, + "MyApiThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/three": { + "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/${MyFunctionThree.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyFunctionThreeApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionThree" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/three", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiThree" + } + } + ] + } + } + }, + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "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": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiOne" + } + } + ] + } + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiTwo" + } + } + ] + } + } + }, + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyFunctionOne": { + "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": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ServerlessUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + }, + { + "ApiId": { + "Ref": "MyApiThree" + }, + "Stage": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "ApiId": { + "Ref": "ServerlessRestApi" + }, + "Stage": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiThree", + "ServerlessRestApi", + "MyApiThree", + "ServerlessRestApi", + "MyApiThree", + "ServerlessRestApi" + ] + }, + "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" + } + }, + { + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": { + "Ref": "MyApiThreeProdStage" + } + }, + { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": { + "Ref": "ServerlessRestApiProdStage" + } + } + ] + }, + "DependsOn": [ + "ServerlessUsagePlan" + ] + }, + "MyApiTwoUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "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": { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "Description": "RestApi deployment id: 0e45b814691166a59217a088512ee30710a12369", + "StageName": "Stage" + } + }, + "MyFunctionTwoImplicitApiEventPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/event", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment0e45b81469" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeploymentdccbc5fda1" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + } + }, + "MyApiThreeDeployment5206882d23": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiThree" + }, + "Description": "RestApi deployment id: 5206882d23d2cf7913f0fffea98644f959b433f2", + "StageName": "Stage" + } + }, + "MyApiThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiThreeDeployment5206882d23" + }, + "RestApiId": { + "Ref": "MyApiThree" + }, + "StageName": "Prod" + } + }, + "ServerlessUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "ServerlessApiKey" + }, + "UsagePlanId": { + "Ref": "ServerlessUsagePlan" + } + }, + "DependsOn": [ + "ServerlessApiKey" + ] + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "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/${MyFunctionOne.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiOneDeploymentdccbc5fda1": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: dccbc5fda163e1abe712073ffacdcc47776a5a09", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyFunctionThree": { + "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": [ + "MyFunctionThreeRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..0a60e70b8 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_usageplans_intrinsics.json @@ -0,0 +1,427 @@ +{ + "Outputs": { + "ApiTwoUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiTwo}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + }, + "ApiOneUrl": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${MyApiOne}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + }, + "Conditions": { + "C1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "UsagePlanType": { + "Default": "PER_API", + "Type": "String" + } + }, + "Resources": { + "MyApiTwoUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiTwoApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiTwoUsagePlan" + } + }, + "DependsOn": [ + "MyApiTwoApiKey" + ] + }, + "MyApiTwoDeployment802c3b471d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "Description": "RestApi deployment id: 802c3b471de66f79d6d2cac656e6c9319fa0ba51" + } + }, + "MyApiOneUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiOne" + }, + "Stage": { + "Ref": "MyApiOneProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiOne" + ] + }, + "MyApiOneDeployment8b73115419": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiOne" + }, + "Description": "RestApi deployment id: 8b731154198483502cc4b2bff739c404e88bfe41" + }, + "Condition": "C1" + }, + "MyApiTwoUsagePlan": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "MyApiTwo" + }, + "Stage": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwo" + ] + }, + "MyApiTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiTwoDeployment802c3b471d" + }, + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": "Prod" + } + }, + "MyApiOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiOneDeployment8b73115419" + }, + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": "Prod" + }, + "Condition": "C1" + }, + "MyApiOneApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiOne" + }, + "StageName": { + "Ref": "MyApiOneProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiOneUsagePlan" + ] + }, + "MyFunctionTwoRole": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiOneUsagePlanKey": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyType": "API_KEY", + "KeyId": { + "Ref": "MyApiOneApiKey" + }, + "UsagePlanId": { + "Ref": "MyApiOneUsagePlan" + } + }, + "DependsOn": [ + "MyApiOneApiKey" + ] + }, + "MyApiTwoApiKey": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true, + "StageKeys": [ + { + "RestApiId": { + "Ref": "MyApiTwo" + }, + "StageName": { + "Ref": "MyApiTwoProdStage" + } + } + ] + }, + "DependsOn": [ + "MyApiTwoUsagePlan" + ] + }, + "MyFunctionOneApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionOne" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/one", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiOne" + } + } + ] + } + } + }, + "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" + } + ] + } + }, + "MyFunctionTwoApiKeyPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionTwo" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/path/two", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiTwo" + } + } + ] + } + } + }, + "MyFunctionOneRole": { + "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": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/two": { + "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/${MyFunctionTwo.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "openapi": "3.0.0", + "components": { + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/path/one": { + "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/${MyFunctionOne.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "openapi": "3.0.0", + "components": { + "securitySchemes": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Condition": "C1" + }, + "MyFunctionOne": { + "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": [ + "MyFunctionOneRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/error_api_with_usage_plan_invalid_parameter.json b/tests/translator/output/error_api_with_usage_plan_invalid_parameter.json new file mode 100644 index 000000000..c5f06ba39 --- /dev/null +++ b/tests/translator/output/error_api_with_usage_plan_invalid_parameter.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "\"Resource with id [MyApiOne] is invalid. Invalid property for 'UsagePlan'\"" + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MyApiOne] is invalid. Invalid property for 'UsagePlan'" +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 173d34c72..54373f86a 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -272,6 +272,8 @@ class TestTranslatorEndToEnd(TestCase): "function_with_event_dest", "function_with_event_dest_basic", "function_with_event_dest_conditional", + "api_with_usageplans", + "api_with_usageplans_intrinsics", ], [ ("aws", "ap-southeast-1"), @@ -603,6 +605,7 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw "error_function_with_event_dest_invalid", "error_function_with_event_dest_type", "error_function_with_api_key_false", + "error_api_with_usage_plan_invalid_parameter", ], ) @patch("boto3.session.Session.region_name", "ap-southeast-1") diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index fe9ae033e..a27af7642 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -1040,6 +1040,8 @@ Define Lambda and Cognito `Authorizers` and specify a `DefaultAuthorizer`. If yo ```yaml Auth: ApiKeyRequired: true # OPTIONAL + UsagePlan: # OPTIONAL + CreateUsagePlan: PER_API # REQUIRED if UsagePlan property is set. accepted values: PER_API, SHARED, NONE DefaultAuthorizer: MyCognitoAuth # OPTIONAL, if you use IAM permissions, specify AWS_IAM. AddDefaultAuthorizerToCorsPreflight: false # OPTIONAL; Default: true ResourcePolicy: @@ -1121,6 +1123,15 @@ Auth: ``` +**UsagePlan:** +Create Usage Plan for API Auth. Usage Plans can be set in Globals level as well for RestApis. +SAM creates a single Usage Plan, Api Key and Usage Plan Api Key resources if `CreateUsagePlan` is `SHARED` and a Usage Plan, Api Key and Usage Plan Api Key resources per Api when `CreateUsagePlan` is `PER_API`. + +```yaml + Auth: + UsagePlan: + CreateUsagePlan: PER_API # Required supported values: SHARED | NONE | PER_API +``` #### Function Auth Object Configure Auth for a specific Api+Path+Method.