diff --git a/api/error.defaults.go b/api/error.defaults.go index 4907ba5..583126f 100644 --- a/api/error.defaults.go +++ b/api/error.defaults.go @@ -1,5 +1,7 @@ package api +import "net/http" + var ( // ErrorUnknown represents any error which cause is unknown. // It might also be used for debug purposes as this error @@ -19,20 +21,17 @@ var ( // unique fields already exists ErrorAlreadyExists Error = 3 - // ErrorConfig has to be set when there is a configuration error - ErrorConfig Error = 4 - // ErrorCreation has to be set when there is a creation/insert error - ErrorCreation Error = 5 + ErrorCreation Error = 4 // ErrorModification has to be set when there is an update/modification error - ErrorModification Error = 6 + ErrorModification Error = 5 // ErrorDeletion has to be set when there is a deletion/removal error - ErrorDeletion Error = 7 + ErrorDeletion Error = 6 // ErrorTransaction has to be set when there is a transactional error - ErrorTransaction Error = 8 + ErrorTransaction Error = 7 // ErrorUpload has to be set when a file upload failed ErrorUpload Error = 100 @@ -90,7 +89,6 @@ var errorReasons = map[Error]string{ ErrorFailure: "it failed", ErrorNoMatchFound: "resource not found", ErrorAlreadyExists: "already exists", - ErrorConfig: "configuration error", ErrorCreation: "create error", ErrorModification: "update error", ErrorDeletion: "delete error", @@ -108,3 +106,26 @@ var errorReasons = map[Error]string{ ErrorInvalidParam: "invalid parameter", ErrorInvalidDefaultParam: "invalid default param", } +var errorStatus = map[Error]int{ + ErrorUnknown: http.StatusOK, + ErrorSuccess: http.StatusOK, + ErrorFailure: http.StatusInternalServerError, + ErrorNoMatchFound: http.StatusOK, + ErrorAlreadyExists: http.StatusOK, + ErrorCreation: http.StatusOK, + ErrorModification: http.StatusOK, + ErrorDeletion: http.StatusOK, + ErrorTransaction: http.StatusOK, + ErrorUpload: http.StatusInternalServerError, + ErrorDownload: http.StatusInternalServerError, + MissingDownloadHeaders: http.StatusBadRequest, + ErrorMissingDownloadBody: http.StatusBadRequest, + ErrorUnknownService: http.StatusServiceUnavailable, + ErrorUncallableService: http.StatusServiceUnavailable, + ErrorNotImplemented: http.StatusNotImplemented, + ErrorPermission: http.StatusUnauthorized, + ErrorToken: http.StatusForbidden, + ErrorMissingParam: http.StatusBadRequest, + ErrorInvalidParam: http.StatusBadRequest, + ErrorInvalidDefaultParam: http.StatusBadRequest, +} diff --git a/api/error.go b/api/error.go index ef6c9fd..52f97f7 100644 --- a/api/error.go +++ b/api/error.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "fmt" + "net/http" ) // Error represents an http response error following the api format. @@ -10,17 +11,23 @@ import ( // directly into the response as JSON alongside response output fields. type Error int -// Error implements the error interface func (e Error) Error() string { - // use unknown error if no reason reason, ok := errorReasons[e] if !ok { return ErrorUnknown.Error() } - return fmt.Sprintf("[%d] %s", e, reason) } +// Status returns the associated HTTP status code +func (e Error) Status() int { + status, ok := errorStatus[e] + if !ok { + return http.StatusOK + } + return status +} + // MarshalJSON implements encoding/json.Marshaler interface func (e Error) MarshalJSON() ([]byte, error) { // use unknown error if no reason diff --git a/api/request.go b/api/request.go deleted file mode 100644 index 8259836..0000000 --- a/api/request.go +++ /dev/null @@ -1,54 +0,0 @@ -package api - -import ( - "net/http" - "strings" -) - -// Request represents an API request i.e. HTTP -type Request struct { - // corresponds to the list of uri components - // featured in the request URI - URI []string - - // Scope from the configuration file of the current service - Scope [][]string - - // original HTTP request - Request *http.Request - - // input parameters - Param RequestParam -} - -// NewRequest builds an interface request from a http.Request -func NewRequest(req *http.Request) *Request { - uri := normaliseURI(req.URL.Path) - uriparts := strings.Split(uri, "/") - - return &Request{ - URI: uriparts, - Scope: nil, - Request: req, - Param: make(RequestParam), - } -} - -// normaliseURI removes the trailing '/' to always -// have the same Uri format for later processing -func normaliseURI(uri string) string { - - if len(uri) < 1 { - return uri - } - - if uri[0] == '/' { - uri = uri[1:] - } - - if len(uri) > 1 && uri[len(uri)-1] == '/' { - uri = uri[0 : len(uri)-1] - } - - return uri -} diff --git a/api/request.param.go b/api/request.param.go deleted file mode 100644 index f0891ee..0000000 --- a/api/request.param.go +++ /dev/null @@ -1,162 +0,0 @@ -package api - -import ( - "fmt" -) - -// cerr allows you to create constant "const" error with type boxing. -type cerr string - -// Error implements the error builtin interface. -func (err cerr) Error() string { - return string(err) -} - -// ErrReqParamNotFound is thrown when a request parameter is not found -const ErrReqParamNotFound = cerr("request parameter not found") - -// ErrReqParamNotType is thrown when a request parameter is not asked with the right type -const ErrReqParamNotType = cerr("request parameter does not fulfills type") - -// RequestParam defines input parameters of an api request -type RequestParam map[string]interface{} - -// Get returns the raw value (not typed) and an error if not found -func (rp RequestParam) Get(key string) (interface{}, error) { - rawValue, found := rp[key] - if !found { - return "", ErrReqParamNotFound - } - return rawValue, nil -} - -// GetString returns a string and an error if not found or invalid type -func (rp RequestParam) GetString(key string) (string, error) { - rawValue, err := rp.Get(key) - if err != nil { - return "", err - } - - switch cast := rawValue.(type) { - case fmt.Stringer: - return cast.String(), nil - case []byte: - return string(cast), nil - case string: - return cast, nil - default: - return "", ErrReqParamNotType - } -} - -// GetFloat returns a float64 and an error if not found or invalid type -func (rp RequestParam) GetFloat(key string) (float64, error) { - rawValue, err := rp.Get(key) - if err != nil { - return 0, err - } - - switch cast := rawValue.(type) { - case float32: - return float64(cast), nil - case float64: - return cast, nil - case int, int8, int16, int32, int64: - intVal, ok := cast.(int) - if !ok || intVal != int(float64(intVal)) { - return 0, ErrReqParamNotType - } - return float64(intVal), nil - case uint, uint8, uint16, uint32, uint64: - uintVal, ok := cast.(uint) - if !ok || uintVal != uint(float64(uintVal)) { - return 0, ErrReqParamNotType - } - return float64(uintVal), nil - default: - return 0, ErrReqParamNotType - } -} - -// GetInt returns an int and an error if not found or invalid type -func (rp RequestParam) GetInt(key string) (int, error) { - rawValue, err := rp.Get(key) - if err != nil { - return 0, err - } - - switch cast := rawValue.(type) { - case float32, float64: - floatVal, ok := cast.(float64) - if !ok || floatVal < 0 || floatVal != float64(int(floatVal)) { - return 0, ErrReqParamNotType - } - return int(floatVal), nil - case int, int8, int16, int32, int64: - intVal, ok := cast.(int) - if !ok || intVal != int(int(intVal)) { - return 0, ErrReqParamNotType - } - return int(intVal), nil - default: - return 0, ErrReqParamNotType - } -} - -// GetUint returns an uint and an error if not found or invalid type -func (rp RequestParam) GetUint(key string) (uint, error) { - rawValue, err := rp.Get(key) - if err != nil { - return 0, err - } - - switch cast := rawValue.(type) { - case float32, float64: - floatVal, ok := cast.(float64) - if !ok || floatVal < 0 || floatVal != float64(uint(floatVal)) { - return 0, ErrReqParamNotType - } - return uint(floatVal), nil - case int, int8, int16, int32, int64: - intVal, ok := cast.(int) - if !ok || intVal != int(uint(intVal)) { - return 0, ErrReqParamNotType - } - return uint(intVal), nil - case uint, uint8, uint16, uint32, uint64: - uintVal, ok := cast.(uint) - if !ok { - return 0, ErrReqParamNotType - } - return uintVal, nil - default: - return 0, ErrReqParamNotType - } -} - -// GetStrings returns an []slice and an error if not found or invalid type -func (rp RequestParam) GetStrings(key string) ([]string, error) { - rawValue, err := rp.Get(key) - if err != nil { - return nil, err - } - - switch cast := rawValue.(type) { - case []fmt.Stringer: - strings := make([]string, len(cast)) - for i, stringer := range cast { - strings[i] = stringer.String() - } - return strings, nil - case [][]byte: - strings := make([]string, len(cast)) - for i, bytes := range cast { - strings[i] = string(bytes) - } - return strings, nil - case []string: - return cast, nil - default: - return nil, ErrReqParamNotType - } -} diff --git a/api/response.go b/api/response.go index 242be3d..a7ce08c 100644 --- a/api/response.go +++ b/api/response.go @@ -26,13 +26,12 @@ func EmptyResponse() *Response { } } -// WithError sets the error from a base error with error arguments. +// WithError sets the error func (res *Response) WithError(err Error) *Response { res.err = err return res } -// Error implements the error interface and dispatches to internal error. func (res *Response) Error() string { return res.err.Error() } @@ -42,36 +41,23 @@ func (res *Response) SetData(name string, value interface{}) { res.Data[name] = value } -// GetData gets a response field -func (res *Response) GetData(name string) interface{} { - value, _ := res.Data[name] - - return 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) } -// ServeHTTP implements http.Handler and writes the API response. func (res *Response) ServeHTTP(w http.ResponseWriter, r *http.Request) error { - w.WriteHeader(res.Status) - + w.WriteHeader(res.err.Status()) encoded, err := json.Marshal(res) if err != nil { return err } w.Write(encoded) - return nil }