refactor-test #15

Merged
xdrm-brackets merged 12 commits from refactor-test into 0.3.0 2020-04-04 15:33:44 +00:00
5 changed files with 41 additions and 243 deletions
Showing only changes of commit 3c453e7f89 - Show all commits

View File

@ -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,
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}