refactor aicra: meaningful defaults, stage renaming Builder.Build() -> Server

This commit is contained in:
Adrien Marquès 2020-04-04 11:46:37 +02:00
parent 1e0fb77d61
commit d69dd2508c
Signed by: xdrm-brackets
GPG Key ID: D75243CA236D825E
3 changed files with 66 additions and 68 deletions

View File

@ -8,8 +8,17 @@ func (err cerr) Error() string {
return string(err)
}
// ErrNoServiceForHandler - no service matching this handler
const ErrNoServiceForHandler = cerr("no service found for this handler")
// ErrLateType - cannot add datatype after setting up the definition
const ErrLateType = cerr("types cannot be added after Setup")
// ErrNoHandlerForService - no handler matching this service
const ErrNoHandlerForService = cerr("no handler found for this service")
// ErrNotSetup - not set up yet
const ErrNotSetup = cerr("not set up")
// ErrAlreadySetup - already set up
const ErrAlreadySetup = cerr("already set up")
// ErrUnknownService - no service matching this handler
const ErrUnknownService = cerr("unknown service")
// ErrMissingHandler - missing handler
const ErrMissingHandler = cerr("missing handler")

26
http.go
View File

@ -9,14 +9,14 @@ import (
)
// httpHandler wraps the aicra server to allow handling http requests
type httpHandler Server
type httpHandler Builder
// ServeHTTP implements http.Handler and has to be called on each request
func (server httpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
// 1. find a matching service in the config
service := server.config.Find(req)
service := server.conf.Find(req)
if service == nil {
response := api.EmptyResponse().WithError(api.ErrorUnknownService)
response.ServeHTTP(res, req)
@ -55,25 +55,15 @@ func (server httpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request)
}
// 6. find a matching handler
var foundHandler *handler
var found bool
for _, handler := range server.handlers {
if handler.Method == service.Method && handler.Path == service.Pattern {
foundHandler = handler
found = true
var handler *apiHandler
for _, h := range server.handlers {
if h.Method == service.Method && h.Path == service.Pattern {
handler = h
}
}
// 7. fail if found no handler
if foundHandler == nil {
if found {
r := api.EmptyResponse().WithError(api.ErrorUncallableService)
r.ServeHTTP(res, req)
logError(r)
return
}
if handler == nil {
r := api.EmptyResponse().WithError(api.ErrorUnknownService)
r.ServeHTTP(res, req)
logError(r)
@ -91,7 +81,7 @@ func (server httpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request)
apireq.Param = dataset.Data
// 10. execute
returned, apiErr := foundHandler.dynHandler.Handle(dataset.Data)
returned, apiErr := handler.dyn.Handle(dataset.Data)
response := api.EmptyResponse().WithError(apiErr)
for key, value := range returned {

View File

@ -2,61 +2,59 @@ package aicra
import (
"fmt"
"io"
"net/http"
"os"
"git.xdrm.io/go/aicra/datatype"
"git.xdrm.io/go/aicra/dynfunc"
"git.xdrm.io/go/aicra/internal/config"
)
// Server represents an AICRA instance featuring: type checkers, services
type Server struct {
config *config.Server
// Builder for an aicra server
type Builder struct {
conf *config.Server
handlers []*apiHandler
}
// represents an server handler
type apiHandler struct {
Method string
Path string
dynHandler *dynfunc.Handler
dyn *dynfunc.Handler
}
// New creates a framework instance from a configuration file
func New(configPath string, dtypes ...datatype.T) (*Server, error) {
var (
err error
configFile io.ReadCloser
)
// 1. init instance
var i = &Server{
config: nil,
handlers: make([]*handler, 0),
// AddType adds an available datatype to the api definition
func (b *Builder) AddType(t datatype.T) {
if b.conf == nil {
b.conf = &config.Server{}
}
if b.conf.Services != nil {
panic(ErrLateType)
}
b.conf.Types = append(b.conf.Types, t)
}
// 2. open config file
configFile, err = os.Open(configPath)
if err != nil {
return nil, err
// Setup the builder with its api definition
// panics if already setup
func (b *Builder) Setup(r io.Reader) error {
if b.conf == nil {
b.conf = &config.Server{}
}
defer configFile.Close()
// 3. load configuration
i.config, err = config.Parse(configFile, dtypes...)
if err != nil {
return nil, err
if b.conf.Services != nil {
panic(ErrAlreadySetup)
}
return b.conf.Parse(r)
}
return i, nil
// Bind a dynamic handler to a REST service
func (b *Builder) Bind(method, path string, fn interface{}) error {
if b.conf.Services == nil {
return ErrNotSetup
}
// Handle sets a new handler for an HTTP method to a path
func (s *Server) Handle(method, path string, fn interface{}) error {
// find associated service
var service *config.Service
for _, s := range s.config.Services {
for _, s := range b.conf.Services {
if method == s.Method && path == s.Pattern {
service = s
break
@ -67,25 +65,26 @@ func (s *Server) Handle(method, path string, fn interface{}) error {
return fmt.Errorf("%s '%s': %w", method, path, ErrUnknownService)
}
dynHandler, err := dynfunc.Build(fn, *service)
dyn, err := dynfunc.Build(fn, *service)
if err != nil {
return fmt.Errorf("%s '%s' handler: %w", method, path, err)
}
s.handlers = append(s.handlers, &apiHandler{
b.handlers = append(b.handlers, &apiHandler{
Path: path,
Method: method,
dynHandler: dynHandler,
dyn: dyn,
})
return nil
}
// ToHTTPServer converts the server to a http.Handler
func (s Server) ToHTTPServer() (http.Handler, error) {
for _, service := range s.config.Services {
// Build a fully-featured HTTP server
func (b Builder) Build() (http.Handler, error) {
for _, service := range b.conf.Services {
var hasAssociatedHandler bool
for _, handler := range s.handlers {
for _, handler := range b.handlers {
if handler.Method == service.Method && handler.Path == service.Pattern {
hasAssociatedHandler = true
break
@ -96,5 +95,5 @@ func (s Server) ToHTTPServer() (http.Handler, error) {
}
}
return httpHandler(s), nil
return httpHandler(b), nil
}