14

I'm trying to invoke a lambda func within another lambda func. I have the invokaction of the lambda function working, however, I can't seem to get the consuming lambda function to receive the payload/body from the sending lambda function.

Lambda go doc on invoking a lambda func

Here's my sending/invoking lambda func

type Response events.APIGatewayProxyResponse

func Handler(ctx context.Context) (Response, error) {
    region := os.Getenv("AWS_REGION")
    session, err := session.NewSession(&aws.Config{ // Use aws sdk to connect to dynamoDB
        Region: &region,
    })
    svc := invoke.New(session)

    payload, err := json.Marshal(map[string]interface{}{
        "message": "message to other lambda func",
    })

    if err != nil {
        fmt.Println("Json Marshalling error")
    }
    input := &invoke.InvokeInput{
        FunctionName:   aws.String("invokeConsume"),
        InvocationType: aws.String("RequestResponse"),
        LogType:        aws.String("Tail"),
        Payload:        payload,
    }
    result, err := svc.Invoke(input)
    if err != nil {
        fmt.Println("error")
        fmt.Println(err.Error())
    }
    var m map[string]interface{}
    json.Unmarshal(result.Payload, &m)
    fmt.Println(m["body"])

    body, err := json.Marshal(m["body"])
    resp := Response{
        StatusCode:      200,
        IsBase64Encoded: false,
        Headers: map[string]string{
            "Content-Type": "application/json",
        },
        Body: string(body),
    }
    fmt.Println(resp)

    return resp, nil
}
func main() {
    lambda.Start(Handler)
}

The response I get from invoked lambda...

{200 map[Content-Type:application/json] "{\"message\":\"Something\"}" false} 

My consuming lambda function

type Response events.APIGatewayProxyResponse

func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (Response, error) {
    fmt.Println(req)

    var m map[string]interface{}
    err := json.Unmarshal([]byte(req.Body), &m)
    if err != nil {
        fmt.Println("Json Unmarshalling error")
        fmt.Println(err.Error())
    }
    fmt.Println(m)

    body, _ := json.Marshal(map[string]interface{}{
        "message": "Something",
    })
    resp := Response{
        StatusCode:      200,
        IsBase64Encoded: false,
        Headers: map[string]string{
            "Content-Type": "application/json",
        },
        Body: string(body),
    }
    return resp, nil
}
func main() {
    lambda.Start(Handler)
}

The logs from the consuming lambda func

{ map[] map[] map[] map[] { { } map[] } false}
Json Unmarshalling error
unexpected end of JSON input
map[]

It seems that the consuming lambda function isn't receiving any events.APIGatewayProxyRequest, however I'm not sure why.

EDIT: My solution - I had to also include the json body object in the payload. Here's how I solved it

body, err := json.Marshal(map[string]interface{}{
    "name": "Jimmy",
})
type Payload struct {
    Body string `json:"body"`
}
p := Payload{
    Body: string(body),
}
payload, err := json.Marshal(p) // This should give you {"body":"{\"name\":\"Jimmy\"}"} if you print it out which is the required format for the lambda request body.
randy
  • 217
  • 2
  • 9

4 Answers4

5

Thanks to @yorodm's input I had to include the body object into the payload as well.

Here is the full solution to invoke a lambda function within another lambda function using golang

region := os.Getenv("AWS_REGION")
session, err := session.NewSession(&aws.Config{ // Use aws sdk to connect to dynamoDB
    Region: &region,
})
svc := invoke.New(session)

body, err := json.Marshal(map[string]interface{}{
    "name": "Jimmy",
})

type Payload struct {
    // You can also include more objects in the structure like below, 
    // but for my purposes body was all that was required
    // Method string `json:"httpMethod"`
    Body string `json:"body"`
}
p := Payload{
    // Method: "POST",
    Body: string(body),
}
payload, err := json.Marshal(p) 
 // Result should be: {"body":"{\"name\":\"Jimmy\"}"} 
 // This is the required format for the lambda request body.

if err != nil {
    fmt.Println("Json Marshalling error")
}
fmt.Println(string(payload))

input := &invoke.InvokeInput{
    FunctionName:   aws.String("invokeConsume"),
    InvocationType: aws.String("RequestResponse"),
    LogType:        aws.String("Tail"),
    Payload:        payload,
}
result, err := svc.Invoke(input)
if err != nil {
    fmt.Println("error")
    fmt.Println(err.Error())
}
var m map[string]interface{}
json.Unmarshal(result.Payload, &m)
fmt.Println(m["body"])

invokeReponse, err := json.Marshal(m["body"])
resp := Response{
    StatusCode:      200,
    IsBase64Encoded: false,
    Headers: map[string]string{
        "Content-Type": "application/json",
    },
    Body: string(invokeReponse),
}
fmt.Println(resp)

return resp, nil
randy
  • 217
  • 2
  • 9
3

Problem seems to be you're not passing a proper API Gateway Proxy Request Event to your consumer lambda:

If you take a look at Amazon's sample events page you can see that API Gateway events have the following structure (more or less)

{
  "path": "/test/hello",
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, lzma, sdch, br",
    "Accept-Language": "en-US,en;q=0.8",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
    "Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==",
    "X-Forwarded-For": "192.168.100.1, 192.168.1.1",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "pathParameters": {
    "proxy": "hello"
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "us4z18",
    "stage": "test",
    "requestId": "41b45ea3-70b5-11e6-b7bd-69b5aaebc7d9",
    "identity": {
      "cognitoIdentityPoolId": "",
      "accountId": "",
      "cognitoIdentityId": "",
      "caller": "",
      "apiKey": "",
      "sourceIp": "192.168.100.1",
      "cognitoAuthenticationType": "",
      "cognitoAuthenticationProvider": "",
      "userArn": "",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
      "user": ""
    },
    "resourcePath": "/{proxy+}",
    "httpMethod": "GET",
    "apiId": "wt6mne2s9k"
  },
  "resource": "/{proxy+}",
  "httpMethod": "GET",
  "queryStringParameters": {
    "name": "me"
  },
  "stageVariables": {
    "stageVarName": "stageVarValue"
  }
}

As you can see most fields are related to HTTP stuff, that's because API Gateway is meant as a way to expose your lambda functions to the web (a.k.a. make REST APIS). You can either change your lambda to accept the new event type (it must be a JSON serializable type) or use as string and serialize it yourself.

yorodm
  • 4,359
  • 24
  • 32
  • Thanks @yorodm! I started messing around with the above thanks to your comment and finally got it to work! I needed to include the json body object in the payload as well, which wasn't super clear in the docs originally (I thought it was all the body). For those that see this later check out my edit to see how I fixed it. – randy Sep 04 '18 at 00:25
3

I think you need to make sure you are selecting Use lambda proxy integration this will let you receive the payload data.

API Gateway integration

JonathanDavidArndt
  • 2,518
  • 13
  • 37
  • 49
amittn
  • 2,249
  • 1
  • 12
  • 19
1

AWS SDK has an example: https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/go/example_code/lambda/aws-go-sdk-lambda-example-run-function.go

package main

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/lambda"
    
    "encoding/json"
    "fmt"
    "os"
    "strconv"
)

type getItemsRequest struct {
    SortBy     string
    SortOrder  string
    ItemsToGet int
}

type getItemsResponseError struct {
    Message string `json:"message"`
}

type getItemsResponseData struct {
    Item string `json:"item"`
}

type getItemsResponseBody struct {
    Result string                 `json:"result"`
    Data   []getItemsResponseData `json:"data"`
    Error  getItemsResponseError  `json:"error"`
}

type getItemsResponseHeaders struct {
    ContentType string `json:"Content-Type"`
}

type getItemsResponse struct {
    StatusCode int                     `json:"statusCode"`
    Headers    getItemsResponseHeaders `json:"headers"`
    Body       getItemsResponseBody    `json:"body"`
}

func main() {
    // Create Lambda service client
    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    client := lambda.New(sess, &aws.Config{Region: aws.String("us-west-2")})

    // Get the 10 most recent items
    request := getItemsRequest{"time", "descending", 10}

    payload, err := json.Marshal(request)
    if err != nil {
        fmt.Println("Error marshalling MyGetItemsFunction request")
        os.Exit(0)
    }

    result, err := client.Invoke(&lambda.InvokeInput{FunctionName: aws.String("MyGetItemsFunction"), Payload: payload})
    if err != nil {
        fmt.Println("Error calling MyGetItemsFunction")
        os.Exit(0)
    }

    var resp getItemsResponse

    err = json.Unmarshal(result.Payload, &resp)
    if err != nil {
        fmt.Println("Error unmarshalling MyGetItemsFunction response")
        os.Exit(0)
    }

    // If the status code is NOT 200, the call failed
    if resp.StatusCode != 200 {
        fmt.Println("Error getting items, StatusCode: " + strconv.Itoa(resp.StatusCode))
        os.Exit(0)
    }

    // If the result is failure, we got an error
    if resp.Body.Result == "failure" {
        fmt.Println("Failed to get items")
        os.Exit(0)
    }

    // Print out items
    if len(resp.Body.Data) > 0 {
        for i := range resp.Body.Data {
            fmt.Println(resp.Body.Data[i].Item)
        }
    } else {
        fmt.Println("There were no items")
    }
}
Priyank Bolia
  • 14,077
  • 14
  • 61
  • 82
  • 1
    this is just a copy paste of the documentation, it does not even attempt to address the OP's question. – glls Feb 10 '22 at 23:16