Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions samtranslator/model/api/http_api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def __init__(
depends_on: Optional[List[str]],
definition_body: Optional[Dict[str, Any]],
definition_uri: Optional[Intrinsicable[str]],
name: Optional[Any],
stage_name: Optional[Intrinsicable[str]],
tags: Optional[Dict[str, Intrinsicable[str]]] = None,
auth: Optional[Dict[str, Intrinsicable[str]]] = None,
Expand All @@ -60,6 +61,7 @@ def __init__(
:param depends_on: Any resources that need to be depended on
:param definition_body: API definition
:param definition_uri: URI to API definition
:param name: Name of the API Gateway resource
:param stage_name: Name of the Stage
:param tags: Stage and API Tags
:param access_log_settings: Whether to send access logs and where for Stage
Expand All @@ -73,6 +75,7 @@ def __init__(
self.definition_body = definition_body
self.definition_uri = definition_uri
self.stage_name = stage_name
self.name = name
if not self.stage_name:
self.stage_name = DefaultStageName
self.auth = auth
Expand Down Expand Up @@ -113,6 +116,7 @@ def _construct_http_api(self) -> ApiGatewayV2HttpApi:
if self.disable_execute_api_endpoint is not None:
self._add_endpoint_configuration()

self._add_title()
self._add_description()

if self.definition_uri:
Expand Down Expand Up @@ -674,6 +678,27 @@ def _add_description(self) -> None:
open_api_editor.add_description(self.description)
self.definition_body = open_api_editor.openapi

def _add_title(self) -> None:
if not self.name:
return

if not self.definition_body:
raise InvalidResourceException(
self.logical_id,
"Name works only with inline OpenApi specified in the 'DefinitionBody' property.",
)

if self.definition_body.get("info", {}).get("title") != OpenApiEditor._DEFAULT_OPENAPI_TITLE:
raise InvalidResourceException(
self.logical_id,
"Unable to set Name because it is already defined within inline OpenAPI specified in the "
"'DefinitionBody' property.",
)

open_api_editor = OpenApiEditor(self.definition_body)
open_api_editor.add_title(self.name)
self.definition_body = open_api_editor.openapi

@cw_timer(prefix="Generator", name="HttpApi") # type: ignore[misc]
def to_cloudformation(
self, route53_record_set_groups: Dict[str, Route53RecordSetGroup]
Expand Down
12 changes: 11 additions & 1 deletion samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@
from .s3_utils.uri_parser import construct_s3_location_object, construct_image_code_object
from .tags.resource_tagging import get_tag_list
from samtranslator.metrics.method_decorator import cw_timer
from samtranslator.model import ResourceResolver, PropertyType, SamResourceMacro, Resource, ResourceTypeResolver
from samtranslator.model import (
ResourceResolver,
Property,
PropertyType,
SamResourceMacro,
Resource,
ResourceTypeResolver,
)
from samtranslator.model.apigateway import (
ApiGatewayDeployment,
ApiGatewayStage,
Expand Down Expand Up @@ -1276,6 +1283,7 @@ class SamHttpApi(SamResourceMacro):
# In the future, we might rename and expose this property to customers so they can have SAM manage Explicit APIs
# Swagger.
"__MANAGE_SWAGGER": PropertyType(False, is_type(bool)),
"Name": Property(False, any_type()),
"StageName": PropertyType(False, one_of(is_str(), is_type(dict))),
"Tags": PropertyType(False, is_type(dict)),
"DefinitionBody": PropertyType(False, is_type(dict)),
Expand All @@ -1292,6 +1300,7 @@ class SamHttpApi(SamResourceMacro):
"DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)),
}

Name: Optional[Any]
StageName: Optional[Intrinsicable[str]]
Tags: Optional[Dict[str, Any]]
DefinitionBody: Optional[Dict[str, Any]]
Expand Down Expand Up @@ -1332,6 +1341,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def]
self.depends_on,
self.DefinitionBody,
self.DefinitionUri,
self.Name,
self.StageName,
tags=self.Tags,
auth=self.Auth,
Expand Down
12 changes: 11 additions & 1 deletion samtranslator/open_api/open_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class OpenApiEditor(object):
_X_ANY_METHOD = "x-amazon-apigateway-any-method"
_ALL_HTTP_METHODS = ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"]
_DEFAULT_PATH = "$default"
_DEFAULT_OPENAPI_TITLE = ref("AWS::StackName")

def __init__(self, doc: Optional[Dict[str, Any]]) -> None:
"""
Expand Down Expand Up @@ -604,6 +605,15 @@ def add_description(self, description: Intrinsicable[str]) -> None:
return
self.info["description"] = description

def add_title(self, title: Intrinsicable[str]) -> None:
"""Add title in open api definition, if it is not already defined

:param string description: Description of the API
"""
if self.info.get("title") != OpenApiEditor._DEFAULT_OPENAPI_TITLE:
return
self.info["title"] = title

def has_api_gateway_cors(self): # type: ignore[no-untyped-def]
if self._doc.get(self._X_APIGW_CORS):
return True
Expand Down Expand Up @@ -660,7 +670,7 @@ def gen_skeleton() -> Py27Dict:
skeleton["openapi"] = "3.0.1"
skeleton["info"] = Py27Dict()
skeleton["info"]["version"] = "1.0"
skeleton["info"]["title"] = ref("AWS::StackName")
skeleton["info"]["title"] = OpenApiEditor._DEFAULT_OPENAPI_TITLE
skeleton["paths"] = Py27Dict()
return skeleton

Expand Down
2 changes: 2 additions & 0 deletions tests/model/api/test_http_api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TestHttpApiGenerator(TestCase):
"depends_on": None,
"definition_body": None,
"definition_uri": None,
"name": None,
"stage_name": None,
"tags": None,
"auth": None,
Expand Down Expand Up @@ -208,6 +209,7 @@ class TestCustomDomains(TestCase):
"depends_on": None,
"definition_body": None,
"definition_uri": "s3://bucket/key",
"name": None,
"stage_name": None,
"tags": None,
"auth": None,
Expand Down
26 changes: 26 additions & 0 deletions tests/translator/input/explicit_http_api_with_name.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Resources:
HttpApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/todo_list.zip
Handler: index.restapi
Runtime: python3.7
Events:
SimpleCase:
Type: HttpApi
Properties:
ApiId: !Ref MyApi
SimpleCase2:
Type: HttpApi
Properties:
ApiId: !Ref MyApiWithIntrinsicName

MyApi:
Type: AWS::Serverless::HttpApi
Properties:
Name: MyHttpApi

MyApiWithIntrinsicName:
Type: AWS::Serverless::HttpApi
Properties:
Name: !Sub "${HttpApiFunction}-HttpApi"
195 changes: 195 additions & 0 deletions tests/translator/output/aws-cn/explicit_http_api_with_name.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
{
"Resources": {
"HttpApiFunction": {
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "todo_list.zip"
},
"Handler": "index.restapi",
"Role": {
"Fn::GetAtt": [
"HttpApiFunctionRole",
"Arn"
]
},
"Runtime": "python3.7",
"Tags": [
{
"Key": "lambda:createdBy",
"Value": "SAM"
}
]
},
"Type": "AWS::Lambda::Function"
},
"HttpApiFunctionRole": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
}
}
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Tags": [
{
"Key": "lambda:createdBy",
"Value": "SAM"
}
]
},
"Type": "AWS::IAM::Role"
},
"HttpApiFunctionSimpleCase2Permission": {
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "HttpApiFunction"
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*",
{
"__ApiId__": {
"Ref": "MyApiWithIntrinsicName"
},
"__Stage__": "*"
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"HttpApiFunctionSimpleCasePermission": {
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "HttpApiFunction"
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*",
{
"__ApiId__": {
"Ref": "MyApi"
},
"__Stage__": "*"
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"MyApi": {
"Properties": {
"Body": {
"info": {
"title": "MyHttpApi",
"version": "1.0"
},
"openapi": "3.0.1",
"paths": {
"$default": {
"x-amazon-apigateway-any-method": {
"isDefaultRoute": true,
"responses": {},
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations"
}
}
}
}
},
"tags": [
{
"name": "httpapi:createdBy",
"x-amazon-apigateway-tag-value": "SAM"
}
]
}
},
"Type": "AWS::ApiGatewayV2::Api"
},
"MyApiApiGatewayDefaultStage": {
"Properties": {
"ApiId": {
"Ref": "MyApi"
},
"AutoDeploy": true,
"StageName": "$default",
"Tags": {
"httpapi:createdBy": "SAM"
}
},
"Type": "AWS::ApiGatewayV2::Stage"
},
"MyApiWithIntrinsicName": {
"Properties": {
"Body": {
"info": {
"title": {
"Fn::Sub": "${HttpApiFunction}-HttpApi"
},
"version": "1.0"
},
"openapi": "3.0.1",
"paths": {
"$default": {
"x-amazon-apigateway-any-method": {
"isDefaultRoute": true,
"responses": {},
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations"
}
}
}
}
},
"tags": [
{
"name": "httpapi:createdBy",
"x-amazon-apigateway-tag-value": "SAM"
}
]
}
},
"Type": "AWS::ApiGatewayV2::Api"
},
"MyApiWithIntrinsicNameApiGatewayDefaultStage": {
"Properties": {
"ApiId": {
"Ref": "MyApiWithIntrinsicName"
},
"AutoDeploy": true,
"StageName": "$default",
"Tags": {
"httpapi:createdBy": "SAM"
}
},
"Type": "AWS::ApiGatewayV2::Stage"
}
}
}
Loading