c1da3ab566
* Create an APIError that should only be used for api returned errors. It'll wrap an error and can have different Kinds and optional code and message. * The http handlers will use the first APIError available in the error chain and generate a json response body containing the code and the user message. The wrapped error is internal and is not sent in the response. If no api error is available in the chain a generic internal server error will be returned. * Add a RemoteError type that will be created from remote services calls (runservice, configstore). It's similar to the APIError but a different type to not propagate to the caller response and it'll not contain any wrapped error. * Gateway: when we call a remote service, by default, we'll create a APIError using the RemoteError Kind (omitting the code and the message that usually must not be propagated). This is done for all the remote service calls as a starting point, in future, if this default behavior is not the right one for a specific remote service call, a new api error with a different kind and/or augmented with the calling service error codes and user messages could be created. * datamanager: Use a dedicated ErrNotExist (and converting objectstorage ErrNotExist).
125 lines
2.4 KiB
Go
125 lines
2.4 KiB
Go
package util
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"net/http"
|
|
|
|
errors "golang.org/x/xerrors"
|
|
)
|
|
|
|
func HTTPResponse(w http.ResponseWriter, code int, res interface{}) error {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
if res != nil {
|
|
resj, err := json.Marshal(res)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return err
|
|
}
|
|
w.WriteHeader(code)
|
|
_, err = w.Write(resj)
|
|
return err
|
|
}
|
|
|
|
w.WriteHeader(code)
|
|
return nil
|
|
}
|
|
|
|
type ErrorResponse struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func ErrorResponseFromError(err error) *ErrorResponse {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
var derr *APIError
|
|
if errors.As(err, &derr) {
|
|
return &ErrorResponse{Code: string(derr.Code), Message: derr.Message}
|
|
}
|
|
|
|
// on generic error return an error response without any code
|
|
return &ErrorResponse{}
|
|
}
|
|
|
|
func HTTPError(w http.ResponseWriter, err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
response := ErrorResponseFromError(err)
|
|
resj, merr := json.Marshal(response)
|
|
if merr != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return true
|
|
}
|
|
|
|
code := http.StatusInternalServerError
|
|
|
|
var derr *APIError
|
|
if errors.As(err, &derr) {
|
|
switch derr.Kind {
|
|
case ErrBadRequest:
|
|
code = http.StatusBadRequest
|
|
case ErrNotExist:
|
|
code = http.StatusNotFound
|
|
case ErrForbidden:
|
|
code = http.StatusForbidden
|
|
case ErrUnauthorized:
|
|
code = http.StatusUnauthorized
|
|
case ErrInternal:
|
|
code = http.StatusInternalServerError
|
|
}
|
|
}
|
|
|
|
w.WriteHeader(code)
|
|
_, _ = w.Write(resj)
|
|
|
|
return true
|
|
}
|
|
|
|
func ErrFromRemote(resp *http.Response) error {
|
|
if resp == nil {
|
|
return nil
|
|
}
|
|
if resp.StatusCode/100 == 2 {
|
|
return nil
|
|
}
|
|
|
|
response := &ErrorResponse{}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Re-populate error response body so it can be parsed by the caller if needed
|
|
resp.Body = ioutil.NopCloser(bytes.NewBuffer(data))
|
|
|
|
if err := json.Unmarshal(data, &response); err != nil {
|
|
return errors.Errorf("unknown api error (status: %d)", resp.StatusCode)
|
|
}
|
|
|
|
kind := ErrInternal
|
|
switch resp.StatusCode {
|
|
case http.StatusBadRequest:
|
|
kind = ErrBadRequest
|
|
case http.StatusNotFound:
|
|
kind = ErrNotExist
|
|
case http.StatusForbidden:
|
|
kind = ErrForbidden
|
|
case http.StatusUnauthorized:
|
|
kind = ErrUnauthorized
|
|
case http.StatusInternalServerError:
|
|
kind = ErrInternal
|
|
}
|
|
|
|
return NewRemoteError(kind, response.Code, response.Message)
|
|
}
|