add golang examples for aws lambda (#602)

This commit is contained in:
Ishaan Malhi 2018-10-15 09:53:15 +05:30 committed by Shahidh K Muhammed
parent 963d86464f
commit e2817b1b1b
6 changed files with 414 additions and 0 deletions

View File

@ -0,0 +1,37 @@
# Go Echo Example for AWS Lambda
### Setup tables
1. Create table:
```
notes:
id: Integer (auto-increment)
note: Text
Primary key: id
```
### Setup AWS Lambda
Create a Lambda function in AWS. This will be our webhook.
1. Sign in to the AWS Management Console and open the AWS Lambda console.
2. Choose "Create a function" under the **Get Started** section.
3. Select "Author from scratch".
4. Specify the **Name** for your lambda.
5. Select go 1.x as the **Runtime**.
6. In **Role** choose **Create new role from template(s)**
7. In **Role name**, enter a name for your role, leave the **Policy Templates** field blank.
8. Build and upload the code to AWS Lambda:
i. Run the bash script provided
`bash build.sh`
ii. Upload *echo.zip* in the lambda console.
10. Click the **save** button for the changes to take effect.
11. Create an API with a lambda proxy integration as specified in the [AWS Documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-build).
### Add the trigger in Hasura GraphQL
1. In events tab, add a trigger
2. Select all insert, update, delete operations for the trigger.
3. Paste the API endpoint of your AWS lambda as the webhook.

View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
echo "Fetching dependencies"
go get .
echo "Building binary"
env GOOS=linux GOARCH=amd64 go build
echo "Binary build complete. Zipping output."
zip -j ./mutation.zip mutation
echo "Zip complete. You can now upload the zip file"

View File

@ -0,0 +1,83 @@
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type TableStruct struct {
Name string `json:"name"`
}
type EventData struct {
Old map[string]interface{} `json:"old"`
New map[string]interface{} `json"new"`
}
type EventStruct struct {
Operation string `json:"op"`
Data EventData `json:"data"`
}
type HasuraEvent struct {
Table *TableStruct `json:"table"`
Event *EventStruct `json:"event"`
Op string `json:"op"`
}
// Handler for the Lambda function to echo back the data
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("Processing Lambda request %+v\n", request.Body)
// parse json body
body := &HasuraEvent{
Table: &TableStruct{},
Event: &EventStruct{},
}
err := json.Unmarshal([]byte(request.Body), body)
if err != nil {
message := map[string]string{
"message": "Unable to parse Hasura Event",
}
responseBody, _ := json.Marshal(message)
return events.APIGatewayProxyResponse{
Body: string(responseBody),
StatusCode: 400,
}, nil
}
var message = "cannot process request"
var data = body.Event.Data
if body.Table.Name == "notes" {
switch body.Event.Operation {
case "INSERT":
message = fmt.Sprintf("New note %v inserted, with data: %v", data.New["id"], data.New["note"])
case "UPDATE":
message = fmt.Sprintf("New note %v updated, with data: %v", data.New["id"], data.New["note"])
case "DELETE":
message = fmt.Sprintf("New note %v delete, with data: %v", data.Old["id"], data.Old["note"])
}
}
resposeBody, _ := json.Marshal(message)
return events.APIGatewayProxyResponse{
Body: string(resposeBody),
StatusCode: 200,
}, nil
}
func main() {
lambda.Start(Handler)
}

View File

@ -0,0 +1,107 @@
# Golang Mutation Example for AWS Lambda
This example trigger updates (executes a mutation) when an event occurs on
another table, written in Golang.
### Create tables
```
Table name: note
Columns:
id Integer auto-increment
note Text
Primary key: id
```
```
Table name: note_revision
Columns:
id Integer auto-increment
note Text
note_id Integer
update_at Timestamp, default: now()
Primary key: id
```
### Setup AWS Lambda
Create a Lambda function in AWS. This will be our webhook.
1. Sign in to the AWS Management Console and open the AWS Lambda console.
2. Choose "Create a function" under the **Get Started** section.
3. Select "Author from scratch".
4. Specify the **Name** for your lambda.
5. Select go 1.x as the **Runtime**.
6. In **Role** choose **Create new role from template(s)**
7. In **Role name**, enter a name for your role, leave the **Policy Templates** field blank.
8. Build and upload the code to AWS Lambda:
i. Run the bash script provided
`bash build.sh`
ii. Upload *mutation.zip* in the lambda console.
9. In the Designer pane, add an environment variable `HGE_ENDPOINT` which is the Hasura GraphQL Engine endpoint.
10. Click the **save** button for the changes to take effect.
11. Create an API with a lambda proxy integration as specified in the [AWS Documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-build).
### Add the trigger in Hasura GraphQL
1. In events tab, add a trigger
2. Select all insert, update, delete operations for the trigger.
3. Paste the API endpoint of your AWS lambda as the webhook.
### Test the trigger
Goto `Data` tab on Hasura console, browse to `note` table and to the Browse rows
tab. Edit an existing note and check if the `note_revision` entry has been
created. Also, checkout the trigger request and response.
Trigger payload (request):
```json
{
"event": {
"op": "UPDATE",
"data": {
"old": {
"note": "note1",
"id": 1
},
"new": {
"note": "note1 updated",
"id": 1
}
}
},
"created_at": "2018-10-02T06:38:22.67311Z",
"id": "f57a1c79-72ba-4c19-8791-37d1b9616bcf",
"trigger": {
"name": "note_revision_trigger",
"id": "5d85cbd1-c134-45ce-810c-7ecd3b4fc1ee"
},
"table": {
"schema": "public",
"name": "note"
}
}
```
Webhook response:
```json
{
"data": {
"insert_note_revision": {
"affected_rows": 1,
"returning": [
{
"id": 2
}
]
}
}
}
```

View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
echo "Fetching dependencies"
go get .
echo "Building binary"
env GOOS=linux GOARCH=amd64 go build
echo "Binary build complete. Zipping output."
zip -j ./mutation.zip mutation
echo "Zip complete. You can now upload the zip file"

View File

@ -0,0 +1,155 @@
package main
import (
"bytes"
"encoding/json"
"log"
"net/http"
"os"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type HasuraEvent struct {
ID string `json:"id"`
Event `json:"event"`
Table `json:"table"`
Trigger `json:"trigger"`
}
type Event struct {
Op string `json:"op"`
Data `json:"data"`
}
type Data struct {
Old map[string]interface{} `json:"old "`
New map[string]interface{} `json:"new"`
}
type Table struct {
Name string `json:"name"`
Schema string `json:"schema"`
}
type Trigger struct {
ID string `json:"id"`
Name string `json:"name"`
}
const MUTATION_UPDATE_NOTE_REVISION = `
mutation updateNoteRevision ($object: note_revision_insert_input!) {
insert_note_revision (objects: [$object]) {
affected_rows
returning {
id
}
}
}
`
func constructErrorResponse(responsePayload map[string]string) (events.APIGatewayProxyResponse, error) {
var responseBody []byte
responseBody, err := json.Marshal(responsePayload)
var statusCode int = 200
if err != nil {
responseBody, _ = json.Marshal(map[string]string{
"message": "Internal error ocurred while constructing error response",
})
statusCode = 500
}
return events.APIGatewayProxyResponse{
Body: string(responseBody),
StatusCode: statusCode,
}, nil
}
var HGE_ENDPOINT = os.Getenv("HGE_ENDPOINT")
// Handler for the Lambda function to echo back the data
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("Processing Lambda request %+v\n", request.Body)
if len(HGE_ENDPOINT) == 0 {
return constructErrorResponse(map[string]string{
"message": "HGE Endpoint not defined in environment variable",
})
}
// parse json body
var body HasuraEvent
err := json.Unmarshal([]byte(request.Body), body)
event := body.Event
if err != nil {
return constructErrorResponse(map[string]string{
"message": "Unable to parse Hasura Event",
})
}
note_id, ok := event.Data.Old["id"]
if !ok {
return constructErrorResponse(map[string]string{
"message": "invalid payload: note id not found",
})
}
note, ok := event.Data.New["note"]
if !ok {
return constructErrorResponse(map[string]string{
"message": "invalid payload: note not found",
})
}
// execute the mutation
payload := map[string]interface{}{
"query": MUTATION_UPDATE_NOTE_REVISION,
"variables": map[string]interface{}{
"object": map[string]interface{}{
"note_id": note_id.(float64),
"note": note.(string),
},
},
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(payload)
res, err := http.Post(HGE_ENDPOINT, "application/json; charset=utf-8", b)
if err != nil {
return constructErrorResponse(map[string]string{
"message": err.Error(),
})
}
defer res.Body.Close()
var response map[string]interface{}
err = json.NewDecoder(res.Body).Decode(&response)
if err != nil {
return constructErrorResponse(map[string]string{
"message": err.Error(),
})
}
responseBody, err := json.Marshal(response)
if err != nil {
return constructErrorResponse(map[string]string{
"message": err.Error(),
})
}
return events.APIGatewayProxyResponse{
Body: string(responseBody),
StatusCode: 200,
}, nil
}
func main() {
lambda.Start(Handler)
}