refactor: handler signature an middlewares for an idiomatic solution #24
|
@ -1,17 +1,42 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ctx contains additional information for handlers
|
// custom context key type
|
||||||
//
|
type ctxKey int
|
||||||
// usually input/output arguments built by aicra are sufficient
|
|
||||||
// but the Ctx lets you manage your request from scratch if required
|
const (
|
||||||
//
|
ctxRequest ctxKey = iota
|
||||||
// If required, set api.Ctx as the first argument of your handler; if you
|
ctxAuth
|
||||||
// don't need it, only use standard input arguments and it will be ignored
|
)
|
||||||
type Ctx struct {
|
|
||||||
Res http.ResponseWriter
|
// Context is a simple wrapper around context.Context that adds helper methods
|
||||||
Req *http.Request
|
// to access additional information
|
||||||
|
type Context struct{ context.Context }
|
||||||
|
|
||||||
|
// Request current request
|
||||||
|
func (c Context) Request() *http.Request {
|
||||||
|
var (
|
||||||
|
raw = c.Value(ctxRequest)
|
||||||
|
cast, ok = raw.(*http.Request)
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cast
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth associated with this request
|
||||||
|
func (c Context) Auth() *Auth {
|
||||||
|
var (
|
||||||
|
raw = c.Value(ctxAuth)
|
||||||
|
cast, ok = raw.(*Auth)
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cast
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ func (s Handler) resolve(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func (s *Handler) handle(input *reqdata.T, handler *apiHandler, service *config.Service, w http.ResponseWriter, r *http.Request) {
|
func (s *Handler) handle(input *reqdata.T, handler *apiHandler, service *config.Service, w http.ResponseWriter, r *http.Request) {
|
||||||
// 5. pass execution to the handler
|
// 5. pass execution to the handler
|
||||||
ctx := api.Ctx{Res: w, Req: r}
|
ctx := api.Context{Res: w, Req: r}
|
||||||
var outData, outErr = handler.dyn.Handle(ctx, input.Data)
|
var outData, outErr = handler.dyn.Handle(ctx, input.Data)
|
||||||
|
|
||||||
// 6. build res from returned data
|
// 6. build res from returned data
|
||||||
|
|
|
@ -82,7 +82,7 @@ func TestWith(t *testing.T) {
|
||||||
t.Fatalf("setup: unexpected error <%v>", err)
|
t.Fatalf("setup: unexpected error <%v>", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pathHandler := func(ctx api.Ctx) (*struct{}, api.Err) {
|
pathHandler := func(ctx api.Context) (*struct{}, api.Err) {
|
||||||
// write value from middlewares into response
|
// write value from middlewares into response
|
||||||
value := ctx.Req.Context().Value(key)
|
value := ctx.Req.Context().Value(key)
|
||||||
if value == nil {
|
if value == nil {
|
||||||
|
@ -237,7 +237,7 @@ func TestWithAuth(t *testing.T) {
|
||||||
t.Fatalf("setup: unexpected error <%v>", err)
|
t.Fatalf("setup: unexpected error <%v>", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pathHandler := func(ctx api.Ctx) (*struct{}, api.Err) {
|
pathHandler := func(ctx api.Context) (*struct{}, api.Err) {
|
||||||
return nil, api.ErrNotImplemented
|
return nil, api.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ctx
|
||||||
|
|
||||||
|
// Key defines a custom context key type
|
||||||
|
type Key int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Request is the key for the current *http.Request
|
||||||
|
Request Key = iota
|
||||||
|
// Response is the key for the associated http.ResponseWriter
|
||||||
|
Response
|
||||||
|
// Auth is the key for the request's authentication information
|
||||||
|
Auth
|
||||||
|
)
|
|
@ -41,7 +41,7 @@ func Build(fn interface{}, service config.Service) (*Handler, error) {
|
||||||
return nil, errHandlerNotFunc
|
return nil, errHandlerNotFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
h.hasContext = impl.NumIn() >= 1 && reflect.TypeOf(api.Ctx{}).AssignableTo(impl.In(0))
|
h.hasContext = impl.NumIn() >= 1 && reflect.TypeOf(api.Context{}).AssignableTo(impl.In(0))
|
||||||
if h.hasContext {
|
if h.hasContext {
|
||||||
h.dataIndex = 1
|
h.dataIndex = 1
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func Build(fn interface{}, service config.Service) (*Handler, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle binds input @data into the dynamic function and returns map output
|
// Handle binds input @data into the dynamic function and returns map output
|
||||||
func (h *Handler) Handle(ctx api.Ctx, data map[string]interface{}) (map[string]interface{}, api.Err) {
|
func (h *Handler) Handle(ctx api.Context, data map[string]interface{}) (map[string]interface{}, api.Err) {
|
||||||
var ert = reflect.TypeOf(api.Err{})
|
var ert = reflect.TypeOf(api.Err{})
|
||||||
var fnv = reflect.ValueOf(h.fn)
|
var fnv = reflect.ValueOf(h.fn)
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ func TestInput(t *testing.T) {
|
||||||
input[key] = val
|
input[key] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
var output, err = handler.Handle(api.Ctx{}, input)
|
var output, err = handler.Handle(api.Context{}, input)
|
||||||
if err != tcase.ExpectedErr {
|
if err != tcase.ExpectedErr {
|
||||||
t.Fatalf("expected api error <%v> got <%v>", tcase.ExpectedErr, err)
|
t.Fatalf("expected api error <%v> got <%v>", tcase.ExpectedErr, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,21 @@ func TestInputCheck(t *testing.T) {
|
||||||
Name: "no input 0 given",
|
Name: "no input 0 given",
|
||||||
Input: map[string]reflect.Type{},
|
Input: map[string]reflect.Type{},
|
||||||
Fn: func() {},
|
Fn: func() {},
|
||||||
FnCtx: func(api.Ctx) {},
|
FnCtx: func(api.Context) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "no input 1 given",
|
Name: "no input 1 given",
|
||||||
Input: map[string]reflect.Type{},
|
Input: map[string]reflect.Type{},
|
||||||
Fn: func(int) {},
|
Fn: func(int) {},
|
||||||
FnCtx: func(api.Ctx, int) {},
|
FnCtx: func(api.Context, int) {},
|
||||||
Err: errUnexpectedInput,
|
Err: errUnexpectedInput,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "no input 2 given",
|
Name: "no input 2 given",
|
||||||
Input: map[string]reflect.Type{},
|
Input: map[string]reflect.Type{},
|
||||||
Fn: func(int, string) {},
|
Fn: func(int, string) {},
|
||||||
FnCtx: func(api.Ctx, int, string) {},
|
FnCtx: func(api.Context, int, string) {},
|
||||||
Err: errUnexpectedInput,
|
Err: errUnexpectedInput,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(int(0)),
|
"Test1": reflect.TypeOf(int(0)),
|
||||||
},
|
},
|
||||||
Fn: func() {},
|
Fn: func() {},
|
||||||
FnCtx: func(api.Ctx) {},
|
FnCtx: func(api.Context) {},
|
||||||
Err: errMissingHandlerArgumentParam,
|
Err: errMissingHandlerArgumentParam,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(int(0)),
|
"Test1": reflect.TypeOf(int(0)),
|
||||||
},
|
},
|
||||||
Fn: func(int) {},
|
Fn: func(int) {},
|
||||||
FnCtx: func(api.Ctx, int) {},
|
FnCtx: func(api.Context, int) {},
|
||||||
Err: errMissingParamArgument,
|
Err: errMissingParamArgument,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -62,7 +62,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"test1": reflect.TypeOf(int(0)),
|
"test1": reflect.TypeOf(int(0)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{}) {},
|
Fn: func(struct{}) {},
|
||||||
FnCtx: func(api.Ctx, struct{}) {},
|
FnCtx: func(api.Context, struct{}) {},
|
||||||
Err: errUnexportedName,
|
Err: errUnexportedName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(int(0)),
|
"Test1": reflect.TypeOf(int(0)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{}) {},
|
Fn: func(struct{}) {},
|
||||||
FnCtx: func(api.Ctx, struct{}) {},
|
FnCtx: func(api.Context, struct{}) {},
|
||||||
Err: errMissingParamFromConfig,
|
Err: errMissingParamFromConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -80,7 +80,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(int(0)),
|
"Test1": reflect.TypeOf(int(0)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 string }) {},
|
Fn: func(struct{ Test1 string }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 string }) {},
|
FnCtx: func(api.Context, struct{ Test1 string }) {},
|
||||||
Err: errWrongParamTypeFromConfig,
|
Err: errWrongParamTypeFromConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -89,7 +89,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(int(0)),
|
"Test1": reflect.TypeOf(int(0)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 int }) {},
|
Fn: func(struct{ Test1 int }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 int }) {},
|
FnCtx: func(api.Context, struct{ Test1 int }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -98,7 +98,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(int)),
|
"Test1": reflect.TypeOf(new(int)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{}) {},
|
Fn: func(struct{}) {},
|
||||||
FnCtx: func(api.Ctx, struct{}) {},
|
FnCtx: func(api.Context, struct{}) {},
|
||||||
Err: errMissingParamFromConfig,
|
Err: errMissingParamFromConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -107,7 +107,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(int)),
|
"Test1": reflect.TypeOf(new(int)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 string }) {},
|
Fn: func(struct{ Test1 string }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 string }) {},
|
FnCtx: func(api.Context, struct{ Test1 string }) {},
|
||||||
Err: errWrongParamTypeFromConfig,
|
Err: errWrongParamTypeFromConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -116,7 +116,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(int)),
|
"Test1": reflect.TypeOf(new(int)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *string }) {},
|
Fn: func(struct{ Test1 *string }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *string }) {},
|
FnCtx: func(api.Context, struct{ Test1 *string }) {},
|
||||||
Err: errWrongParamTypeFromConfig,
|
Err: errWrongParamTypeFromConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(int)),
|
"Test1": reflect.TypeOf(new(int)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *int }) {},
|
Fn: func(struct{ Test1 *int }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *int }) {},
|
FnCtx: func(api.Context, struct{ Test1 *int }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -134,7 +134,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(string("")),
|
"Test1": reflect.TypeOf(string("")),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 string }) {},
|
Fn: func(struct{ Test1 string }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 string }) {},
|
FnCtx: func(api.Context, struct{ Test1 string }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -143,7 +143,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(uint(0)),
|
"Test1": reflect.TypeOf(uint(0)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 uint }) {},
|
Fn: func(struct{ Test1 uint }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 uint }) {},
|
FnCtx: func(api.Context, struct{ Test1 uint }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -152,7 +152,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(float64(0)),
|
"Test1": reflect.TypeOf(float64(0)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 float64 }) {},
|
Fn: func(struct{ Test1 float64 }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 float64 }) {},
|
FnCtx: func(api.Context, struct{ Test1 float64 }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -161,7 +161,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf([]byte("")),
|
"Test1": reflect.TypeOf([]byte("")),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 []byte }) {},
|
Fn: func(struct{ Test1 []byte }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 []byte }) {},
|
FnCtx: func(api.Context, struct{ Test1 []byte }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -170,7 +170,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf([]rune("")),
|
"Test1": reflect.TypeOf([]rune("")),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 []rune }) {},
|
Fn: func(struct{ Test1 []rune }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 []rune }) {},
|
FnCtx: func(api.Context, struct{ Test1 []rune }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -179,7 +179,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(string)),
|
"Test1": reflect.TypeOf(new(string)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *string }) {},
|
Fn: func(struct{ Test1 *string }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *string }) {},
|
FnCtx: func(api.Context, struct{ Test1 *string }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(uint)),
|
"Test1": reflect.TypeOf(new(uint)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *uint }) {},
|
Fn: func(struct{ Test1 *uint }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *uint }) {},
|
FnCtx: func(api.Context, struct{ Test1 *uint }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -197,7 +197,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new(float64)),
|
"Test1": reflect.TypeOf(new(float64)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *float64 }) {},
|
Fn: func(struct{ Test1 *float64 }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *float64 }) {},
|
FnCtx: func(api.Context, struct{ Test1 *float64 }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -206,7 +206,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new([]byte)),
|
"Test1": reflect.TypeOf(new([]byte)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *[]byte }) {},
|
Fn: func(struct{ Test1 *[]byte }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *[]byte }) {},
|
FnCtx: func(api.Context, struct{ Test1 *[]byte }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -215,7 +215,7 @@ func TestInputCheck(t *testing.T) {
|
||||||
"Test1": reflect.TypeOf(new([]rune)),
|
"Test1": reflect.TypeOf(new([]rune)),
|
||||||
},
|
},
|
||||||
Fn: func(struct{ Test1 *[]rune }) {},
|
Fn: func(struct{ Test1 *[]rune }) {},
|
||||||
FnCtx: func(api.Ctx, struct{ Test1 *[]rune }) {},
|
FnCtx: func(api.Context, struct{ Test1 *[]rune }) {},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue