2022-02-21 11:19:55 +00:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
|
2022-02-22 14:01:29 +00:00
|
|
|
"agola.io/agola/internal/errors"
|
2022-02-21 11:19:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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)
|
2022-02-22 14:01:29 +00:00
|
|
|
return errors.WithStack(err)
|
2022-02-21 11:19:55 +00:00
|
|
|
}
|
|
|
|
w.WriteHeader(code)
|
|
|
|
_, err = w.Write(resj)
|
2022-02-22 14:01:29 +00:00
|
|
|
return errors.WithStack(err)
|
2022-02-21 11:19:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-02-22 14:01:29 +00:00
|
|
|
return errors.WithStack(err)
|
2022-02-21 11:19:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|