Skip to content

Commit fe958f3

Browse files
authored
Merge pull request #2969 from pradhapanr/feat-appsync
chore: rebase feat-appsync
2 parents dc8e028 + cb9be38 commit fe958f3

File tree

178 files changed

+35946
-8630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

178 files changed

+35946
-8630
lines changed

.cfnlintrc.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ ignore_templates:
5757
- tests/translator/output/**/connector_sfn_to_function.json
5858
- tests/translator/output/**/connector_sns_to_function.json
5959
- tests/translator/output/**/connector_table_to_function.json
60+
- tests/translator/output/**/documentdb_with_intrinsics.json # TODO: remove once DocumentDDB is available
6061
- tests/translator/output/**/eventbridgerule_with_dlq.json
6162
- tests/translator/output/**/function_event_conditions.json
6263
- tests/translator/output/**/function_with_alias_and_code_sha256.json
@@ -77,6 +78,8 @@ ignore_templates:
7778
- tests/translator/output/**/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json
7879
- tests/translator/output/**/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json
7980
- tests/translator/output/**/function_with_dlq.json
81+
- tests/translator/output/**/function_with_documentdb_with_kms.json # TODO: remove once DocumentDDB is available
82+
- tests/translator/output/**/function_with_documentdb.json # TODO: remove once DocumentDDB is available
8083
- tests/translator/output/**/function_with_event_dest.json
8184
- tests/translator/output/**/function_with_event_dest_basic.json
8285
- tests/translator/output/**/function_with_event_dest_conditional.json
@@ -117,6 +120,7 @@ ignore_templates:
117120
- tests/translator/output/**/state_machine_with_schedule_dlq_retry_policy.json
118121
- tests/translator/output/**/globals_for_function.json # RuntimeManagementConfig
119122
- tests/translator/output/**/function_with_runtime_config.json # RuntimeManagementConfig
123+
- tests/translator/output/**/managed_policies_minimal.json # Intentionally has non-existent managed policy name
120124
ignore_checks:
121125
- E2531 # Deprecated runtime; not relevant for transform tests
122126
- W2531 # EOL runtime; not relevant for transform tests

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ branch = True
33
omit =
44
# Schema tested by `make check-black`
55
samtranslator/schema/*
6+
samtranslator/internal/schema_source/*
67
[report]
78
exclude_lines =
89
pragma: no cover

DEVELOPMENT_GUIDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Integration tests are covered in detail in the [INTEGRATION_TESTS.md file](INTEG
167167

168168
For new properties of SAM resources, use [`Property`](https://github.com/aws/serverless-application-model/blob/c5830b63857f52e540fec13b29f029458edc539a/samtranslator/model/__init__.py#L36-L45) or [`PassThroughProperty`](https://github.com/aws/serverless-application-model/blob/dd79f535500158baa8e367f081d6a12113497e45/samtranslator/model/__init__.py#L48-L56) instead of [`PropertyType`](https://github.com/aws/serverless-application-model/blob/c39c2807bbf327255de8abed8b8150b18c60f053/samtranslator/model/__init__.py#L13-L33). This avoids [sneaky bugs](https://github.com/aws/serverless-application-model/pull/2495#discussion_r976715242) and ensures valid templates do not cause transform failures.
169169

170-
For new properties of CloudFormation resources, use `GeneratedProperty`. It performs no runtime validation, reducing the risk of valid values causing transform failures.
170+
For new properties of CloudFormation resources, use [`GeneratedProperty`](https://github.com/aws/serverless-application-model/blob/79452f69bc1fcf918b8625c2f9005c74ab874801/samtranslator/model/__init__.py#L74-L82). It performs no runtime validation, reducing the risk of valid values causing transform failures.
171171

172172
Code conventions
173173
----------------

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ recursive-include samtranslator/validator/sam_schema *.json
88
include samtranslator/policy_templates_data/policy_templates.json
99
include samtranslator/policy_templates_data/schema.json
1010
include samtranslator/model/connector_profiles/profiles.json
11+
include samtranslator/internal/data/aws_managed_policies.json
12+
include samtranslator/internal/schema_source/sam-docs.json
1113
include README.md
1214
include THIRD_PARTY_LICENSES
1315

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ black:
2626
black-check:
2727
# Checking latest schema was generated (run `make schema` if this fails)
2828
mkdir -p .tmp
29-
python -m schema_source.schema --sam-schema .tmp/sam.schema.json --cfn-schema schema_source/cloudformation.schema.json --unified-schema .tmp/schema.json
29+
python -m samtranslator.internal.schema_source.schema --sam-schema .tmp/sam.schema.json --cfn-schema schema_source/cloudformation.schema.json --unified-schema .tmp/schema.json
3030
diff -u schema_source/sam.schema.json .tmp/sam.schema.json
3131
diff -u samtranslator/schema/schema.json .tmp/schema.json
3232
black --check setup.py samtranslator tests integration bin schema_source
@@ -60,14 +60,14 @@ fetch-schema-data:
6060

6161
update-schema-data:
6262
# Parse docs
63-
bin/parse_docs.py .tmp/aws-sam-developer-guide/doc_source > schema_source/docs.json
63+
bin/parse_docs.py .tmp/aws-sam-developer-guide/doc_source > samtranslator/internal/schema_source/sam-docs.json
6464
bin/parse_docs.py --cfn .tmp/aws-cloudformation-user-guide/doc_source > schema_source/cloudformation-docs.json
6565

6666
# Add CloudFormation docs to CloudFormation schema
6767
python bin/add_docs_cfn_schema.py --schema .tmp/cloudformation.schema.json --docs schema_source/cloudformation-docs.json > schema_source/cloudformation.schema.json
6868

6969
schema:
70-
python -m schema_source.schema --sam-schema schema_source/sam.schema.json --cfn-schema schema_source/cloudformation.schema.json --unified-schema samtranslator/schema/schema.json
70+
python -m samtranslator.internal.schema_source.schema --sam-schema schema_source/sam.schema.json --cfn-schema schema_source/cloudformation.schema.json --unified-schema samtranslator/schema/schema.json
7171

7272
# Update all schema data and schemas
7373
schema-all: fetch-schema-data update-schema-data schema

bin/parse_docs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ def main() -> None:
8989
if args.cfn and not re.match(r"^\w+::\w+::\w+( \w+)?$", title):
9090
continue
9191
page = title if args.cfn else path.stem
92-
for name, description in parse(text):
92+
for name, raw_description in parse(text):
9393
if page not in props:
9494
props[page] = {}
95-
description = remove_first_line(description) # Remove property name; already in the schema title
95+
description = remove_first_line(raw_description) # Remove property name; already in the schema title
9696
description = fix_markdown_code_link(description)
9797
prefix = (
9898
"https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/"

bin/sam-translate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def transform_template(input_file_path, output_file_path): # type: ignore[no-un
106106
sam_template = yaml_parse(f) # type: ignore[no-untyped-call]
107107

108108
try:
109-
cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client)) # type: ignore[no-untyped-call]
109+
cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client))
110110
cloud_formation_template_prettified = json.dumps(cloud_formation_template, indent=1)
111111

112112
with open(output_file_path, "w") as f:

docs/globals.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Currently, the following resources and properties are being supported:
8181
Auth:
8282
Name:
8383
DefinitionUri:
84+
MergeDefinitions:
8485
CacheClusterEnabled:
8586
CacheClusterSize:
8687
Variables:

integration/combination/test_connectors.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def tearDown(self):
2626

2727
@parameterized.expand(
2828
[
29+
("combination/connector_appsync_to_table",),
2930
("combination/connector_function_to_function",),
3031
("combination/connector_restapi_to_function",),
3132
("combination/connector_httpapi_to_function",),

integration/config/service_names.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@
3535
EVENT_INVOKE_CONFIG = "EventInvokeConfig"
3636
EPHEMERAL_STORAGE = "EphemeralStorage"
3737
API_KEY = "ApiKey"
38+
APP_SYNC = "AppSync"

integration/helpers/resource.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Any, Callable, Dict, Set
66

77
from integration.config.service_names import (
8+
APP_SYNC,
89
DYNAMO_DB,
910
HTTP_API,
1011
LOCATION,
@@ -223,6 +224,7 @@ def _resource_using_s3_events(resource: Dict[str, Any]) -> bool:
223224
_resource_using_s3_events(resource) for resource in template_dict.get("Resources", {}).values()
224225
),
225226
LOCATION: lambda template_dict, cfn_resource_types: "AWS::Location::PlaceIndex" in cfn_resource_types,
227+
APP_SYNC: lambda template_dict, cfn_resource_types: "AWS::AppSync::GraphQLApi" in cfn_resource_types,
226228
}
227229

228230

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"LogicalResourceId": "NotesTable",
4+
"ResourceType": "AWS::DynamoDB::Table"
5+
},
6+
{
7+
"LogicalResourceId": "DynamoDBRole",
8+
"ResourceType": "AWS::IAM::Role"
9+
},
10+
{
11+
"LogicalResourceId": "AppSyncApi",
12+
"ResourceType": "AWS::AppSync::GraphQLApi"
13+
},
14+
{
15+
"LogicalResourceId": "ApiKey",
16+
"ResourceType": "AWS::AppSync::ApiKey"
17+
},
18+
{
19+
"LogicalResourceId": "ApiSchema",
20+
"ResourceType": "AWS::AppSync::GraphQLSchema"
21+
},
22+
{
23+
"LogicalResourceId": "NotesTableDataSource",
24+
"ResourceType": "AWS::AppSync::DataSource"
25+
},
26+
{
27+
"LogicalResourceId": "TriggerFunction",
28+
"ResourceType": "AWS::Lambda::Function"
29+
},
30+
{
31+
"LogicalResourceId": "TriggerFunctionRole",
32+
"ResourceType": "AWS::IAM::Role"
33+
},
34+
{
35+
"LogicalResourceId": "DataSourceToTableConnectorPolicy",
36+
"ResourceType": "AWS::IAM::ManagedPolicy"
37+
}
38+
]
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
Resources:
2+
NotesTable:
3+
Type: AWS::Serverless::SimpleTable
4+
Properties:
5+
PrimaryKey:
6+
Name: NoteId
7+
Type: String
8+
9+
DynamoDBRole:
10+
Type: AWS::IAM::Role
11+
Properties:
12+
AssumeRolePolicyDocument:
13+
Version: '2012-10-17'
14+
Statement:
15+
- Effect: Allow
16+
Action:
17+
- sts:AssumeRole
18+
Principal:
19+
Service:
20+
- appsync.amazonaws.com
21+
22+
AppSyncApi:
23+
Type: AWS::AppSync::GraphQLApi
24+
Properties:
25+
AuthenticationType: API_KEY
26+
Name: AppSyncApi
27+
28+
ApiKey:
29+
Type: AWS::AppSync::ApiKey
30+
Properties:
31+
ApiId: !GetAtt AppSyncApi.ApiId
32+
33+
ApiSchema:
34+
Type: AWS::AppSync::GraphQLSchema
35+
Properties:
36+
ApiId: !GetAtt AppSyncApi.ApiId
37+
Definition: |
38+
type Note {
39+
NoteId: ID!
40+
title: String
41+
content: String
42+
}
43+
type Query {
44+
getNote(NoteId: ID!): Note
45+
}
46+
type Mutation {
47+
saveNote(NoteId: ID!, title: String!, content: String!): Note!
48+
}
49+
type Schema {
50+
query: Query
51+
mutation: Mutation
52+
}
53+
54+
NotesTableDataSource:
55+
Type: AWS::AppSync::DataSource
56+
Properties:
57+
ApiId: !GetAtt AppSyncApi.ApiId
58+
Name: NotesTableDataSource
59+
Type: AMAZON_DYNAMODB
60+
ServiceRoleArn: !GetAtt DynamoDBRole.Arn
61+
DynamoDBConfig:
62+
TableName: !Ref NotesTable
63+
AwsRegion: !Sub ${AWS::Region}
64+
65+
DataSourceToTableConnector:
66+
Type: AWS::Serverless::Connector
67+
Properties:
68+
Source:
69+
Id: NotesTableDataSource
70+
Destination:
71+
Id: NotesTable
72+
Permissions:
73+
- Read
74+
- Write
75+
76+
TriggerFunction:
77+
Type: AWS::Serverless::Function
78+
Properties:
79+
Environment:
80+
Variables:
81+
API_KEY: !GetAtt ApiKey.ApiKey
82+
GRAPHQL_URL: !GetAtt AppSyncApi.GraphQLUrl
83+
Runtime: nodejs14.x
84+
Handler: index.handler
85+
InlineCode: |
86+
const https = require("https");
87+
88+
exports.handler = async (_) => {
89+
const queries = {
90+
getNote: /* GraphQL */ `
91+
query {
92+
getNote(NoteId: "1") {
93+
title
94+
content
95+
}
96+
}
97+
`,
98+
saveNote: /* GraphQL */ `
99+
mutation {
100+
saveNote(content: "some note", NoteId: "1", title: "1st note") {
101+
title
102+
content
103+
}
104+
}
105+
`,
106+
};
107+
108+
const fetch = async (url, options) =>
109+
new Promise((resolve, reject) => {
110+
const req = https.request(url, options, (res) => {
111+
const body = [];
112+
res.on("data", (chunk) => body.push(chunk));
113+
res.on("end", () => {
114+
const resString = Buffer.concat(body).toString();
115+
resolve(resString);
116+
});
117+
});
118+
119+
req.on("error", (err) => {
120+
reject(err);
121+
});
122+
123+
req.on("timeout", () => {
124+
req.destroy();
125+
reject(new Error("Request time out"));
126+
});
127+
128+
req.write(options.body);
129+
req.end();
130+
});
131+
132+
const makeRequest = async (queryName) => {
133+
const options = {
134+
method: "POST",
135+
headers: {
136+
"x-api-key": process.env.API_KEY,
137+
},
138+
body: JSON.stringify({ query: queries[queryName] }),
139+
timeout: 10000, // ms
140+
};
141+
142+
let statusCode;
143+
let body;
144+
let response;
145+
146+
try {
147+
response = await fetch(process.env.GRAPHQL_URL, options);
148+
body = JSON.parse(response);
149+
const data = body.data?.[queryName];
150+
const hasNoErrors = body.errors === undefined;
151+
const allFieldsAreSet =
152+
data?.title === "1st note" && data?.content === "some note";
153+
statusCode = hasNoErrors && allFieldsAreSet ? 200 : 400;
154+
if (hasNoErrors) {
155+
body = body.data;
156+
} else {
157+
body = {
158+
[queryName]: {
159+
errors: body.errors,
160+
},
161+
};
162+
}
163+
} catch (error) {
164+
statusCode = 400;
165+
body = {
166+
[queryName]: {
167+
errors: [
168+
{
169+
status: response.status,
170+
message: error.message,
171+
stack: error.stack,
172+
},
173+
],
174+
},
175+
};
176+
}
177+
return {
178+
statusCode,
179+
body,
180+
};
181+
};
182+
183+
let response = await makeRequest("saveNote");
184+
if (response.statusCode !== 200) {
185+
return {
186+
StatusCode: response.statusCode,
187+
Body: response.body,
188+
};
189+
}
190+
let body = response.body;
191+
192+
response = await makeRequest("getNote");
193+
body = { ...body, ...response.body };
194+
195+
return {
196+
StatusCode: response.statusCode,
197+
Body: body,
198+
};
199+
};
200+
201+
202+
Metadata:
203+
SamTransformTest: true

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.black]
22
line-length = 120
3-
target_version = ['py37', 'py36', 'py38']
3+
target_version = ['py37', 'py38', 'py39', 'py310']
44
exclude = '''
55
66
(

requirements/base.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
boto3>=1.19.5,==1.*
22
jsonschema<5,>=3.2 # TODO: evaluate risk of removing jsonschema 3.x support
3-
typing_extensions~=4.4.0 # 3.7 doesn't have Literal
3+
typing_extensions>=4.4,<5 # 3.7 doesn't have Literal
4+
5+
# resource validation & schema generation
6+
pydantic~=1.8

0 commit comments

Comments
 (0)