Skip to content

Commit

Permalink
AppSync Api -> Lambda connector (#3145)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssenchenko committed May 9, 2023
1 parent c3c8bde commit 7b5de1a
Show file tree
Hide file tree
Showing 10 changed files with 599 additions and 0 deletions.
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_api_to_lambda",),
("combination/connector_appsync_to_lambda",),
("combination/connector_appsync_to_table",),
("combination/connector_function_to_function",),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{
"LogicalResourceId": "Api",
"ResourceType": "AWS::AppSync::GraphQLApi"
},
{
"LogicalResourceId": "ApiSchema",
"ResourceType": "AWS::AppSync::GraphQLSchema"
},
{
"LogicalResourceId": "NoneDataSource",
"ResourceType": "AWS::AppSync::DataSource"
},
{
"LogicalResourceId": "SayHelloResolver",
"ResourceType": "AWS::AppSync::Resolver"
},
{
"LogicalResourceId": "SayHelloFunc",
"ResourceType": "AWS::AppSync::FunctionConfiguration"
},
{
"LogicalResourceId": "Authorizer",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "AuthorizerRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "TriggerFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "TriggerFunctionRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "GraphQlApiToLambdaConnectorWriteLambdaPermission",
"ResourceType": "AWS::Lambda::Permission"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
Resources:
Api:
Type: AWS::AppSync::GraphQLApi
Properties:
Name: Api
AuthenticationType: AWS_LAMBDA
LambdaAuthorizerConfig:
AuthorizerUri: !GetAtt Authorizer.Arn

ApiSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId: !GetAtt Api.ApiId
Definition: |
type Query {
sayHello: String!
}
schema {
query: Query
}
NoneDataSource:
Type: AWS::AppSync::DataSource
Properties:
Type: NONE
ApiId: !GetAtt Api.ApiId
Name: NoneDataSource

SayHelloResolver:
DependsOn: ApiSchema
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt Api.ApiId
TypeName: Query
FieldName: sayHello
Kind: PIPELINE
PipelineConfig:
Functions:
- !GetAtt SayHelloFunc.FunctionId
Code: |
export function request(ctx) {
return {};
}
export function response(ctx) {
return ctx.prev.result;
}
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0

SayHelloFunc:
Type: AWS::AppSync::FunctionConfiguration
Properties:
ApiId: !GetAtt Api.ApiId
Name: SayHelloFunc
DataSourceName: !GetAtt NoneDataSource.Name
Code: |
export function request(ctx) {
return {};
}
export function response(ctx) {
return "Hello World";
}
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0

GraphQlApiToLambdaConnector:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: Api
Destination:
Id: Authorizer
Permissions:
- Write

Authorizer:
Type: AWS::Serverless::Function
Properties:
InlineCode: |
exports.handler = async (_) => {
return {
isAuthorized: true,
deniedFields: [],
}
}
PackageType: Zip
Runtime: nodejs14.x
Handler: index.handler

TriggerFunction:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
GRAPHQL_URL: !GetAtt Api.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: {
"Authorization": "n'importe quoi",
},
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
3 changes: 3 additions & 0 deletions samtranslator/model/connector/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ def _get_resource_queue_url(logical_id: str, resource_type: str) -> Optional[Dic
def _get_resource_id(logical_id: str, resource_type: str) -> Optional[Dict[str, Any]]:
if resource_type in ["AWS::ApiGateway::RestApi", "AWS::ApiGatewayV2::Api"]:
return ref(logical_id)
if resource_type == "AWS::AppSync::GraphQLApi":
# unfortunately ref(AppSyncApi) == arn
return fnGetAtt(logical_id, "ApiId")
return None


Expand Down
15 changes: 15 additions & 0 deletions samtranslator/model/connector_profiles/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,21 @@
}
}
}
},
"AWS::AppSync::GraphQLApi": {
"AWS::Lambda::Function": {
"Type": "AWS_LAMBDA_PERMISSION",
"Properties": {
"SourcePolicy": false,
"AccessCategories": {
"Write": {
"Action": "lambda:InvokeFunction",
"Principal": "appsync.amazonaws.com",
"SourceArn": "arn:${AWS::Partition}:appsync:${AWS::Region}:${AWS::AccountId}:apis/%{Source.ResourceId}"
}
}
}
}
}
}
}
7 changes: 7 additions & 0 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -18715,6 +18715,13 @@
"Condition": {
"type": "string"
},
"Connectors": {
"additionalProperties": {
"$ref": "#/definitions/EmbeddedConnector"
},
"title": "Connectors",
"type": "object"
},
"DeletionPolicy": {
"enum": [
"Delete",
Expand Down
32 changes: 32 additions & 0 deletions tests/translator/input/connector_appsync_api_to_lambda.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Resources:
Api:
Type: AWS::AppSync::GraphQLApi
Properties:
Name: Api
AuthenticationType: AWS_LAMBDA
LambdaAuthorizerConfig:
AuthorizerUri: !GetAtt Authorizer.Arn

GraphQlApiToLambdaConnector:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: Api
Destination:
Id: Authorizer
Permissions:
- Write

Authorizer:
Type: AWS::Serverless::Function
Properties:
InlineCode: |
exports.handler = async (_) => {
return {
isAuthorized: true,
deniedFields: [],
}
}
PackageType: Zip
Runtime: nodejs14.x
Handler: index.handler
Loading

0 comments on commit 7b5de1a

Please sign in to comment.