From 17bedc07a1fb40682909984abf924d50fc7d4833 Mon Sep 17 00:00:00 2001 From: Ricardo Osorio Date: Wed, 9 Feb 2022 12:55:37 +0000 Subject: [PATCH] Add files previously hidden within autogenerated code (#85) --- errors.go | 159 ++++++++++++++++++++++++++++++++++++++++ map_errors.go | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 errors.go create mode 100644 map_errors.go diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..70139f0 --- /dev/null +++ b/errors.go @@ -0,0 +1,159 @@ +package storageos + +import ( + "fmt" + "net/http" + + api "github.com/storageos/go-api/autogenerated" +) + +// badRequestError indicates that the request made by the client is invalid. +type badRequestError struct { + msg string +} + +func (e badRequestError) Error() string { + if e.msg == "" { + return "bad request" + } + return e.msg +} + +func NewBadRequestError(msg string) badRequestError { + return badRequestError{ + msg: msg, + } +} + +// notFoundError indicates that a resource involved in carrying out the API +// request was not found. +type notFoundError struct { + msg string +} + +func (e notFoundError) Error() string { + if e.msg == "" { + return "not found" + } + return e.msg +} + +func NewNotFoundError(msg string) notFoundError { + return notFoundError{ + msg: msg, + } +} + +// conflictError indicates that the requested operation could not be carried +// out due to a conflict between the current state and the desired state. +type conflictError struct { + msg string +} + +func (e conflictError) Error() string { + if e.msg == "" { + return "conflict" + } + return e.msg +} + +func NewConflictError(msg string) conflictError { + return conflictError{ + msg: msg, + } +} + +type openAPIError struct { + inner api.Error +} + +func (e openAPIError) Error() string { + return e.inner.Error +} + +// MapAPIError will given err and its corresponding resp attempt to map the +// HTTP error to an application level error. +// +// err is returned as is when any of the following are true: +// +// → resp is nil +// → err is not a GenericOpenAPIError or the unexported openAPIError +// +// Some response codes must be mapped by the caller in order to provide useful +// application level errors: +// +// → http.StatusBadRequest returns a badRequestError, which must have a 1-to-1 +// mapping to a context specific application error +// → http.StatusNotFound returns a notFoundError, which must have a 1-to-1 +// mapping to a context specific application error +// → http.StatusConflict returns a conflictError which must have a 1-to-1 +// mapping to a context specific application error +// +func MapAPIError(err error, resp *http.Response) error { + if resp == nil { + return err + } + + var details string + switch v := err.(type) { + case api.GenericOpenAPIError: + switch model := v.Model().(type) { + case api.Error: + details = model.Error + default: + details = string(v.Body()) + } + case openAPIError: + details = v.Error() + default: + return err + } + + switch resp.StatusCode { + + // 4XX + case http.StatusBadRequest: + return NewBadRequestError(details) + + case http.StatusUnauthorized: + return NewAuthenticationError(details) + + case http.StatusForbidden: + return NewUnauthorisedError(details) + + case http.StatusNotFound: + return NewNotFoundError(details) + + case http.StatusConflict: + return NewConflictError(details) + + case http.StatusPreconditionFailed: + return NewStaleWriteError(details) + + case http.StatusUnprocessableEntity: + return NewInvalidStateTransitionError(details) + + case http.StatusLocked: + return NewLockedError(details) + + // This may need changing to present a friendly error, or it may be done up + // the call stack. + case http.StatusUnavailableForLegalReasons: + return NewLicenceCapabilityError(details) + + // 5XX + case http.StatusInternalServerError: + return NewServerError(details) + + case http.StatusServiceUnavailable: + return NewStoreError(details) + + default: + // If details were obtained from the error, decorate it - even when + // unknown. + if details != "" { + err = fmt.Errorf("%w: %v", err, details) + } + return err + } +} diff --git a/map_errors.go b/map_errors.go new file mode 100644 index 0000000..73128bd --- /dev/null +++ b/map_errors.go @@ -0,0 +1,198 @@ +package storageos + +import ( + "fmt" +) + +// AuthenticationError indicates that the requested operation could not be +// performed for the client due to an issue with the authentication credentials +// provided by the client. +type AuthenticationError struct { + msg string +} + +func (e AuthenticationError) Error() string { + if e.msg == "" { + return "authentication error" + } + return e.msg +} + +// NewAuthenticationError returns a new AuthenticationError using msg as an +// optional error message if given. +func NewAuthenticationError(msg string) AuthenticationError { + return AuthenticationError{ + msg: msg, + } +} + +// UnauthorisedError indicates that the requested operation is disallowed +// for the user which the client is authenticated as. +type UnauthorisedError struct { + msg string +} + +func (e UnauthorisedError) Error() string { + if e.msg == "" { + return "authenticated user is not authorised to perform that action" + } + return e.msg +} + +// NewUnauthorisedError returns a new UnauthorisedError using msg as an +// optional error message if given. +func NewUnauthorisedError(msg string) UnauthorisedError { + return UnauthorisedError{ + msg: msg, + } +} + +// StaleWriteError indicates that the target resource for the requested +// operation has been concurrently updated, invalidating the request. The client +// should fetch the latest version of the resource before attempting to perform +// another update. +type StaleWriteError struct { + msg string +} + +func (e StaleWriteError) Error() string { + if e.msg == "" { + return "stale write attempted" + } + return e.msg +} + +// NewStaleWriteError returns a new StaleWriteError using msg as an optional +// error message if given. +func NewStaleWriteError(msg string) StaleWriteError { + return StaleWriteError{ + msg: msg, + } +} + +// InvalidStateTransitionError indicates that the requested operation cannot +// be performed for the target resource in its current state. +type InvalidStateTransitionError struct { + msg string +} + +func (e InvalidStateTransitionError) Error() string { + if e.msg == "" { + return "target resource is in an invalid state for carrying out the request" + } + return e.msg +} + +// NewInvalidStateTransitionError returns a new InvalidStateTransitionError +// using msg as an optional error message if given. +func NewInvalidStateTransitionError(msg string) InvalidStateTransitionError { + return InvalidStateTransitionError{ + msg: msg, + } +} + +// LockedError indicates that the requested operation cannot be performed +// because a lock is held for the target resource. +type LockedError struct { + msg string +} + +func (e LockedError) Error() string { + if e.msg == "" { + return "requsted operation cannot be safely completed as the target resource is locked" + } + return e.msg +} + +// NewLockedError returns a new LockedError using msg as an optional error +// message if given. +func NewLockedError(msg string) LockedError { + return LockedError{ + msg: msg, + } +} + +// LicenceCapabilityError indicates that the requested operation cannot be +// carried out due to a licensing issue with the cluster. +type LicenceCapabilityError struct { + msg string +} + +func (e LicenceCapabilityError) Error() string { + if e.msg == "" { + return "licence capability error" + } + return e.msg +} + +// NewLicenceCapabilityError returns a new LicenceCapabilityError using msg as +// an optional error message if given. +func NewLicenceCapabilityError(msg string) LicenceCapabilityError { + return LicenceCapabilityError{ + msg: msg, + } +} + +// ServerError indicates that an unrecoverable error occurred while attempting +// to perform the requested operation. +type ServerError struct { + msg string +} + +func (e ServerError) Error() string { + if e.msg == "" { + return "server encountered internal error" + } + return e.msg +} + +// NewServerError returns a new ServerError using msg as an optional error +// message if given. +func NewServerError(msg string) ServerError { + return ServerError{ + msg: msg, + } +} + +// StoreError indicates that the requested operation could not be performed due +// to a store outage. +type StoreError struct { + msg string +} + +func (e StoreError) Error() string { + if e.msg == "" { + return "server encountered store outage" + } + return e.msg +} + +// NewStoreError returns a new StoreError using msg as an optional error +// message if given. +func NewStoreError(msg string) StoreError { + return StoreError{ + msg: msg, + } +} + +// EncodingError provides a unified error type which transport encoding +// implementations return when given a value that cannot be encoded with the +// target encoding. +type EncodingError struct { + err error + targetType interface{} + value interface{} +} + +func (e EncodingError) Error() string { + return fmt.Sprintf("cannot encode %v as %T: %s", e.value, e.targetType, e.err) +} + +// NewEncodingError wraps err as an encoding error for value into targetType. +func NewEncodingError(err error, targetType, value interface{}) EncodingError { + return EncodingError{ + err: err, + targetType: targetType, + value: value, + } +}