refactor-test #15
|
@ -1,5 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrorUnknown represents any error which cause is unknown.
|
// ErrorUnknown represents any error which cause is unknown.
|
||||||
// It might also be used for debug purposes as this error
|
// It might also be used for debug purposes as this error
|
||||||
|
@ -19,20 +21,17 @@ var (
|
||||||
// unique fields already exists
|
// unique fields already exists
|
||||||
ErrorAlreadyExists Error = 3
|
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 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 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 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 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 has to be set when a file upload failed
|
||||||
ErrorUpload Error = 100
|
ErrorUpload Error = 100
|
||||||
|
@ -90,7 +89,6 @@ var errorReasons = map[Error]string{
|
||||||
ErrorFailure: "it failed",
|
ErrorFailure: "it failed",
|
||||||
ErrorNoMatchFound: "resource not found",
|
ErrorNoMatchFound: "resource not found",
|
||||||
ErrorAlreadyExists: "already exists",
|
ErrorAlreadyExists: "already exists",
|
||||||
ErrorConfig: "configuration error",
|
|
||||||
ErrorCreation: "create error",
|
ErrorCreation: "create error",
|
||||||
ErrorModification: "update error",
|
ErrorModification: "update error",
|
||||||
ErrorDeletion: "delete error",
|
ErrorDeletion: "delete error",
|
||||||
|
@ -108,3 +106,26 @@ var errorReasons = map[Error]string{
|
||||||
ErrorInvalidParam: "invalid parameter",
|
ErrorInvalidParam: "invalid parameter",
|
||||||
ErrorInvalidDefaultParam: "invalid default param",
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error represents an http response error following the api format.
|
// 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.
|
// directly into the response as JSON alongside response output fields.
|
||||||
type Error int
|
type Error int
|
||||||
|
|
||||||
// Error implements the error interface
|
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
// use unknown error if no reason
|
|
||||||
reason, ok := errorReasons[e]
|
reason, ok := errorReasons[e]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrorUnknown.Error()
|
return ErrorUnknown.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("[%d] %s", e, reason)
|
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
|
// MarshalJSON implements encoding/json.Marshaler interface
|
||||||
func (e Error) MarshalJSON() ([]byte, error) {
|
func (e Error) MarshalJSON() ([]byte, error) {
|
||||||
// use unknown error if no reason
|
// 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 {
|
func (res *Response) WithError(err Error) *Response {
|
||||||
res.err = err
|
res.err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements the error interface and dispatches to internal error.
|
|
||||||
func (res *Response) Error() string {
|
func (res *Response) Error() string {
|
||||||
return res.err.Error()
|
return res.err.Error()
|
||||||
}
|
}
|
||||||
|
@ -42,36 +41,23 @@ func (res *Response) SetData(name string, value interface{}) {
|
||||||
res.Data[name] = value
|
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
|
// MarshalJSON implements the 'json.Marshaler' interface and is used
|
||||||
// to generate the JSON representation of the response
|
// to generate the JSON representation of the response
|
||||||
func (res *Response) MarshalJSON() ([]byte, error) {
|
func (res *Response) MarshalJSON() ([]byte, error) {
|
||||||
fmt := make(map[string]interface{})
|
fmt := make(map[string]interface{})
|
||||||
|
|
||||||
for k, v := range res.Data {
|
for k, v := range res.Data {
|
||||||
fmt[k] = v
|
fmt[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt["error"] = res.err
|
fmt["error"] = res.err
|
||||||
|
|
||||||
return json.Marshal(fmt)
|
return json.Marshal(fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler and writes the API response.
|
|
||||||
func (res *Response) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
|
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)
|
encoded, err := json.Marshal(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.Write(encoded)
|
w.Write(encoded)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue