diff --git a/api/context.go b/api/context.go index d8e390e..f265f2a 100644 --- a/api/context.go +++ b/api/context.go @@ -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 } diff --git a/handler.go b/handler.go index 87ce076..7bda72e 100644 --- a/handler.go +++ b/handler.go @@ -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 diff --git a/handler_test.go b/handler_test.go index 6d0cfd8..7c951b7 100644 --- a/handler_test.go +++ b/handler_test.go @@ -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 } diff --git a/internal/ctx/ctx.go b/internal/ctx/ctx.go new file mode 100644 index 0000000..dd59756 --- /dev/null +++ b/internal/ctx/ctx.go @@ -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 +) diff --git a/internal/dynfunc/handler.go b/internal/dynfunc/handler.go index 634cfc6..b4a371a 100644 --- a/internal/dynfunc/handler.go +++ b/internal/dynfunc/handler.go @@ -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) diff --git a/internal/dynfunc/handler_test.go b/internal/dynfunc/handler_test.go index a457f1e..d9167c4 100644 --- a/internal/dynfunc/handler_test.go +++ b/internal/dynfunc/handler_test.go @@ -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) } diff --git a/internal/dynfunc/signature_test.go b/internal/dynfunc/signature_test.go index 8860a92..205f58e 100644 --- a/internal/dynfunc/signature_test.go +++ b/internal/dynfunc/signature_test.go @@ -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, }, }