refactor: unexport api.Response into aicra.response
This commit is contained in:
parent
f17622195a
commit
cff4106bf5
|
@ -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
|
|
||||||
}
|
|
48
handler.go
48
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)
|
// ServeHTTP implements http.Handler and wraps it in middlewares (adapters)
|
||||||
func (s Handler) resolve(w http.ResponseWriter, r *http.Request) {
|
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)
|
var service = s.conf.Find(r)
|
||||||
if service == nil {
|
if service == nil {
|
||||||
handleError(api.ErrUnknownService, w, r)
|
newResponse().WithError(api.ErrUnknownService).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract request data
|
// extract request data
|
||||||
var input, err = extractInput(service, *r)
|
var input, err = extractInput(service, *r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(api.ErrMissingParam, w, r)
|
newResponse().WithError(api.ErrMissingParam).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// find a matching handler
|
// match handler
|
||||||
var handler *apiHandler
|
var handler *apiHandler
|
||||||
for _, h := range s.handlers {
|
for _, h := range s.handlers {
|
||||||
if h.Method == service.Method && h.Path == service.Pattern {
|
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 {
|
if handler == nil {
|
||||||
handleError(api.ErrUncallableService, w, r)
|
newResponse().WithError(api.ErrUncallableService).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// build context with builtin data
|
// add info into context
|
||||||
c := r.Context()
|
c := r.Context()
|
||||||
c = context.WithValue(c, ctx.Request, r)
|
c = context.WithValue(c, ctx.Request, r)
|
||||||
c = context.WithValue(c, ctx.Response, w)
|
c = context.WithValue(c, ctx.Response, w)
|
||||||
|
@ -63,63 +63,55 @@ func (s Handler) resolve(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// create http handler
|
// create http handler
|
||||||
var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// should not happen
|
||||||
auth := api.GetAuth(r.Context())
|
auth := api.GetAuth(r.Context())
|
||||||
if auth == nil {
|
if auth == nil {
|
||||||
handleError(api.ErrPermission, w, r)
|
newResponse().WithError(api.ErrPermission).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// reject non granted requests
|
// reject non granted requests
|
||||||
if !auth.Granted() {
|
if !auth.Granted() {
|
||||||
handleError(api.ErrPermission, w, r)
|
newResponse().WithError(api.ErrPermission).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// use context defined in the request
|
// execute the service handler
|
||||||
s.handle(r.Context(), input, handler, service, w, r)
|
s.handle(r.Context(), input, handler, service, w, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
// run middlewares the handler
|
// run contextual middlewares
|
||||||
for _, mw := range s.ctxMiddlewares {
|
for _, mw := range s.ctxMiddlewares {
|
||||||
h = mw(h)
|
h = mw(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// serve using the context with values
|
// serve using the pre-filled context
|
||||||
h.ServeHTTP(w, r.WithContext(c))
|
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) {
|
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)
|
var outData, outErr = handler.dyn.Handle(c, input.Data)
|
||||||
|
|
||||||
// build response from returned arguments
|
// build response from output arguments
|
||||||
var res = api.EmptyResponse().WithError(outErr)
|
var res = newResponse().WithError(outErr)
|
||||||
for key, value := range outData {
|
for key, value := range outData {
|
||||||
|
|
||||||
// find original name from 'rename' field
|
// find original name from 'rename' field
|
||||||
for name, param := range service.Output {
|
for name, param := range service.Output {
|
||||||
if param.Rename == key {
|
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")
|
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)
|
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) {
|
func extractInput(service *config.Service, req http.Request) (*reqdata.T, error) {
|
||||||
var dataset = reqdata.New(service)
|
var dataset = reqdata.New(service)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue