feat: fix error creation to bind arguments directly + add optional error argument in NewResponse()

This commit is contained in:
Adrien Marquès 2019-05-01 18:01:32 +02:00
parent 4e25c647b2
commit b9f240b86b
6 changed files with 73 additions and 49 deletions

View File

@ -13,6 +13,14 @@ type Error struct {
Arguments []interface{} `json:"arguments"` Arguments []interface{} `json:"arguments"`
} }
// NewError returns a new error from a base error with errorarguments.
func NewError(baseError Error, arguments ...interface{}) Error {
for _, arg := range arguments {
baseError.Put(arg)
}
return baseError
}
// Put adds an argument to the error // Put adds an argument to the error
// to be displayed back to API caller // to be displayed back to API caller
func (e *Error) Put(arg interface{}) { func (e *Error) Put(arg interface{}) {

View File

@ -16,14 +16,21 @@ type Response struct {
Err Error Err Error
} }
// NewResponse creates an empty response // NewResponse creates an empty response. An optional error can be passed as its first argument.
func NewResponse() *Response { func NewResponse(errors ...Error) *Response {
return &Response{ res := &Response{
Status: http.StatusOK, Status: http.StatusOK,
Data: make(ResponseData), Data: make(ResponseData),
Err: ErrorFailure(), Err: ErrorFailure(),
Headers: make(http.Header), Headers: make(http.Header),
} }
// optional error
if len(errors) == 1 {
res.Err = errors[0]
}
return res
} }
// SetData adds/overrides a new response field // SetData adds/overrides a new response field
@ -51,3 +58,17 @@ func (i *Response) MarshalJSON() ([]byte, error) {
return json.Marshal(fmt) return json.Marshal(fmt)
} }
// Write writes to an HTTP response.
func (i *Response) Write(w http.ResponseWriter) error {
w.WriteHeader(i.Status)
w.Header().Add("Content-Type", "application/json")
fmt, err := json.Marshal(i)
if err != nil {
return err
}
w.Write(fmt)
return nil
}

View File

@ -6,19 +6,18 @@ import (
"io" "io"
) )
// NewReader craetes a new reader // NewReader creates a new reader from a reader and a boundary.
func NewReader(_src io.Reader, _boundary string) (*Reader, error) { func NewReader(r io.Reader, boundary string) (*Reader, error) {
reader := &Reader{ reader := &Reader{
reader: nil, reader: nil,
boundary: fmt.Sprintf("--%s", _boundary), boundary: fmt.Sprintf("--%s", boundary),
Data: make(map[string]*Component), Data: make(map[string]*Component),
} }
// 1. Create reader // 1. Create reader
dst, ok := _src.(*bufio.Reader) dst, ok := r.(*bufio.Reader)
if !ok { if !ok {
dst = bufio.NewReader(_src) dst = bufio.NewReader(r)
} }
reader.reader = dst reader.reader = dst

View File

@ -2,17 +2,24 @@ package multipart
import ( import (
"bufio" "bufio"
"errors"
) )
// ConstError is a wrapper to set constant errors
type ConstError string
// Error implements error
func (err ConstError) Error() string {
return string(err)
}
// ErrMissingDataName is set when a multipart variable/file has no name="..." // ErrMissingDataName is set when a multipart variable/file has no name="..."
var ErrMissingDataName = errors.New("data has no name") var ErrMissingDataName = ConstError("data has no name")
// ErrDataNameConflict is set when a multipart variable/file name is already used // ErrDataNameConflict is set when a multipart variable/file name is already used
var ErrDataNameConflict = errors.New("data name conflict") var ErrDataNameConflict = ConstError("data name conflict")
// ErrNoHeader is set when a multipart variable/file has no (valid) header // ErrNoHeader is set when a multipart variable/file has no (valid) header
var ErrNoHeader = errors.New("data has no header") var ErrNoHeader = ConstError("data has no header")
// Component represents a multipart variable/file // Component represents a multipart variable/file
type Component struct { type Component struct {

View File

@ -51,7 +51,6 @@ func New(configPath string) (*Server, error) {
// ServeHTTP implements http.Handler and has to be called on each request // ServeHTTP implements http.Handler and has to be called on each request
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) { func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
defer req.Body.Close() defer req.Body.Close()
// 1. build API request from HTTP request // 1. build API request from HTTP request
@ -73,7 +72,9 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// 3. check if matching methodDef exists in config */ // 3. check if matching methodDef exists in config */
var methodDef = serviceDef.Method(req.Method) var methodDef = serviceDef.Method(req.Method)
if methodDef == nil { if methodDef == nil {
httpError(res, api.ErrorUnknownMethod()) apiResponse := api.NewResponse(api.ErrorUnknownMethod())
apiResponse.Write(res)
logError(apiResponse)
return return
} }
@ -86,7 +87,9 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// Fail if argument check failed // Fail if argument check failed
if paramError.Code != api.ErrorSuccess().Code { if paramError.Code != api.ErrorSuccess().Code {
httpError(res, paramError) apiResponse := api.NewResponse(paramError)
apiResponse.Write(res)
logError(apiResponse)
return return
} }
@ -109,16 +112,17 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// fail if found no handler // fail if found no handler
if serviceHandler == nil { if serviceHandler == nil {
if serviceFound { if serviceFound {
apiError := api.ErrorUncallableMethod() apiError := api.NewError(api.ErrorUncallableMethod(), servicePath, req.Method)
apiError.Put(servicePath) apiResponse := api.NewResponse(apiError)
apiError.Put(req.Method) apiResponse.Write(res)
httpError(res, apiError) logError(apiResponse)
return return
} }
apiError := api.ErrorUncallableService() apiError := api.NewError(api.ErrorUncallableService(), servicePath)
apiError.Put(servicePath) apiResponse := api.NewResponse(apiError)
httpError(res, apiError) apiResponse.Write(res)
logError(apiResponse)
return return
} }
@ -138,8 +142,8 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
} }
} }
// 3. build JSON apiResponse // 3. write to response
httpPrint(res, apiResponse) apiResponse.Write(res)
return return
} }

33
util.go
View File

@ -1,9 +1,7 @@
package aicra package aicra
import ( import (
"encoding/json"
"log" "log"
"net/http"
"git.xdrm.io/go/aicra/api" "git.xdrm.io/go/aicra/api"
"git.xdrm.io/go/aicra/internal/config" "git.xdrm.io/go/aicra/internal/config"
@ -21,17 +19,17 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
// for each param of the config // for each param of the config
for name, param := range methodParam { for name, param := range methodParam {
/* (1) Extract value */ // 1. extract value
p, isset := store.Set[name] p, isset := store.Set[name]
/* (2) Required & missing */ // 2. fail if required & missing
if !isset && !param.Optional { if !isset && !param.Optional {
apiError = api.ErrorMissingParam() apiError = api.ErrorMissingParam()
apiError.Put(name) apiError.Put(name)
return nil, apiError return nil, apiError
} }
/* (3) Optional & missing: set default value */ // 3. optional & missing: set default value
if !isset { if !isset {
p = &reqdata.Parameter{ p = &reqdata.Parameter{
Parsed: true, Parsed: true,
@ -47,12 +45,12 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
continue continue
} }
/* (4) Parse parameter if not file */ // 4. parse parameter if not file
if !p.File { if !p.File {
p.Parse() p.Parse()
} }
/* (5) Fail on unexpected multipart file */ // 5. fail on unexpected multipart file
waitFile, gotFile := param.Type == "FILE", p.File waitFile, gotFile := param.Type == "FILE", p.File
if gotFile && !waitFile || !gotFile && waitFile { if gotFile && !waitFile || !gotFile && waitFile {
apiError = api.ErrorInvalidParam() apiError = api.ErrorInvalidParam()
@ -61,13 +59,13 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
return nil, apiError return nil, apiError
} }
/* (6) Do not check if file */ // 6. do not check if file
if gotFile { if gotFile {
parameters[param.Rename] = p.Value parameters[param.Rename] = p.Value
continue continue
} }
/* (7) Check type */ // 7. check type
if s.Checkers.Run(param.Type, p.Value) != nil { if s.Checkers.Run(param.Type, p.Value) != nil {
apiError = api.ErrorInvalidParam() apiError = api.ErrorInvalidParam()
@ -85,20 +83,7 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
return parameters, apiError return parameters, apiError
} }
// Prints an HTTP response
func httpPrint(r http.ResponseWriter, res *api.Response) {
r.WriteHeader(res.Status)
// write this json
jsonResponse, _ := json.Marshal(res)
r.Header().Add("Content-Type", "application/json")
r.Write(jsonResponse)
}
// Prints an error as HTTP response // Prints an error as HTTP response
func httpError(r http.ResponseWriter, e api.Error) { func logError(res *api.Response) {
JSON, _ := json.Marshal(e) log.Printf("[http.fail] %v\n", res.Err)
r.Header().Add("Content-Type", "application/json")
r.Write(JSON)
log.Printf("[http.fail] %s\n", e.Reason)
} }