Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: appsync datasource to lambda connector #3046

Merged
merged 14 commits into from
Mar 22, 2023
1 change: 1 addition & 0 deletions integration/combination/test_connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def tearDown(self):

@parameterized.expand(
[
("combination/connector_appsync_to_lambda",),
("combination/connector_appsync_to_table",),
("combination/connector_function_to_function",),
("combination/connector_restapi_to_function",),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[
{
"LogicalResourceId": "HelloLambda",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "HelloLambdaRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "AppSyncApi",
"ResourceType": "AWS::AppSync::GraphQLApi"
},
{
"LogicalResourceId": "ApiKey",
"ResourceType": "AWS::AppSync::ApiKey"
},
{
"LogicalResourceId": "ApiSchema",
"ResourceType": "AWS::AppSync::GraphQLSchema"
},
{
"LogicalResourceId": "AppSyncLambdaDataSource",
"ResourceType": "AWS::AppSync::DataSource"
},
{
"LogicalResourceId": "TriggerFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "TriggerFunctionRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "AppSyncApiLambdaInvocationRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "DataSourceToLambdaConnectorPolicy",
"ResourceType": "AWS::IAM::ManagedPolicy"
},
{
"LogicalResourceId": "AppSyncSayHelloResolver",
"ResourceType": "AWS::AppSync::Resolver"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
Resources:
AppSyncApiLambdaInvocationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: appsync.amazonaws.com
Action:
- sts:AssumeRole

HelloLambda:
Type: AWS::Serverless::Function
Properties:
InlineCode: |
exports.handler = async (_) => {
return "Hello World"
}
Handler: index.handler
Runtime: nodejs14.x

AppSyncApi:
Type: AWS::AppSync::GraphQLApi
Properties:
AuthenticationType: API_KEY
Name: AppSyncApi

ApiSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
Definition: |
type Query {
sayHello: String!
}
schema {
query: Query
}

AppSyncLambdaDataSource:
Type: AWS::AppSync::DataSource
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
Name: AppSyncLambdaDataSource
Type: AWS_LAMBDA
ServiceRoleArn: !GetAtt AppSyncApiLambdaInvocationRole.Arn
LambdaConfig:
LambdaFunctionArn: !GetAtt HelloLambda.Arn

AppSyncSayHelloResolver:
DependsOn: ApiSchema
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Query
FieldName: sayHello
DataSourceName: !GetAtt AppSyncLambdaDataSource.Name
RequestMappingTemplate: |
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson($context.args)
}
ResponseMappingTemplate: |
$util.toJson($context.result)

DataSourceToLambdaConnector:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: AppSyncLambdaDataSource
Destination:
Id: HelloLambda
Permissions:
- Write

ApiKey:
Type: AWS::AppSync::ApiKey
Properties:
ApiId: !GetAtt AppSyncApi.ApiId

TriggerFunction:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
API_KEY: !GetAtt ApiKey.ApiKey
GRAPHQL_URL: !GetAtt AppSyncApi.GraphQLUrl
Runtime: nodejs14.x
Handler: index.handler
InlineCode: |
const https = require("https");

exports.handler = async (_) => {
const queries = {
sayHello: /* GraphQL */ `
query {
sayHello
}
`,
};


const fetch = async (url, options) =>
new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
const body = [];
res.on("data", (chunk) => body.push(chunk));
res.on("end", () => {
const resString = Buffer.concat(body).toString();
resolve(resString);
});
});

req.on("error", (err) => {
reject(err);
});

req.on("timeout", () => {
req.destroy();
reject(new Error("Request time out"));
});

req.write(options.body);
req.end();
});


const makeRequest = async (queryName) => {
const options = {
method: "POST",
headers: {
"x-api-key": process.env.API_KEY,
},
body: JSON.stringify({ query: queries[queryName] }),
timeout: 10000, // ms
};

const response = await fetch(process.env.GRAPHQL_URL, options);
const body = JSON.parse(response);
const data = body.data?.[queryName];

if (body.errors !== undefined) {
throw JSON.stringify(body.errors);
}

if (data !== "Hello World") {
throw new Error(`${queryName} error: '${data}' must be 'Hello World'`);
}

return body.data;
};


return await makeRequest("sayHello");
};


Metadata:
SamTransformTest: true
23 changes: 23 additions & 0 deletions samtranslator/model/connector_profiles/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,29 @@
}
}
}
},
"AWS::Lambda::Function": {
"Type": "AWS_IAM_ROLE_MANAGED_POLICY",
"Properties": {
"SourcePolicy": true,
"AccessCategories": {
"Write": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeAsync",
"lambda:InvokeFunction"
],
"Resource": [
"%{Destination.Arn}",
"%{Destination.Arn}:*"
]
}
]
}
}
}
}
}
}
Expand Down
60 changes: 60 additions & 0 deletions tests/translator/input/connector_appsync_to_lambda.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Resources:
AppSyncApiLambdaInvocationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: appsync.amazonaws.com
Action:
- sts:AssumeRole

HelloLambda:
Type: AWS::Serverless::Function
Properties:
InlineCode: |
exports.handler = async (_) => {
return "Hello World"
}
Handler: index.handler
Runtime: nodejs14.x

AppSyncApi:
Type: AWS::AppSync::GraphQLApi
Properties:
AuthenticationType: API_KEY
Name: AppSyncApi

ApiSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
Definition: |
type Query {
sayHello: String!
}
schema {
query: Query
}

AppSyncLambdaDataSource:
Type: AWS::AppSync::DataSource
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
Name: AppSyncLambdaDataSource
Type: AWS_LAMBDA
ServiceRoleArn: !GetAtt AppSyncApiLambdaInvocationRole.Arn
LambdaConfig:
LambdaFunctionArn: !GetAtt HelloLambda.Arn

DataSourceToLambdaConnector:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: AppSyncLambdaDataSource
Destination:
Id: HelloLambda
Permissions:
- Write
Loading