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

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
}
}