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