Skip to content

Commit eb5fc39

Browse files
authored
feat: appsync datasource to lambda connector (#3046)
1 parent c98d3ce commit eb5fc39

File tree

8 files changed

+820
-0
lines changed

8 files changed

+820
-0
lines changed

integration/combination/test_connectors.py

+1
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_lambda",),
2930
("combination/connector_appsync_to_table",),
3031
("combination/connector_function_to_function",),
3132
("combination/connector_restapi_to_function",),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[
2+
{
3+
"LogicalResourceId": "HelloLambda",
4+
"ResourceType": "AWS::Lambda::Function"
5+
},
6+
{
7+
"LogicalResourceId": "HelloLambdaRole",
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": "AppSyncLambdaDataSource",
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": "AppSyncApiLambdaInvocationRole",
36+
"ResourceType": "AWS::IAM::Role"
37+
},
38+
{
39+
"LogicalResourceId": "DataSourceToLambdaConnectorPolicy",
40+
"ResourceType": "AWS::IAM::ManagedPolicy"
41+
},
42+
{
43+
"LogicalResourceId": "AppSyncSayHelloResolver",
44+
"ResourceType": "AWS::AppSync::Resolver"
45+
}
46+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
Resources:
2+
AppSyncApiLambdaInvocationRole:
3+
Type: AWS::IAM::Role
4+
Properties:
5+
AssumeRolePolicyDocument:
6+
Version: '2012-10-17'
7+
Statement:
8+
- Effect: Allow
9+
Principal:
10+
Service: appsync.amazonaws.com
11+
Action:
12+
- sts:AssumeRole
13+
14+
HelloLambda:
15+
Type: AWS::Serverless::Function
16+
Properties:
17+
InlineCode: |
18+
exports.handler = async (_) => {
19+
return "Hello World"
20+
}
21+
Handler: index.handler
22+
Runtime: nodejs14.x
23+
24+
AppSyncApi:
25+
Type: AWS::AppSync::GraphQLApi
26+
Properties:
27+
AuthenticationType: API_KEY
28+
Name: AppSyncApi
29+
30+
ApiSchema:
31+
Type: AWS::AppSync::GraphQLSchema
32+
Properties:
33+
ApiId: !GetAtt AppSyncApi.ApiId
34+
Definition: |
35+
type Query {
36+
sayHello: String!
37+
}
38+
schema {
39+
query: Query
40+
}
41+
42+
AppSyncLambdaDataSource:
43+
Type: AWS::AppSync::DataSource
44+
Properties:
45+
ApiId: !GetAtt AppSyncApi.ApiId
46+
Name: AppSyncLambdaDataSource
47+
Type: AWS_LAMBDA
48+
ServiceRoleArn: !GetAtt AppSyncApiLambdaInvocationRole.Arn
49+
LambdaConfig:
50+
LambdaFunctionArn: !GetAtt HelloLambda.Arn
51+
52+
AppSyncSayHelloResolver:
53+
DependsOn: ApiSchema
54+
Type: AWS::AppSync::Resolver
55+
Properties:
56+
ApiId: !GetAtt AppSyncApi.ApiId
57+
TypeName: Query
58+
FieldName: sayHello
59+
DataSourceName: !GetAtt AppSyncLambdaDataSource.Name
60+
RequestMappingTemplate: |
61+
{
62+
"version" : "2017-02-28",
63+
"operation": "Invoke",
64+
"payload": $util.toJson($context.args)
65+
}
66+
ResponseMappingTemplate: |
67+
$util.toJson($context.result)
68+
69+
DataSourceToLambdaConnector:
70+
Type: AWS::Serverless::Connector
71+
Properties:
72+
Source:
73+
Id: AppSyncLambdaDataSource
74+
Destination:
75+
Id: HelloLambda
76+
Permissions:
77+
- Write
78+
79+
ApiKey:
80+
Type: AWS::AppSync::ApiKey
81+
Properties:
82+
ApiId: !GetAtt AppSyncApi.ApiId
83+
84+
TriggerFunction:
85+
Type: AWS::Serverless::Function
86+
Properties:
87+
Environment:
88+
Variables:
89+
API_KEY: !GetAtt ApiKey.ApiKey
90+
GRAPHQL_URL: !GetAtt AppSyncApi.GraphQLUrl
91+
Runtime: nodejs14.x
92+
Handler: index.handler
93+
InlineCode: |
94+
const https = require("https");
95+
96+
exports.handler = async (_) => {
97+
const queries = {
98+
sayHello: /* GraphQL */ `
99+
query {
100+
sayHello
101+
}
102+
`,
103+
};
104+
105+
106+
const fetch = async (url, options) =>
107+
new Promise((resolve, reject) => {
108+
const req = https.request(url, options, (res) => {
109+
const body = [];
110+
res.on("data", (chunk) => body.push(chunk));
111+
res.on("end", () => {
112+
const resString = Buffer.concat(body).toString();
113+
resolve(resString);
114+
});
115+
});
116+
117+
req.on("error", (err) => {
118+
reject(err);
119+
});
120+
121+
req.on("timeout", () => {
122+
req.destroy();
123+
reject(new Error("Request time out"));
124+
});
125+
126+
req.write(options.body);
127+
req.end();
128+
});
129+
130+
131+
const makeRequest = async (queryName) => {
132+
const options = {
133+
method: "POST",
134+
headers: {
135+
"x-api-key": process.env.API_KEY,
136+
},
137+
body: JSON.stringify({ query: queries[queryName] }),
138+
timeout: 10000, // ms
139+
};
140+
141+
const response = await fetch(process.env.GRAPHQL_URL, options);
142+
const body = JSON.parse(response);
143+
const data = body.data?.[queryName];
144+
145+
if (body.errors !== undefined) {
146+
throw JSON.stringify(body.errors);
147+
}
148+
149+
if (data !== "Hello World") {
150+
throw new Error(`${queryName} error: '${data}' must be 'Hello World'`);
151+
}
152+
153+
return body.data;
154+
};
155+
156+
157+
return await makeRequest("sayHello");
158+
};
159+
160+
161+
Metadata:
162+
SamTransformTest: true

samtranslator/model/connector_profiles/profiles.json

+23
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,29 @@
767767
}
768768
}
769769
}
770+
},
771+
"AWS::Lambda::Function": {
772+
"Type": "AWS_IAM_ROLE_MANAGED_POLICY",
773+
"Properties": {
774+
"SourcePolicy": true,
775+
"AccessCategories": {
776+
"Write": {
777+
"Statement": [
778+
{
779+
"Effect": "Allow",
780+
"Action": [
781+
"lambda:InvokeAsync",
782+
"lambda:InvokeFunction"
783+
],
784+
"Resource": [
785+
"%{Destination.Arn}",
786+
"%{Destination.Arn}:*"
787+
]
788+
}
789+
]
790+
}
791+
}
792+
}
770793
}
771794
}
772795
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
Resources:
2+
AppSyncApiLambdaInvocationRole:
3+
Type: AWS::IAM::Role
4+
Properties:
5+
AssumeRolePolicyDocument:
6+
Version: '2012-10-17'
7+
Statement:
8+
- Effect: Allow
9+
Principal:
10+
Service: appsync.amazonaws.com
11+
Action:
12+
- sts:AssumeRole
13+
14+
HelloLambda:
15+
Type: AWS::Serverless::Function
16+
Properties:
17+
InlineCode: |
18+
exports.handler = async (_) => {
19+
return "Hello World"
20+
}
21+
Handler: index.handler
22+
Runtime: nodejs14.x
23+
24+
AppSyncApi:
25+
Type: AWS::AppSync::GraphQLApi
26+
Properties:
27+
AuthenticationType: API_KEY
28+
Name: AppSyncApi
29+
30+
ApiSchema:
31+
Type: AWS::AppSync::GraphQLSchema
32+
Properties:
33+
ApiId: !GetAtt AppSyncApi.ApiId
34+
Definition: |
35+
type Query {
36+
sayHello: String!
37+
}
38+
schema {
39+
query: Query
40+
}
41+
42+
AppSyncLambdaDataSource:
43+
Type: AWS::AppSync::DataSource
44+
Properties:
45+
ApiId: !GetAtt AppSyncApi.ApiId
46+
Name: AppSyncLambdaDataSource
47+
Type: AWS_LAMBDA
48+
ServiceRoleArn: !GetAtt AppSyncApiLambdaInvocationRole.Arn
49+
LambdaConfig:
50+
LambdaFunctionArn: !GetAtt HelloLambda.Arn
51+
52+
DataSourceToLambdaConnector:
53+
Type: AWS::Serverless::Connector
54+
Properties:
55+
Source:
56+
Id: AppSyncLambdaDataSource
57+
Destination:
58+
Id: HelloLambda
59+
Permissions:
60+
- Write

0 commit comments

Comments
 (0)