Skip to content

Commit f7f5412

Browse files
pradrxGavinZZ
authored andcommitted
feat: make Logging property for GraphQLApi (aws#2971)
1 parent 45ecbe6 commit f7f5412

39 files changed

+1250
-29
lines changed

samtranslator/internal/model/appsync.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,27 @@ class DynamoDBConfigType(TypedDict, total=False):
2525
DeltaSyncConfig: DeltaSyncConfigType
2626

2727

28+
class LogConfigType(TypedDict, total=False):
29+
CloudWatchLogsRoleArn: Intrinsicable[str]
30+
ExcludeVerboseContent: bool
31+
FieldLogLevel: str
32+
33+
2834
class GraphQLApi(Resource):
2935
resource_type = "AWS::AppSync::GraphQLApi"
3036
property_types = {
3137
"Name": GeneratedProperty(),
3238
"Tags": GeneratedProperty(),
3339
"XrayEnabled": GeneratedProperty(),
3440
"AuthenticationType": GeneratedProperty(),
41+
"LogConfig": GeneratedProperty(),
3542
}
3643

3744
Name: str
3845
AuthenticationType: str
3946
Tags: Optional[List[Dict[str, Any]]]
4047
XrayEnabled: Optional[bool]
48+
LogConfig: Optional[LogConfigType]
4149

4250
runtime_attrs = {"api_id": lambda self: fnGetAtt(self.logical_id, "ApiId")}
4351

samtranslator/internal/schema_source/aws_serverless_graphqlapi.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
from typing import Optional
1+
from typing import Optional, Union
22

33
from typing_extensions import Literal
4-
5-
<<<<<<<< HEAD:samtranslator/schema/aws_serverless_graphqlapi.py
6-
from samtranslator.schema.common import BaseModel, get_prop, DictStrAny
7-
========
8-
from samtranslator.internal.schema_source.common import BaseModel, DictStrAny, get_prop
9-
>>>>>>>> cb9be389 (move schema files over):samtranslator/internal/schema_source/aws_serverless_graphqlapi.py
4+
from samtranslator.internal.schema_source.common import BaseModel, DictStrAny, PassThroughProp, get_prop
105

116
properties = get_prop("sam-resource-graphqlapi")
127

@@ -16,13 +11,20 @@ class Auth(BaseModel):
1611
Type: str
1712

1813

14+
class Logging(BaseModel):
15+
CloudWatchLogsRoleArn: Optional[str]
16+
ExcludeVerboseContent: Optional[PassThroughProp]
17+
FieldLogLevel: Optional[str]
18+
19+
1920
class Properties(BaseModel):
2021
Auth: Auth
2122
Tags: Optional[DictStrAny]
2223
Name: Optional[str]
2324
XrayEnabled: Optional[bool]
2425
SchemaInline: Optional[str]
2526
SchemaUri: Optional[str]
27+
Logging: Optional[Union[Logging, bool]]
2628

2729

2830
class Resource(BaseModel):

samtranslator/model/sam_resources.py

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
DynamoDBConfigType,
1919
GraphQLApi,
2020
GraphQLSchema,
21+
LogConfigType,
2122
)
2223
from samtranslator.intrinsics.resolver import IntrinsicsResolver
2324
from samtranslator.metrics.method_decorator import cw_timer
@@ -2146,6 +2147,7 @@ class SamGraphQLApi(SamResourceMacro):
21462147
"Auth": Property(True, IS_DICT),
21472148
"SchemaInline": Property(False, IS_STR),
21482149
"SchemaUri": Property(False, IS_STR),
2150+
"Logging": Property(False, one_of(IS_DICT, is_type(bool))),
21492151
}
21502152

21512153
Auth: Dict[str, Any]
@@ -2154,16 +2156,21 @@ class SamGraphQLApi(SamResourceMacro):
21542156
Name: Optional[str]
21552157
SchemaInline: Optional[str]
21562158
SchemaUri: Optional[str]
2159+
Logging: Optional[Union[Dict[str, Any], bool]]
21572160

21582161
@cw_timer
21592162
def to_cloudformation(self, **kwargs: Any) -> List[Resource]:
2160-
appsync_api = self._construct_appsync_api()
2163+
appsync_api, cloudwatch_role = self._construct_appsync_api_resources()
21612164
appsync_schema = self._construct_appsync_schema(appsync_api.get_runtime_attr("api_id"))
2165+
21622166
resources: List[Resource] = [appsync_api, appsync_schema]
21632167

2168+
if cloudwatch_role:
2169+
resources.append(cloudwatch_role)
2170+
21642171
return resources
21652172

2166-
def _construct_appsync_api(self) -> GraphQLApi:
2173+
def _construct_appsync_api_resources(self) -> Tuple[GraphQLApi, Optional[IAMRole]]:
21672174
api = GraphQLApi(logical_id=self.logical_id, depends_on=self.depends_on, attributes=self.resource_attributes)
21682175

21692176
api.AuthenticationType = sam_expect(self.Auth.get("Type"), self.logical_id, "Auth.Type").to_be_a_string()
@@ -2173,7 +2180,63 @@ def _construct_appsync_api(self) -> GraphQLApi:
21732180
if self.Tags:
21742181
api.Tags = get_tag_list(self.Tags)
21752182

2176-
return api
2183+
# Logging has 3 possible types: dict, bool, and None.
2184+
# GraphQLApi will not include logging if and only if the user explicity sets Logging as false boolean.
2185+
# It will for every other value (including true boolean which is essentially same as None).
2186+
if isinstance(self.Logging, bool) and self.Logging is False:
2187+
return api, None
2188+
2189+
api.LogConfig, cloudwatch_role = self._parse_logging_properties()
2190+
2191+
return api, cloudwatch_role
2192+
2193+
def _create_logging_default(self) -> Tuple[LogConfigType, IAMRole]:
2194+
"""
2195+
Create a default logging configuration.
2196+
2197+
This function is used when "Logging" property is a False boolean or NoneType.
2198+
"""
2199+
log_config: LogConfigType = {}
2200+
log_config["FieldLogLevel"] = "ALL"
2201+
cloudwatch_role = self._construct_cloudwatch_role()
2202+
log_config["CloudWatchLogsRoleArn"] = cloudwatch_role.get_runtime_attr("arn")
2203+
2204+
return log_config, cloudwatch_role
2205+
2206+
def _parse_logging_properties(self) -> Tuple[LogConfigType, Optional[IAMRole]]:
2207+
"""Parse logging properties from SAM template, and use defaults if required keys dont exist."""
2208+
if not isinstance(self.Logging, dict):
2209+
return self._create_logging_default()
2210+
2211+
log_config: LogConfigType = {}
2212+
2213+
if "ExcludeVerboseContent" in self.Logging:
2214+
log_config["ExcludeVerboseContent"] = self.Logging["ExcludeVerboseContent"]
2215+
2216+
log_config["FieldLogLevel"] = self.Logging.get("FieldLogLevel", "ALL")
2217+
log_config["CloudWatchLogsRoleArn"] = self.Logging.get("CloudWatchLogsRoleArn", None)
2218+
2219+
if log_config["CloudWatchLogsRoleArn"]:
2220+
return log_config, None
2221+
2222+
cloudwatch_role = self._construct_cloudwatch_role()
2223+
log_config["CloudWatchLogsRoleArn"] = cloudwatch_role.get_runtime_attr("arn")
2224+
2225+
return log_config, cloudwatch_role
2226+
2227+
def _construct_cloudwatch_role(self) -> IAMRole:
2228+
role = IAMRole(
2229+
logical_id=f"{self.logical_id}CloudWatchRole",
2230+
depends_on=self.depends_on,
2231+
attributes=self.resource_attributes,
2232+
)
2233+
role.AssumeRolePolicyDocument = IAMRolePolicies.construct_assume_role_policy_for_service_principal(
2234+
"appsync.amazonaws.com"
2235+
)
2236+
role.ManagedPolicyArns = [
2237+
{"Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs"}
2238+
]
2239+
return role
21772240

21782241
def _construct_appsync_schema(self, api_id: Intrinsicable[str]) -> GraphQLSchema:
21792242
schema = GraphQLSchema(

samtranslator/schema/schema.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195437,6 +195437,24 @@
195437195437
"title": "Location",
195438195438
"type": "object"
195439195439
},
195440+
"Logging": {
195441+
"additionalProperties": false,
195442+
"properties": {
195443+
"CloudWatchLogsRoleArn": {
195444+
"title": "Cloudwatchlogsrolearn",
195445+
"type": "string"
195446+
},
195447+
"ExcludeVerboseContent": {
195448+
"$ref": "#/definitions/PassThroughProp"
195449+
},
195450+
"FieldLogLevel": {
195451+
"title": "Fieldloglevel",
195452+
"type": "string"
195453+
}
195454+
},
195455+
"title": "Logging",
195456+
"type": "object"
195457+
},
195440195458
"MQEvent": {
195441195459
"additionalProperties": false,
195442195460
"properties": {

schema_source/sam.schema.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,24 @@
19311931
"title": "Location",
19321932
"type": "object"
19331933
},
1934+
"Logging": {
1935+
"additionalProperties": false,
1936+
"properties": {
1937+
"CloudWatchLogsRoleArn": {
1938+
"title": "Cloudwatchlogsrolearn",
1939+
"type": "string"
1940+
},
1941+
"ExcludeVerboseContent": {
1942+
"$ref": "#/definitions/PassThroughProp"
1943+
},
1944+
"FieldLogLevel": {
1945+
"title": "Fieldloglevel",
1946+
"type": "string"
1947+
}
1948+
},
1949+
"title": "Logging",
1950+
"type": "object"
1951+
},
19341952
"MQEvent": {
19351953
"additionalProperties": false,
19361954
"properties": {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Transform: AWS::Serverless-2016-10-31
2+
Resources:
3+
NoAuthAPI:
4+
Type: AWS::Serverless::GraphQLApi
5+
Properties:
6+
XrayEnabled: true
7+
Tags:
8+
Key1: Value1
9+
Key2: Value2
10+
11+
NoSchemaPropertiesAPI:
12+
Type: AWS::Serverless::GraphQLApi
13+
Properties:
14+
XrayEnabled: true
15+
Auth:
16+
Type: AWS_IAM
17+
Tags:
18+
key1: value1
19+
key2: value2
20+
21+
BothSchemaInlineAndUriAPI:
22+
Type: AWS::Serverless::GraphQLApi
23+
Properties:
24+
SchemaInline: |
25+
type Mutation {
26+
addTodo(id: ID!, name: String, description: String, priority: Int): Todo
27+
}
28+
SchemaUri: https://bucket-name.s3.region-code.amazonaws.com/key-name
29+
XrayEnabled: true
30+
Auth:
31+
Type: AWS_IAM
32+
Tags:
33+
key1: value1
34+
key2: value2

tests/translator/input/error_graphqlapi_no_auth.yaml

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/translator/input/error_graphqlapi_both_schema_inline_and_uri.yaml renamed to tests/translator/input/graphqlapi_default_logging.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Resources:
77
type Mutation {
88
addTodo(id: ID!, name: String, description: String, priority: Int): Todo
99
}
10-
SchemaUri: https://bucket-name.s3.region-code.amazonaws.com/key-name
1110
XrayEnabled: true
1211
Auth:
1312
Type: AWS_IAM
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Transform: AWS::Serverless-2016-10-31
2+
Resources:
3+
SuperCoolAPI:
4+
Type: AWS::Serverless::GraphQLApi
5+
Properties:
6+
SchemaInline: |
7+
type Mutation {
8+
addTodo(id: ID!, name: String, description: String, priority: Int): Todo
9+
}
10+
XrayEnabled: true
11+
Auth:
12+
Type: AWS_IAM
13+
Tags:
14+
key1: value1
15+
key2: value2
16+
Logging:
17+
CloudWatchLogsRoleArn: some-arn
18+
FieldLogLevel: ERROR
19+
ExcludeVerboseContent: true
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# This is not an expected user use case (they should just not define Logging if they want defaults), but still testing.
2+
Transform: AWS::Serverless-2016-10-31
3+
Resources:
4+
SuperCoolAPI:
5+
Type: AWS::Serverless::GraphQLApi
6+
Properties:
7+
SchemaInline: |
8+
type Mutation {
9+
addTodo(id: ID!, name: String, description: String, priority: Int): Todo
10+
}
11+
XrayEnabled: true
12+
Auth:
13+
Type: AWS_IAM
14+
Tags:
15+
key1: value1
16+
key2: value2
17+
Logging: true

0 commit comments

Comments
 (0)