refactor: handler signature an middlewares for an idiomatic solution #24
|
@ -1,17 +1,42 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Ctx contains additional information for handlers
|
||||
//
|
||||
// usually input/output arguments built by aicra are sufficient
|
||||
// but the Ctx lets you manage your request from scratch if required
|
||||
//
|
||||
// If required, set api.Ctx as the first argument of your handler; if you
|
||||
// don't need it, only use standard input arguments and it will be ignored
|
||||
type Ctx struct {
|
||||
Res http.ResponseWriter
|
||||
Req *http.Request
|
||||
// custom context key type
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
ctxRequest ctxKey = iota
|
||||
ctxAuth
|
||||
)
|
||||
|
||||
// Context is a simple wrapper around context.Context that adds helper methods
|
||||
// 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) {
|
||||
// 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)
|
||||
|
||||
// 6. build res from returned data
|
||||
|
|
|
@ -82,7 +82,7 @@ func TestWith(t *testing.T) {
|
|||
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
|
||||
value := ctx.Req.Context().Value(key)
|
||||
if value == nil {
|
||||
|
@ -237,7 +237,7 @@ func TestWithAuth(t *testing.T) {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
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 fnv = reflect.ValueOf(h.fn)
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ func TestInput(t *testing.T) {
|
|||
input[key] = val
|
||||
}
|
||||
|
||||
var output, err = handler.Handle(api.Ctx{}, input)
|
||||
var output, err = handler.Handle(api.Context{}, input)
|
||||
if err != tcase.ExpectedErr {
|
||||
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",
|
||||
Input: map[string]reflect.Type{},
|
||||
Fn: func() {},
|
||||
FnCtx: func(api.Ctx) {},
|
||||
FnCtx: func(api.Context) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "no input 1 given",
|
||||
Input: map[string]reflect.Type{},
|
||||
Fn: func(int) {},
|
||||
FnCtx: func(api.Ctx, int) {},
|
||||
FnCtx: func(api.Context, int) {},
|
||||
Err: errUnexpectedInput,
|
||||
},
|
||||
{
|
||||
Name: "no input 2 given",
|
||||
Input: map[string]reflect.Type{},
|
||||
Fn: func(int, string) {},
|
||||
FnCtx: func(api.Ctx, int, string) {},
|
||||
FnCtx: func(api.Context, int, string) {},
|
||||
Err: errUnexpectedInput,
|
||||
},
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(int(0)),
|
||||
},
|
||||
Fn: func() {},
|
||||
FnCtx: func(api.Ctx) {},
|
||||
FnCtx: func(api.Context) {},
|
||||
Err: errMissingHandlerArgumentParam,
|
||||
},
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(int(0)),
|
||||
},
|
||||
Fn: func(int) {},
|
||||
FnCtx: func(api.Ctx, int) {},
|
||||
FnCtx: func(api.Context, int) {},
|
||||
Err: errMissingParamArgument,
|
||||
},
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"test1": reflect.TypeOf(int(0)),
|
||||
},
|
||||
Fn: func(struct{}) {},
|
||||
FnCtx: func(api.Ctx, struct{}) {},
|
||||
FnCtx: func(api.Context, struct{}) {},
|
||||
Err: errUnexportedName,
|
||||
},
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(int(0)),
|
||||
},
|
||||
Fn: func(struct{}) {},
|
||||
FnCtx: func(api.Ctx, struct{}) {},
|
||||
FnCtx: func(api.Context, struct{}) {},
|
||||
Err: errMissingParamFromConfig,
|
||||
},
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(int(0)),
|
||||
},
|
||||
Fn: func(struct{ Test1 string }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 string }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 string }) {},
|
||||
Err: errWrongParamTypeFromConfig,
|
||||
},
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(int(0)),
|
||||
},
|
||||
Fn: func(struct{ Test1 int }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 int }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 int }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(int)),
|
||||
},
|
||||
Fn: func(struct{}) {},
|
||||
FnCtx: func(api.Ctx, struct{}) {},
|
||||
FnCtx: func(api.Context, struct{}) {},
|
||||
Err: errMissingParamFromConfig,
|
||||
},
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(int)),
|
||||
},
|
||||
Fn: func(struct{ Test1 string }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 string }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 string }) {},
|
||||
Err: errWrongParamTypeFromConfig,
|
||||
},
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(int)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *string }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *string }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *string }) {},
|
||||
Err: errWrongParamTypeFromConfig,
|
||||
},
|
||||
{
|
||||
|
@ -125,7 +125,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(int)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *int }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *int }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *int }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -134,7 +134,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(string("")),
|
||||
},
|
||||
Fn: func(struct{ Test1 string }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 string }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 string }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(uint(0)),
|
||||
},
|
||||
Fn: func(struct{ Test1 uint }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 uint }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 uint }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -152,7 +152,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(float64(0)),
|
||||
},
|
||||
Fn: func(struct{ Test1 float64 }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 float64 }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 float64 }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -161,7 +161,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf([]byte("")),
|
||||
},
|
||||
Fn: func(struct{ Test1 []byte }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 []byte }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 []byte }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -170,7 +170,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf([]rune("")),
|
||||
},
|
||||
Fn: func(struct{ Test1 []rune }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 []rune }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 []rune }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -179,7 +179,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(string)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *string }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *string }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *string }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -188,7 +188,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(uint)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *uint }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *uint }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *uint }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -197,7 +197,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new(float64)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *float64 }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *float64 }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *float64 }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -206,7 +206,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new([]byte)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *[]byte }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *[]byte }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *[]byte }) {},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
|
@ -215,7 +215,7 @@ func TestInputCheck(t *testing.T) {
|
|||
"Test1": reflect.TypeOf(new([]rune)),
|
||||
},
|
||||
Fn: func(struct{ Test1 *[]rune }) {},
|
||||
FnCtx: func(api.Ctx, struct{ Test1 *[]rune }) {},
|
||||
FnCtx: func(api.Context, struct{ Test1 *[]rune }) {},
|
||||
Err: nil,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue