From c02c86e394cb0e902146cf659195e05cb019184b Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 22 Dec 2018 16:06:30 +0700 Subject: [PATCH 01/31] base serving, encode, decode functions --- transport/awslambda/doc.go | 2 + transport/awslambda/encode_decode.go | 20 ++++++++++ transport/awslambda/server.go | 55 ++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 transport/awslambda/doc.go create mode 100644 transport/awslambda/encode_decode.go create mode 100644 transport/awslambda/server.go diff --git a/transport/awslambda/doc.go b/transport/awslambda/doc.go new file mode 100644 index 000000000..a09f8bd28 --- /dev/null +++ b/transport/awslambda/doc.go @@ -0,0 +1,2 @@ +// Package awslambda provide AWS Lambda with API Gateway Event transport layer. +package awslambda diff --git a/transport/awslambda/encode_decode.go b/transport/awslambda/encode_decode.go new file mode 100644 index 000000000..8c66fe336 --- /dev/null +++ b/transport/awslambda/encode_decode.go @@ -0,0 +1,20 @@ +package awslambda + +import ( + "context" + + "github.com/aws/aws-lambda-go/events" +) + +// DecodeRequestFunc extracts a user-domain request object from an +// AWS API gateway event. One straightforward DecodeRequestFunc could be +// something that JSON decodes from the request body to the concrete request type. +type DecodeRequestFunc func( + context.Context, events.APIGatewayProxyRequest, +) (request interface{}, err error) + +// EncodeResponseFunc encodes the passed response object into +// API gateway proxy response format. +type EncodeResponseFunc func( + ctx context.Context, response interface{}, +) (events.APIGatewayProxyResponse, error) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go new file mode 100644 index 000000000..8955e0579 --- /dev/null +++ b/transport/awslambda/server.go @@ -0,0 +1,55 @@ +package awslambda + +import ( + "context" + + "github.com/aws/aws-lambda-go/events" + "github.com/go-kit/kit/endpoint" +) + +// Server wraps an endpoint. +type Server struct { + e endpoint.Endpoint + dec DecodeRequestFunc + enc EncodeResponseFunc +} + +// NewServer constructs a new server, which implements the rules +// of handling AWS API gateway event with AWS lambda. +func NewServer( + e endpoint.Endpoint, + dec DecodeRequestFunc, + enc EncodeResponseFunc, +) *Server { + s := &Server{ + e: e, + dec: dec, + enc: enc, + } + return s +} + +// ServeHTTPLambda implements the rules of AWS lambda handler of +// AWS API gateway event. +func (s *Server) ServeHTTPLambda( + ctx context.Context, req events.APIGatewayProxyRequest, +) ( + resp events.APIGatewayProxyResponse, err error, +) { + + request, err := s.dec(ctx, req) + if err != nil { + return + } + + response, err := s.e(ctx, request) + if err != nil { + return + } + + if resp, err = s.enc(ctx, response); err != nil { + return + } + + return resp, err +} From 92a7ef3d36a1b3e96e3bc5ed7becaa80fe961727 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 22 Dec 2018 16:13:24 +0700 Subject: [PATCH 02/31] logger --- transport/awslambda/server.go | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index 8955e0579..ff4562583 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -5,13 +5,15 @@ import ( "github.com/aws/aws-lambda-go/events" "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/log" ) // Server wraps an endpoint. type Server struct { - e endpoint.Endpoint - dec DecodeRequestFunc - enc EncodeResponseFunc + e endpoint.Endpoint + dec DecodeRequestFunc + enc EncodeResponseFunc + logger log.Logger } // NewServer constructs a new server, which implements the rules @@ -20,15 +22,29 @@ func NewServer( e endpoint.Endpoint, dec DecodeRequestFunc, enc EncodeResponseFunc, + options ...ServerOption, ) *Server { s := &Server{ - e: e, - dec: dec, - enc: enc, + e: e, + dec: dec, + enc: enc, + logger: log.NewNopLogger(), + } + for _, option := range options { + option(s) } return s } +// ServerOption sets an optional parameter for servers. +type ServerOption func(*Server) + +// ServerErrorLogger is used to log non-terminal errors. By default, no errors +// are logged. +func ServerErrorLogger(logger log.Logger) ServerOption { + return func(s *Server) { s.logger = logger } +} + // ServeHTTPLambda implements the rules of AWS lambda handler of // AWS API gateway event. func (s *Server) ServeHTTPLambda( @@ -36,18 +52,20 @@ func (s *Server) ServeHTTPLambda( ) ( resp events.APIGatewayProxyResponse, err error, ) { - request, err := s.dec(ctx, req) if err != nil { + s.logger.Log("err", err) return } response, err := s.e(ctx, request) if err != nil { + s.logger.Log("err", err) return } if resp, err = s.enc(ctx, response); err != nil { + s.logger.Log("err", err) return } From 42506909e372ec885d26da7c4221e5494f1b78ca Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 22 Dec 2018 16:22:16 +0700 Subject: [PATCH 03/31] add before encode options --- transport/awslambda/doc.go | 3 ++- transport/awslambda/request_response_funcs.go | 13 +++++++++++++ transport/awslambda/server.go | 19 +++++++++++++++---- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 transport/awslambda/request_response_funcs.go diff --git a/transport/awslambda/doc.go b/transport/awslambda/doc.go index a09f8bd28..e6596817a 100644 --- a/transport/awslambda/doc.go +++ b/transport/awslambda/doc.go @@ -1,2 +1,3 @@ -// Package awslambda provide AWS Lambda with API Gateway Event transport layer. +// Package awslambda provides AWS lambda Handler +// with APIGatewayProxy Event transport layer. package awslambda diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go new file mode 100644 index 000000000..ec4a3e24e --- /dev/null +++ b/transport/awslambda/request_response_funcs.go @@ -0,0 +1,13 @@ +package awslambda + +import ( + "context" + + "github.com/aws/aws-lambda-go/events" +) + +// ServerRequestFunc may take information from the received +// AWS APIGatewayProxy request and use it to place items +// in the request scoped context. ServerRequestFuncs are executed +// prior to invoking the endpoint. +type ServerRequestFunc func(context.Context, events.APIGatewayProxyRequest) context.Context diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index ff4562583..898b3fcaf 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -13,11 +13,12 @@ type Server struct { e endpoint.Endpoint dec DecodeRequestFunc enc EncodeResponseFunc + before []ServerRequestFunc logger log.Logger } // NewServer constructs a new server, which implements the rules -// of handling AWS API gateway event with AWS lambda. +// of handling AWS APIGatewayProxy event with AWS lambda. func NewServer( e endpoint.Endpoint, dec DecodeRequestFunc, @@ -39,19 +40,29 @@ func NewServer( // ServerOption sets an optional parameter for servers. type ServerOption func(*Server) -// ServerErrorLogger is used to log non-terminal errors. By default, no errors -// are logged. +// ServerBefore functions are executed on the AWS APIGatewayProxy +// request object before the request is decoded. +func ServerBefore(before ...ServerRequestFunc) ServerOption { + return func(s *Server) { s.before = append(s.before, before...) } +} + +// ServerErrorLogger is used to log non-terminal errors. +// By default, no errors are logged. func ServerErrorLogger(logger log.Logger) ServerOption { return func(s *Server) { s.logger = logger } } // ServeHTTPLambda implements the rules of AWS lambda handler of -// AWS API gateway event. +// AWS APIGatewayProxy event. func (s *Server) ServeHTTPLambda( ctx context.Context, req events.APIGatewayProxyRequest, ) ( resp events.APIGatewayProxyResponse, err error, ) { + for _, f := range s.before { + ctx = f(ctx, req) + } + request, err := s.dec(ctx, req) if err != nil { s.logger.Log("err", err) From 0c20a3a801e5e552ed7d3f504b9cdbaeb1bfa5fa Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 22 Dec 2018 16:30:18 +0700 Subject: [PATCH 04/31] add ServerAfter functions --- transport/awslambda/request_response_funcs.go | 6 ++++++ transport/awslambda/server.go | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index ec4a3e24e..c1c3bb589 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -11,3 +11,9 @@ import ( // in the request scoped context. ServerRequestFuncs are executed // prior to invoking the endpoint. type ServerRequestFunc func(context.Context, events.APIGatewayProxyRequest) context.Context + +// ServerResponseFunc may take information from a request context +// and use it to manipulate APIGatewayProxyResponse. +// ServerResponseFunc are only executed after invoking the endpoint +// but prior to returning a response. +type ServerResponseFunc func(context.Context, events.APIGatewayProxyResponse) context.Context diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index 898b3fcaf..09963ffa2 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -14,6 +14,7 @@ type Server struct { dec DecodeRequestFunc enc EncodeResponseFunc before []ServerRequestFunc + after []ServerResponseFunc logger log.Logger } @@ -46,6 +47,12 @@ func ServerBefore(before ...ServerRequestFunc) ServerOption { return func(s *Server) { s.before = append(s.before, before...) } } +// ServerAfter functions are only executed after invoking the endpoint +// but prior to returning a response. +func ServerAfter(after ...ServerResponseFunc) ServerOption { + return func(s *Server) { s.after = append(s.after, after...) } +} + // ServerErrorLogger is used to log non-terminal errors. // By default, no errors are logged. func ServerErrorLogger(logger log.Logger) ServerOption { @@ -75,6 +82,10 @@ func (s *Server) ServeHTTPLambda( return } + for _, f := range s.after { + ctx = f(ctx, resp) + } + if resp, err = s.enc(ctx, response); err != nil { s.logger.Log("err", err) return From 4d460b1f11aad158ae79849a27d7c3ddf57033a1 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 22 Dec 2018 16:39:20 +0700 Subject: [PATCH 05/31] add error encoder --- transport/awslambda/encode_decode.go | 7 ++++++- transport/awslambda/server.go | 24 +++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/transport/awslambda/encode_decode.go b/transport/awslambda/encode_decode.go index 8c66fe336..5942a78f0 100644 --- a/transport/awslambda/encode_decode.go +++ b/transport/awslambda/encode_decode.go @@ -16,5 +16,10 @@ type DecodeRequestFunc func( // EncodeResponseFunc encodes the passed response object into // API gateway proxy response format. type EncodeResponseFunc func( - ctx context.Context, response interface{}, + ctx context.Context, response interface{}, resp events.APIGatewayProxyResponse, +) (events.APIGatewayProxyResponse, error) + +// ErrorEncoder is responsible for encoding an error. +type ErrorEncoder func( + ctx context.Context, err error, resp events.APIGatewayProxyResponse, ) (events.APIGatewayProxyResponse, error) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index 09963ffa2..ed7fe530f 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -10,12 +10,13 @@ import ( // Server wraps an endpoint. type Server struct { - e endpoint.Endpoint - dec DecodeRequestFunc - enc EncodeResponseFunc - before []ServerRequestFunc - after []ServerResponseFunc - logger log.Logger + e endpoint.Endpoint + dec DecodeRequestFunc + enc EncodeResponseFunc + before []ServerRequestFunc + after []ServerResponseFunc + errorEncoder ErrorEncoder + logger log.Logger } // NewServer constructs a new server, which implements the rules @@ -59,6 +60,12 @@ func ServerErrorLogger(logger log.Logger) ServerOption { return func(s *Server) { s.logger = logger } } +// ServerErrorEncoder is used to encode errors for +// AWS APIGatewayProxy response. +func ServerErrorEncoder(ee ErrorEncoder) ServerOption { + return func(s *Server) { s.errorEncoder = ee } +} + // ServeHTTPLambda implements the rules of AWS lambda handler of // AWS APIGatewayProxy event. func (s *Server) ServeHTTPLambda( @@ -73,12 +80,14 @@ func (s *Server) ServeHTTPLambda( request, err := s.dec(ctx, req) if err != nil { s.logger.Log("err", err) + resp, err = s.errorEncoder(ctx, err, resp) return } response, err := s.e(ctx, request) if err != nil { s.logger.Log("err", err) + resp, err = s.errorEncoder(ctx, err, resp) return } @@ -86,8 +95,9 @@ func (s *Server) ServeHTTPLambda( ctx = f(ctx, resp) } - if resp, err = s.enc(ctx, response); err != nil { + if resp, err = s.enc(ctx, response, resp); err != nil { s.logger.Log("err", err) + resp, err = s.errorEncoder(ctx, err, resp) return } From 095df04c513c7ab59469a186b707330e64b19328 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 22 Dec 2018 23:26:35 +0700 Subject: [PATCH 06/31] add finalizer --- transport/awslambda/request_response_funcs.go | 4 ++++ transport/awslambda/server.go | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index c1c3bb589..075de34be 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -17,3 +17,7 @@ type ServerRequestFunc func(context.Context, events.APIGatewayProxyRequest) cont // ServerResponseFunc are only executed after invoking the endpoint // but prior to returning a response. type ServerResponseFunc func(context.Context, events.APIGatewayProxyResponse) context.Context + +// ServerFinalizerFunc is executed at the end of every +// APIGatewayProxy request. +type ServerFinalizerFunc func(context.Context, events.APIGatewayProxyResponse, error) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index ed7fe530f..d886d8389 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -16,6 +16,7 @@ type Server struct { before []ServerRequestFunc after []ServerResponseFunc errorEncoder ErrorEncoder + finalizer []ServerFinalizerFunc logger log.Logger } @@ -66,6 +67,12 @@ func ServerErrorEncoder(ee ErrorEncoder) ServerOption { return func(s *Server) { s.errorEncoder = ee } } +// ServerFinalizer sets finalizer which are called at the end of +// request. By default no finalizer is registered. +func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption { + return func(s *Server) { s.finalizer = append(s.finalizer, f...) } +} + // ServeHTTPLambda implements the rules of AWS lambda handler of // AWS APIGatewayProxy event. func (s *Server) ServeHTTPLambda( @@ -73,6 +80,14 @@ func (s *Server) ServeHTTPLambda( ) ( resp events.APIGatewayProxyResponse, err error, ) { + if len(s.finalizer) < 0 { + defer func() { + for _, f := range s.finalizer { + f(ctx, resp, err) + } + }() + } + for _, f := range s.before { ctx = f(ctx, req) } From a81b28eff3a971837d320b5856af7ab3f46765e1 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 23 Dec 2018 08:06:30 +0700 Subject: [PATCH 07/31] add basic happy flow --- transport/awslambda/server_test.go | 85 ++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 transport/awslambda/server_test.go diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go new file mode 100644 index 000000000..02cde1fa0 --- /dev/null +++ b/transport/awslambda/server_test.go @@ -0,0 +1,85 @@ +package awslambda + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/aws/aws-lambda-go/events" + "github.com/go-kit/kit/endpoint" +) + +func TestServeHTTPLambdaHappyPath(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + decodeHelloRequest, + encodeResponse, + ) + + ctx := context.Background() + resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + + if err != nil { + t.Fatalf("\nshould have no error, but got: %+v", err) + } + + response := helloResponse{} + err = json.Unmarshal([]byte(resp.Body), &response) + if err != nil { + t.Fatalf("\nshould have no error, but got: %+v", err) + } + + expectedGreeting := "hello john doe" + if response.Greeting != expectedGreeting { + t.Fatalf( + "\nexpect: %s\nactual: %s", expectedGreeting, response.Greeting) + } +} + +func decodeHelloRequest( + _ context.Context, req events.APIGatewayProxyRequest, +) (interface{}, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(req.Body), &request) + return request, err +} + +func encodeResponse( + _ context.Context, response interface{}, resp events.APIGatewayProxyResponse, +) (events.APIGatewayProxyResponse, error) { + respByte, err := json.Marshal(response) + if err != nil { + return resp, err + } + + resp.Body = string(respByte) + resp.StatusCode = 200 + return resp, nil +} + +type helloRequest struct { + Name string `json:"name"` +} + +type helloResponse struct { + Greeting string `json:"greeting"` +} + +func makeTest01HelloEndpoint(svc serviceTest01) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(helloRequest) + greeting := svc.hello(req.Name) + return helloResponse{greeting}, nil + } +} + +type serviceTest01 struct{} + +func (ts *serviceTest01) hello(name string) string { + return fmt.Sprintf("hello %s", name) +} From 9561ebf3828d8e5550099b93f64fbd9341a869f0 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 23 Dec 2018 08:26:53 +0700 Subject: [PATCH 08/31] add ServerBefore tests --- transport/awslambda/server.go | 12 ++++++--- transport/awslambda/server_test.go | 40 ++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index d886d8389..efe5356c8 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -95,14 +95,18 @@ func (s *Server) ServeHTTPLambda( request, err := s.dec(ctx, req) if err != nil { s.logger.Log("err", err) - resp, err = s.errorEncoder(ctx, err, resp) + if s.errorEncoder != nil { + resp, err = s.errorEncoder(ctx, err, resp) + } return } response, err := s.e(ctx, request) if err != nil { s.logger.Log("err", err) - resp, err = s.errorEncoder(ctx, err, resp) + if s.errorEncoder != nil { + resp, err = s.errorEncoder(ctx, err, resp) + } return } @@ -112,7 +116,9 @@ func (s *Server) ServeHTTPLambda( if resp, err = s.enc(ctx, response, resp); err != nil { s.logger.Log("err", err) - resp, err = s.errorEncoder(ctx, err, resp) + if s.errorEncoder != nil { + resp, err = s.errorEncoder(ctx, err, resp) + } return } diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index 02cde1fa0..3547e38b6 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -10,6 +10,13 @@ import ( "github.com/go-kit/kit/endpoint" ) +type key int + +const ( + KeyBeforeOne key = iota + KeyBeforeTwo key = iota +) + func TestServeHTTPLambdaHappyPath(t *testing.T) { svc := serviceTest01{} @@ -17,6 +24,18 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { makeTest01HelloEndpoint(svc), decodeHelloRequest, encodeResponse, + ServerBefore(func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) context.Context { + ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") + return ctx + }), + ServerBefore(func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) context.Context { + ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") + return ctx + }), ) ctx := context.Background() @@ -34,7 +53,7 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { t.Fatalf("\nshould have no error, but got: %+v", err) } - expectedGreeting := "hello john doe" + expectedGreeting := "hello john doe bef1 bef2" if response.Greeting != expectedGreeting { t.Fatalf( "\nexpect: %s\nactual: %s", expectedGreeting, response.Greeting) @@ -42,10 +61,27 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { } func decodeHelloRequest( - _ context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, req events.APIGatewayProxyRequest, ) (interface{}, error) { request := helloRequest{} err := json.Unmarshal([]byte(req.Body), &request) + if err != nil { + return request, err + } + + valOne, ok := ctx.Value(KeyBeforeOne).(string) + if !ok { + return request, fmt.Errorf( + "Value was not set properly when multiple ServerBefores are used") + } + + valTwo, ok := ctx.Value(KeyBeforeTwo).(string) + if !ok { + return request, fmt.Errorf( + "Value was not set properly when multiple ServerBefores are used") + } + + request.Name += " " + valOne + " " + valTwo return request, err } From 4b8444be2b4f2a039f295ef8b80a3046ea80899a Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 23 Dec 2018 08:32:06 +0700 Subject: [PATCH 09/31] test error flow on decoding stage --- transport/awslambda/server_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index 3547e38b6..5996101a2 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -60,6 +60,36 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { } } +func TestServeHTTPLambdaFailDecode(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + decodeHelloRequest, + encodeResponse, + ServerErrorEncoder(func( + ctx context.Context, err error, resp events.APIGatewayProxyResponse, + ) (events.APIGatewayProxyResponse, error) { + resp.Body = `{"error":"yes"}` + resp.StatusCode = 500 + return resp, err + }), + ) + + ctx := context.Background() + resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + + if err == nil { + t.Fatalf("\nshould have error, but got: %+v", err) + } + + if resp.StatusCode != 500 { + t.Fatalf("\nexpect status code of 500, instead of %d", resp.StatusCode) + } +} + func decodeHelloRequest( ctx context.Context, req events.APIGatewayProxyRequest, ) (interface{}, error) { From d05a7edfff84e91cef6330424dc78526d2d5f98b Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 24 Dec 2018 00:06:23 +0700 Subject: [PATCH 10/31] complete the test scenarios --- transport/awslambda/server.go | 2 +- transport/awslambda/server_test.go | 137 ++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index efe5356c8..ac316dc07 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -80,7 +80,7 @@ func (s *Server) ServeHTTPLambda( ) ( resp events.APIGatewayProxyResponse, err error, ) { - if len(s.finalizer) < 0 { + if len(s.finalizer) > 0 { defer func() { for _, f := range s.finalizer { f(ctx, resp, err) diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index 5996101a2..8f50a9bfb 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-lambda-go/events" "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/log" ) type key int @@ -15,6 +16,8 @@ type key int const ( KeyBeforeOne key = iota KeyBeforeTwo key = iota + KeyAfterOne key = iota + KeyEncMode key = iota ) func TestServeHTTPLambdaHappyPath(t *testing.T) { @@ -24,6 +27,7 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { makeTest01HelloEndpoint(svc), decodeHelloRequest, encodeResponse, + ServerErrorLogger(log.NewNopLogger()), ServerBefore(func( ctx context.Context, req events.APIGatewayProxyRequest, ) context.Context { @@ -36,6 +40,35 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), + ServerAfter(func( + ctx context.Context, resp events.APIGatewayProxyResponse, + ) context.Context { + ctx = context.WithValue(ctx, KeyAfterOne, "af1") + return ctx + }), + ServerAfter(func( + ctx context.Context, resp events.APIGatewayProxyResponse, + ) context.Context { + if _, ok := ctx.Value(KeyAfterOne).(string); !ok { + t.Fatalf("\nValue was not set properly during multi ServerAfter") + } + return ctx + }), + ServerFinalizer(func( + _ context.Context, resp events.APIGatewayProxyResponse, _ error, + ) { + response := helloResponse{} + err := json.Unmarshal([]byte(resp.Body), &response) + if err != nil { + t.Fatalf("\nshould have no error, but got: %+v", err) + } + + expectedGreeting := "hello john doe bef1 bef2" + if response.Greeting != expectedGreeting { + t.Fatalf( + "\nexpect: %s\nactual: %s", expectedGreeting, response.Greeting) + } + }), ) ctx := context.Background() @@ -90,6 +123,96 @@ func TestServeHTTPLambdaFailDecode(t *testing.T) { } } +func TestServeHTTPLambdaFailEndpoint(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01FailEndpoint(svc), + decodeHelloRequest, + encodeResponse, + ServerBefore(func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) context.Context { + ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") + return ctx + }), + ServerBefore(func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) context.Context { + ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") + return ctx + }), + ServerErrorEncoder(func( + ctx context.Context, err error, resp events.APIGatewayProxyResponse, + ) (events.APIGatewayProxyResponse, error) { + resp.Body = `{"error":"yes"}` + resp.StatusCode = 500 + return resp, err + }), + ) + + ctx := context.Background() + resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + + if err == nil { + t.Fatalf("\nshould have error, but got: %+v", err) + } + + if resp.StatusCode != 500 { + t.Fatalf("\nexpect status code of 500, instead of %d", resp.StatusCode) + } +} + +func TestServeHTTPLambdaFailEncode(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + decodeHelloRequest, + encodeResponse, + ServerBefore(func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) context.Context { + ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") + return ctx + }), + ServerBefore(func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) context.Context { + ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") + return ctx + }), + ServerAfter(func( + ctx context.Context, resp events.APIGatewayProxyResponse, + ) context.Context { + ctx = context.WithValue(ctx, KeyEncMode, "fail_encode") + return ctx + }), + ServerErrorEncoder(func( + ctx context.Context, err error, resp events.APIGatewayProxyResponse, + ) (events.APIGatewayProxyResponse, error) { + resp.Body = `{"error":"yes"}` + resp.StatusCode = 500 + return resp, err + }), + ) + + ctx := context.Background() + resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + + if err == nil { + t.Fatalf("\nshould have error, but got: %+v", err) + } + + if resp.StatusCode != 500 { + t.Fatalf("\nexpect status code of 500, instead of %d", resp.StatusCode) + } +} + func decodeHelloRequest( ctx context.Context, req events.APIGatewayProxyRequest, ) (interface{}, error) { @@ -116,8 +239,14 @@ func decodeHelloRequest( } func encodeResponse( - _ context.Context, response interface{}, resp events.APIGatewayProxyResponse, + ctx context.Context, response interface{}, resp events.APIGatewayProxyResponse, ) (events.APIGatewayProxyResponse, error) { + mode, ok := ctx.Value(KeyEncMode).(string) + fmt.Println(mode) + if ok && mode == "fail_encode" { + return resp, fmt.Errorf("fail encoding") + } + respByte, err := json.Marshal(response) if err != nil { return resp, err @@ -144,6 +273,12 @@ func makeTest01HelloEndpoint(svc serviceTest01) endpoint.Endpoint { } } +func makeTest01FailEndpoint(_ serviceTest01) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + return nil, fmt.Errorf("test error endpoint") + } +} + type serviceTest01 struct{} func (ts *serviceTest01) hello(name string) string { From 6a3be3ae4b45e7a218bd4bb5a285f4f318013e6d Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 24 Dec 2018 23:19:18 +0700 Subject: [PATCH 11/31] refactor into more generic support by implementing lambda.Handler --- transport/awslambda/doc.go | 3 +- transport/awslambda/encode_decode.go | 21 +-- transport/awslambda/request_response_funcs.go | 20 ++- transport/awslambda/server.go | 37 ++--- transport/awslambda/server_test.go | 144 ++++++++++++------ 5 files changed, 128 insertions(+), 97 deletions(-) diff --git a/transport/awslambda/doc.go b/transport/awslambda/doc.go index e6596817a..e81acba18 100644 --- a/transport/awslambda/doc.go +++ b/transport/awslambda/doc.go @@ -1,3 +1,2 @@ -// Package awslambda provides AWS lambda Handler -// with APIGatewayProxy Event transport layer. +// Package awslambda provides AWS lambda Handler transport layer. package awslambda diff --git a/transport/awslambda/encode_decode.go b/transport/awslambda/encode_decode.go index 5942a78f0..6d2018540 100644 --- a/transport/awslambda/encode_decode.go +++ b/transport/awslambda/encode_decode.go @@ -2,24 +2,15 @@ package awslambda import ( "context" - - "github.com/aws/aws-lambda-go/events" ) // DecodeRequestFunc extracts a user-domain request object from an -// AWS API gateway event. One straightforward DecodeRequestFunc could be -// something that JSON decodes from the request body to the concrete request type. -type DecodeRequestFunc func( - context.Context, events.APIGatewayProxyRequest, -) (request interface{}, err error) +// AWS lambda's payload. +type DecodeRequestFunc func(context.Context, []byte) (interface{}, error) -// EncodeResponseFunc encodes the passed response object into -// API gateway proxy response format. -type EncodeResponseFunc func( - ctx context.Context, response interface{}, resp events.APIGatewayProxyResponse, -) (events.APIGatewayProxyResponse, error) +// EncodeResponseFunc encodes the passed response object into []byte, +// ready to be sent as AWS lambda response. +type EncodeResponseFunc func(context.Context, interface{}) ([]byte, error) // ErrorEncoder is responsible for encoding an error. -type ErrorEncoder func( - ctx context.Context, err error, resp events.APIGatewayProxyResponse, -) (events.APIGatewayProxyResponse, error) +type ErrorEncoder func(ctx context.Context, err error) ([]byte, error) diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index 075de34be..5b8da3e84 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -2,22 +2,20 @@ package awslambda import ( "context" - - "github.com/aws/aws-lambda-go/events" ) // ServerRequestFunc may take information from the received -// AWS APIGatewayProxy request and use it to place items -// in the request scoped context. ServerRequestFuncs are executed -// prior to invoking the endpoint. -type ServerRequestFunc func(context.Context, events.APIGatewayProxyRequest) context.Context +// payload and use it to place items in the request scoped context. +// ServerRequestFuncs are executed prior to invoking the endpoint and +// decoding of the payload. +type ServerRequestFunc func(ctx context.Context, payload []byte) context.Context // ServerResponseFunc may take information from a request context -// and use it to manipulate APIGatewayProxyResponse. +// and use it to manipulate response (before marshalled.) // ServerResponseFunc are only executed after invoking the endpoint // but prior to returning a response. -type ServerResponseFunc func(context.Context, events.APIGatewayProxyResponse) context.Context +type ServerResponseFunc func(ctx context.Context, response interface{}) context.Context -// ServerFinalizerFunc is executed at the end of every -// APIGatewayProxy request. -type ServerFinalizerFunc func(context.Context, events.APIGatewayProxyResponse, error) +// ServerFinalizerFunc is executed at the end of Invocation. +// This can be used for logging purposes. +type ServerFinalizerFunc func(ctx context.Context, resp []byte, err error) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index ac316dc07..dae69b383 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -3,7 +3,6 @@ package awslambda import ( "context" - "github.com/aws/aws-lambda-go/events" "github.com/go-kit/kit/endpoint" "github.com/go-kit/kit/log" ) @@ -20,8 +19,8 @@ type Server struct { logger log.Logger } -// NewServer constructs a new server, which implements the rules -// of handling AWS APIGatewayProxy event with AWS lambda. +// NewServer constructs a new server, which implements +// the AWS lambda.Handler interface. func NewServer( e endpoint.Endpoint, dec DecodeRequestFunc, @@ -43,8 +42,8 @@ func NewServer( // ServerOption sets an optional parameter for servers. type ServerOption func(*Server) -// ServerBefore functions are executed on the AWS APIGatewayProxy -// request object before the request is decoded. +// ServerBefore functions are executed on the payload byte, +// before the request is decoded. func ServerBefore(before ...ServerRequestFunc) ServerOption { return func(s *Server) { s.before = append(s.before, before...) } } @@ -61,8 +60,7 @@ func ServerErrorLogger(logger log.Logger) ServerOption { return func(s *Server) { s.logger = logger } } -// ServerErrorEncoder is used to encode errors for -// AWS APIGatewayProxy response. +// ServerErrorEncoder is used to encode errors. func ServerErrorEncoder(ee ErrorEncoder) ServerOption { return func(s *Server) { s.errorEncoder = ee } } @@ -73,13 +71,10 @@ func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption { return func(s *Server) { s.finalizer = append(s.finalizer, f...) } } -// ServeHTTPLambda implements the rules of AWS lambda handler of -// AWS APIGatewayProxy event. -func (s *Server) ServeHTTPLambda( - ctx context.Context, req events.APIGatewayProxyRequest, -) ( - resp events.APIGatewayProxyResponse, err error, -) { +// Invoke represents implementation of the AWS lambda.Handler interface. +func (s *Server) Invoke( + ctx context.Context, payload []byte, +) (resp []byte, err error) { if len(s.finalizer) > 0 { defer func() { for _, f := range s.finalizer { @@ -89,14 +84,14 @@ func (s *Server) ServeHTTPLambda( } for _, f := range s.before { - ctx = f(ctx, req) + ctx = f(ctx, payload) } - request, err := s.dec(ctx, req) + request, err := s.dec(ctx, payload) if err != nil { s.logger.Log("err", err) if s.errorEncoder != nil { - resp, err = s.errorEncoder(ctx, err, resp) + resp, err = s.errorEncoder(ctx, err) } return } @@ -105,19 +100,19 @@ func (s *Server) ServeHTTPLambda( if err != nil { s.logger.Log("err", err) if s.errorEncoder != nil { - resp, err = s.errorEncoder(ctx, err, resp) + resp, err = s.errorEncoder(ctx, err) } return } for _, f := range s.after { - ctx = f(ctx, resp) + ctx = f(ctx, response) } - if resp, err = s.enc(ctx, response, resp); err != nil { + if resp, err = s.enc(ctx, response); err != nil { s.logger.Log("err", err) if s.errorEncoder != nil { - resp, err = s.errorEncoder(ctx, err, resp) + resp, err = s.errorEncoder(ctx, err) } return } diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index 8f50a9bfb..06d550733 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -20,7 +20,7 @@ const ( KeyEncMode key = iota ) -func TestServeHTTPLambdaHappyPath(t *testing.T) { +func TestInvokeHappyPath(t *testing.T) { svc := serviceTest01{} helloHandler := NewServer( @@ -29,25 +29,25 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { encodeResponse, ServerErrorLogger(log.NewNopLogger()), ServerBefore(func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), ServerBefore(func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), ServerAfter(func( - ctx context.Context, resp events.APIGatewayProxyResponse, + ctx context.Context, response interface{}, ) context.Context { ctx = context.WithValue(ctx, KeyAfterOne, "af1") return ctx }), ServerAfter(func( - ctx context.Context, resp events.APIGatewayProxyResponse, + ctx context.Context, response interface{}, ) context.Context { if _, ok := ctx.Value(KeyAfterOne).(string); !ok { t.Fatalf("\nValue was not set properly during multi ServerAfter") @@ -55,10 +55,16 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { return ctx }), ServerFinalizer(func( - _ context.Context, resp events.APIGatewayProxyResponse, _ error, + _ context.Context, resp []byte, _ error, ) { + apigwResp := events.APIGatewayProxyResponse{} + err := json.Unmarshal(resp, &apigwResp) + if err != nil { + t.Fatalf("\nshould have no error, but got: %+v", err) + } + response := helloResponse{} - err := json.Unmarshal([]byte(resp.Body), &response) + err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { t.Fatalf("\nshould have no error, but got: %+v", err) } @@ -72,16 +78,23 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { ) ctx := context.Background() - resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + req, _ := json.Marshal(events.APIGatewayProxyRequest{ Body: `{"name":"john doe"}`, }) + resp, err := helloHandler.Invoke(ctx, req) + + if err != nil { + t.Fatalf("\nshould have no error, but got: %+v", err) + } + apigwResp := events.APIGatewayProxyResponse{} + err = json.Unmarshal(resp, &apigwResp) if err != nil { t.Fatalf("\nshould have no error, but got: %+v", err) } response := helloResponse{} - err = json.Unmarshal([]byte(resp.Body), &response) + err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { t.Fatalf("\nshould have no error, but got: %+v", err) } @@ -93,7 +106,7 @@ func TestServeHTTPLambdaHappyPath(t *testing.T) { } } -func TestServeHTTPLambdaFailDecode(t *testing.T) { +func TestInvokeFailDecode(t *testing.T) { svc := serviceTest01{} helloHandler := NewServer( @@ -101,29 +114,37 @@ func TestServeHTTPLambdaFailDecode(t *testing.T) { decodeHelloRequest, encodeResponse, ServerErrorEncoder(func( - ctx context.Context, err error, resp events.APIGatewayProxyResponse, - ) (events.APIGatewayProxyResponse, error) { - resp.Body = `{"error":"yes"}` - resp.StatusCode = 500 + ctx context.Context, err error, + ) ([]byte, error) { + apigwResp := events.APIGatewayProxyResponse{} + apigwResp.Body = `{"error":"yes"}` + apigwResp.StatusCode = 500 + resp, merr := json.Marshal(apigwResp) + if merr != nil { + return resp, merr + } return resp, err }), ) ctx := context.Background() - resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + req, _ := json.Marshal(events.APIGatewayProxyRequest{ Body: `{"name":"john doe"}`, }) + resp, err := helloHandler.Invoke(ctx, req) if err == nil { t.Fatalf("\nshould have error, but got: %+v", err) } - if resp.StatusCode != 500 { - t.Fatalf("\nexpect status code of 500, instead of %d", resp.StatusCode) + apigwResp := events.APIGatewayProxyResponse{} + json.Unmarshal(resp, &apigwResp) + if apigwResp.StatusCode != 500 { + t.Fatalf("\nexpect status code of 500, instead of %d", apigwResp.StatusCode) } } -func TestServeHTTPLambdaFailEndpoint(t *testing.T) { +func TestInvokeFailEndpoint(t *testing.T) { svc := serviceTest01{} helloHandler := NewServer( @@ -131,41 +152,49 @@ func TestServeHTTPLambdaFailEndpoint(t *testing.T) { decodeHelloRequest, encodeResponse, ServerBefore(func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), ServerBefore(func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), ServerErrorEncoder(func( - ctx context.Context, err error, resp events.APIGatewayProxyResponse, - ) (events.APIGatewayProxyResponse, error) { - resp.Body = `{"error":"yes"}` - resp.StatusCode = 500 + ctx context.Context, err error, + ) ([]byte, error) { + apigwResp := events.APIGatewayProxyResponse{} + apigwResp.Body = `{"error":"yes"}` + apigwResp.StatusCode = 500 + resp, merr := json.Marshal(apigwResp) + if merr != nil { + return resp, merr + } return resp, err }), ) ctx := context.Background() - resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + req, _ := json.Marshal(events.APIGatewayProxyRequest{ Body: `{"name":"john doe"}`, }) + resp, err := helloHandler.Invoke(ctx, req) if err == nil { t.Fatalf("\nshould have error, but got: %+v", err) } - if resp.StatusCode != 500 { - t.Fatalf("\nexpect status code of 500, instead of %d", resp.StatusCode) + apigwResp := events.APIGatewayProxyResponse{} + json.Unmarshal(resp, &apigwResp) + if apigwResp.StatusCode != 500 { + t.Fatalf("\nexpect status code of 500, instead of %d", apigwResp.StatusCode) } } -func TestServeHTTPLambdaFailEncode(t *testing.T) { +func TestInvokeFailEncode(t *testing.T) { svc := serviceTest01{} helloHandler := NewServer( @@ -173,51 +202,65 @@ func TestServeHTTPLambdaFailEncode(t *testing.T) { decodeHelloRequest, encodeResponse, ServerBefore(func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), ServerBefore(func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), ServerAfter(func( - ctx context.Context, resp events.APIGatewayProxyResponse, + ctx context.Context, response interface{}, ) context.Context { ctx = context.WithValue(ctx, KeyEncMode, "fail_encode") return ctx }), ServerErrorEncoder(func( - ctx context.Context, err error, resp events.APIGatewayProxyResponse, - ) (events.APIGatewayProxyResponse, error) { - resp.Body = `{"error":"yes"}` - resp.StatusCode = 500 + ctx context.Context, err error, + ) ([]byte, error) { + apigwResp := events.APIGatewayProxyResponse{} + apigwResp.Body = `{"error":"yes"}` + apigwResp.StatusCode = 500 + resp, merr := json.Marshal(apigwResp) + if merr != nil { + return resp, merr + } return resp, err }), ) ctx := context.Background() - resp, err := helloHandler.ServeHTTPLambda(ctx, events.APIGatewayProxyRequest{ + req, _ := json.Marshal(events.APIGatewayProxyRequest{ Body: `{"name":"john doe"}`, }) + resp, err := helloHandler.Invoke(ctx, req) if err == nil { t.Fatalf("\nshould have error, but got: %+v", err) } - if resp.StatusCode != 500 { - t.Fatalf("\nexpect status code of 500, instead of %d", resp.StatusCode) + apigwResp := events.APIGatewayProxyResponse{} + json.Unmarshal(resp, &apigwResp) + if apigwResp.StatusCode != 500 { + t.Fatalf("\nexpect status code of 500, instead of %d", apigwResp.StatusCode) } } func decodeHelloRequest( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, req []byte, ) (interface{}, error) { + apigwReq := events.APIGatewayProxyRequest{} + err := json.Unmarshal([]byte(req), &apigwReq) + if err != nil { + return apigwReq, err + } + request := helloRequest{} - err := json.Unmarshal([]byte(req.Body), &request) + err = json.Unmarshal([]byte(apigwReq.Body), &request) if err != nil { return request, err } @@ -239,22 +282,27 @@ func decodeHelloRequest( } func encodeResponse( - ctx context.Context, response interface{}, resp events.APIGatewayProxyResponse, -) (events.APIGatewayProxyResponse, error) { + ctx context.Context, response interface{}, +) ([]byte, error) { + apigwResp := events.APIGatewayProxyResponse{} + mode, ok := ctx.Value(KeyEncMode).(string) - fmt.Println(mode) + fmt.Printf("\nmode: %s ok: %+v\n", mode, ok) if ok && mode == "fail_encode" { - return resp, fmt.Errorf("fail encoding") + fmt.Printf("\nEnter\n") + return nil, fmt.Errorf("fail encoding") } respByte, err := json.Marshal(response) if err != nil { - return resp, err + return nil, err } - resp.Body = string(respByte) - resp.StatusCode = 200 - return resp, nil + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + + resp, err := json.Marshal(apigwResp) + return resp, err } type helloRequest struct { From d29f5a392c541c288c1d5b267f767c219da54ab6 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 26 Dec 2018 07:43:23 +0700 Subject: [PATCH 12/31] testing: ErrorEncoder to reflect behavior of encoding error as payload for AWS APIGateway event handling, as if we return error not nil to lambda it shall treated as error, and the API response would not be pretty --- transport/awslambda/server_test.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index 06d550733..0b2ca8637 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -119,10 +119,7 @@ func TestInvokeFailDecode(t *testing.T) { apigwResp := events.APIGatewayProxyResponse{} apigwResp.Body = `{"error":"yes"}` apigwResp.StatusCode = 500 - resp, merr := json.Marshal(apigwResp) - if merr != nil { - return resp, merr - } + resp, err := json.Marshal(apigwResp) return resp, err }), ) @@ -169,10 +166,7 @@ func TestInvokeFailEndpoint(t *testing.T) { apigwResp := events.APIGatewayProxyResponse{} apigwResp.Body = `{"error":"yes"}` apigwResp.StatusCode = 500 - resp, merr := json.Marshal(apigwResp) - if merr != nil { - return resp, merr - } + resp, err := json.Marshal(apigwResp) return resp, err }), ) @@ -225,10 +219,7 @@ func TestInvokeFailEncode(t *testing.T) { apigwResp := events.APIGatewayProxyResponse{} apigwResp.Body = `{"error":"yes"}` apigwResp.StatusCode = 500 - resp, merr := json.Marshal(apigwResp) - if merr != nil { - return resp, merr - } + resp, err := json.Marshal(apigwResp) return resp, err }), ) From 4fd90b081ae14284c3f87eb16373e8a92b13137c Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 27 Dec 2018 03:49:40 +0700 Subject: [PATCH 13/31] add wrapper --- transport/awslambda/server_test.go | 45 ++- transport/awslambda/wrapper.go | 285 +++++++++++++++++ transport/awslambda/wrapper_test.go | 473 ++++++++++++++++++++++++++++ 3 files changed, 780 insertions(+), 23 deletions(-) create mode 100644 transport/awslambda/wrapper.go create mode 100644 transport/awslambda/wrapper_test.go diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index 0b2ca8637..ed1f4129b 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -25,7 +25,7 @@ func TestInvokeHappyPath(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), - decodeHelloRequest, + decodeHelloRequestWithTwoBefores, encodeResponse, ServerErrorLogger(log.NewNopLogger()), ServerBefore(func( @@ -60,19 +60,19 @@ func TestInvokeHappyPath(t *testing.T) { apigwResp := events.APIGatewayProxyResponse{} err := json.Unmarshal(resp, &apigwResp) if err != nil { - t.Fatalf("\nshould have no error, but got: %+v", err) + t.Fatalf("\nShould have no error, but got: %+v", err) } response := helloResponse{} err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { - t.Fatalf("\nshould have no error, but got: %+v", err) + t.Fatalf("\nShould have no error, but got: %+v", err) } expectedGreeting := "hello john doe bef1 bef2" if response.Greeting != expectedGreeting { t.Fatalf( - "\nexpect: %s\nactual: %s", expectedGreeting, response.Greeting) + "\nExpect: %s\nActual: %s", expectedGreeting, response.Greeting) } }), ) @@ -84,25 +84,25 @@ func TestInvokeHappyPath(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nshould have no error, but got: %+v", err) + t.Fatalf("\nShould have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} err = json.Unmarshal(resp, &apigwResp) if err != nil { - t.Fatalf("\nshould have no error, but got: %+v", err) + t.Fatalf("\nShould have no error, but got: %+v", err) } response := helloResponse{} err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { - t.Fatalf("\nshould have no error, but got: %+v", err) + t.Fatalf("\nShould have no error, but got: %+v", err) } expectedGreeting := "hello john doe bef1 bef2" if response.Greeting != expectedGreeting { t.Fatalf( - "\nexpect: %s\nactual: %s", expectedGreeting, response.Greeting) + "\nExpect: %s\nActual: %s", expectedGreeting, response.Greeting) } } @@ -111,7 +111,7 @@ func TestInvokeFailDecode(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), - decodeHelloRequest, + decodeHelloRequestWithTwoBefores, encodeResponse, ServerErrorEncoder(func( ctx context.Context, err error, @@ -130,14 +130,14 @@ func TestInvokeFailDecode(t *testing.T) { }) resp, err := helloHandler.Invoke(ctx, req) - if err == nil { - t.Fatalf("\nshould have error, but got: %+v", err) + if err != nil { + t.Fatalf("\nShould have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nexpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) } } @@ -146,7 +146,7 @@ func TestInvokeFailEndpoint(t *testing.T) { helloHandler := NewServer( makeTest01FailEndpoint(svc), - decodeHelloRequest, + decodeHelloRequestWithTwoBefores, encodeResponse, ServerBefore(func( ctx context.Context, payload []byte, @@ -177,14 +177,14 @@ func TestInvokeFailEndpoint(t *testing.T) { }) resp, err := helloHandler.Invoke(ctx, req) - if err == nil { - t.Fatalf("\nshould have error, but got: %+v", err) + if err != nil { + t.Fatalf("\nShould have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nexpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) } } @@ -193,7 +193,7 @@ func TestInvokeFailEncode(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), - decodeHelloRequest, + decodeHelloRequestWithTwoBefores, encodeResponse, ServerBefore(func( ctx context.Context, payload []byte, @@ -216,6 +216,7 @@ func TestInvokeFailEncode(t *testing.T) { ServerErrorEncoder(func( ctx context.Context, err error, ) ([]byte, error) { + // convert error into proper APIGateway response. apigwResp := events.APIGatewayProxyResponse{} apigwResp.Body = `{"error":"yes"}` apigwResp.StatusCode = 500 @@ -230,18 +231,18 @@ func TestInvokeFailEncode(t *testing.T) { }) resp, err := helloHandler.Invoke(ctx, req) - if err == nil { - t.Fatalf("\nshould have error, but got: %+v", err) + if err != nil { + t.Fatalf("\nShould have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nexpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) } } -func decodeHelloRequest( +func decodeHelloRequestWithTwoBefores( ctx context.Context, req []byte, ) (interface{}, error) { apigwReq := events.APIGatewayProxyRequest{} @@ -278,9 +279,7 @@ func encodeResponse( apigwResp := events.APIGatewayProxyResponse{} mode, ok := ctx.Value(KeyEncMode).(string) - fmt.Printf("\nmode: %s ok: %+v\n", mode, ok) if ok && mode == "fail_encode" { - fmt.Printf("\nEnter\n") return nil, fmt.Errorf("fail encoding") } diff --git a/transport/awslambda/wrapper.go b/transport/awslambda/wrapper.go new file mode 100644 index 000000000..160ee4f76 --- /dev/null +++ b/transport/awslambda/wrapper.go @@ -0,0 +1,285 @@ +package awslambda + +import ( + "context" + "encoding/json" + "fmt" + "reflect" +) + +// DecodeRequestWrapper wraps the given decoderSymbol function, and generates +// the proper DecodeRequestFunc, based on the decoderSymbol function signature. +// The decoderSymbol function signature has to receive 2 args, which is the +// context.Context and event request to decode. +// It has also to return 2 values, the user-domain object and error. +func DecodeRequestWrapper(decoderSymbol interface{}) DecodeRequestFunc { + if decoderSymbol == nil { + return errorDecoderRequest(fmt.Errorf("decoder is nil")) + } + + decoder := reflect.ValueOf(decoderSymbol) + decoderType := reflect.TypeOf(decoderSymbol) + if decoderType.Kind() != reflect.Func { + return errorDecoderRequest(fmt.Errorf( + "decoder kind %s is not %s", decoder.Kind(), reflect.Func)) + } + + if err := decoderValidateArguments(decoderType); err != nil { + return errorDecoderRequest(err) + } + + if err := decoderValidateReturns(decoderType); err != nil { + return errorDecoderRequest(err) + } + + return func(ctx context.Context, payload []byte) (interface{}, error) { + // construct arguments + var args []reflect.Value + args = append(args, reflect.ValueOf(ctx)) + + eventType := decoderType.In(decoderType.NumIn() - 1) + event := reflect.New(eventType) + + if err := json.Unmarshal(payload, event.Interface()); err != nil { + return nil, err + } + + args = append(args, event.Elem()) + + response := decoder.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 + } +} + +func errorDecoderRequest(err error) DecodeRequestFunc { + return func(context.Context, []byte) (interface{}, error) { + return nil, err + } +} + +func decoderValidateArguments(decoderType reflect.Type) error { + if decoderType.NumIn() != 2 { + return fmt.Errorf( + "decoder must take two arguments, but it takes %d", + decoderType.NumIn()) + } + + contextType := reflect.TypeOf((*context.Context)(nil)).Elem() + argumentType := decoderType.In(0) + if !argumentType.Implements(contextType) { + return fmt.Errorf("decoder takes two arguments, but the first is not Context. got %s", argumentType.Kind()) + } + + return nil +} + +func decoderValidateReturns(decoderType reflect.Type) error { + errorType := reflect.TypeOf((*error)(nil)).Elem() + if decoderType.NumOut() != 2 { + return fmt.Errorf("decoder must return two values") + } + + if !decoderType.Out(1).Implements(errorType) { + return fmt.Errorf("decoder returns two values, but the second does not implement error") + } + return nil +} + +// EncodeResponseWrapper wraps an encoder into EncoderResponseFunc. +// The encoderSymbol function has to take in 2 arguments. The first one is +// a context.Context, the second argument is a user-domain response +// object. +// The encoderSymbol function has also to return 2 values. The first one is +// the intended response event, the second value is about error. +// An example for first return value is event.APIGatewayProxyResponse. +func EncodeResponseWrapper(encoderSymbol interface{}) EncodeResponseFunc { + if encoderSymbol == nil { + return errorEncodeResponse(fmt.Errorf("encoder is nil")) + } + + encoder := reflect.ValueOf(encoderSymbol) + encoderType := reflect.TypeOf(encoderSymbol) + if encoderType.Kind() != reflect.Func { + return errorEncodeResponse(fmt.Errorf( + "encoder kind %s is not %s", encoderType.Kind(), reflect.Func)) + } + + if err := encoderValidateArguments(encoderType); err != nil { + return errorEncodeResponse(err) + } + + if err := encoderValidateReturns(encoderType); err != nil { + return errorEncodeResponse(err) + } + + return func(ctx context.Context, response interface{}) ([]byte, error) { + // construct arguments + var args []reflect.Value + args = append(args, reflect.ValueOf(ctx)) + args = append(args, reflect.ValueOf(response)) + + rawResponse := encoder.Call(args) + + // convert return values into (interface{}, error) + var err error + if len(rawResponse) > 0 { + if errVal, ok := rawResponse[len(rawResponse)-1].Interface().(error); ok { + err = errVal + } + } + var val interface{} + if len(rawResponse) > 1 { + val = rawResponse[0].Interface() + } + + // convert return values into ([]byte, error) + if err != nil { + return nil, err + } + + responseByte, err := json.Marshal(val) + return responseByte, err + } +} + +func errorEncodeResponse(err error) EncodeResponseFunc { + return func(context.Context, interface{}) ([]byte, error) { + return nil, err + } +} + +func encoderValidateArguments(encoderType reflect.Type) error { + if encoderType.NumIn() != 2 { + return fmt.Errorf( + "encoder must take two arguments, but it takes %d", + encoderType.NumIn()) + } + + contextType := reflect.TypeOf((*context.Context)(nil)).Elem() + argumentType := encoderType.In(0) + if !argumentType.Implements(contextType) { + return fmt.Errorf("encoder takes two arguments, but the first is not Context. got %s", argumentType.Kind()) + } + + return nil +} + +func encoderValidateReturns(encoderType reflect.Type) error { + errorType := reflect.TypeOf((*error)(nil)).Elem() + if encoderType.NumOut() != 2 { + return fmt.Errorf("encoder must return two values") + } + + if !encoderType.Out(1).Implements(errorType) { + return fmt.Errorf("encoder returns two values, but the second does not implement error") + } + return nil +} + +// ErrorEncoderWrapper wraps a errorEncoder into a ErrorEncoder. +// The errorEncoder function has to accept 2 arguments. The first one +// is context.Context, and the second one is error. +// The errorEncoder function has to return 2 values. The first one is +// the intended event response, and the second one is error. +func ErrorEncoderWrapper(errorEncoderSymbol interface{}) ErrorEncoder { + if errorEncoderSymbol == nil { + return errorErrorEncoder(fmt.Errorf("errorEncoder is nil")) + } + + errorEncoder := reflect.ValueOf(errorEncoderSymbol) + errorEncoderType := reflect.TypeOf(errorEncoderSymbol) + if errorEncoderType.Kind() != reflect.Func { + return errorErrorEncoder(fmt.Errorf( + "errorEncoder kind %s is not %s", errorEncoderType.Kind(), reflect.Func)) + } + + if err := errorEncoderValidateArguments(errorEncoderType); err != nil { + return errorErrorEncoder(err) + } + + if err := errorEncoderValidateReturns(errorEncoderType); err != nil { + return errorErrorEncoder(err) + } + + return func(ctx context.Context, err error) ([]byte, error) { + // construct arguments + var args []reflect.Value + args = append(args, reflect.ValueOf(ctx)) + args = append(args, reflect.ValueOf(err)) + + rawResponse := errorEncoder.Call(args) + + // convert return values into (interface{}, error) + var returnErr error + if len(rawResponse) > 0 { + if errVal, ok := rawResponse[len(rawResponse)-1].Interface().(error); ok { + returnErr = errVal + } + } + var val interface{} + if len(rawResponse) > 1 { + val = rawResponse[0].Interface() + } + + // convert return values into ([]byte, error) + if returnErr != nil { + return nil, returnErr + } + + responseByte, returnErr := json.Marshal(val) + return responseByte, returnErr + } +} + +func errorErrorEncoder(err error) ErrorEncoder { + return func(ctx context.Context, inErr error) ([]byte, error) { + return nil, err + } +} + +func errorEncoderValidateArguments(errorEncoderType reflect.Type) error { + if errorEncoderType.NumIn() != 2 { + return fmt.Errorf( + "errorEncoder must take two arguments, but it takes %d", + errorEncoderType.NumIn()) + } + + contextType := reflect.TypeOf((*context.Context)(nil)).Elem() + argumentType := errorEncoderType.In(0) + if !argumentType.Implements(contextType) { + return fmt.Errorf("errorEncoder takes two arguments, but the first is not Context. got %s", argumentType.Kind()) + } + + errorType := reflect.TypeOf((*error)(nil)).Elem() + argumentType = errorEncoderType.In(1) + if !argumentType.Implements(errorType) { + return fmt.Errorf("errorEncoder takes two arguments, but the second is not error. got %s", argumentType.Kind()) + } + + return nil +} + +func errorEncoderValidateReturns(errorEncoderType reflect.Type) error { + errorType := reflect.TypeOf((*error)(nil)).Elem() + if errorEncoderType.NumOut() != 2 { + return fmt.Errorf("errorEncoder must return two values") + } + + if !errorEncoderType.Out(1).Implements(errorType) { + return fmt.Errorf("errorEncoder returns two values, but the second does not implement error") + } + return nil +} diff --git a/transport/awslambda/wrapper_test.go b/transport/awslambda/wrapper_test.go new file mode 100644 index 000000000..3157bdae6 --- /dev/null +++ b/transport/awslambda/wrapper_test.go @@ -0,0 +1,473 @@ +package awslambda + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/aws/aws-lambda-go/events" +) + +func TestInvokeWithWrapperHappyPath(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }), + EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, err + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, err + }), + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + resp, err := helloHandler.Invoke(ctx, req) + + if err != nil { + t.Fatalf("\nshould have no error, but got: %+v", err) + } + + apigwResp := events.APIGatewayProxyResponse{} + err = json.Unmarshal(resp, &apigwResp) + if err != nil { + t.Fatalf("\nShould have no error, but got: %+v", err) + } + + response := helloResponse{} + err = json.Unmarshal([]byte(apigwResp.Body), &response) + if err != nil { + t.Fatalf("\nShould have no error, but got: %+v", err) + } + + expectedGreeting := "hello john doe" + if response.Greeting != expectedGreeting { + t.Fatalf( + "\nExpect: %s\nActual: %s", expectedGreeting, response.Greeting) + } +} + +func TestInvokeWithWrapperErrorEncoder(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }), + EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, err + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, err + }), + ServerErrorEncoder(ErrorEncoderWrapper(func( + _ context.Context, err error, + ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { + apigwResp.Body = `{"error":"yes"}` + apigwResp.StatusCode = 500 + return apigwResp, nil + })), + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: "", + }) + resp, err := helloHandler.Invoke(ctx, req) + + if err != nil { + t.Fatalf("\nShould have no error, but got: %+v", err) + } + + apigwResp := events.APIGatewayProxyResponse{} + json.Unmarshal(resp, &apigwResp) + if apigwResp.StatusCode != 500 { + t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) + } +} + +func TestInvalidDecodeRequestWrapper(t *testing.T) { + svc := serviceTest01{} + validEncodeResponse := EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, err + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, err + }) + + testCases := []struct { + decoder interface{} + expectedErrMsg string + }{ + { + decoder: nil, + expectedErrMsg: "decoder is nil", + }, + { + decoder: "hello", + expectedErrMsg: "decoder kind string is not func", + }, + { + decoder: func() {}, + expectedErrMsg: "decoder must take two arguments, but it takes 0", + }, + { + decoder: func(s string, b string) {}, + expectedErrMsg: "decoder takes two arguments, but the first is not Context. got string", + }, + { + decoder: func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) { + }, + expectedErrMsg: "decoder must return two values", + }, + { + decoder: func( + ctx context.Context, req events.APIGatewayProxyRequest, + ) (helloRequest, string) { + request := helloRequest{} + return request, "yes" + }, + expectedErrMsg: "decoder returns two values, but the second does not implement error", + }, + } + + for _, tc := range testCases { + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + DecodeRequestWrapper(tc.decoder), + validEncodeResponse, + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + + _, err := helloHandler.Invoke(ctx, req) + + if err == nil { + t.Errorf("\nShould have error") + } + if err.Error() != tc.expectedErrMsg { + t.Fatalf( + "\nExpected:\n%+v\nActual:\n%+v", + tc.expectedErrMsg, err.Error()) + } + } +} + +func TestInvalidEncodeResponseWrapper(t *testing.T) { + svc := serviceTest01{} + validDecoder := DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }) + + testCases := []struct { + encoder interface{} + expectedErrMsg string + }{ + { + encoder: nil, + expectedErrMsg: "encoder is nil", + }, + { + encoder: "hello", + expectedErrMsg: "encoder kind string is not func", + }, + { + encoder: func() {}, + expectedErrMsg: "encoder must take two arguments, but it takes 0", + }, + { + encoder: func(s string, b string) {}, + expectedErrMsg: "encoder takes two arguments, but the first is not Context. got string", + }, + { + encoder: func( + ctx context.Context, response helloResponse, + ) { + }, + expectedErrMsg: "encoder must return two values", + }, + { + encoder: func( + ctx context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, s string) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, "err" + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, "err" + }, + expectedErrMsg: "encoder returns two values, but the second does not implement error", + }, + } + + for _, tc := range testCases { + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + validDecoder, + EncodeResponseWrapper(tc.encoder), + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + + _, err := helloHandler.Invoke(ctx, req) + + if err == nil { + t.Errorf("\nShould have error") + } + if err.Error() != tc.expectedErrMsg { + t.Fatalf( + "\nExpected:\n%+v\nActual:\n%+v", + tc.expectedErrMsg, err.Error()) + } + } +} + +func TestInvalidErrorEncoderWrapper(t *testing.T) { + svc := serviceTest01{} + validDecoder := DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }) + validEncoder := EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, err + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, err + }) + + testCases := []struct { + errorEncoder interface{} + expectedErrMsg string + }{ + { + errorEncoder: nil, + expectedErrMsg: "errorEncoder is nil", + }, + { + errorEncoder: "hello", + expectedErrMsg: "errorEncoder kind string is not func", + }, + { + errorEncoder: func() {}, + expectedErrMsg: "errorEncoder must take two arguments, but it takes 0", + }, + { + errorEncoder: func(s string, b string) {}, + expectedErrMsg: "errorEncoder takes two arguments, but the first is not Context. got string", + }, + { + errorEncoder: func( + ctx context.Context, b string, + ) { + }, + expectedErrMsg: "errorEncoder takes two arguments, but the second is not error. got string", + }, + { + errorEncoder: func( + ctx context.Context, err error, + ) { + }, + expectedErrMsg: "errorEncoder must return two values", + }, + { + errorEncoder: func( + ctx context.Context, err error, + ) (apigwResp events.APIGatewayProxyResponse, s string) { + apigwResp.Body = `{"error":"yes"}` + apigwResp.StatusCode = 500 + return apigwResp, "nil" + }, + expectedErrMsg: "errorEncoder returns two values, but the second does not implement error", + }, + } + + for _, tc := range testCases { + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + validDecoder, + validEncoder, + ServerErrorEncoder(ErrorEncoderWrapper(tc.errorEncoder)), + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: ``, + }) + + _, err := helloHandler.Invoke(ctx, req) + + if err == nil { + t.Errorf("\nShould have error") + } + if err.Error() != tc.expectedErrMsg { + t.Fatalf( + "\nExpected:\n%+v\nActual:\n%+v", + tc.expectedErrMsg, err.Error()) + } + } +} + +func TestWrapperInvalidPayloadFormat(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }), + EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, err + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, err + }), + ) + + ctx := context.Background() + req := []byte("") + _, err := helloHandler.Invoke(ctx, req) + + if err == nil { + t.Fatalf("\nShould have error") + } +} + +func TestWrapperErrorInEncodeResponse(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }), + EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + return apigwResp, fmt.Errorf("error") + }), + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: `{"name":"john doe"}`, + }) + _, err := helloHandler.Invoke(ctx, req) + + if err == nil { + t.Fatalf("\nShould have error") + } +} + +func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { + svc := serviceTest01{} + + helloHandler := NewServer( + makeTest01HelloEndpoint(svc), + DecodeRequestWrapper(func( + _ context.Context, apigwReq events.APIGatewayProxyRequest, + ) (helloRequest, error) { + request := helloRequest{} + err := json.Unmarshal([]byte(apigwReq.Body), &request) + return request, err + }), + EncodeResponseWrapper(func( + _ context.Context, response helloResponse, + ) (apigwResp events.APIGatewayProxyResponse, err error) { + respByte, err := json.Marshal(response) + if err != nil { + return apigwResp, err + } + + apigwResp.Body = string(respByte) + apigwResp.StatusCode = 200 + return apigwResp, err + }), + ServerErrorEncoder(ErrorEncoderWrapper(func( + _ context.Context, err error, + ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { + return apigwResp, fmt.Errorf("error") + })), + ) + + ctx := context.Background() + req, _ := json.Marshal(events.APIGatewayProxyRequest{ + Body: "", + }) + _, err := helloHandler.Invoke(ctx, req) + + if err == nil { + t.Fatalf("\nShould have error") + } +} From 50e89617c5c7669dd0d47fcd4e9a771734c58c9b Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 20 Jan 2019 21:57:27 +0800 Subject: [PATCH 14/31] capitalize Lambda --- transport/awslambda/doc.go | 2 +- transport/awslambda/encode_decode.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/transport/awslambda/doc.go b/transport/awslambda/doc.go index e81acba18..3827f8409 100644 --- a/transport/awslambda/doc.go +++ b/transport/awslambda/doc.go @@ -1,2 +1,2 @@ -// Package awslambda provides AWS lambda Handler transport layer. +// Package awslambda provides AWS Lambda Handler transport layer. package awslambda diff --git a/transport/awslambda/encode_decode.go b/transport/awslambda/encode_decode.go index 6d2018540..6cb5a69fe 100644 --- a/transport/awslambda/encode_decode.go +++ b/transport/awslambda/encode_decode.go @@ -5,11 +5,11 @@ import ( ) // DecodeRequestFunc extracts a user-domain request object from an -// AWS lambda's payload. +// AWS Lambda payload. type DecodeRequestFunc func(context.Context, []byte) (interface{}, error) // EncodeResponseFunc encodes the passed response object into []byte, -// ready to be sent as AWS lambda response. +// ready to be sent as AWS Lambda response. type EncodeResponseFunc func(context.Context, interface{}) ([]byte, error) // ErrorEncoder is responsible for encoding an error. From 356f72201f1249b1d34bad206efdcf3452bed0d8 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 20 Jan 2019 21:59:05 +0800 Subject: [PATCH 15/31] add particle an --- transport/awslambda/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/doc.go b/transport/awslambda/doc.go index 3827f8409..75411f7dd 100644 --- a/transport/awslambda/doc.go +++ b/transport/awslambda/doc.go @@ -1,2 +1,2 @@ -// Package awslambda provides AWS Lambda Handler transport layer. +// Package awslambda provides an AWS Lambda Handler transport layer. package awslambda From bceaf55a94d3d5d9ebd66f1293e68608dda02fc2 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 20 Jan 2019 22:01:56 +0800 Subject: [PATCH 16/31] tidy up doc words as per suggested --- transport/awslambda/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/doc.go b/transport/awslambda/doc.go index 75411f7dd..18942a136 100644 --- a/transport/awslambda/doc.go +++ b/transport/awslambda/doc.go @@ -1,2 +1,2 @@ -// Package awslambda provides an AWS Lambda Handler transport layer. +// Package awslambda provides an AWS Lambda transport layer. package awslambda From fe44f2d95b65b093237c9696b1b26470b4f6f11b Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sun, 20 Jan 2019 22:05:41 +0800 Subject: [PATCH 17/31] Update transport/awslambda/request_response_funcs.go Co-Authored-By: suekto-andreas --- transport/awslambda/request_response_funcs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index 5b8da3e84..a6f20f5dd 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -11,7 +11,7 @@ import ( type ServerRequestFunc func(ctx context.Context, payload []byte) context.Context // ServerResponseFunc may take information from a request context -// and use it to manipulate response (before marshalled.) +// and use it to manipulate the response before it's marshaled. // ServerResponseFunc are only executed after invoking the endpoint // but prior to returning a response. type ServerResponseFunc func(ctx context.Context, response interface{}) context.Context From 4c4334aebfd883d3655db11178931f42252dddd4 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sun, 20 Jan 2019 22:05:55 +0800 Subject: [PATCH 18/31] Update transport/awslambda/request_response_funcs.go Co-Authored-By: suekto-andreas --- transport/awslambda/request_response_funcs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index a6f20f5dd..06ce4da03 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -16,6 +16,6 @@ type ServerRequestFunc func(ctx context.Context, payload []byte) context.Context // but prior to returning a response. type ServerResponseFunc func(ctx context.Context, response interface{}) context.Context -// ServerFinalizerFunc is executed at the end of Invocation. +// ServerFinalizerFunc is executed at the end of Invoke. // This can be used for logging purposes. type ServerFinalizerFunc func(ctx context.Context, resp []byte, err error) From 9e2d959ab2c1107c061976cb6cf7eddb34a83059 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sun, 20 Jan 2019 22:06:42 +0800 Subject: [PATCH 19/31] Update transport/awslambda/server.go Co-Authored-By: suekto-andreas --- transport/awslambda/server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index dae69b383..b6c64a108 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -73,7 +73,8 @@ func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption { // Invoke represents implementation of the AWS lambda.Handler interface. func (s *Server) Invoke( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) (resp []byte, err error) { if len(s.finalizer) > 0 { defer func() { From cad1fdcdb61f5925d9cb69eb9760f53311e52ea1 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Sun, 20 Jan 2019 22:07:13 +0800 Subject: [PATCH 20/31] Update transport/awslambda/request_response_funcs.go Co-Authored-By: suekto-andreas --- transport/awslambda/request_response_funcs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index 06ce4da03..23285eb04 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -12,7 +12,7 @@ type ServerRequestFunc func(ctx context.Context, payload []byte) context.Context // ServerResponseFunc may take information from a request context // and use it to manipulate the response before it's marshaled. -// ServerResponseFunc are only executed after invoking the endpoint +// ServerResponseFunc are executed after invoking the endpoint // but prior to returning a response. type ServerResponseFunc func(ctx context.Context, response interface{}) context.Context From 3de95b5cc04b9fde6982631c339f757bafd83ef0 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 20 Jan 2019 22:14:49 +0800 Subject: [PATCH 21/31] refactor multiline to keep them 1 per line --- transport/awslambda/server_test.go | 40 ++++++++++++------ transport/awslambda/wrapper_test.go | 63 +++++++++++++++++++---------- 2 files changed, 69 insertions(+), 34 deletions(-) diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index ed1f4129b..d08e4ffaf 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -29,25 +29,29 @@ func TestInvokeHappyPath(t *testing.T) { encodeResponse, ServerErrorLogger(log.NewNopLogger()), ServerBefore(func( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), ServerBefore(func( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), ServerAfter(func( - ctx context.Context, response interface{}, + ctx context.Context, + response interface{}, ) context.Context { ctx = context.WithValue(ctx, KeyAfterOne, "af1") return ctx }), ServerAfter(func( - ctx context.Context, response interface{}, + ctx context.Context, + response interface{}, ) context.Context { if _, ok := ctx.Value(KeyAfterOne).(string); !ok { t.Fatalf("\nValue was not set properly during multi ServerAfter") @@ -55,7 +59,9 @@ func TestInvokeHappyPath(t *testing.T) { return ctx }), ServerFinalizer(func( - _ context.Context, resp []byte, _ error, + _ context.Context, + resp []byte, + _ error, ) { apigwResp := events.APIGatewayProxyResponse{} err := json.Unmarshal(resp, &apigwResp) @@ -114,7 +120,8 @@ func TestInvokeFailDecode(t *testing.T) { decodeHelloRequestWithTwoBefores, encodeResponse, ServerErrorEncoder(func( - ctx context.Context, err error, + ctx context.Context, + err error, ) ([]byte, error) { apigwResp := events.APIGatewayProxyResponse{} apigwResp.Body = `{"error":"yes"}` @@ -149,19 +156,22 @@ func TestInvokeFailEndpoint(t *testing.T) { decodeHelloRequestWithTwoBefores, encodeResponse, ServerBefore(func( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), ServerBefore(func( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), ServerErrorEncoder(func( - ctx context.Context, err error, + ctx context.Context, + err error, ) ([]byte, error) { apigwResp := events.APIGatewayProxyResponse{} apigwResp.Body = `{"error":"yes"}` @@ -196,25 +206,29 @@ func TestInvokeFailEncode(t *testing.T) { decodeHelloRequestWithTwoBefores, encodeResponse, ServerBefore(func( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), ServerBefore(func( - ctx context.Context, payload []byte, + ctx context.Context, + payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), ServerAfter(func( - ctx context.Context, response interface{}, + ctx context.Context, + response interface{}, ) context.Context { ctx = context.WithValue(ctx, KeyEncMode, "fail_encode") return ctx }), ServerErrorEncoder(func( - ctx context.Context, err error, + ctx context.Context, + err error, ) ([]byte, error) { // convert error into proper APIGateway response. apigwResp := events.APIGatewayProxyResponse{} diff --git a/transport/awslambda/wrapper_test.go b/transport/awslambda/wrapper_test.go index 3157bdae6..55d543c2d 100644 --- a/transport/awslambda/wrapper_test.go +++ b/transport/awslambda/wrapper_test.go @@ -15,14 +15,16 @@ func TestInvokeWithWrapperHappyPath(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) return request, err }), EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { respByte, err := json.Marshal(response) if err != nil { @@ -70,14 +72,16 @@ func TestInvokeWithWrapperErrorEncoder(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) return request, err }), EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { respByte, err := json.Marshal(response) if err != nil { @@ -89,7 +93,8 @@ func TestInvokeWithWrapperErrorEncoder(t *testing.T) { return apigwResp, err }), ServerErrorEncoder(ErrorEncoderWrapper(func( - _ context.Context, err error, + _ context.Context, + err error, ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { apigwResp.Body = `{"error":"yes"}` apigwResp.StatusCode = 500 @@ -117,7 +122,8 @@ func TestInvokeWithWrapperErrorEncoder(t *testing.T) { func TestInvalidDecodeRequestWrapper(t *testing.T) { svc := serviceTest01{} validEncodeResponse := EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { respByte, err := json.Marshal(response) if err != nil { @@ -151,7 +157,8 @@ func TestInvalidDecodeRequestWrapper(t *testing.T) { }, { decoder: func( - ctx context.Context, req events.APIGatewayProxyRequest, + ctx context.Context, + req events.APIGatewayProxyRequest, ) { }, expectedErrMsg: "decoder must return two values", @@ -195,7 +202,8 @@ func TestInvalidDecodeRequestWrapper(t *testing.T) { func TestInvalidEncodeResponseWrapper(t *testing.T) { svc := serviceTest01{} validDecoder := DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) @@ -231,7 +239,8 @@ func TestInvalidEncodeResponseWrapper(t *testing.T) { }, { encoder: func( - ctx context.Context, response helloResponse, + ctx context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, s string) { respByte, err := json.Marshal(response) if err != nil { @@ -274,14 +283,16 @@ func TestInvalidEncodeResponseWrapper(t *testing.T) { func TestInvalidErrorEncoderWrapper(t *testing.T) { svc := serviceTest01{} validDecoder := DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) return request, err }) validEncoder := EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { respByte, err := json.Marshal(response) if err != nil { @@ -315,21 +326,24 @@ func TestInvalidErrorEncoderWrapper(t *testing.T) { }, { errorEncoder: func( - ctx context.Context, b string, + ctx context.Context, + b string, ) { }, expectedErrMsg: "errorEncoder takes two arguments, but the second is not error. got string", }, { errorEncoder: func( - ctx context.Context, err error, + ctx context.Context, + err error, ) { }, expectedErrMsg: "errorEncoder must return two values", }, { errorEncoder: func( - ctx context.Context, err error, + ctx context.Context, + err error, ) (apigwResp events.APIGatewayProxyResponse, s string) { apigwResp.Body = `{"error":"yes"}` apigwResp.StatusCode = 500 @@ -371,14 +385,16 @@ func TestWrapperInvalidPayloadFormat(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) return request, err }), EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { respByte, err := json.Marshal(response) if err != nil { @@ -406,14 +422,16 @@ func TestWrapperErrorInEncodeResponse(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) return request, err }), EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { return apigwResp, fmt.Errorf("error") }), @@ -436,14 +454,16 @@ func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { helloHandler := NewServer( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( - _ context.Context, apigwReq events.APIGatewayProxyRequest, + _ context.Context, + apigwReq events.APIGatewayProxyRequest, ) (helloRequest, error) { request := helloRequest{} err := json.Unmarshal([]byte(apigwReq.Body), &request) return request, err }), EncodeResponseWrapper(func( - _ context.Context, response helloResponse, + _ context.Context, + response helloResponse, ) (apigwResp events.APIGatewayProxyResponse, err error) { respByte, err := json.Marshal(response) if err != nil { @@ -455,7 +475,8 @@ func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { return apigwResp, err }), ServerErrorEncoder(ErrorEncoderWrapper(func( - _ context.Context, err error, + _ context.Context, + err error, ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { return apigwResp, fmt.Errorf("error") })), From 62efc642e6fd2362e25ce314ca8ad47120405da8 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 20 Jan 2019 23:04:17 +0800 Subject: [PATCH 22/31] remove \n from test --- transport/awslambda/server_test.go | 28 +++++++++++++-------------- transport/awslambda/wrapper_test.go | 30 ++++++++++++++--------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/transport/awslambda/server_test.go b/transport/awslambda/server_test.go index d08e4ffaf..ee0a2d731 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/server_test.go @@ -54,7 +54,7 @@ func TestInvokeHappyPath(t *testing.T) { response interface{}, ) context.Context { if _, ok := ctx.Value(KeyAfterOne).(string); !ok { - t.Fatalf("\nValue was not set properly during multi ServerAfter") + t.Fatalf("Value was not set properly during multi ServerAfter") } return ctx }), @@ -66,19 +66,19 @@ func TestInvokeHappyPath(t *testing.T) { apigwResp := events.APIGatewayProxyResponse{} err := json.Unmarshal(resp, &apigwResp) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } response := helloResponse{} err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } expectedGreeting := "hello john doe bef1 bef2" if response.Greeting != expectedGreeting { t.Fatalf( - "\nExpect: %s\nActual: %s", expectedGreeting, response.Greeting) + "Expect: %s, Actual: %s", expectedGreeting, response.Greeting) } }), ) @@ -90,25 +90,25 @@ func TestInvokeHappyPath(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} err = json.Unmarshal(resp, &apigwResp) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } response := helloResponse{} err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } expectedGreeting := "hello john doe bef1 bef2" if response.Greeting != expectedGreeting { t.Fatalf( - "\nExpect: %s\nActual: %s", expectedGreeting, response.Greeting) + "Expect: %s, Actual: %s", expectedGreeting, response.Greeting) } } @@ -138,13 +138,13 @@ func TestInvokeFailDecode(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("Expect status code of 500, instead of %d", apigwResp.StatusCode) } } @@ -188,13 +188,13 @@ func TestInvokeFailEndpoint(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("Expect status code of 500, instead of %d", apigwResp.StatusCode) } } @@ -246,13 +246,13 @@ func TestInvokeFailEncode(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("Expect status code of 500, instead of %d", apigwResp.StatusCode) } } diff --git a/transport/awslambda/wrapper_test.go b/transport/awslambda/wrapper_test.go index 55d543c2d..95c37e1f3 100644 --- a/transport/awslambda/wrapper_test.go +++ b/transport/awslambda/wrapper_test.go @@ -44,25 +44,25 @@ func TestInvokeWithWrapperHappyPath(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nshould have no error, but got: %+v", err) + t.Fatalf("should have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} err = json.Unmarshal(resp, &apigwResp) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } response := helloResponse{} err = json.Unmarshal([]byte(apigwResp.Body), &response) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } expectedGreeting := "hello john doe" if response.Greeting != expectedGreeting { t.Fatalf( - "\nExpect: %s\nActual: %s", expectedGreeting, response.Greeting) + "Expect: %s, Actual: %s", expectedGreeting, response.Greeting) } } @@ -109,13 +109,13 @@ func TestInvokeWithWrapperErrorEncoder(t *testing.T) { resp, err := helloHandler.Invoke(ctx, req) if err != nil { - t.Fatalf("\nShould have no error, but got: %+v", err) + t.Fatalf("Should have no error, but got: %+v", err) } apigwResp := events.APIGatewayProxyResponse{} json.Unmarshal(resp, &apigwResp) if apigwResp.StatusCode != 500 { - t.Fatalf("\nExpect status code of 500, instead of %d", apigwResp.StatusCode) + t.Fatalf("Expect status code of 500, instead of %d", apigwResp.StatusCode) } } @@ -189,11 +189,11 @@ func TestInvalidDecodeRequestWrapper(t *testing.T) { _, err := helloHandler.Invoke(ctx, req) if err == nil { - t.Errorf("\nShould have error") + t.Errorf("Should have error") } if err.Error() != tc.expectedErrMsg { t.Fatalf( - "\nExpected:\n%+v\nActual:\n%+v", + "Expected: %+v Actual: %+v", tc.expectedErrMsg, err.Error()) } } @@ -270,11 +270,11 @@ func TestInvalidEncodeResponseWrapper(t *testing.T) { _, err := helloHandler.Invoke(ctx, req) if err == nil { - t.Errorf("\nShould have error") + t.Errorf("Should have error") } if err.Error() != tc.expectedErrMsg { t.Fatalf( - "\nExpected:\n%+v\nActual:\n%+v", + "Expected: %+v Actual: %+v", tc.expectedErrMsg, err.Error()) } } @@ -369,11 +369,11 @@ func TestInvalidErrorEncoderWrapper(t *testing.T) { _, err := helloHandler.Invoke(ctx, req) if err == nil { - t.Errorf("\nShould have error") + t.Errorf("Should have error") } if err.Error() != tc.expectedErrMsg { t.Fatalf( - "\nExpected:\n%+v\nActual:\n%+v", + "Expected: %+v Actual: %+v", tc.expectedErrMsg, err.Error()) } } @@ -412,7 +412,7 @@ func TestWrapperInvalidPayloadFormat(t *testing.T) { _, err := helloHandler.Invoke(ctx, req) if err == nil { - t.Fatalf("\nShould have error") + t.Fatalf("Should have error") } } @@ -444,7 +444,7 @@ func TestWrapperErrorInEncodeResponse(t *testing.T) { _, err := helloHandler.Invoke(ctx, req) if err == nil { - t.Fatalf("\nShould have error") + t.Fatalf("Should have error") } } @@ -489,6 +489,6 @@ func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { _, err := helloHandler.Invoke(ctx, req) if err == nil { - t.Fatalf("\nShould have error") + t.Fatalf("Should have error") } } From 19d5afe1bcf2f2477088319f98f49a9fab870b50 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 28 Jan 2019 21:42:14 +0800 Subject: [PATCH 23/31] defines a DefaultErrorEncoder, refactor the handling of errorEncoder during Invoke --- transport/awslambda/server.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/transport/awslambda/server.go b/transport/awslambda/server.go index b6c64a108..d853a6a7d 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/server.go @@ -28,10 +28,11 @@ func NewServer( options ...ServerOption, ) *Server { s := &Server{ - e: e, - dec: dec, - enc: enc, - logger: log.NewNopLogger(), + e: e, + dec: dec, + enc: enc, + logger: log.NewNopLogger(), + errorEncoder: DefaultErrorEncoder, } for _, option := range options { option(s) @@ -71,6 +72,12 @@ func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption { return func(s *Server) { s.finalizer = append(s.finalizer, f...) } } +// DefaultErrorEncoder defines the default behavior of encoding an error response, +// where it returns nil, and the error itself. +func DefaultErrorEncoder(ctx context.Context, err error) ([]byte, error) { + return nil, err +} + // Invoke represents implementation of the AWS lambda.Handler interface. func (s *Server) Invoke( ctx context.Context, @@ -91,18 +98,14 @@ func (s *Server) Invoke( request, err := s.dec(ctx, payload) if err != nil { s.logger.Log("err", err) - if s.errorEncoder != nil { - resp, err = s.errorEncoder(ctx, err) - } + resp, err = s.errorEncoder(ctx, err) return } response, err := s.e(ctx, request) if err != nil { s.logger.Log("err", err) - if s.errorEncoder != nil { - resp, err = s.errorEncoder(ctx, err) - } + resp, err = s.errorEncoder(ctx, err) return } @@ -112,9 +115,7 @@ func (s *Server) Invoke( if resp, err = s.enc(ctx, response); err != nil { s.logger.Log("err", err) - if s.errorEncoder != nil { - resp, err = s.errorEncoder(ctx, err) - } + resp, err = s.errorEncoder(ctx, err) return } From 6a69716c2627c3f21bb917a4dca29bb55643ea46 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 28 Jan 2019 21:44:49 +0800 Subject: [PATCH 24/31] refactor Server into Handler --- transport/awslambda/{server.go => handler.go} | 56 +++++++++---------- .../{server_test.go => handler_test.go} | 42 +++++++------- transport/awslambda/request_response_funcs.go | 16 +++--- transport/awslambda/wrapper_test.go | 22 ++++---- 4 files changed, 68 insertions(+), 68 deletions(-) rename transport/awslambda/{server.go => handler.go} (57%) rename transport/awslambda/{server_test.go => handler_test.go} (91%) diff --git a/transport/awslambda/server.go b/transport/awslambda/handler.go similarity index 57% rename from transport/awslambda/server.go rename to transport/awslambda/handler.go index d853a6a7d..51bae4df7 100644 --- a/transport/awslambda/server.go +++ b/transport/awslambda/handler.go @@ -7,27 +7,27 @@ import ( "github.com/go-kit/kit/log" ) -// Server wraps an endpoint. -type Server struct { +// Handler wraps an endpoint. +type Handler struct { e endpoint.Endpoint dec DecodeRequestFunc enc EncodeResponseFunc - before []ServerRequestFunc - after []ServerResponseFunc + before []HandlerRequestFunc + after []HandlerResponseFunc errorEncoder ErrorEncoder - finalizer []ServerFinalizerFunc + finalizer []HandlerFinalizerFunc logger log.Logger } -// NewServer constructs a new server, which implements +// NewHandler constructs a new handler, which implements // the AWS lambda.Handler interface. -func NewServer( +func NewHandler( e endpoint.Endpoint, dec DecodeRequestFunc, enc EncodeResponseFunc, - options ...ServerOption, -) *Server { - s := &Server{ + options ...HandlerOption, +) *Handler { + s := &Handler{ e: e, dec: dec, enc: enc, @@ -40,36 +40,36 @@ func NewServer( return s } -// ServerOption sets an optional parameter for servers. -type ServerOption func(*Server) +// HandlerOption sets an optional parameter for handlers. +type HandlerOption func(*Handler) -// ServerBefore functions are executed on the payload byte, +// HandlerBefore functions are executed on the payload byte, // before the request is decoded. -func ServerBefore(before ...ServerRequestFunc) ServerOption { - return func(s *Server) { s.before = append(s.before, before...) } +func HandlerBefore(before ...HandlerRequestFunc) HandlerOption { + return func(s *Handler) { s.before = append(s.before, before...) } } -// ServerAfter functions are only executed after invoking the endpoint +// HandlerAfter functions are only executed after invoking the endpoint // but prior to returning a response. -func ServerAfter(after ...ServerResponseFunc) ServerOption { - return func(s *Server) { s.after = append(s.after, after...) } +func HandlerAfter(after ...HandlerResponseFunc) HandlerOption { + return func(s *Handler) { s.after = append(s.after, after...) } } -// ServerErrorLogger is used to log non-terminal errors. +// HandlerErrorLogger is used to log non-terminal errors. // By default, no errors are logged. -func ServerErrorLogger(logger log.Logger) ServerOption { - return func(s *Server) { s.logger = logger } +func HandlerErrorLogger(logger log.Logger) HandlerOption { + return func(s *Handler) { s.logger = logger } } -// ServerErrorEncoder is used to encode errors. -func ServerErrorEncoder(ee ErrorEncoder) ServerOption { - return func(s *Server) { s.errorEncoder = ee } +// HandlerErrorEncoder is used to encode errors. +func HandlerErrorEncoder(ee ErrorEncoder) HandlerOption { + return func(s *Handler) { s.errorEncoder = ee } } -// ServerFinalizer sets finalizer which are called at the end of +// HandlerFinalizer sets finalizer which are called at the end of // request. By default no finalizer is registered. -func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption { - return func(s *Server) { s.finalizer = append(s.finalizer, f...) } +func HandlerFinalizer(f ...HandlerFinalizerFunc) HandlerOption { + return func(s *Handler) { s.finalizer = append(s.finalizer, f...) } } // DefaultErrorEncoder defines the default behavior of encoding an error response, @@ -79,7 +79,7 @@ func DefaultErrorEncoder(ctx context.Context, err error) ([]byte, error) { } // Invoke represents implementation of the AWS lambda.Handler interface. -func (s *Server) Invoke( +func (s *Handler) Invoke( ctx context.Context, payload []byte, ) (resp []byte, err error) { diff --git a/transport/awslambda/server_test.go b/transport/awslambda/handler_test.go similarity index 91% rename from transport/awslambda/server_test.go rename to transport/awslambda/handler_test.go index ee0a2d731..809ed0753 100644 --- a/transport/awslambda/server_test.go +++ b/transport/awslambda/handler_test.go @@ -23,42 +23,42 @@ const ( func TestInvokeHappyPath(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), decodeHelloRequestWithTwoBefores, encodeResponse, - ServerErrorLogger(log.NewNopLogger()), - ServerBefore(func( + HandlerErrorLogger(log.NewNopLogger()), + HandlerBefore(func( ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), - ServerBefore(func( + HandlerBefore(func( ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), - ServerAfter(func( + HandlerAfter(func( ctx context.Context, response interface{}, ) context.Context { ctx = context.WithValue(ctx, KeyAfterOne, "af1") return ctx }), - ServerAfter(func( + HandlerAfter(func( ctx context.Context, response interface{}, ) context.Context { if _, ok := ctx.Value(KeyAfterOne).(string); !ok { - t.Fatalf("Value was not set properly during multi ServerAfter") + t.Fatalf("Value was not set properly during multi HandlerAfter") } return ctx }), - ServerFinalizer(func( + HandlerFinalizer(func( _ context.Context, resp []byte, _ error, @@ -115,11 +115,11 @@ func TestInvokeHappyPath(t *testing.T) { func TestInvokeFailDecode(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), decodeHelloRequestWithTwoBefores, encodeResponse, - ServerErrorEncoder(func( + HandlerErrorEncoder(func( ctx context.Context, err error, ) ([]byte, error) { @@ -151,25 +151,25 @@ func TestInvokeFailDecode(t *testing.T) { func TestInvokeFailEndpoint(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01FailEndpoint(svc), decodeHelloRequestWithTwoBefores, encodeResponse, - ServerBefore(func( + HandlerBefore(func( ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), - ServerBefore(func( + HandlerBefore(func( ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), - ServerErrorEncoder(func( + HandlerErrorEncoder(func( ctx context.Context, err error, ) ([]byte, error) { @@ -201,32 +201,32 @@ func TestInvokeFailEndpoint(t *testing.T) { func TestInvokeFailEncode(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), decodeHelloRequestWithTwoBefores, encodeResponse, - ServerBefore(func( + HandlerBefore(func( ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeOne, "bef1") return ctx }), - ServerBefore(func( + HandlerBefore(func( ctx context.Context, payload []byte, ) context.Context { ctx = context.WithValue(ctx, KeyBeforeTwo, "bef2") return ctx }), - ServerAfter(func( + HandlerAfter(func( ctx context.Context, response interface{}, ) context.Context { ctx = context.WithValue(ctx, KeyEncMode, "fail_encode") return ctx }), - ServerErrorEncoder(func( + HandlerErrorEncoder(func( ctx context.Context, err error, ) ([]byte, error) { @@ -274,13 +274,13 @@ func decodeHelloRequestWithTwoBefores( valOne, ok := ctx.Value(KeyBeforeOne).(string) if !ok { return request, fmt.Errorf( - "Value was not set properly when multiple ServerBefores are used") + "Value was not set properly when multiple HandlerBefores are used") } valTwo, ok := ctx.Value(KeyBeforeTwo).(string) if !ok { return request, fmt.Errorf( - "Value was not set properly when multiple ServerBefores are used") + "Value was not set properly when multiple HandlerBefores are used") } request.Name += " " + valOne + " " + valTwo diff --git a/transport/awslambda/request_response_funcs.go b/transport/awslambda/request_response_funcs.go index 23285eb04..d85f27328 100644 --- a/transport/awslambda/request_response_funcs.go +++ b/transport/awslambda/request_response_funcs.go @@ -4,18 +4,18 @@ import ( "context" ) -// ServerRequestFunc may take information from the received +// HandlerRequestFunc may take information from the received // payload and use it to place items in the request scoped context. -// ServerRequestFuncs are executed prior to invoking the endpoint and +// HandlerRequestFuncs are executed prior to invoking the endpoint and // decoding of the payload. -type ServerRequestFunc func(ctx context.Context, payload []byte) context.Context +type HandlerRequestFunc func(ctx context.Context, payload []byte) context.Context -// ServerResponseFunc may take information from a request context +// HandlerResponseFunc may take information from a request context // and use it to manipulate the response before it's marshaled. -// ServerResponseFunc are executed after invoking the endpoint +// HandlerResponseFunc are executed after invoking the endpoint // but prior to returning a response. -type ServerResponseFunc func(ctx context.Context, response interface{}) context.Context +type HandlerResponseFunc func(ctx context.Context, response interface{}) context.Context -// ServerFinalizerFunc is executed at the end of Invoke. +// HandlerFinalizerFunc is executed at the end of Invoke. // This can be used for logging purposes. -type ServerFinalizerFunc func(ctx context.Context, resp []byte, err error) +type HandlerFinalizerFunc func(ctx context.Context, resp []byte, err error) diff --git a/transport/awslambda/wrapper_test.go b/transport/awslambda/wrapper_test.go index 95c37e1f3..1a2796c3b 100644 --- a/transport/awslambda/wrapper_test.go +++ b/transport/awslambda/wrapper_test.go @@ -12,7 +12,7 @@ import ( func TestInvokeWithWrapperHappyPath(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( _ context.Context, @@ -69,7 +69,7 @@ func TestInvokeWithWrapperHappyPath(t *testing.T) { func TestInvokeWithWrapperErrorEncoder(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( _ context.Context, @@ -92,7 +92,7 @@ func TestInvokeWithWrapperErrorEncoder(t *testing.T) { apigwResp.StatusCode = 200 return apigwResp, err }), - ServerErrorEncoder(ErrorEncoderWrapper(func( + HandlerErrorEncoder(ErrorEncoderWrapper(func( _ context.Context, err error, ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { @@ -175,7 +175,7 @@ func TestInvalidDecodeRequestWrapper(t *testing.T) { } for _, tc := range testCases { - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(tc.decoder), validEncodeResponse, @@ -256,7 +256,7 @@ func TestInvalidEncodeResponseWrapper(t *testing.T) { } for _, tc := range testCases { - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), validDecoder, EncodeResponseWrapper(tc.encoder), @@ -354,11 +354,11 @@ func TestInvalidErrorEncoderWrapper(t *testing.T) { } for _, tc := range testCases { - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), validDecoder, validEncoder, - ServerErrorEncoder(ErrorEncoderWrapper(tc.errorEncoder)), + HandlerErrorEncoder(ErrorEncoderWrapper(tc.errorEncoder)), ) ctx := context.Background() @@ -382,7 +382,7 @@ func TestInvalidErrorEncoderWrapper(t *testing.T) { func TestWrapperInvalidPayloadFormat(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( _ context.Context, @@ -419,7 +419,7 @@ func TestWrapperInvalidPayloadFormat(t *testing.T) { func TestWrapperErrorInEncodeResponse(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( _ context.Context, @@ -451,7 +451,7 @@ func TestWrapperErrorInEncodeResponse(t *testing.T) { func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { svc := serviceTest01{} - helloHandler := NewServer( + helloHandler := NewHandler( makeTest01HelloEndpoint(svc), DecodeRequestWrapper(func( _ context.Context, @@ -474,7 +474,7 @@ func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { apigwResp.StatusCode = 200 return apigwResp, err }), - ServerErrorEncoder(ErrorEncoderWrapper(func( + HandlerErrorEncoder(ErrorEncoderWrapper(func( _ context.Context, err error, ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { From 5a7cbf48baf113efd6ec7c15a9afecc13d7f00f5 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 29 Jan 2019 05:54:59 +0800 Subject: [PATCH 25/31] remove wrapper --- transport/awslambda/wrapper.go | 285 ---------------- transport/awslambda/wrapper_test.go | 494 ---------------------------- 2 files changed, 779 deletions(-) delete mode 100644 transport/awslambda/wrapper.go delete mode 100644 transport/awslambda/wrapper_test.go diff --git a/transport/awslambda/wrapper.go b/transport/awslambda/wrapper.go deleted file mode 100644 index 160ee4f76..000000000 --- a/transport/awslambda/wrapper.go +++ /dev/null @@ -1,285 +0,0 @@ -package awslambda - -import ( - "context" - "encoding/json" - "fmt" - "reflect" -) - -// DecodeRequestWrapper wraps the given decoderSymbol function, and generates -// the proper DecodeRequestFunc, based on the decoderSymbol function signature. -// The decoderSymbol function signature has to receive 2 args, which is the -// context.Context and event request to decode. -// It has also to return 2 values, the user-domain object and error. -func DecodeRequestWrapper(decoderSymbol interface{}) DecodeRequestFunc { - if decoderSymbol == nil { - return errorDecoderRequest(fmt.Errorf("decoder is nil")) - } - - decoder := reflect.ValueOf(decoderSymbol) - decoderType := reflect.TypeOf(decoderSymbol) - if decoderType.Kind() != reflect.Func { - return errorDecoderRequest(fmt.Errorf( - "decoder kind %s is not %s", decoder.Kind(), reflect.Func)) - } - - if err := decoderValidateArguments(decoderType); err != nil { - return errorDecoderRequest(err) - } - - if err := decoderValidateReturns(decoderType); err != nil { - return errorDecoderRequest(err) - } - - return func(ctx context.Context, payload []byte) (interface{}, error) { - // construct arguments - var args []reflect.Value - args = append(args, reflect.ValueOf(ctx)) - - eventType := decoderType.In(decoderType.NumIn() - 1) - event := reflect.New(eventType) - - if err := json.Unmarshal(payload, event.Interface()); err != nil { - return nil, err - } - - args = append(args, event.Elem()) - - response := decoder.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 - } -} - -func errorDecoderRequest(err error) DecodeRequestFunc { - return func(context.Context, []byte) (interface{}, error) { - return nil, err - } -} - -func decoderValidateArguments(decoderType reflect.Type) error { - if decoderType.NumIn() != 2 { - return fmt.Errorf( - "decoder must take two arguments, but it takes %d", - decoderType.NumIn()) - } - - contextType := reflect.TypeOf((*context.Context)(nil)).Elem() - argumentType := decoderType.In(0) - if !argumentType.Implements(contextType) { - return fmt.Errorf("decoder takes two arguments, but the first is not Context. got %s", argumentType.Kind()) - } - - return nil -} - -func decoderValidateReturns(decoderType reflect.Type) error { - errorType := reflect.TypeOf((*error)(nil)).Elem() - if decoderType.NumOut() != 2 { - return fmt.Errorf("decoder must return two values") - } - - if !decoderType.Out(1).Implements(errorType) { - return fmt.Errorf("decoder returns two values, but the second does not implement error") - } - return nil -} - -// EncodeResponseWrapper wraps an encoder into EncoderResponseFunc. -// The encoderSymbol function has to take in 2 arguments. The first one is -// a context.Context, the second argument is a user-domain response -// object. -// The encoderSymbol function has also to return 2 values. The first one is -// the intended response event, the second value is about error. -// An example for first return value is event.APIGatewayProxyResponse. -func EncodeResponseWrapper(encoderSymbol interface{}) EncodeResponseFunc { - if encoderSymbol == nil { - return errorEncodeResponse(fmt.Errorf("encoder is nil")) - } - - encoder := reflect.ValueOf(encoderSymbol) - encoderType := reflect.TypeOf(encoderSymbol) - if encoderType.Kind() != reflect.Func { - return errorEncodeResponse(fmt.Errorf( - "encoder kind %s is not %s", encoderType.Kind(), reflect.Func)) - } - - if err := encoderValidateArguments(encoderType); err != nil { - return errorEncodeResponse(err) - } - - if err := encoderValidateReturns(encoderType); err != nil { - return errorEncodeResponse(err) - } - - return func(ctx context.Context, response interface{}) ([]byte, error) { - // construct arguments - var args []reflect.Value - args = append(args, reflect.ValueOf(ctx)) - args = append(args, reflect.ValueOf(response)) - - rawResponse := encoder.Call(args) - - // convert return values into (interface{}, error) - var err error - if len(rawResponse) > 0 { - if errVal, ok := rawResponse[len(rawResponse)-1].Interface().(error); ok { - err = errVal - } - } - var val interface{} - if len(rawResponse) > 1 { - val = rawResponse[0].Interface() - } - - // convert return values into ([]byte, error) - if err != nil { - return nil, err - } - - responseByte, err := json.Marshal(val) - return responseByte, err - } -} - -func errorEncodeResponse(err error) EncodeResponseFunc { - return func(context.Context, interface{}) ([]byte, error) { - return nil, err - } -} - -func encoderValidateArguments(encoderType reflect.Type) error { - if encoderType.NumIn() != 2 { - return fmt.Errorf( - "encoder must take two arguments, but it takes %d", - encoderType.NumIn()) - } - - contextType := reflect.TypeOf((*context.Context)(nil)).Elem() - argumentType := encoderType.In(0) - if !argumentType.Implements(contextType) { - return fmt.Errorf("encoder takes two arguments, but the first is not Context. got %s", argumentType.Kind()) - } - - return nil -} - -func encoderValidateReturns(encoderType reflect.Type) error { - errorType := reflect.TypeOf((*error)(nil)).Elem() - if encoderType.NumOut() != 2 { - return fmt.Errorf("encoder must return two values") - } - - if !encoderType.Out(1).Implements(errorType) { - return fmt.Errorf("encoder returns two values, but the second does not implement error") - } - return nil -} - -// ErrorEncoderWrapper wraps a errorEncoder into a ErrorEncoder. -// The errorEncoder function has to accept 2 arguments. The first one -// is context.Context, and the second one is error. -// The errorEncoder function has to return 2 values. The first one is -// the intended event response, and the second one is error. -func ErrorEncoderWrapper(errorEncoderSymbol interface{}) ErrorEncoder { - if errorEncoderSymbol == nil { - return errorErrorEncoder(fmt.Errorf("errorEncoder is nil")) - } - - errorEncoder := reflect.ValueOf(errorEncoderSymbol) - errorEncoderType := reflect.TypeOf(errorEncoderSymbol) - if errorEncoderType.Kind() != reflect.Func { - return errorErrorEncoder(fmt.Errorf( - "errorEncoder kind %s is not %s", errorEncoderType.Kind(), reflect.Func)) - } - - if err := errorEncoderValidateArguments(errorEncoderType); err != nil { - return errorErrorEncoder(err) - } - - if err := errorEncoderValidateReturns(errorEncoderType); err != nil { - return errorErrorEncoder(err) - } - - return func(ctx context.Context, err error) ([]byte, error) { - // construct arguments - var args []reflect.Value - args = append(args, reflect.ValueOf(ctx)) - args = append(args, reflect.ValueOf(err)) - - rawResponse := errorEncoder.Call(args) - - // convert return values into (interface{}, error) - var returnErr error - if len(rawResponse) > 0 { - if errVal, ok := rawResponse[len(rawResponse)-1].Interface().(error); ok { - returnErr = errVal - } - } - var val interface{} - if len(rawResponse) > 1 { - val = rawResponse[0].Interface() - } - - // convert return values into ([]byte, error) - if returnErr != nil { - return nil, returnErr - } - - responseByte, returnErr := json.Marshal(val) - return responseByte, returnErr - } -} - -func errorErrorEncoder(err error) ErrorEncoder { - return func(ctx context.Context, inErr error) ([]byte, error) { - return nil, err - } -} - -func errorEncoderValidateArguments(errorEncoderType reflect.Type) error { - if errorEncoderType.NumIn() != 2 { - return fmt.Errorf( - "errorEncoder must take two arguments, but it takes %d", - errorEncoderType.NumIn()) - } - - contextType := reflect.TypeOf((*context.Context)(nil)).Elem() - argumentType := errorEncoderType.In(0) - if !argumentType.Implements(contextType) { - return fmt.Errorf("errorEncoder takes two arguments, but the first is not Context. got %s", argumentType.Kind()) - } - - errorType := reflect.TypeOf((*error)(nil)).Elem() - argumentType = errorEncoderType.In(1) - if !argumentType.Implements(errorType) { - return fmt.Errorf("errorEncoder takes two arguments, but the second is not error. got %s", argumentType.Kind()) - } - - return nil -} - -func errorEncoderValidateReturns(errorEncoderType reflect.Type) error { - errorType := reflect.TypeOf((*error)(nil)).Elem() - if errorEncoderType.NumOut() != 2 { - return fmt.Errorf("errorEncoder must return two values") - } - - if !errorEncoderType.Out(1).Implements(errorType) { - return fmt.Errorf("errorEncoder returns two values, but the second does not implement error") - } - return nil -} diff --git a/transport/awslambda/wrapper_test.go b/transport/awslambda/wrapper_test.go deleted file mode 100644 index 1a2796c3b..000000000 --- a/transport/awslambda/wrapper_test.go +++ /dev/null @@ -1,494 +0,0 @@ -package awslambda - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - "github.com/aws/aws-lambda-go/events" -) - -func TestInvokeWithWrapperHappyPath(t *testing.T) { - svc := serviceTest01{} - - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }), - EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, err - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, err - }), - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: `{"name":"john doe"}`, - }) - resp, err := helloHandler.Invoke(ctx, req) - - if err != nil { - t.Fatalf("should have no error, but got: %+v", err) - } - - apigwResp := events.APIGatewayProxyResponse{} - err = json.Unmarshal(resp, &apigwResp) - if err != nil { - t.Fatalf("Should have no error, but got: %+v", err) - } - - response := helloResponse{} - err = json.Unmarshal([]byte(apigwResp.Body), &response) - if err != nil { - t.Fatalf("Should have no error, but got: %+v", err) - } - - expectedGreeting := "hello john doe" - if response.Greeting != expectedGreeting { - t.Fatalf( - "Expect: %s, Actual: %s", expectedGreeting, response.Greeting) - } -} - -func TestInvokeWithWrapperErrorEncoder(t *testing.T) { - svc := serviceTest01{} - - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }), - EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, err - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, err - }), - HandlerErrorEncoder(ErrorEncoderWrapper(func( - _ context.Context, - err error, - ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { - apigwResp.Body = `{"error":"yes"}` - apigwResp.StatusCode = 500 - return apigwResp, nil - })), - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: "", - }) - resp, err := helloHandler.Invoke(ctx, req) - - if err != nil { - t.Fatalf("Should have no error, but got: %+v", err) - } - - apigwResp := events.APIGatewayProxyResponse{} - json.Unmarshal(resp, &apigwResp) - if apigwResp.StatusCode != 500 { - t.Fatalf("Expect status code of 500, instead of %d", apigwResp.StatusCode) - } -} - -func TestInvalidDecodeRequestWrapper(t *testing.T) { - svc := serviceTest01{} - validEncodeResponse := EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, err - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, err - }) - - testCases := []struct { - decoder interface{} - expectedErrMsg string - }{ - { - decoder: nil, - expectedErrMsg: "decoder is nil", - }, - { - decoder: "hello", - expectedErrMsg: "decoder kind string is not func", - }, - { - decoder: func() {}, - expectedErrMsg: "decoder must take two arguments, but it takes 0", - }, - { - decoder: func(s string, b string) {}, - expectedErrMsg: "decoder takes two arguments, but the first is not Context. got string", - }, - { - decoder: func( - ctx context.Context, - req events.APIGatewayProxyRequest, - ) { - }, - expectedErrMsg: "decoder must return two values", - }, - { - decoder: func( - ctx context.Context, req events.APIGatewayProxyRequest, - ) (helloRequest, string) { - request := helloRequest{} - return request, "yes" - }, - expectedErrMsg: "decoder returns two values, but the second does not implement error", - }, - } - - for _, tc := range testCases { - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - DecodeRequestWrapper(tc.decoder), - validEncodeResponse, - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: `{"name":"john doe"}`, - }) - - _, err := helloHandler.Invoke(ctx, req) - - if err == nil { - t.Errorf("Should have error") - } - if err.Error() != tc.expectedErrMsg { - t.Fatalf( - "Expected: %+v Actual: %+v", - tc.expectedErrMsg, err.Error()) - } - } -} - -func TestInvalidEncodeResponseWrapper(t *testing.T) { - svc := serviceTest01{} - validDecoder := DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }) - - testCases := []struct { - encoder interface{} - expectedErrMsg string - }{ - { - encoder: nil, - expectedErrMsg: "encoder is nil", - }, - { - encoder: "hello", - expectedErrMsg: "encoder kind string is not func", - }, - { - encoder: func() {}, - expectedErrMsg: "encoder must take two arguments, but it takes 0", - }, - { - encoder: func(s string, b string) {}, - expectedErrMsg: "encoder takes two arguments, but the first is not Context. got string", - }, - { - encoder: func( - ctx context.Context, response helloResponse, - ) { - }, - expectedErrMsg: "encoder must return two values", - }, - { - encoder: func( - ctx context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, s string) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, "err" - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, "err" - }, - expectedErrMsg: "encoder returns two values, but the second does not implement error", - }, - } - - for _, tc := range testCases { - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - validDecoder, - EncodeResponseWrapper(tc.encoder), - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: `{"name":"john doe"}`, - }) - - _, err := helloHandler.Invoke(ctx, req) - - if err == nil { - t.Errorf("Should have error") - } - if err.Error() != tc.expectedErrMsg { - t.Fatalf( - "Expected: %+v Actual: %+v", - tc.expectedErrMsg, err.Error()) - } - } -} - -func TestInvalidErrorEncoderWrapper(t *testing.T) { - svc := serviceTest01{} - validDecoder := DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }) - validEncoder := EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, err - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, err - }) - - testCases := []struct { - errorEncoder interface{} - expectedErrMsg string - }{ - { - errorEncoder: nil, - expectedErrMsg: "errorEncoder is nil", - }, - { - errorEncoder: "hello", - expectedErrMsg: "errorEncoder kind string is not func", - }, - { - errorEncoder: func() {}, - expectedErrMsg: "errorEncoder must take two arguments, but it takes 0", - }, - { - errorEncoder: func(s string, b string) {}, - expectedErrMsg: "errorEncoder takes two arguments, but the first is not Context. got string", - }, - { - errorEncoder: func( - ctx context.Context, - b string, - ) { - }, - expectedErrMsg: "errorEncoder takes two arguments, but the second is not error. got string", - }, - { - errorEncoder: func( - ctx context.Context, - err error, - ) { - }, - expectedErrMsg: "errorEncoder must return two values", - }, - { - errorEncoder: func( - ctx context.Context, - err error, - ) (apigwResp events.APIGatewayProxyResponse, s string) { - apigwResp.Body = `{"error":"yes"}` - apigwResp.StatusCode = 500 - return apigwResp, "nil" - }, - expectedErrMsg: "errorEncoder returns two values, but the second does not implement error", - }, - } - - for _, tc := range testCases { - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - validDecoder, - validEncoder, - HandlerErrorEncoder(ErrorEncoderWrapper(tc.errorEncoder)), - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: ``, - }) - - _, err := helloHandler.Invoke(ctx, req) - - if err == nil { - t.Errorf("Should have error") - } - if err.Error() != tc.expectedErrMsg { - t.Fatalf( - "Expected: %+v Actual: %+v", - tc.expectedErrMsg, err.Error()) - } - } -} - -func TestWrapperInvalidPayloadFormat(t *testing.T) { - svc := serviceTest01{} - - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }), - EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, err - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, err - }), - ) - - ctx := context.Background() - req := []byte("") - _, err := helloHandler.Invoke(ctx, req) - - if err == nil { - t.Fatalf("Should have error") - } -} - -func TestWrapperErrorInEncodeResponse(t *testing.T) { - svc := serviceTest01{} - - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }), - EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - return apigwResp, fmt.Errorf("error") - }), - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: `{"name":"john doe"}`, - }) - _, err := helloHandler.Invoke(ctx, req) - - if err == nil { - t.Fatalf("Should have error") - } -} - -func TestInvokeWithWrapperErrorEncoderReturnsError(t *testing.T) { - svc := serviceTest01{} - - helloHandler := NewHandler( - makeTest01HelloEndpoint(svc), - DecodeRequestWrapper(func( - _ context.Context, - apigwReq events.APIGatewayProxyRequest, - ) (helloRequest, error) { - request := helloRequest{} - err := json.Unmarshal([]byte(apigwReq.Body), &request) - return request, err - }), - EncodeResponseWrapper(func( - _ context.Context, - response helloResponse, - ) (apigwResp events.APIGatewayProxyResponse, err error) { - respByte, err := json.Marshal(response) - if err != nil { - return apigwResp, err - } - - apigwResp.Body = string(respByte) - apigwResp.StatusCode = 200 - return apigwResp, err - }), - HandlerErrorEncoder(ErrorEncoderWrapper(func( - _ context.Context, - err error, - ) (apigwResp events.APIGatewayProxyResponse, returnErr error) { - return apigwResp, fmt.Errorf("error") - })), - ) - - ctx := context.Background() - req, _ := json.Marshal(events.APIGatewayProxyRequest{ - Body: "", - }) - _, err := helloHandler.Invoke(ctx, req) - - if err == nil { - t.Fatalf("Should have error") - } -} From e831b16d6ff6e6a903edb7c64e445deebf47b063 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 29 Jan 2019 05:55:27 +0800 Subject: [PATCH 26/31] refactor variable s into h --- transport/awslambda/handler.go | 50 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/transport/awslambda/handler.go b/transport/awslambda/handler.go index 51bae4df7..d73261d15 100644 --- a/transport/awslambda/handler.go +++ b/transport/awslambda/handler.go @@ -27,7 +27,7 @@ func NewHandler( enc EncodeResponseFunc, options ...HandlerOption, ) *Handler { - s := &Handler{ + h := &Handler{ e: e, dec: dec, enc: enc, @@ -35,41 +35,41 @@ func NewHandler( errorEncoder: DefaultErrorEncoder, } for _, option := range options { - option(s) + option(h) } - return s + return h } -// HandlerOption sets an optional parameter for handlers. +// HandlerOption sets an optional parameter for handlerh. type HandlerOption func(*Handler) // HandlerBefore functions are executed on the payload byte, // before the request is decoded. func HandlerBefore(before ...HandlerRequestFunc) HandlerOption { - return func(s *Handler) { s.before = append(s.before, before...) } + return func(h *Handler) { h.before = append(h.before, before...) } } // HandlerAfter functions are only executed after invoking the endpoint // but prior to returning a response. func HandlerAfter(after ...HandlerResponseFunc) HandlerOption { - return func(s *Handler) { s.after = append(s.after, after...) } + return func(h *Handler) { h.after = append(h.after, after...) } } -// HandlerErrorLogger is used to log non-terminal errors. +// HandlerErrorLogger is used to log non-terminal errorh. // By default, no errors are logged. func HandlerErrorLogger(logger log.Logger) HandlerOption { - return func(s *Handler) { s.logger = logger } + return func(h *Handler) { h.logger = logger } } -// HandlerErrorEncoder is used to encode errors. +// HandlerErrorEncoder is used to encode errorh. func HandlerErrorEncoder(ee ErrorEncoder) HandlerOption { - return func(s *Handler) { s.errorEncoder = ee } + return func(h *Handler) { h.errorEncoder = ee } } // HandlerFinalizer sets finalizer which are called at the end of // request. By default no finalizer is registered. func HandlerFinalizer(f ...HandlerFinalizerFunc) HandlerOption { - return func(s *Handler) { s.finalizer = append(s.finalizer, f...) } + return func(h *Handler) { h.finalizer = append(h.finalizer, f...) } } // DefaultErrorEncoder defines the default behavior of encoding an error response, @@ -79,43 +79,43 @@ func DefaultErrorEncoder(ctx context.Context, err error) ([]byte, error) { } // Invoke represents implementation of the AWS lambda.Handler interface. -func (s *Handler) Invoke( +func (h *Handler) Invoke( ctx context.Context, payload []byte, ) (resp []byte, err error) { - if len(s.finalizer) > 0 { + if len(h.finalizer) > 0 { defer func() { - for _, f := range s.finalizer { + for _, f := range h.finalizer { f(ctx, resp, err) } }() } - for _, f := range s.before { + for _, f := range h.before { ctx = f(ctx, payload) } - request, err := s.dec(ctx, payload) + request, err := h.dec(ctx, payload) if err != nil { - s.logger.Log("err", err) - resp, err = s.errorEncoder(ctx, err) + h.logger.Log("err", err) + resp, err = h.errorEncoder(ctx, err) return } - response, err := s.e(ctx, request) + response, err := h.e(ctx, request) if err != nil { - s.logger.Log("err", err) - resp, err = s.errorEncoder(ctx, err) + h.logger.Log("err", err) + resp, err = h.errorEncoder(ctx, err) return } - for _, f := range s.after { + for _, f := range h.after { ctx = f(ctx, response) } - if resp, err = s.enc(ctx, response); err != nil { - s.logger.Log("err", err) - resp, err = s.errorEncoder(ctx, err) + if resp, err = h.enc(ctx, response); err != nil { + h.logger.Log("err", err) + resp, err = h.errorEncoder(ctx, err) return } From 1be9848c661fcdf8563c8da20dcd8e56397e15ff Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 29 Jan 2019 05:56:31 +0800 Subject: [PATCH 27/31] simplify the return of errorEncoder --- transport/awslambda/handler.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/transport/awslambda/handler.go b/transport/awslambda/handler.go index d73261d15..4eea41fdc 100644 --- a/transport/awslambda/handler.go +++ b/transport/awslambda/handler.go @@ -98,15 +98,13 @@ func (h *Handler) Invoke( request, err := h.dec(ctx, payload) if err != nil { h.logger.Log("err", err) - resp, err = h.errorEncoder(ctx, err) - return + return h.errorEncoder(ctx, err) } response, err := h.e(ctx, request) if err != nil { h.logger.Log("err", err) - resp, err = h.errorEncoder(ctx, err) - return + return h.errorEncoder(ctx, err) } for _, f := range h.after { @@ -115,8 +113,7 @@ func (h *Handler) Invoke( if resp, err = h.enc(ctx, response); err != nil { h.logger.Log("err", err) - resp, err = h.errorEncoder(ctx, err) - return + return h.errorEncoder(ctx, err) } return resp, err From 4f010575563ad30844b4fc9be69d58ee4a3057ca Mon Sep 17 00:00:00 2001 From: Mathis Wiehl Date: Tue, 29 Jan 2019 22:55:22 +0800 Subject: [PATCH 28/31] Update transport/awslambda/handler.go Co-Authored-By: suekto-andreas --- transport/awslambda/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/handler.go b/transport/awslambda/handler.go index 4eea41fdc..93170c642 100644 --- a/transport/awslambda/handler.go +++ b/transport/awslambda/handler.go @@ -61,7 +61,7 @@ func HandlerErrorLogger(logger log.Logger) HandlerOption { return func(h *Handler) { h.logger = logger } } -// HandlerErrorEncoder is used to encode errorh. +// HandlerErrorEncoder is used to encode errors. func HandlerErrorEncoder(ee ErrorEncoder) HandlerOption { return func(h *Handler) { h.errorEncoder = ee } } From a04bdd6ba3c591bad67401a05f408c102da17874 Mon Sep 17 00:00:00 2001 From: Mathis Wiehl Date: Tue, 29 Jan 2019 22:55:50 +0800 Subject: [PATCH 29/31] Update transport/awslambda/handler.go Co-Authored-By: suekto-andreas --- transport/awslambda/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/handler.go b/transport/awslambda/handler.go index 93170c642..9a9d923e3 100644 --- a/transport/awslambda/handler.go +++ b/transport/awslambda/handler.go @@ -40,7 +40,7 @@ func NewHandler( return h } -// HandlerOption sets an optional parameter for handlerh. +// HandlerOption sets an optional parameter for handlers. type HandlerOption func(*Handler) // HandlerBefore functions are executed on the payload byte, From 40f8b1aa046d7799c90d35350a7b74d7240857d4 Mon Sep 17 00:00:00 2001 From: Mathis Wiehl Date: Tue, 29 Jan 2019 22:56:03 +0800 Subject: [PATCH 30/31] Update transport/awslambda/handler.go Co-Authored-By: suekto-andreas --- transport/awslambda/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/awslambda/handler.go b/transport/awslambda/handler.go index 9a9d923e3..1aedb286c 100644 --- a/transport/awslambda/handler.go +++ b/transport/awslambda/handler.go @@ -55,7 +55,7 @@ func HandlerAfter(after ...HandlerResponseFunc) HandlerOption { return func(h *Handler) { h.after = append(h.after, after...) } } -// HandlerErrorLogger is used to log non-terminal errorh. +// HandlerErrorLogger is used to log non-terminal errors. // By default, no errors are logged. func HandlerErrorLogger(logger log.Logger) HandlerOption { return func(h *Handler) { h.logger = logger } From 1a313386a870a505534d47b3eb1a253eadb595b0 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 29 Jan 2019 23:03:55 +0800 Subject: [PATCH 31/31] DefaultErrorEncoder unit test --- transport/awslambda/handler_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/transport/awslambda/handler_test.go b/transport/awslambda/handler_test.go index 809ed0753..8add6be19 100644 --- a/transport/awslambda/handler_test.go +++ b/transport/awslambda/handler_test.go @@ -20,6 +20,18 @@ const ( KeyEncMode key = iota ) +func TestDefaultErrorEncoder(t *testing.T) { + ctx := context.Background() + rootErr := fmt.Errorf("root") + b, err := DefaultErrorEncoder(ctx, rootErr) + if b != nil { + t.Fatalf("DefaultErrorEncoder should return nil as []byte") + } + if err != rootErr { + t.Fatalf("DefaultErrorEncoder expects return back the given error.") + } +} + func TestInvokeHappyPath(t *testing.T) { svc := serviceTest01{}