aws / aws-lambda-go

Libraries, samples and tools to help Go developers develop AWS Lambda functions.
Apache License 2.0
3.61k stars 548 forks source link

Functions with multiple event triggers #25

Open yosriady opened 6 years ago

yosriady commented 6 years ago

Do you have an example of how the handler code might look like for a function that can be triggered by more than one event?

For example, let's say we have a function that can be triggered by both API Gateway and DynamoDB events.

jkaflik commented 6 years ago

You can try to receive interface{} or json.RawMessage instead of event struct. Then you need manually detect which event it is and marshal it to proper event

iverberk commented 6 years ago

@jkaflik could you maybe give an example of how that would work with an interface{}? Right now my handler looks like this:

func handler(event interface{}) error {
    sesEvent, ok := event.(sesevt.Event)
    if ok {
        ...
    }

    _, ok = event.(cloudwatchschedevt.Event)
    if ok {
                ...
    }

    return fmt.Errorf("Event type not recognized: %+v", event)
}

When SES sends an event it is not recognized, any idea why? It looks like the data is already unmarshalled (a print of the event reveals a structure with maps and data) so the type assertion is probably wrong...

iverberk commented 6 years ago

Fixed it with the mapstructure library. map[string]interface{} as handler argument and do basic checking on the top-level fields, then decode with the library to the appropriate struct. Seems like a bit of a hack but the initial json unmarshall in the Lambda Go library is hidden from us and unavoidable right now.

eggsbenjamin commented 6 years ago

Creating a lambda for each event seems cleaner (if you need to access event datas that is), otherwise you run the risk of cluttered handlers that a difficult to reason about.

https://code.tutsplus.com/tutorials/solid-part-1-the-single-responsibility-principle--net-36074

https://www.youtube.com/watch?v=PAAkCSZUG1c&t=7m36s

soloradish commented 5 years ago

I encounter this scenario, I have to handle S3 event direct from S3 or relay by SNS. In Python code, I can simply check the payload keys like this.

But I don't know how to do the same thing in Golang's way.

harrisonhjones commented 5 years ago

@soloradish if you have an example of each payload it would be easier to provide an example of how to tackle. What I've done in the past is this:

  1. I used lambda.StartHandler and passed it a struct that implements Invoke(ctx context.Context, event []byte) ([]byte, error). Now I have access to the raw event payload.
  2. Inside of Invoke I marshal event into two different structs. In my case this was an API Gateway proxy request and a dynamodbevent request.
  3. I then did some basic checks on the marshalled requests to see which one was valid. Once I knew that I knew what kind of request I had and handled it accordingly.
johntdyer commented 4 years ago

Does anyone have a complete example of how to unmarshel that I can extrapolate from ?

harrisonhjones commented 4 years ago

Does anyone have a complete example of how to unmarshel that I can extrapolate from ?

What have you tried? Which different event types do you need to disambiguate?

cloudshiftchris commented 1 year ago

Created a Golang library to simplify demuxing multiple event types.

yokoto commented 9 months ago

How about like this:

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

//  handler argument type is `map[string]interface{}`
func handler(ctx context.Context, event map[string]interface{}) (interface{}, error) {
  jsonEvent, err := json.Marshal(event)
  if err != nil {
    return nil, err
  }

  // apigateway's event includes `httpMethod` field.
  if _, ok := event["httpMethod"]; ok {
    var agwEvent events.APIGatewayProxyRequest
    if err := json.Unmarshal(jsonEvent, &agwEvent); err != nil {
      return nil, err
    }
    return agwEvent, nil
  }

  // sqs's event includes `Records` field.
  if _, ok := event["Records"]; ok {
    var sqsEvent events.SQSEvent
    if err := json.Unmarshal(jsonEvent, &sqsEvent); err != nil {
      return nil, err
    }
    return sqsEvent, nil
  }

  return nil, errors.New("unsupported event type")
}