refactor-test #15
|
@ -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,
|
||||
}
|
||||
|
|
13
api/error.go
13
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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue