From cff4106bf5b15732bdddfe597ed1cd881d2eaec7 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Sun, 20 Jun 2021 21:52:43 +0200 Subject: [PATCH] refactor: unexport api.Response into aicra.response --- api/response.go | 63 ------------------------------------------------- handler.go | 48 ++++++++++++++++--------------------- response.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 91 deletions(-) delete mode 100644 api/response.go create mode 100644 response.go diff --git a/api/response.go b/api/response.go deleted file mode 100644 index ae403f8..0000000 --- a/api/response.go +++ /dev/null @@ -1,63 +0,0 @@ -package api - -import ( - "encoding/json" - "net/http" -) - -// ResponseData defines format for response parameters to return -type ResponseData map[string]interface{} - -// Response represents an API response to be sent -type Response struct { - Data ResponseData - Status int - Headers http.Header - err Err -} - -// EmptyResponse creates an empty response. -func EmptyResponse() *Response { - return &Response{ - Status: http.StatusOK, - Data: make(ResponseData), - err: ErrFailure, - Headers: make(http.Header), - } -} - -// WithError sets the error -func (res *Response) WithError(err Err) *Response { - res.err = err - return res -} - -func (res *Response) Error() string { - return res.err.Error() -} - -// SetData adds/overrides a new response field -func (res *Response) SetData(name string, value interface{}) { - res.Data[name] = value -} - -// MarshalJSON implements the 'json.Marshaler' interface and is used -// to generate the JSON representation of the response -func (res *Response) MarshalJSON() ([]byte, error) { - fmt := make(map[string]interface{}) - for k, v := range res.Data { - fmt[k] = v - } - fmt["error"] = res.err - return json.Marshal(fmt) -} - -func (res *Response) ServeHTTP(w http.ResponseWriter, r *http.Request) error { - w.WriteHeader(res.err.Status) - encoded, err := json.Marshal(res) - if err != nil { - return err - } - w.Write(encoded) - return nil -} diff --git a/handler.go b/handler.go index de3c151..856ae17 100644 --- a/handler.go +++ b/handler.go @@ -27,21 +27,21 @@ func (s Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // ServeHTTP implements http.Handler and wraps it in middlewares (adapters) func (s Handler) resolve(w http.ResponseWriter, r *http.Request) { - // 1ind a matching service from config + // match service from config var service = s.conf.Find(r) if service == nil { - handleError(api.ErrUnknownService, w, r) + newResponse().WithError(api.ErrUnknownService).ServeHTTP(w, r) return } // extract request data var input, err = extractInput(service, *r) if err != nil { - handleError(api.ErrMissingParam, w, r) + newResponse().WithError(api.ErrMissingParam).ServeHTTP(w, r) return } - // find a matching handler + // match handler var handler *apiHandler for _, h := range s.handlers { if h.Method == service.Method && h.Path == service.Pattern { @@ -49,13 +49,13 @@ func (s Handler) resolve(w http.ResponseWriter, r *http.Request) { } } - // fail on no matching handler + // no handler found if handler == nil { - handleError(api.ErrUncallableService, w, r) + newResponse().WithError(api.ErrUncallableService).ServeHTTP(w, r) return } - // build context with builtin data + // add info into context c := r.Context() c = context.WithValue(c, ctx.Request, r) c = context.WithValue(c, ctx.Response, w) @@ -63,63 +63,55 @@ func (s Handler) resolve(w http.ResponseWriter, r *http.Request) { // create http handler var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // should not happen auth := api.GetAuth(r.Context()) if auth == nil { - handleError(api.ErrPermission, w, r) + newResponse().WithError(api.ErrPermission).ServeHTTP(w, r) return } // reject non granted requests if !auth.Granted() { - handleError(api.ErrPermission, w, r) + newResponse().WithError(api.ErrPermission).ServeHTTP(w, r) return } - // use context defined in the request + // execute the service handler s.handle(r.Context(), input, handler, service, w, r) }) - // run middlewares the handler + // run contextual middlewares for _, mw := range s.ctxMiddlewares { h = mw(h) } - // serve using the context with values + // serve using the pre-filled context h.ServeHTTP(w, r.WithContext(c)) } +// handle the service request with the associated handler func and respond using +// the handler func output func (s *Handler) handle(c context.Context, input *reqdata.T, handler *apiHandler, service *config.Service, w http.ResponseWriter, r *http.Request) { - // pass execution to the handler + // pass execution to the handler function var outData, outErr = handler.dyn.Handle(c, input.Data) - // build response from returned arguments - var res = api.EmptyResponse().WithError(outErr) + // build response from output arguments + var res = newResponse().WithError(outErr) for key, value := range outData { // find original name from 'rename' field for name, param := range service.Output { if param.Rename == key { - res.SetData(name, value) + res.SetValue(name, value) } } } - // 7. apply headers + // write response and close request w.Header().Set("Content-Type", "application/json; charset=utf-8") - for key, values := range res.Headers { - for _, value := range values { - w.Header().Add(key, value) - } - } - res.ServeHTTP(w, r) } -func handleError(err api.Err, w http.ResponseWriter, r *http.Request) { - var response = api.EmptyResponse().WithError(err) - response.ServeHTTP(w, r) -} - func extractInput(service *config.Service, req http.Request) (*reqdata.T, error) { var dataset = reqdata.New(service) diff --git a/response.go b/response.go new file mode 100644 index 0000000..d8a8c6a --- /dev/null +++ b/response.go @@ -0,0 +1,62 @@ +package aicra + +import ( + "encoding/json" + "net/http" + + "github.com/xdrm-io/aicra/api" +) + +// response for an service call +type response struct { + Data map[string]interface{} + Status int + Headers http.Header + err api.Err +} + +// newResponse creates an empty response. +func newResponse() *response { + return &response{ + Status: http.StatusOK, + Data: make(map[string]interface{}), + err: api.ErrFailure, + Headers: make(http.Header), + } +} + +// WithError sets the response error +func (res *response) WithError(err api.Err) *response { + res.err = err + return res +} + +// SetValue sets a response value +func (res *response) SetValue(name string, value interface{}) { + res.Data[name] = value +} + +// MarshalJSON generates the JSON representation of the response +// +// implements json.Marshaler +func (res *response) MarshalJSON() ([]byte, error) { + fmt := make(map[string]interface{}) + for k, v := range res.Data { + fmt[k] = v + } + fmt["error"] = res.err + return json.Marshal(fmt) +} + +// ServeHTTP writes the response representation back to the http.ResponseWriter +// +// implements http.Handler +func (res *response) ServeHTTP(w http.ResponseWriter, r *http.Request) error { + w.WriteHeader(res.err.Status) + encoded, err := json.Marshal(res) + if err != nil { + return err + } + w.Write(encoded) + return nil +}