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"`
}
// 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
// to be displayed back to API caller
func (e *Error) Put(arg interface{}) {

View File

@ -16,14 +16,21 @@ type Response struct {
Err Error
}
// NewResponse creates an empty response
func NewResponse() *Response {
return &Response{
// NewResponse creates an empty response. An optional error can be passed as its first argument.
func NewResponse(errors ...Error) *Response {
res := &Response{
Status: http.StatusOK,
Data: make(ResponseData),
Err: ErrorFailure(),
Headers: make(http.Header),
}
// optional error
if len(errors) == 1 {
res.Err = errors[0]
}
return res
}
// SetData adds/overrides a new response field
@ -51,3 +58,17 @@ func (i *Response) MarshalJSON() ([]byte, error) {
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"
)
// NewReader craetes a new reader
func NewReader(_src io.Reader, _boundary string) (*Reader, error) {
// NewReader creates a new reader from a reader and a boundary.
func NewReader(r io.Reader, boundary string) (*Reader, error) {
reader := &Reader{
reader: nil,
boundary: fmt.Sprintf("--%s", _boundary),
boundary: fmt.Sprintf("--%s", boundary),
Data: make(map[string]*Component),
}
// 1. Create reader
dst, ok := _src.(*bufio.Reader)
dst, ok := r.(*bufio.Reader)
if !ok {
dst = bufio.NewReader(_src)
dst = bufio.NewReader(r)
}
reader.reader = dst

View File

@ -2,17 +2,24 @@ package multipart
import (
"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="..."
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
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
var ErrNoHeader = errors.New("data has no header")
var ErrNoHeader = ConstError("data has no header")
// Component represents a multipart variable/file
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
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
// 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 */
var methodDef = serviceDef.Method(req.Method)
if methodDef == nil {
httpError(res, api.ErrorUnknownMethod())
apiResponse := api.NewResponse(api.ErrorUnknownMethod())
apiResponse.Write(res)
logError(apiResponse)
return
}
@ -86,7 +87,9 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// Fail if argument check failed
if paramError.Code != api.ErrorSuccess().Code {
httpError(res, paramError)
apiResponse := api.NewResponse(paramError)
apiResponse.Write(res)
logError(apiResponse)
return
}
@ -109,16 +112,17 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// fail if found no handler
if serviceHandler == nil {
if serviceFound {
apiError := api.ErrorUncallableMethod()
apiError.Put(servicePath)
apiError.Put(req.Method)
httpError(res, apiError)
apiError := api.NewError(api.ErrorUncallableMethod(), servicePath, req.Method)
apiResponse := api.NewResponse(apiError)
apiResponse.Write(res)
logError(apiResponse)
return
}
apiError := api.ErrorUncallableService()
apiError.Put(servicePath)
httpError(res, apiError)
apiError := api.NewError(api.ErrorUncallableService(), servicePath)
apiResponse := api.NewResponse(apiError)
apiResponse.Write(res)
logError(apiResponse)
return
}
@ -138,8 +142,8 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
}
}
// 3. build JSON apiResponse
httpPrint(res, apiResponse)
// 3. write to response
apiResponse.Write(res)
return
}

33
util.go
View File

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