127 lines
2.9 KiB
Go
127 lines
2.9 KiB
Go
package linodego
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
)
|
|
|
|
const (
|
|
// ErrorFromString is the Code identifying Errors created by string types
|
|
ErrorFromString = 1
|
|
// ErrorFromError is the Code identifying Errors created by error types
|
|
ErrorFromError = 2
|
|
// ErrorFromStringer is the Code identifying Errors created by fmt.Stringer types
|
|
ErrorFromStringer = 3
|
|
)
|
|
|
|
// Error wraps the LinodeGo error with the relevant http.Response
|
|
type Error struct {
|
|
Response *http.Response
|
|
Code int
|
|
Message string
|
|
}
|
|
|
|
// APIErrorReason is an individual invalid request message returned by the Linode API
|
|
type APIErrorReason struct {
|
|
Reason string `json:"reason"`
|
|
Field string `json:"field"`
|
|
}
|
|
|
|
func (r APIErrorReason) Error() string {
|
|
if len(r.Field) == 0 {
|
|
return r.Reason
|
|
}
|
|
|
|
return fmt.Sprintf("[%s] %s", r.Field, r.Reason)
|
|
}
|
|
|
|
// APIError is the error-set returned by the Linode API when presented with an invalid request
|
|
type APIError struct {
|
|
Errors []APIErrorReason `json:"errors"`
|
|
}
|
|
|
|
func coupleAPIErrors(r *resty.Response, err error) (*resty.Response, error) {
|
|
if err != nil {
|
|
return nil, NewError(err)
|
|
}
|
|
|
|
if r.Error() != nil {
|
|
// Check that response is of the correct content-type before unmarshalling
|
|
expectedContentType := r.Request.Header.Get("Accept")
|
|
responseContentType := r.Header().Get("Content-Type")
|
|
|
|
if responseContentType != expectedContentType {
|
|
msg := fmt.Sprintf(
|
|
"Unexpected Content-Type: Expected: %v, Received: %v",
|
|
expectedContentType,
|
|
responseContentType,
|
|
)
|
|
|
|
return nil, NewError(msg)
|
|
}
|
|
|
|
apiError, ok := r.Error().(*APIError)
|
|
if !ok || (ok && len(apiError.Errors) == 0) {
|
|
return r, nil
|
|
}
|
|
|
|
return nil, NewError(r)
|
|
}
|
|
|
|
return r, nil
|
|
}
|
|
|
|
func (e APIError) Error() string {
|
|
x := []string{}
|
|
for _, msg := range e.Errors {
|
|
x = append(x, msg.Error())
|
|
}
|
|
|
|
return strings.Join(x, "; ")
|
|
}
|
|
|
|
func (g Error) Error() string {
|
|
return fmt.Sprintf("[%03d] %s", g.Code, g.Message)
|
|
}
|
|
|
|
// NewError creates a linodego.Error with a Code identifying the source err type,
|
|
// - ErrorFromString (1) from a string
|
|
// - ErrorFromError (2) for an error
|
|
// - ErrorFromStringer (3) for a Stringer
|
|
// - HTTP Status Codes (100-600) for a resty.Response object
|
|
func NewError(err interface{}) *Error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
switch e := err.(type) {
|
|
case *Error:
|
|
return e
|
|
case *resty.Response:
|
|
apiError, ok := e.Error().(*APIError)
|
|
|
|
if !ok {
|
|
log.Fatalln("Unexpected Resty Error Response")
|
|
}
|
|
|
|
return &Error{
|
|
Code: e.RawResponse.StatusCode,
|
|
Message: apiError.Error(),
|
|
Response: e.RawResponse,
|
|
}
|
|
case error:
|
|
return &Error{Code: ErrorFromError, Message: e.Error()}
|
|
case string:
|
|
return &Error{Code: ErrorFromString, Message: e}
|
|
case fmt.Stringer:
|
|
return &Error{Code: ErrorFromStringer, Message: e.String()}
|
|
default:
|
|
log.Fatalln("Unsupported type to linodego.NewError")
|
|
panic(err)
|
|
}
|
|
}
|