package influxdb import ( "encoding/json" "errors" "fmt" "strings" ) // Some error code constant, ideally we want define common platform codes here // projects on use platform's error, should have their own central place like this. // Any time this set of constants changes, you must also update the swagger for Error.properties.code.enum. const ( EInternal = "internal error" ENotFound = "not found" EConflict = "conflict" // action cannot be performed EInvalid = "invalid" // validation failed EUnprocessableEntity = "unprocessable entity" // data type is correct, but out of range EEmptyValue = "empty value" EUnavailable = "unavailable" EForbidden = "forbidden" EUnauthorized = "unauthorized" EMethodNotAllowed = "method not allowed" ) // Error is the error struct of platform. // // Errors may have error codes, human-readable messages, // and a logical stack trace. // // The Code targets automated handlers so that recovery can occur. // Msg is used by the system operator to help diagnose and fix the problem. // Op and Err chain errors together in a logical stack trace to // further help operators. // // To create a simple error, // &Error{ // Code:ENotFound, // } // To show where the error happens, add Op. // &Error{ // Code: ENotFound, // Op: "bolt.FindUserByID" // } // To show an error with a unpredictable value, add the value in Msg. // &Error{ // Code: EConflict, // Message: fmt.Sprintf("organization with name %s already exist", aName), // } // To show an error wrapped with another error. // &Error{ // Code:EInternal, // Err: err, // }. type Error struct { Code string Msg string Op string Err error } // NewError returns an instance of an error. func NewError(options ...func(*Error)) *Error { err := &Error{} for _, o := range options { o(err) } return err } // WithErrorErr sets the err on the error. func WithErrorErr(err error) func(*Error) { return func(e *Error) { e.Err = err } } // WithErrorCode sets the code on the error. func WithErrorCode(code string) func(*Error) { return func(e *Error) { e.Code = code } } // WithErrorMsg sets the message on the error. func WithErrorMsg(msg string) func(*Error) { return func(e *Error) { e.Msg = msg } } // WithErrorOp sets the message on the error. func WithErrorOp(op string) func(*Error) { return func(e *Error) { e.Op = op } } // Error implement the error interface by outputing the Code and Err. func (e *Error) Error() string { var b strings.Builder // Print the current operation in our stack, if any. if e.Op != "" { fmt.Fprintf(&b, "%s: ", e.Op) } // If wrapping an error, print its Error() message. // Otherwise print the error code & message. if e.Err != nil { b.WriteString(e.Err.Error()) } else { if e.Code != "" { fmt.Fprintf(&b, "<%s>", e.Code) if e.Msg != "" { b.WriteString(" ") } } b.WriteString(e.Msg) } return b.String() } // ErrorCode returns the code of the root error, if available; otherwise returns EINTERNAL. func ErrorCode(err error) string { if err == nil { return "" } e, ok := err.(*Error) if !ok { return EInternal } if e == nil { return "" } if e.Code != "" { return e.Code } if e.Err != nil { return ErrorCode(e.Err) } return EInternal } // ErrorOp returns the op of the error, if available; otherwise return empty string. func ErrorOp(err error) string { if err == nil { return "" } e, ok := err.(*Error) if !ok { return "" } if e == nil { return "" } if e.Op != "" { return e.Op } if e.Err != nil { return ErrorOp(e.Err) } return "" } // ErrorMessage returns the human-readable message of the error, if available. // Otherwise returns a generic error message. func ErrorMessage(err error) string { if err == nil { return "" } e, ok := err.(*Error) if !ok { return "An internal error has occurred." } if e == nil { return "" } if e.Msg != "" { return e.Msg } if e.Err != nil { return ErrorMessage(e.Err) } return "An internal error has occurred." } // errEncode an JSON encoding helper that is needed to handle the recursive stack of errors. type errEncode struct { Code string `json:"code"` // Code is the machine-readable error code. Msg string `json:"message,omitempty"` // Msg is a human-readable message. Op string `json:"op,omitempty"` // Op describes the logical code operation during error. Err interface{} `json:"error,omitempty"` // Err is a stack of additional errors. } // MarshalJSON recursively marshals the stack of Err. func (e *Error) MarshalJSON() (result []byte, err error) { ee := errEncode{ Code: e.Code, Msg: e.Msg, Op: e.Op, } if e.Err != nil { if _, ok := e.Err.(*Error); ok { _, err := e.Err.(*Error).MarshalJSON() if err != nil { return result, err } ee.Err = e.Err } else { ee.Err = e.Err.Error() } } return json.Marshal(ee) } // UnmarshalJSON recursively unmarshals the error stack. func (e *Error) UnmarshalJSON(b []byte) (err error) { ee := new(errEncode) err = json.Unmarshal(b, ee) e.Code = ee.Code e.Msg = ee.Msg e.Op = ee.Op e.Err = decodeInternalError(ee.Err) return err } func decodeInternalError(target interface{}) error { if errStr, ok := target.(string); ok { return errors.New(errStr) } if internalErrMap, ok := target.(map[string]interface{}); ok { internalErr := new(Error) if code, ok := internalErrMap["code"].(string); ok { internalErr.Code = code } if msg, ok := internalErrMap["message"].(string); ok { internalErr.Msg = msg } if op, ok := internalErrMap["op"].(string); ok { internalErr.Op = op } internalErr.Err = decodeInternalError(internalErrMap["error"]) return internalErr } return nil }