* feat: add support for logging * fix linting * chore: remove old APIError in favor of ServerError
150 lines
4.0 KiB
Go
150 lines
4.0 KiB
Go
package unifi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("not found")
|
|
|
|
type Meta struct {
|
|
RC string `json:"rc"`
|
|
Message string `json:"msg"`
|
|
}
|
|
|
|
func (m *Meta) error() error {
|
|
if m.RC != "ok" {
|
|
return &ServerError{
|
|
ErrorCode: m.RC,
|
|
Message: m.Message,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type DefaultResponseErrorHandler struct{}
|
|
|
|
type apiV2ResponseError struct {
|
|
Code string `json:"code"`
|
|
ErrorCode int `json:"errorCode"`
|
|
Message string `json:"message"`
|
|
Details apiV2ResponseErrorDetails `json:"details"`
|
|
}
|
|
|
|
type apiV2ResponseErrorDetails struct {
|
|
// probably there are more fields, but I didn't get any response with more fields
|
|
InvalidFields []string `json:"invalid_fields"`
|
|
}
|
|
|
|
type apiV1ResponseError struct {
|
|
Meta Meta `json:"Meta"`
|
|
Data []apiV1ResponseErrorData `json:"data"`
|
|
}
|
|
|
|
type apiV1ResponseErrorData struct {
|
|
Meta Meta `json:"Meta"`
|
|
ValidationError ServerValidationError `json:"validationError"`
|
|
RC string `json:"rc"`
|
|
Message string `json:"msg"`
|
|
}
|
|
|
|
type apiResponseError struct {
|
|
apiV1ResponseError
|
|
apiV2ResponseError
|
|
}
|
|
|
|
type ServerValidationError struct {
|
|
Field string `json:"field"`
|
|
Pattern string `json:"pattern"`
|
|
}
|
|
|
|
type ServerErrorDetails struct {
|
|
Message string
|
|
ValidationError ServerValidationError
|
|
}
|
|
|
|
type ServerError struct {
|
|
StatusCode int
|
|
RequestMethod string
|
|
RequestURL string
|
|
Message string
|
|
ErrorCode string
|
|
Details []ServerErrorDetails
|
|
}
|
|
|
|
func (s *ServerError) Error() string {
|
|
var b strings.Builder
|
|
b.WriteString(fmt.Sprintf("Server error (%d) for %s %s: %s", s.StatusCode, s.RequestMethod, s.RequestURL, s.Message))
|
|
for _, detail := range s.Details {
|
|
b.WriteString("\n")
|
|
if detail.Message != "" {
|
|
b.WriteString(detail.Message + ": ")
|
|
}
|
|
if detail.ValidationError.Field != "" && detail.ValidationError.Pattern != "" {
|
|
b.WriteString(fmt.Sprintf("field '%s' should match '%s'", detail.ValidationError.Field, detail.ValidationError.Pattern))
|
|
} else if detail.ValidationError.Field != "" {
|
|
b.WriteString(fmt.Sprintf("field '%s' is invalid", detail.ValidationError.Field))
|
|
} else if detail.ValidationError.Pattern != "" {
|
|
b.WriteString(fmt.Sprintf("field should match '%s'", detail.ValidationError.Pattern))
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func parseApiV2Error(err apiV2ResponseError, serverError *ServerError) {
|
|
serverError.Message = err.Message
|
|
serverError.ErrorCode = err.Code
|
|
for _, field := range err.Details.InvalidFields {
|
|
details := ServerErrorDetails{}
|
|
details.ValidationError.Field = field
|
|
serverError.Details = append(serverError.Details, details)
|
|
}
|
|
}
|
|
|
|
func parseApiV1Error(err apiV1ResponseError, serverError *ServerError) {
|
|
for _, d := range err.Data {
|
|
if d.Meta.RC == "error" || d.RC == "error" {
|
|
details := ServerErrorDetails{}
|
|
details.Message = d.Message
|
|
if details.Message == "" {
|
|
details.Message = d.Meta.Message
|
|
}
|
|
if d.ValidationError.Field != "" || d.ValidationError.Pattern != "" {
|
|
details.ValidationError = d.ValidationError
|
|
}
|
|
serverError.Details = append(serverError.Details, details)
|
|
}
|
|
}
|
|
if serverError.Message == "" {
|
|
serverError.Message = err.Meta.Message
|
|
}
|
|
serverError.ErrorCode = err.Meta.RC
|
|
}
|
|
|
|
func (d *DefaultResponseErrorHandler) HandleError(resp *http.Response) error {
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
return nil
|
|
}
|
|
|
|
var errBody apiResponseError
|
|
if err := json.NewDecoder(resp.Body).Decode(&errBody); err != nil {
|
|
return err
|
|
}
|
|
serverError := ServerError{
|
|
StatusCode: resp.StatusCode,
|
|
RequestMethod: resp.Request.Method,
|
|
RequestURL: resp.Request.URL.String(),
|
|
}
|
|
if errBody.apiV2ResponseError.Code != "" || errBody.apiV2ResponseError.Message != "" {
|
|
parseApiV2Error(errBody.apiV2ResponseError, &serverError)
|
|
} else {
|
|
parseApiV1Error(errBody.apiV1ResponseError, &serverError)
|
|
}
|
|
|
|
return &serverError
|
|
}
|