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(new-ex): add AppSync triggering Event Bridge Example #103

Merged
merged 7 commits into from
Jan 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions typescript/appsync-graphql-eventbridge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# AppSync GraphQL sending events to Event Bridge

This an example of an AppSync GraphQL API sending events to Event Bridge and trigerring a rule that invokes a Lambda function

![Overview](media.png)

## Build

To build this app, you need run the following commands from the root folder:

```bash
npm install -g aws-cdk
npm install
npm run build
```

This will install the necessary CDK, then this example's dependencies, and then build your TypeScript files and your CloudFormation template.

## Deploy

Run `cdk deploy`. This will deploy / redeploy your Stack to your AWS Account.

After the deployment you will see the API's URL, which represents the url you can then use.

## Synthesize Cloudformation Template

To see the Cloudformation template generated by the CDK, run `cdk synth`, then check the output file in the "cdk.out" directory.

## The Component Structure

This Stack contains:

- a **GraphQL API** with an API Key (Use with caution, each key is only valid for 7 days.)
- a **GraphQL Schema** with a Mutation that sends an event to Event Bridge
- an **IAM Role** that allows AppSync to send events to your Event Bus
- an **AppSync DataSource**, connecting your API to Event Bridge using a HTTP Resolver
- an **AppSync Resolver** for a Mutation `putEvent` that sends a custom event to Event Bridge
- an **Event Rule** that listens for the custom event comming from AppSync and triggers a Lambda function in response to the event
- a **Lambda** function that just prints out the event, you can confirm it's triggered by the Event Bridge's rule by checking the function logs on CloudWatch.
3 changes: 3 additions & 0 deletions typescript/appsync-graphql-eventbridge/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "node index"
}
141 changes: 141 additions & 0 deletions typescript/appsync-graphql-eventbridge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import cdk = require("@aws-cdk/core");
import {
CfnGraphQLApi,
CfnApiKey,
CfnGraphQLSchema,
CfnDataSource,
CfnResolver
} from "@aws-cdk/aws-appsync";
import { Role, ServicePrincipal, PolicyStatement } from "@aws-cdk/aws-iam";
import { Rule } from "@aws-cdk/aws-events";
import lambda = require("@aws-cdk/aws-lambda");
import targets = require("@aws-cdk/aws-events-targets");

export class AppSyncCdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const appSync2EventBridgeGraphQLApi = new CfnGraphQLApi(
this,
"AppSync2EventBridgeApi",
{
name: "AppSync2EventBridge-API",
authenticationType: "API_KEY"
}
);

new CfnApiKey(this, "AppSync2EventBridgeApiKey", {
apiId: appSync2EventBridgeGraphQLApi.attrApiId
});

const apiSchema = new CfnGraphQLSchema(this, "ItemsSchema", {
apiId: appSync2EventBridgeGraphQLApi.attrApiId,
definition: `type Event {
result: String
}

type Mutation {
putEvent(event: String!): Event
}

type Query {
getEvent: Event
}

schema {
query: Query
mutation: Mutation
}`
});

const appsyncEventBridgeRole = new Role(this, "AppSyncEventBridgeRole", {
assumedBy: new ServicePrincipal("appsync.amazonaws.com")
});

appsyncEventBridgeRole.addToPolicy(
new PolicyStatement({
resources: ["*"],
actions: ["events:Put*"]
})
);

const dataSource = new CfnDataSource(this, "ItemsDataSource", {
apiId: appSync2EventBridgeGraphQLApi.attrApiId,
name: "EventBridgeDataSource",
type: "HTTP",
httpConfig: {
authorizationConfig: {
authorizationType: "AWS_IAM",
awsIamConfig: {
signingRegion: this.region,
signingServiceName: "events"
}
},
endpoint: "https://events." + this.region + ".amazonaws.com/"
},
serviceRoleArn: appsyncEventBridgeRole.roleArn
});

const putEventResolver = new CfnResolver(this, "PutEventMutationResolver", {
apiId: appSync2EventBridgeGraphQLApi.attrApiId,
typeName: "Mutation",
fieldName: "putEvent",
dataSourceName: dataSource.name,
requestMappingTemplate: `{
"version": "2018-05-29",
"method": "POST",
"resourcePath": "/",
"params": {
"headers": {
"content-type": "application/x-amz-json-1.1",
"x-amz-target":"AWSEvents.PutEvents"
},
"body": {
"Entries":[
{
"Source":"appsync",
"EventBusName": "default",
"Detail":"{ \\\"event\\\": \\\"$ctx.arguments.event\\\"}",
"DetailType":"Event Bridge via GraphQL"
}
]
}
}
}`,
responseMappingTemplate: `## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
## if the response status code is not 200, then return an error. Else return the body **
#if($ctx.result.statusCode == 200)
## If response is 200, return the body.
{
"result": "$util.parseJson($ctx.result.body)"
}
#else
## If response is not 200, append the response to error block.
$utils.appendError($ctx.result.body, $ctx.result.statusCode)
#end`
});
putEventResolver.addDependsOn(apiSchema);

const echoLambda = new lambda.Function(this, "echoFunction", {
code: lambda.Code.fromInline(
"exports.handler = (event, context) => { console.log(event); context.succeed(event); }"
),
handler: "index.handler",
runtime: lambda.Runtime.NODEJS_10_X
});

const rule = new Rule(this, "AppSyncEventBridgeRle", {
eventPattern: {
source: ["appsync"]
}
});
rule.addTarget(new targets.LambdaFunction(echoLambda));
}
}

const app = new cdk.App();
new AppSyncCdkStack(app, "AppSyncEventBridge");
app.synth();
Binary file added typescript/appsync-graphql-eventbridge/media.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions typescript/appsync-graphql-eventbridge/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "appsync-graphql-eventbridge",
"version": "1.0.0",
"description": "Sends events to Event Bridge via an AppSync GraphQL API",
"private": true,
"bin": {
"cdk": "bin/cdk.js"
},
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"cdk": "cdk"
},
"license": "MIT",
"devDependencies": {
"@types/node": "10.17.0",
"typescript": "~3.7.2",
"ts-node": "^8.1.0",
"aws-cdk": "^1.18.0"
},
"dependencies": {
"@aws-cdk/aws-appsync": "*",
"@aws-cdk/aws-iam": "*",
"@aws-cdk/aws-events": "*",
"@aws-cdk/aws-events-targets": "*",
"@aws-cdk/aws-lambda": "*",
"@aws-cdk/core": "*",
"source-map-support": "^0.5.9"
}
}
22 changes: 22 additions & 0 deletions typescript/appsync-graphql-eventbridge/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target":"ES2018",
"module": "commonjs",
"lib": ["es2016", "es2017.object", "es2017.string"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": false,
"inlineSourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictPropertyInitialization":false
},
"exclude": ["cdk.out"]
}