You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.5 KiB
168 lines
4.5 KiB
package apperrors
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type APPError interface {
|
|
GetType() ErrorKey
|
|
GetHTTPStatus() int
|
|
GetDetail() string
|
|
SetDetail(string)
|
|
Error() string
|
|
}
|
|
|
|
// Error response structure
|
|
type Error struct {
|
|
Code int `json:"code"`
|
|
Type ErrorKey `json:"-"`
|
|
Message string `json:"message"`
|
|
Detail string `json:"detail"`
|
|
HTTPStatus int `json:"-"`
|
|
ObjectID uint64 `json:"-"`
|
|
ObjectName string `json:"-"`
|
|
}
|
|
|
|
func (err Error) GetType() ErrorKey {
|
|
return err.Type
|
|
}
|
|
|
|
func (err Error) GetDetail() string {
|
|
return err.Detail
|
|
}
|
|
|
|
func (err Error) SetDetail(detail string) {
|
|
err.Detail = detail
|
|
}
|
|
|
|
func (err Error) GetHTTPStatus() int {
|
|
return customErrors[err.Type].HTTPStatus
|
|
}
|
|
|
|
func (err Error) Error() string {
|
|
return err.Detail
|
|
}
|
|
|
|
func NewError(errType ErrorKey, detail string) (err APPError) {
|
|
switch errType {
|
|
case DBError, InvalidDBQuery:
|
|
detail = humanReadableDatabaseError(detail)
|
|
case ValidationFailed:
|
|
detail = humanReadableMarshalError(detail)
|
|
}
|
|
return Error{
|
|
Type: errType,
|
|
Code: customErrors[errType].Code,
|
|
Message: customErrors[errType].Message,
|
|
Detail: detail,
|
|
HTTPStatus: customErrors[errType].HTTPStatus,
|
|
}
|
|
}
|
|
|
|
// customErrors object contains self defined error codes and messages
|
|
var customErrors = map[ErrorKey]Error{
|
|
ItemNotFound: {
|
|
Code: 1000,
|
|
Message: MessageItemNotFound,
|
|
HTTPStatus: http.StatusNotFound,
|
|
},
|
|
InvalidID: {
|
|
Code: 1001,
|
|
Message: MessageInvalidID,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
},
|
|
ValidationFailed: {
|
|
Code: 1002,
|
|
Message: MessageValidationFailed,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
},
|
|
PathNotFound: {
|
|
Code: 4000,
|
|
Message: MessagePathNotFound,
|
|
HTTPStatus: http.StatusNotFound,
|
|
},
|
|
MalformedQueryParameter: {
|
|
Code: 4001,
|
|
Message: MessageMalformedQueryParameter,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
},
|
|
MalformedFilterString: {
|
|
Code: 4002,
|
|
Message: MessageMalformedFilterString,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
},
|
|
InvalidPagination: {
|
|
Code: 4003,
|
|
Message: MessageInvalidPagination,
|
|
HTTPStatus: http.StatusBadRequest,
|
|
},
|
|
UnknownError: {
|
|
Code: 5000,
|
|
Message: MessageUnknownError,
|
|
HTTPStatus: http.StatusInternalServerError,
|
|
},
|
|
DBError: {
|
|
Code: 5002,
|
|
Message: MessageDBError,
|
|
HTTPStatus: http.StatusInternalServerError,
|
|
},
|
|
InvalidDBQuery: {
|
|
Code: 5003,
|
|
Message: MessageInvalidDBQuery,
|
|
HTTPStatus: http.StatusInternalServerError,
|
|
},
|
|
EmptyDBResponse: {
|
|
Code: 5004,
|
|
Message: MessageEmptyDBResponse,
|
|
HTTPStatus: http.StatusNotFound,
|
|
},
|
|
Unauthorized: {
|
|
Code: 6000,
|
|
Message: MessageUnauthorized,
|
|
HTTPStatus: http.StatusUnauthorized,
|
|
},
|
|
Forbidden: {
|
|
Code: 6001,
|
|
Message: MessageForbidden,
|
|
HTTPStatus: http.StatusForbidden,
|
|
},
|
|
ReportBug: {
|
|
Code: 9999,
|
|
Message: MessageReportBug,
|
|
HTTPStatus: http.StatusInternalServerError,
|
|
},
|
|
}
|
|
|
|
// humanReadableMarshalError translates some cryptic error messages
|
|
func humanReadableMarshalError(detail string) string {
|
|
switch {
|
|
case strings.Contains(detail, "decode slice: expect [ or n, but found {"):
|
|
return "payload must be a list"
|
|
case strings.Contains(detail, "json: cannot unmarshal object into Go value of type ["):
|
|
return "payload must be a list"
|
|
case strings.Contains(detail, "strconv.ParseUint: parsing "):
|
|
return "id must be numeric"
|
|
case strings.Contains(detail, "json: cannot unmarshal array into Go value of type"),
|
|
strings.HasPrefix(detail, "readObjectStart: expect { or n, but found ["):
|
|
return "payload must be an object"
|
|
default:
|
|
return detail
|
|
}
|
|
}
|
|
|
|
// humanReadableDatabaseError translates some cryptic error messages
|
|
func humanReadableDatabaseError(detail string) string {
|
|
switch {
|
|
case strings.Contains(detail, "Cannot insert duplicate key row in object 'dbo.users' with unique index 'username'."):
|
|
return "username already in use, no duplicate usernames allowed"
|
|
case strings.Contains(detail, "Cannot insert duplicate key row in object 'dbo.roles' with unique index 'rolename'."):
|
|
return "rolename already in use, no duplicate rolenames allowed"
|
|
case (strings.Contains(detail, "Duplicate entry") && strings.HasSuffix(detail, "for key 'idx_property_name_tenant_sid'")):
|
|
return "propertyname already in use, no duplicate propertynames allowed: " + detail
|
|
case strings.Contains(detail, "Cannot insert duplicate key in object 'dbo.tenants'."):
|
|
return "Tenant name or id in use, no duplicates allowed"
|
|
default:
|
|
return detail
|
|
}
|
|
}
|