diff --git a/api/adapter.go b/api/adapter.go new file mode 100644 index 0000000..08a9959 --- /dev/null +++ b/api/adapter.go @@ -0,0 +1,6 @@ +package api + +import "net/http" + +// Adapter to encapsulate incoming requests +type Adapter func(http.HandlerFunc) http.HandlerFunc diff --git a/builder.go b/builder.go index 65c5240..f65fefa 100644 --- a/builder.go +++ b/builder.go @@ -5,6 +5,7 @@ import ( "io" "net/http" + "git.xdrm.io/go/aicra/api" "git.xdrm.io/go/aicra/datatype" "git.xdrm.io/go/aicra/internal/config" "git.xdrm.io/go/aicra/internal/dynfunc" @@ -14,6 +15,7 @@ import ( type Builder struct { conf *config.Server handlers []*apiHandler + adapters []api.Adapter } // represents an api handler (method-pattern combination) @@ -31,9 +33,23 @@ func (b *Builder) AddType(t datatype.T) { if b.conf.Services != nil { panic(errLateType) } + if b.conf.Types == nil { + b.conf.Types = make([]datatype.T, 0) + } b.conf.Types = append(b.conf.Types, t) } +// Use adds an http adapter (middleware) +func (b *Builder) Use(adapter api.Adapter) { + if b.conf == nil { + b.conf = &config.Server{} + } + if b.adapters == nil { + b.adapters = make([]api.Adapter, 0) + } + b.adapters = append(b.adapters, adapter) +} + // Setup the builder with its api definition file // panics if already setup func (b *Builder) Setup(r io.Reader) error { diff --git a/handler.go b/handler.go index e85fe49..b6feeb3 100644 --- a/handler.go +++ b/handler.go @@ -1,7 +1,6 @@ package aicra import ( - "log" "net/http" "git.xdrm.io/go/aicra/api" @@ -12,17 +11,17 @@ import ( // Handler wraps the builder to handle requests type Handler Builder -// ServeHTTP implements http.Handler +// ServeHTTP implements http.Handler and wraps it in middlewares (adapters) func (s Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer func() { - if rc := recover(); rc != nil { - log.Printf("recovering request: %s\n", rc) - // try to send error response - api.EmptyResponse().WithError(api.ErrUncallableService).ServeHTTP(w, r) - } - }() - defer r.Body.Close() + var h = http.HandlerFunc(s.handleRequest) + for _, adapter := range s.adapters { + h = adapter(h) + } + h(w, r) +} + +func (s Handler) handleRequest(w http.ResponseWriter, r *http.Request) { // 1. find a matching service from config var service = s.conf.Find(r) if service == nil { diff --git a/internal/dynfunc/handler.go b/internal/dynfunc/handler.go index c12069c..4e36b71 100644 --- a/internal/dynfunc/handler.go +++ b/internal/dynfunc/handler.go @@ -2,6 +2,7 @@ package dynfunc import ( "fmt" + "log" "reflect" "git.xdrm.io/go/aicra/api" @@ -70,6 +71,29 @@ func (h *Handler) Handle(data map[string]interface{}) (map[string]interface{}, a if !inData { continue } + + var refvalue = reflect.ValueOf(value) + + // T to pointer of T + if field.Kind() == reflect.Ptr { + var ptrType = field.Type().Elem() + + if !refvalue.Type().ConvertibleTo(ptrType) { + log.Printf("Cannot convert %v into %v", refvalue.Type(), ptrType) + return nil, api.ErrUncallableService + } + + ptr := reflect.New(ptrType) + ptr.Elem().Set(reflect.ValueOf(value).Convert(ptrType)) + + field.Set(ptr) + continue + } + if !reflect.ValueOf(value).Type().ConvertibleTo(field.Type()) { + log.Printf("Cannot convert %v into %v", reflect.ValueOf(value).Type(), field.Type()) + return nil, api.ErrUncallableService + } + field.Set(reflect.ValueOf(value).Convert(field.Type())) } callArgs = append(callArgs, callStruct)