Add dynamic handler management #13

Merged
xdrm-brackets merged 12 commits from feature/dynamic-handler-signature into 0.3.0 2020-03-29 15:10:06 +00:00
4 changed files with 69 additions and 43 deletions
Showing only changes of commit e7dd1e7a56 - Show all commits

View File

@ -1,34 +0,0 @@
package api
import (
"strings"
)
// HandlerFn defines the handler signature
type HandlerFn func(req Request, res *Response) Error
// Handler is an API handler ready to be bound
type Handler struct {
path string
method string
Fn HandlerFn
}
// NewHandler builds a handler from its http method and path
func NewHandler(method, path string, fn HandlerFn) (*Handler, error) {
return &Handler{
path: path,
method: strings.ToUpper(method),
Fn: fn,
}, nil
}
// GetMethod returns the handler's HTTP method
func (h *Handler) GetMethod() string {
return h.method
}
// GetPath returns the handler's path
func (h *Handler) GetPath() string {
return h.path
}

15
errors.go Normal file
View File

@ -0,0 +1,15 @@
package aicra
// cerr allows you to create constant "const" error with type boxing.
type cerr string
// Error implements the error builtin interface.
func (err cerr) Error() string {
return string(err)
}
// ErrNoServiceForHandler - no service matching this handler
const ErrNoServiceForHandler = cerr("no service found for this handler")
// ErrNoHandlerForService - no handler matching this service
const ErrNoHandlerForService = cerr("no handler found for this service")

32
handler.go Normal file
View File

@ -0,0 +1,32 @@
package aicra
import (
"fmt"
"strings"
"git.xdrm.io/go/aicra/dynamic"
"git.xdrm.io/go/aicra/internal/config"
)
type handler struct {
Method string
Path string
dynHandler *dynamic.Handler
}
// createHandler builds a handler from its http method and path
// also it checks whether the function signature is valid
func createHandler(method, path string, service config.Service, fn dynamic.HandlerFn) (*handler, error) {
method = strings.ToUpper(method)
dynHandler, err := dynamic.Build(fn, service)
if err != nil {
return nil, fmt.Errorf("%s '%s' handler: %w", method, path, err)
}
return &handler{
Path: path,
Method: method,
dynHandler: dynHandler,
}, nil
}

View File

@ -5,15 +5,15 @@ import (
"io" "io"
"os" "os"
"git.xdrm.io/go/aicra/api"
"git.xdrm.io/go/aicra/datatype" "git.xdrm.io/go/aicra/datatype"
"git.xdrm.io/go/aicra/dynamic"
"git.xdrm.io/go/aicra/internal/config" "git.xdrm.io/go/aicra/internal/config"
) )
// Server represents an AICRA instance featuring: type checkers, services // Server represents an AICRA instance featuring: type checkers, services
type Server struct { type Server struct {
config *config.Server config *config.Server
handlers []*api.Handler handlers []*handler
} }
// New creates a framework instance from a configuration file // New creates a framework instance from a configuration file
@ -26,7 +26,7 @@ func New(configPath string, dtypes ...datatype.T) (*Server, error) {
// 1. init instance // 1. init instance
var i = &Server{ var i = &Server{
config: nil, config: nil,
handlers: make([]*api.Handler, 0), handlers: make([]*handler, 0),
} }
// 2. open config file // 2. open config file
@ -46,13 +46,26 @@ func New(configPath string, dtypes ...datatype.T) (*Server, error) {
} }
// HandleFunc sets a new handler for an HTTP method to a path // Handle sets a new handler for an HTTP method to a path
func (s *Server) Handle(httpMethod, path string, fn api.HandlerFn) { func (s *Server) Handle(method, path string, fn dynamic.HandlerFn) error {
handler, err := api.NewHandler(httpMethod, path, fn) // find associated service
var found *config.Service = nil
for _, service := range s.config.Services {
if method == service.Method && path == service.Pattern {
found = service
break
}
}
if found == nil {
return fmt.Errorf("%s '%s': %w", method, path, ErrNoServiceForHandler)
}
handler, err := createHandler(method, path, *found, fn)
if err != nil { if err != nil {
panic(err) return err
} }
s.handlers = append(s.handlers, handler) s.handlers = append(s.handlers, handler)
return nil
} }
// ToHTTPServer converts the server to a http server // ToHTTPServer converts the server to a http server
@ -62,13 +75,13 @@ func (s Server) ToHTTPServer() (*httpServer, error) {
for _, service := range s.config.Services { for _, service := range s.config.Services {
found := false found := false
for _, handler := range s.handlers { for _, handler := range s.handlers {
if handler.GetMethod() == service.Method && handler.GetPath() == service.Pattern { if handler.Method == service.Method && handler.Path == service.Pattern {
found = true found = true
break break
} }
} }
if !found { if !found {
return nil, fmt.Errorf("missing handler for %s '%s'", service.Method, service.Pattern) return nil, fmt.Errorf("%s '%s': %w", service.Method, service.Pattern, ErrNoHandlerForService)
} }
} }