diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index 0643db332..5a96ea6e7 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -11,6 +11,7 @@ AuthProperties = namedtuple("_AuthProperties", ["Authorizers", "DefaultAuthorizer"]) AuthProperties.__new__.__defaults__ = (None, None) DefaultStageName = "$default" +HttpApiTagName = "httpapi:createdBy" class HttpApiGenerator(object): @@ -70,6 +71,7 @@ def _construct_http_api(self): ) self._add_auth() + self._add_tags() if self.definition_uri: http_api.BodyS3Location = self._construct_body_s3_dict() @@ -83,9 +85,6 @@ def _construct_http_api(self): "add a 'HttpApi' event to an 'AWS::Serverless::Function'", ) - if self.tags is not None: - http_api.Tags = get_tag_list(self.tags) - return http_api def _add_auth(self): @@ -97,7 +96,7 @@ def _add_auth(self): if self.auth and not self.definition_body: raise InvalidResourceException( - self.logical_id, "Auth works only with inline Swagger specified in " "'DefinitionBody' property" + self.logical_id, "Auth works only with inline OpenApi specified in the 'DefinitionBody' property." ) # Make sure keys in the dict are recognized @@ -107,7 +106,7 @@ def _add_auth(self): if not OpenApiEditor.is_valid(self.definition_body): raise InvalidResourceException( self.logical_id, - "Unable to add Auth configuration because " "'DefinitionBody' does not contain a valid Swagger", + "Unable to add Auth configuration because 'DefinitionBody' does not contain a valid OpenApi definition.", ) open_api_editor = OpenApiEditor(self.definition_body) auth_properties = AuthProperties(**self.auth) @@ -120,6 +119,36 @@ def _add_auth(self): ) self.definition_body = open_api_editor.openapi + def _add_tags(self): + """ + Adds tags to the Http Api, including a default SAM tag. + """ + if self.tags and not self.definition_body: + raise InvalidResourceException( + self.logical_id, "Tags works only with inline OpenApi specified in the 'DefinitionBody' property." + ) + + if not self.definition_body: + return + + if self.tags and not OpenApiEditor.is_valid(self.definition_body): + raise InvalidResourceException( + self.logical_id, + "Unable to add `Tags` because 'DefinitionBody' does not contain a valid OpenApi definition.", + ) + elif not OpenApiEditor.is_valid(self.definition_body): + return + + if not self.tags: + self.tags = {} + self.tags[HttpApiTagName] = "SAM" + + open_api_editor = OpenApiEditor(self.definition_body) + + # authorizers is guaranteed to return a value or raise an exception + open_api_editor.add_tags(self.tags) + self.definition_body = open_api_editor.openapi + def _set_default_authorizer(self, open_api_editor, authorizers, default_authorizer, api_authorizers): """ Sets the default authorizer if one is given in the template @@ -134,7 +163,9 @@ def _set_default_authorizer(self, open_api_editor, authorizers, default_authoriz if not authorizers.get(default_authorizer): raise InvalidResourceException( self.logical_id, - "Unable to set DefaultAuthorizer because '" + default_authorizer + "' was not defined in 'Authorizers'", + "Unable to set DefaultAuthorizer because '" + + default_authorizer + + "' was not defined in 'Authorizers'.", ) for path in open_api_editor.iter_on_path(): @@ -151,7 +182,7 @@ def _get_authorizers(self, authorizers_config, default_authorizer=None): authorizers = {} if not isinstance(authorizers_config, dict): - raise InvalidResourceException(self.logical_id, "Authorizers must be a dictionary") + raise InvalidResourceException(self.logical_id, "Authorizers must be a dictionary.") for authorizer_name, authorizer in authorizers_config.items(): if not isinstance(authorizer, dict): @@ -179,7 +210,7 @@ def _construct_body_s3_dict(self): if not self.definition_uri.get("Bucket", None) or not self.definition_uri.get("Key", None): # DefinitionUri is a dictionary but does not contain Bucket or Key property raise InvalidResourceException( - self.logical_id, "'DefinitionUri' requires Bucket and Key properties to be specified" + self.logical_id, "'DefinitionUri' requires Bucket and Key properties to be specified." ) s3_pointer = self.definition_uri @@ -226,9 +257,6 @@ def _construct_stage(self): stage.AccessLogSettings = self.access_log_settings stage.AutoDeploy = True - if self.tags is not None: - stage.Tags = get_tag_list(self.tags) - return stage def to_cloudformation(self): diff --git a/samtranslator/open_api/open_api.py b/samtranslator/open_api/open_api.py index 5c4820c3f..76569e787 100644 --- a/samtranslator/open_api/open_api.py +++ b/samtranslator/open_api/open_api.py @@ -16,6 +16,7 @@ class OpenApiEditor(object): """ _X_APIGW_INTEGRATION = "x-amazon-apigateway-integration" + _X_APIGW_TAG_VALUE = "x-amazon-apigateway-tag-value" _CONDITIONAL_IF = "Fn::If" _X_ANY_METHOD = "x-amazon-apigateway-any-method" _ALL_HTTP_METHODS = ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"] @@ -26,8 +27,8 @@ def __init__(self, doc): Initialize the class with a swagger dictionary. This class creates a copy of the Swagger and performs all modifications on this copy. - :param dict doc: Swagger document as a dictionary - :raises ValueError: If the input Swagger document does not meet the basic Swagger requirements. + :param dict doc: OpenApi document as a dictionary + :raises ValueError: If the input OpenApi document does not meet the basic OpenApi requirements. """ if not OpenApiEditor.is_valid(doc): raise ValueError( @@ -39,6 +40,7 @@ def __init__(self, doc): self.paths = self._doc["paths"] self.security_schemes = self._doc.get("components", {}).get("securitySchemes", {}) self.definitions = self._doc.get("definitions", {}) + self.tags = self._doc.get("tags", []) def get_path(self, path): """ @@ -358,6 +360,22 @@ def _set_method_authorizer(self, path, method_name, authorizer_name, authorizers if security: method_definition["security"] = security + def add_tags(self, tags): + """ + Adds tags to the OpenApi definition using an ApiGateway extension for tag values. + + :param dict tags: dictionary of tagName:tagValue pairs. + """ + for name, value in tags.items(): + # find an existing tag with this name if it exists + existing_tag = next((existing_tag for existing_tag in self.tags if existing_tag.get("name") == name), None) + if existing_tag: + # overwrite tag value for an existing tag + existing_tag[self._X_APIGW_TAG_VALUE] = value + else: + tag = {"name": name, self._X_APIGW_TAG_VALUE: value} + self.tags.append(tag) + @property def openapi(self): """ @@ -369,6 +387,9 @@ def openapi(self): # Make sure any changes to the paths are reflected back in output self._doc["paths"] = self.paths + if self.tags: + self._doc["tags"] = self.tags + if self.security_schemes: self._doc.setdefault("components", {}) self._doc["components"]["securitySchemes"] = self.security_schemes diff --git a/tests/translator/input/error_http_api_tags.yaml b/tests/translator/input/error_http_api_tags.yaml new file mode 100644 index 000000000..2af7574b4 --- /dev/null +++ b/tests/translator/input/error_http_api_tags.yaml @@ -0,0 +1,8 @@ +Resources: + Api: + Type: AWS::Serverless::HttpApi + Properties: + DefinitionBody: + invalid: def_body + Tags: + Tag: value \ No newline at end of file diff --git a/tests/translator/input/error_http_api_tags_def_uri.yaml b/tests/translator/input/error_http_api_tags_def_uri.yaml new file mode 100644 index 000000000..dfe4cb642 --- /dev/null +++ b/tests/translator/input/error_http_api_tags_def_uri.yaml @@ -0,0 +1,7 @@ +Resources: + Api: + Type: AWS::Serverless::HttpApi + Properties: + DefinitionUri: s3://bucket/key + Tags: + Tag: value \ No newline at end of file diff --git a/tests/translator/input/http_api_def_uri.yaml b/tests/translator/input/http_api_def_uri.yaml index 4e6c80316..ae1c901ff 100644 --- a/tests/translator/input/http_api_def_uri.yaml +++ b/tests/translator/input/http_api_def_uri.yaml @@ -3,8 +3,6 @@ Resources: Type: AWS::Serverless::HttpApi Properties: DefinitionUri: s3://bucket/key - Tags: - Tag: value StageName: !Join ["", ["Stage", "Name"]] MyApi2: @@ -14,8 +12,6 @@ Resources: Bucket: bucket Key: key Version: version - Tags: - Tag: value Function: Type: AWS::Serverless::Function diff --git a/tests/translator/input/http_api_existing_openapi_conditions.yaml b/tests/translator/input/http_api_existing_openapi_conditions.yaml index 7a42bd458..7b0b5fec1 100644 --- a/tests/translator/input/http_api_existing_openapi_conditions.yaml +++ b/tests/translator/input/http_api_existing_openapi_conditions.yaml @@ -35,6 +35,9 @@ Resources: MyApi: Type: AWS::Serverless::HttpApi Properties: + Tags: + Tag1: value1 + Tag2: value2 Auth: Authorizers: OAuth2: @@ -106,6 +109,9 @@ Resources: - scope4 responses: {} openapi: 3.0.1 + tags: + - name: Tag1 + description: this tag exists, but doesn't have an amazon extension value components: securitySchemes: oauth2Auth: diff --git a/tests/translator/output/aws-cn/explicit_http_api.json b/tests/translator/output/aws-cn/explicit_http_api.json index d29519d7d..f104793d3 100644 --- a/tests/translator/output/aws-cn/explicit_http_api.json +++ b/tests/translator/output/aws-cn/explicit_http_api.json @@ -74,6 +74,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { @@ -177,6 +183,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/aws-cn/explicit_http_api_minimum.json b/tests/translator/output/aws-cn/explicit_http_api_minimum.json index 992ca5675..01e8424af 100644 --- a/tests/translator/output/aws-cn/explicit_http_api_minimum.json +++ b/tests/translator/output/aws-cn/explicit_http_api_minimum.json @@ -54,6 +54,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": {}, "openapi": "3.0.1" } @@ -99,6 +105,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/aws-cn/http_api_def_uri.json b/tests/translator/output/aws-cn/http_api_def_uri.json index 2bb57f180..044fd771e 100644 --- a/tests/translator/output/aws-cn/http_api_def_uri.json +++ b/tests/translator/output/aws-cn/http_api_def_uri.json @@ -57,13 +57,7 @@ "Name" ] ] - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } }, "FunctionRole": { @@ -103,13 +97,7 @@ "Ref": "MyApi2" }, "AutoDeploy": true, - "StageName": "$default", - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + "StageName": "$default" } }, "MyApi2": { @@ -119,13 +107,7 @@ "Version": "version", "Bucket": "bucket", "Key": "key" - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } }, "FunctionApi2Permission": { @@ -155,13 +137,7 @@ "BodyS3Location": { "Bucket": "bucket", "Key": "key" - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } } } diff --git a/tests/translator/output/aws-cn/http_api_existing_openapi.json b/tests/translator/output/aws-cn/http_api_existing_openapi.json index 0b33f2d0c..76cf73628 100644 --- a/tests/translator/output/aws-cn/http_api_existing_openapi.json +++ b/tests/translator/output/aws-cn/http_api_existing_openapi.json @@ -101,6 +101,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/basic": { "post": { diff --git a/tests/translator/output/aws-cn/http_api_existing_openapi_conditions.json b/tests/translator/output/aws-cn/http_api_existing_openapi_conditions.json index 07f4dd47a..2e839b97a 100644 --- a/tests/translator/output/aws-cn/http_api_existing_openapi_conditions.json +++ b/tests/translator/output/aws-cn/http_api_existing_openapi_conditions.json @@ -104,7 +104,7 @@ "title": { "Ref": "AWS::StackName" } - }, + }, "paths": { "/basic": { "post": { @@ -268,7 +268,22 @@ } } }, - "openapi": "3.0.1" + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + }, + { + "name": "Tag1", + "x-amazon-apigateway-tag-value": "value1", + "description": "this tag exists, but doesn't have an amazon extension value" + }, + { + "name": "Tag2", + "x-amazon-apigateway-tag-value": "value2" + } + ] } } } diff --git a/tests/translator/output/aws-cn/http_api_explicit_stage.json b/tests/translator/output/aws-cn/http_api_explicit_stage.json index afc2fe33d..5d18dc916 100644 --- a/tests/translator/output/aws-cn/http_api_explicit_stage.json +++ b/tests/translator/output/aws-cn/http_api_explicit_stage.json @@ -114,7 +114,13 @@ } } }, - "openapi": "3.0.1" + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] } } } diff --git a/tests/translator/output/aws-cn/implicit_http_api.json b/tests/translator/output/aws-cn/implicit_http_api.json index 6f589a57b..72bb1a03d 100644 --- a/tests/translator/output/aws-cn/implicit_http_api.json +++ b/tests/translator/output/aws-cn/implicit_http_api.json @@ -82,6 +82,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/basic2": { "post": { diff --git a/tests/translator/output/aws-cn/implicit_http_api_auth_and_simple_case.json b/tests/translator/output/aws-cn/implicit_http_api_auth_and_simple_case.json index 34b58b18a..48f64be9c 100644 --- a/tests/translator/output/aws-cn/implicit_http_api_auth_and_simple_case.json +++ b/tests/translator/output/aws-cn/implicit_http_api_auth_and_simple_case.json @@ -95,6 +95,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/scope3": { "post": { diff --git a/tests/translator/output/aws-cn/implicit_http_api_with_many_conditions.json b/tests/translator/output/aws-cn/implicit_http_api_with_many_conditions.json index 577ba4ed0..519cb8083 100644 --- a/tests/translator/output/aws-cn/implicit_http_api_with_many_conditions.json +++ b/tests/translator/output/aws-cn/implicit_http_api_with_many_conditions.json @@ -199,6 +199,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/hello/again": { "Fn::If": [ diff --git a/tests/translator/output/aws-us-gov/explicit_http_api.json b/tests/translator/output/aws-us-gov/explicit_http_api.json index 1da7c4eee..50cb6158a 100644 --- a/tests/translator/output/aws-us-gov/explicit_http_api.json +++ b/tests/translator/output/aws-us-gov/explicit_http_api.json @@ -74,6 +74,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { @@ -177,6 +183,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/aws-us-gov/explicit_http_api_minimum.json b/tests/translator/output/aws-us-gov/explicit_http_api_minimum.json index 7cce5345a..0a35ee12b 100644 --- a/tests/translator/output/aws-us-gov/explicit_http_api_minimum.json +++ b/tests/translator/output/aws-us-gov/explicit_http_api_minimum.json @@ -54,6 +54,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": {}, "openapi": "3.0.1" } @@ -99,6 +105,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/aws-us-gov/http_api_def_uri.json b/tests/translator/output/aws-us-gov/http_api_def_uri.json index c80037029..58af9f809 100644 --- a/tests/translator/output/aws-us-gov/http_api_def_uri.json +++ b/tests/translator/output/aws-us-gov/http_api_def_uri.json @@ -57,13 +57,7 @@ "Name" ] ] - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } }, "FunctionRole": { @@ -103,13 +97,7 @@ "Ref": "MyApi2" }, "AutoDeploy": true, - "StageName": "$default", - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + "StageName": "$default" } }, "MyApi2": { @@ -119,13 +107,7 @@ "Version": "version", "Bucket": "bucket", "Key": "key" - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } }, "FunctionApi2Permission": { @@ -155,13 +137,7 @@ "BodyS3Location": { "Bucket": "bucket", "Key": "key" - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } } } diff --git a/tests/translator/output/aws-us-gov/http_api_existing_openapi.json b/tests/translator/output/aws-us-gov/http_api_existing_openapi.json index 70f4149d0..fa5bb07dc 100644 --- a/tests/translator/output/aws-us-gov/http_api_existing_openapi.json +++ b/tests/translator/output/aws-us-gov/http_api_existing_openapi.json @@ -101,6 +101,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/basic": { "post": { diff --git a/tests/translator/output/aws-us-gov/http_api_existing_openapi_conditions.json b/tests/translator/output/aws-us-gov/http_api_existing_openapi_conditions.json index 77517a796..07e7c5158 100644 --- a/tests/translator/output/aws-us-gov/http_api_existing_openapi_conditions.json +++ b/tests/translator/output/aws-us-gov/http_api_existing_openapi_conditions.json @@ -268,7 +268,22 @@ } } }, - "openapi": "3.0.1" + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + }, + { + "name": "Tag1", + "x-amazon-apigateway-tag-value": "value1", + "description": "this tag exists, but doesn't have an amazon extension value" + }, + { + "name": "Tag2", + "x-amazon-apigateway-tag-value": "value2" + } + ] } } } diff --git a/tests/translator/output/aws-us-gov/http_api_explicit_stage.json b/tests/translator/output/aws-us-gov/http_api_explicit_stage.json index e7969c893..b3fbfa498 100644 --- a/tests/translator/output/aws-us-gov/http_api_explicit_stage.json +++ b/tests/translator/output/aws-us-gov/http_api_explicit_stage.json @@ -98,6 +98,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/aws-us-gov/implicit_http_api.json b/tests/translator/output/aws-us-gov/implicit_http_api.json index 7cf122de5..aaee74da6 100644 --- a/tests/translator/output/aws-us-gov/implicit_http_api.json +++ b/tests/translator/output/aws-us-gov/implicit_http_api.json @@ -82,6 +82,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/basic2": { "post": { diff --git a/tests/translator/output/aws-us-gov/implicit_http_api_auth_and_simple_case.json b/tests/translator/output/aws-us-gov/implicit_http_api_auth_and_simple_case.json index 3d5bce2b8..4e4d6387f 100644 --- a/tests/translator/output/aws-us-gov/implicit_http_api_auth_and_simple_case.json +++ b/tests/translator/output/aws-us-gov/implicit_http_api_auth_and_simple_case.json @@ -95,6 +95,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/scope3": { "post": { diff --git a/tests/translator/output/aws-us-gov/implicit_http_api_with_many_conditions.json b/tests/translator/output/aws-us-gov/implicit_http_api_with_many_conditions.json index b97e6db6b..c5eea99b6 100644 --- a/tests/translator/output/aws-us-gov/implicit_http_api_with_many_conditions.json +++ b/tests/translator/output/aws-us-gov/implicit_http_api_with_many_conditions.json @@ -199,6 +199,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/hello/again": { "Fn::If": [ diff --git a/tests/translator/output/error_http_api_tags.json b/tests/translator/output/error_http_api_tags.json new file mode 100644 index 000000000..97bd009e4 --- /dev/null +++ b/tests/translator/output/error_http_api_tags.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [Api] is invalid. Unable to add `Tags` because 'DefinitionBody' does not contain a valid OpenApi definition." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [Api] is invalid. Unable to add `Tags` because 'DefinitionBody' does not contain a valid OpenApi definition." +} \ No newline at end of file diff --git a/tests/translator/output/error_http_api_tags_def_uri.json b/tests/translator/output/error_http_api_tags_def_uri.json new file mode 100644 index 000000000..5b9eaa569 --- /dev/null +++ b/tests/translator/output/error_http_api_tags_def_uri.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [Api] is invalid. Tags works only with inline OpenApi specified in the 'DefinitionBody' property." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [Api] is invalid. Tags works only with inline OpenApi specified in the 'DefinitionBody' property." +} \ No newline at end of file diff --git a/tests/translator/output/explicit_http_api.json b/tests/translator/output/explicit_http_api.json index 920023cbe..8fc5d920d 100644 --- a/tests/translator/output/explicit_http_api.json +++ b/tests/translator/output/explicit_http_api.json @@ -74,6 +74,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { @@ -177,6 +183,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/explicit_http_api_minimum.json b/tests/translator/output/explicit_http_api_minimum.json index ae96554bc..6695f3404 100644 --- a/tests/translator/output/explicit_http_api_minimum.json +++ b/tests/translator/output/explicit_http_api_minimum.json @@ -54,6 +54,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": {}, "openapi": "3.0.1" } @@ -99,6 +105,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/http_api_def_uri.json b/tests/translator/output/http_api_def_uri.json index ea5493bd8..3f5ad79f6 100644 --- a/tests/translator/output/http_api_def_uri.json +++ b/tests/translator/output/http_api_def_uri.json @@ -57,13 +57,7 @@ "Name" ] ] - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } }, "FunctionRole": { @@ -103,13 +97,7 @@ "Ref": "MyApi2" }, "AutoDeploy": true, - "StageName": "$default", - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + "StageName": "$default" } }, "MyApi2": { @@ -119,13 +107,7 @@ "Version": "version", "Bucket": "bucket", "Key": "key" - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } }, "FunctionApi2Permission": { @@ -155,13 +137,7 @@ "BodyS3Location": { "Bucket": "bucket", "Key": "key" - }, - "Tags": [ - { - "Value": "value", - "Key": "Tag" - } - ] + } } } } diff --git a/tests/translator/output/http_api_existing_openapi.json b/tests/translator/output/http_api_existing_openapi.json index 23eb20b29..5a3306c38 100644 --- a/tests/translator/output/http_api_existing_openapi.json +++ b/tests/translator/output/http_api_existing_openapi.json @@ -101,6 +101,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/basic": { "post": { diff --git a/tests/translator/output/http_api_existing_openapi_conditions.json b/tests/translator/output/http_api_existing_openapi_conditions.json index 1c5adc9c2..9dd6bcace 100644 --- a/tests/translator/output/http_api_existing_openapi_conditions.json +++ b/tests/translator/output/http_api_existing_openapi_conditions.json @@ -104,7 +104,7 @@ "title": { "Ref": "AWS::StackName" } - }, + }, "paths": { "/basic": { "post": { @@ -268,7 +268,22 @@ } } }, - "openapi": "3.0.1" + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + }, + { + "name": "Tag1", + "x-amazon-apigateway-tag-value": "value1", + "description": "this tag exists, but doesn't have an amazon extension value" + }, + { + "name": "Tag2", + "x-amazon-apigateway-tag-value": "value2" + } + ] } } } diff --git a/tests/translator/output/http_api_explicit_stage.json b/tests/translator/output/http_api_explicit_stage.json index 43c58813e..b3f7ad778 100644 --- a/tests/translator/output/http_api_explicit_stage.json +++ b/tests/translator/output/http_api_explicit_stage.json @@ -98,6 +98,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "$default": { "x-amazon-apigateway-any-method": { diff --git a/tests/translator/output/implicit_http_api.json b/tests/translator/output/implicit_http_api.json index c2ba5006e..6218d2117 100644 --- a/tests/translator/output/implicit_http_api.json +++ b/tests/translator/output/implicit_http_api.json @@ -82,6 +82,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/basic2": { "post": { diff --git a/tests/translator/output/implicit_http_api_auth_and_simple_case.json b/tests/translator/output/implicit_http_api_auth_and_simple_case.json index 6bbfafa11..efcd8d3bb 100644 --- a/tests/translator/output/implicit_http_api_auth_and_simple_case.json +++ b/tests/translator/output/implicit_http_api_auth_and_simple_case.json @@ -95,6 +95,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/scope3": { "post": { diff --git a/tests/translator/output/implicit_http_api_with_many_conditions.json b/tests/translator/output/implicit_http_api_with_many_conditions.json index 86e20884e..ac14371a1 100644 --- a/tests/translator/output/implicit_http_api_with_many_conditions.json +++ b/tests/translator/output/implicit_http_api_with_many_conditions.json @@ -199,6 +199,12 @@ "Ref": "AWS::StackName" } }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ], "paths": { "/hello/again": { "Fn::If": [ diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 7a271f507..e9d33c447 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -602,6 +602,8 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw "error_http_api_event_invalid_api", "error_http_api_invalid_auth", "error_http_api_invalid_openapi", + "error_http_api_tags", + "error_http_api_tags_def_uri", "error_implicit_http_api_method", "error_implicit_http_api_path", "error_http_api_event_multiple_same_path", diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index c9ec6b5b2..add5be90f 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -278,6 +278,7 @@ StageName | `string` | The name of the API stage. If a name is not given, SAM wi DefinitionUri | `string` | [S3 Location Object](#s3-location-object) | S3 URI or location to the Swagger document describing the API. If neither `DefinitionUri` nor `DefinitionBody` are specified, SAM will generate a `DefinitionBody` for you based on your template configuration. **Note** Intrinsic functions are not supported in external OpenApi files, instead use DefinitionBody to define OpenApi definition. DefinitionBody | `JSON or YAML Object` | OpenApi specification that describes your API. If neither `DefinitionUri` nor `DefinitionBody` are specified, SAM will generate a `DefinitionBody` for you based on your template configuration. Auth | [HTTP API Auth Object](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-httpapi-httpapiauth.html) | Configure authorization to control access to your API Gateway API. +Tags | Map of `string` to `string` | A map (string to string) that specifies the [tags](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) to be added to this HTTP API. When the stack is created, SAM will automatically add the following tag: `httpapi:createdBy: SAM`. AccessLogSettings | [AccessLogSettings](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-stage-accesslogsettings.html) | Settings for logging access in a stage. ##### Return values