// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. package lambda import ( "context" "encoding/json" "fmt" "reflect" ) // lambdaHandler is the generic function type type lambdaHandler func(context.Context, []byte) (interface{}, error) // Invoke calls the handler, and serializes the response. // If the underlying handler returned an error, or an error occurs during serialization, error is returned. func (handler lambdaHandler) Invoke(ctx context.Context, payload []byte) ([]byte, error) { response, err := handler(ctx, payload) if err != nil { return nil, err } responseBytes, err := json.Marshal(response) if err != nil { return nil, err } return responseBytes, nil } func errorHandler(e error) lambdaHandler { return func(ctx context.Context, event []byte) (interface{}, error) { return nil, e } } func validateArguments(handler reflect.Type) (bool, error) { handlerTakesContext := false if handler.NumIn() > 2 { return false, fmt.Errorf("handlers may not take more than two arguments, but handler takes %d", handler.NumIn()) } else if handler.NumIn() > 0 { contextType := reflect.TypeOf((*context.Context)(nil)).Elem() argumentType := handler.In(0) handlerTakesContext = argumentType.Implements(contextType) if handler.NumIn() > 1 && !handlerTakesContext { return false, fmt.Errorf("handler takes two arguments, but the first is not Context. got %s", argumentType.Kind()) } } return handlerTakesContext, nil } func validateReturns(handler reflect.Type) error { errorType := reflect.TypeOf((*error)(nil)).Elem() if handler.NumOut() > 2 { return fmt.Errorf("handler may not return more than two values") } else if handler.NumOut() > 1 { if !handler.Out(1).Implements(errorType) { return fmt.Errorf("handler returns two values, but the second does not implement error") } } else if handler.NumOut() == 1 { if !handler.Out(0).Implements(errorType) { return fmt.Errorf("handler returns a single value, but it does not implement error") } } return nil } // newHandler Creates the base lambda handler, which will do basic payload unmarshaling before defering to handlerSymbol. // If handlerSymbol is not a valid handler, the returned function will be a handler that just reports the validation error. func newHandler(handlerSymbol interface{}) lambdaHandler { if handlerSymbol == nil { return errorHandler(fmt.Errorf("handler is nil")) } handler := reflect.ValueOf(handlerSymbol) handlerType := reflect.TypeOf(handlerSymbol) if handlerType.Kind() != reflect.Func { return errorHandler(fmt.Errorf("handler kind %s is not %s", handlerType.Kind(), reflect.Func)) } takesContext, err := validateArguments(handlerType) if err != nil { return errorHandler(err) } if err := validateReturns(handlerType); err != nil { return errorHandler(err) } return func(ctx context.Context, payload []byte) (interface{}, error) { // construct arguments var args []reflect.Value if takesContext { args = append(args, reflect.ValueOf(ctx)) } if (handlerType.NumIn() == 1 && !takesContext) || handlerType.NumIn() == 2 { eventType := handlerType.In(handlerType.NumIn() - 1) event := reflect.New(eventType) if err := json.Unmarshal(payload, event.Interface()); err != nil { return nil, err } args = append(args, event.Elem()) } response := handler.Call(args) // convert return values into (interface{}, error) var err error if len(response) > 0 { if errVal, ok := response[len(response)-1].Interface().(error); ok { err = errVal } } var val interface{} if len(response) > 1 { val = response[0].Interface() } return val, err } }