Rosalita
Rosalita

Reputation: 1

How to capture AWS AppSync events inside serverless lambda datasources

I am using a serverless lambda datasource for an AppSync API

I tried the following code in my lambda function

package main

import (
    "context"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "log"
)

func main() {
    lambda.Start(Handler)
}
// Handler is your serverless lambda function
func Handler(ctx context.Context, event events.AppSyncResolverTemplate) error {
    log.Println(ctx)
    log.Println(event)
    return nil
}

When I made a query to the API, the context was logged correctly, but the event was logged as { []}

I tried changing the lambda code to use an event that is an empty interface

package main

import (
    "context"
    "github.com/aws/aws-lambda-go/lambda"
    "log"
)


func main() {
    lambda.Start(Handler)
}

// Handler is your serverless lambda function
func Handler(ctx context.Context, event interface{}) error {
    log.Println(ctx)
    log.Println(event)
    return nil
}

Querying the API now I can see that there is a map in the logs map[field:getPerson arguments:map[personId:1]]

My question is what is the valid type that should be used in the handler signature to capture the AppSync event?

Upvotes: 0

Views: 1180

Answers (3)

vtzan
vtzan

Reputation: 31

If you are using the new Direct Lambda Resolver introduced in August 2020 (https://aws.amazon.com/blogs/mobile/appsync-direct-lambda/) then there is no way to find the event type on the Appsync in the current Golang AWS SDK until today.

They only have AppSyncResolverTemplate, AppSyncIAMIdentity & AppSyncCognitoIdentity struct(https://github.com/aws/aws-lambda-go/blob/v1.16.0/events/appsync.go) so you must decode yourself the incoming request if you are going to use a direct lambda resolver.

The reason they haven't done it so far is because until now you could design your own schema using the Template Resolver (inside appsync) but now that Direct Lambda feature is out I think they must create a new AppSync event type for this.

Until then I am sharing with you the code I wrote extracting the whole event piece by piece for that purpose hoping to save some time for the next person who will be in in my situation.

***You will notice that there is no error handling here just the code needed to decode AWS Event coming from AppSync ;-)

package main

import (
    "fmt"
    "context"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-lambda-go/lambdacontext"
    "log"
    "encoding/json"
    "bytes"
)

type Event struct {
    Arguments               map[string]string `json:"arguments"`
    Identity                string `json:"identity"`
    Info struct {
        FieldName           string `json:"fieldName"`
        ParentTypeName      string `json:"parentTypeName"`
        SelectionSetGraphQL string `json:"selectionSetGraphQL"`
        SelectionSetList    []string `json:"selectionSetList"`
        Variables           map[string]string `json:"variables"`
    }
    Prev                    string `json:"prev"`
    Request struct { 
        Headers struct {
            Accept                      string `json:"accept"`
            AcceptEncoding              string `json:"accept-encoding"`
            AcceptLanguage              string `json:"accept-language"`
            CloudfrontForwardedProto    string `json:"cloudfront-forwarded-proto"`
            CloudfrontIsDesktopViewer   string `json:"cloudfront-is-desktop-viewer"`
            CloudfrontIsMobileViewer    string `json:"cloudfront-is-mobile-viewer"`
            CloudfrontIsSmarttvViewer   string `json:"cloudfront-is-smarttv-viewer"`
            CloudfrontViewerCountry     string `json:"cloudfront-viewer-country"`
            CloudfrontIsTabletViewer    string `json:"cloudfront-is-tablet-viewer"`
            ContentLength               string `json:"content-length"`
            ContentType                 string `json:"content-type"`
            Host                        string `json:"host"`
            Hrigin                      string `json:"origin"`
            Referer                     string `json:"Referer"`
            SecFetchDest                string `json:"sec-fetch-dest"`
            SecFetchMode                string `json:"sec-fetch-mode"`
            SecFetchSite                string `json:"sec-fetch-site"`
            UserAgent                   string `json:"user-agent"`
            Via                         string `json:"via"`
            XAmzCfID                    string `json:"x-amz-cf-id"`
            XAmzUserAgent               string `json:"x-amz-user-agent"`
            XAmznTraceID                string `json:"x-amzn-trace-id"`
            XApiKey                     string `json:"x-api-key"`
            XForwardedFor               string `json:"x-forwarded-for"`
            XForwardedPort              string `json:"x-forwarded-port"`
            XForwardedProto             string `json:"x-forwarded-proto"`
        }
    }
    Source              string `json:"source"`
    Stash               map[string]string `json:"stash"`
}


func main() {
    lambda.Start(HandleLambdaEvent)
}

func HandleLambdaEvent(ctx context.Context, event interface{}) error {

    fmt.Printf("---------------{LAMBDA ctx Start}---------------\n")
    // Event context
    lc, _ := lambdacontext.FromContext(ctx)
    //fmt.Println("Context:",ctx)
    fmt.Println("AwsRequestID:",lc.AwsRequestID)
    fmt.Println("Identity:",lc.Identity)
    fmt.Println("InvokedFunctionArn:",lc.InvokedFunctionArn)
    fmt.Println("ClientContext:",lc.ClientContext)
    fmt.Println("ClientContext.Client:",lc.ClientContext.Client)
    fmt.Println("CognitoIdentityID:",lc.Identity.CognitoIdentityID)
    fmt.Println("CognitoIdentityPoolID:",lc.Identity.CognitoIdentityPoolID)
    deadline, _ := ctx.Deadline()
    fmt.Println("ContextDeadline:",deadline)


    fmt.Printf("---------------{LAMBDA AWS Event Start}---------------\n")
    log.Println(event)


    fmt.Printf("---------------{Marshal Event Start}---------------\n")
    //eventJsonm, _ := json.Marshal(event)
    eventJsonm, _ := json.MarshalIndent(event, "", "    ")
    log.Printf("EVENT Marsal: %s", eventJsonm)
    

    fmt.Printf("---------------{Decode Start}---------------\n")
    var Event Event
    r := bytes.NewReader([]byte(eventJsonm))
    json.NewDecoder(r).Decode(&Event)
    fmt.Println("Arguments:",Event.Arguments)
    fmt.Println("Identity:",Event.Identity)
    fmt.Println("Info.FieldName:",Event.Info.FieldName)
    fmt.Println("Info.ParentTypeName:",Event.Info.ParentTypeName)
    fmt.Println("Info.SelectionSetGraphQL:",Event.Info.SelectionSetGraphQL)
    fmt.Println("Info.SelectionSetList:",Event.Info.SelectionSetList)
    fmt.Println("Info.Variables:",Event.Info.Variables)
    fmt.Println("Prev:",Event.Prev)
    // fmt.Println("Event.Request.Headers:",Event.Request.Headers)
    // fmt.Println("Event.Request.Headers.Accept:",Event.Request.Headers.Accept)
    // fmt.Println("Event.Request.Headers.AcceptEncoding:",Event.Request.Headers.AcceptEncoding)
    // fmt.Println("Event.Request.Headers.AcceptLanguage:",Event.Request.Headers.AcceptLanguage)
    // ...

    fmt.Println("Source:",Event.Source)
    fmt.Println("Stash:",Event.Stash)

    
    return nil
}

Enjoy ;-)

Upvotes: 3

Rosalita
Rosalita

Reputation: 1

I have solved this now.

It turns out that the shape of data passed into a serverless lambda datasource is user defined.

In the SamTemplate I looked at the resolver for this query and it looked like

 getPersonQueryResolver:
    Type: "AWS::AppSync::Resolver"
    Properties:
      ApiId: !GetAtt accountApi.ApiId
      TypeName: "Query"
      FieldName: "getPerson"
      DataSourceName: !GetAtt PersonDatasource.Name
      RequestMappingTemplate: |
        {
          "version" : "2017-02-28",
          "operation": "Invoke",
          "payload": {
              "field": "getPerson",
              "arguments":  $utils.toJson($context.args)
          }
        }
      ResponseMappingTemplate: |
        $utils.toJson($context.result)

I changed this resolver to

getPersonQueryResolver:
    Type: "AWS::AppSync::Resolver"
    Properties:
      ApiId: !GetAtt accountApi.ApiId
      TypeName: "Query"
      FieldName: "getPerson"
      DataSourceName: !GetAtt PersonDatasource.Name
      RequestMappingTemplate: |
        {
          "version" : "2017-02-28",
          "operation": "Invoke",
          "payload": $utils.toJson($context.args)
        }
      ResponseMappingTemplate: |
        $utils.toJson($context.result)

and then I changed the lambda code to

package main

import (
    "context"
    "log"

    "github.com/aws/aws-lambda-go/lambda"
)

type GetPerson struct {
    PersonID string `json:"personId"`
}

func main() {
    lambda.Start(Handler)
}

// Handler is your serverless lambda function
func Handler(ctx context.Context, event GetPerson) error {
    log.Println(ctx)
    log.Println(event)

    return nil
}

This successfully marshalled the event and logged it as {1}

Upvotes: 0

dbala
dbala

Reputation: 366

I don't believe that the events.AppSyncResolverTemplate is the proper value that should exist in an events object passed to the Datasource.

I may be wrong but I believe what you are seeing in the logs where the events is printed is your context.info which shows your query and the arguments you are passing.

query {
  getPerson(personId: "1")
}

This can be used for various reasons within the lambda.

Can you provide more information in what you mean by the AppSync event you want to capture?

What do you expect to see in that event?

Upvotes: 0

Related Questions