refactor: handler signature an middlewares for an idiomatic solution #24

Merged
xdrm-brackets merged 6 commits from refactor/api-context into 0.4.0 2021-06-20 07:51:42 +00:00
7 changed files with 77 additions and 39 deletions
Showing only changes of commit fa1ecfd97f - Show all commits

View File

@ -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
}

View File

@ -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

View File

@ -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
}

13
internal/ctx/ctx.go Normal file
View File

@ -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
)

View File

@ -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)

View File

@ -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)
}

View File

@ -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,
},
}