diff --git a/REST-Tests/lists.http b/REST-Tests/lists.http index 666108c890..1ee3762647 100644 --- a/REST-Tests/lists.http +++ b/REST-Tests/lists.http @@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}} ### # Get one list -GET http://localhost:8080/api/v1/lists/13 +GET http://localhost:8080/api/v1/lists/13546850 Authorization: Bearer {{auth_token}} ### diff --git a/REST-Tests/namespaces.http b/REST-Tests/namespaces.http index 290cea5206..87bd39c678 100644 --- a/REST-Tests/namespaces.http +++ b/REST-Tests/namespaces.http @@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}} ### # Get one namespaces -GET http://localhost:8080/api/v1/namespaces/12 +GET http://localhost:8080/api/v1/namespaces/125476 Authorization: Bearer {{auth_token}} ### diff --git a/models/error.go b/models/error.go index 89ee02456d..95466cc40a 100644 --- a/models/error.go +++ b/models/error.go @@ -1,6 +1,21 @@ package models -import "fmt" +import ( + "fmt" + "net/http" +) + +// HTTPErrorProcessor is executed when the defined error is thrown, it will make sure the user sees an appropriate error message and http status code +type HTTPErrorProcessor interface { + HTTPError() HTTPError +} + +// HTTPError holds informations about an http error +type HTTPError struct { + HTTPCode int `json:"-"` + Code int `json:"code"` + Message string `json:"message"` +} // ===================== // User Operation Errors @@ -22,6 +37,14 @@ func (err ErrUsernameExists) Error() string { return fmt.Sprintf("a user with this username does already exist [user id: %d, username: %s]", err.UserID, err.Username) } +// ErrorCodeUsernameExists holds the unique world-error code of this error +const ErrorCodeUsernameExists = 1001 + +// HTTPError holds the http error description +func (err ErrUsernameExists) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."} +} + // ErrUserEmailExists represents a "UserEmailExists" kind of error. type ErrUserEmailExists struct { UserID int64 @@ -38,6 +61,14 @@ func (err ErrUserEmailExists) Error() string { return fmt.Sprintf("a user with this email does already exist [user id: %d, email: %s]", err.UserID, err.Email) } +// ErrorCodeUserEmailExists holds the unique world-error code of this error +const ErrorCodeUserEmailExists = 1002 + +// HTTPError holds the http error description +func (err ErrUserEmailExists) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."} +} + // ErrNoUsername represents a "UsernameAlreadyExists" kind of error. type ErrNoUsername struct { UserID int64 @@ -53,6 +84,14 @@ func (err ErrNoUsername) Error() string { return fmt.Sprintf("you need to specify a username [user id: %d]", err.UserID) } +// ErrCodeNoUsername holds the unique world-error code of this error +const ErrCodeNoUsername = 1003 + +// HTTPError holds the http error description +func (err ErrNoUsername) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsername, Message: "Please specify a username."} +} + // ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error. type ErrNoUsernamePassword struct{} @@ -66,6 +105,14 @@ func (err ErrNoUsernamePassword) Error() string { return fmt.Sprintf("you need to specify a username and a password") } +// ErrCodeNoUsernamePassword holds the unique world-error code of this error +const ErrCodeNoUsernamePassword = 1004 + +// HTTPError holds the http error description +func (err ErrNoUsernamePassword) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."} +} + // ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error. type ErrUserDoesNotExist struct { UserID int64 @@ -81,6 +128,14 @@ func (err ErrUserDoesNotExist) Error() string { return fmt.Sprintf("this user does not exist [user id: %d]", err.UserID) } +// ErrCodeUserDoesNotExist holds the unique world-error code of this error +const ErrCodeUserDoesNotExist = 1005 + +// HTTPError holds the http error description +func (err ErrUserDoesNotExist) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."} +} + // ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error. type ErrCouldNotGetUserID struct{} @@ -94,6 +149,14 @@ func (err ErrCouldNotGetUserID) Error() string { return fmt.Sprintf("could not get user ID") } +// ErrCodeCouldNotGetUserID holds the unique world-error code of this error +const ErrCodeCouldNotGetUserID = 1006 + +// HTTPError holds the http error description +func (err ErrCouldNotGetUserID) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."} +} + // ErrCannotDeleteLastUser represents a "ErrCannotDeleteLastUser" kind of error. type ErrCannotDeleteLastUser struct{} @@ -107,6 +170,14 @@ func (err ErrCannotDeleteLastUser) Error() string { return fmt.Sprintf("cannot delete last user") } +// ErrCodeCannotDeleteLastUser holds the unique world-error code of this error +const ErrCodeCannotDeleteLastUser = 1007 + +// HTTPError holds the http error description +func (err ErrCannotDeleteLastUser) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeCannotDeleteLastUser, Message: "Cannot delete the last user on the server."} +} + // =================== // Empty things errors // =================== @@ -124,6 +195,14 @@ func (err ErrIDCannotBeZero) Error() string { return fmt.Sprintf("ID cannot be 0") } +// ErrCodeIDCannotBeZero holds the unique world-error code of this error +const ErrCodeIDCannotBeZero = 2001 + +// HTTPError holds the http error description +func (err ErrIDCannotBeZero) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeIDCannotBeZero, Message: "The ID cannot be empty or 0."} +} + // =========== // List errors // =========== @@ -143,6 +222,14 @@ func (err ErrListDoesNotExist) Error() string { return fmt.Sprintf("List does not exist [ID: %d]", err.ID) } +// ErrCodeListDoesNotExist holds the unique world-error code of this error +const ErrCodeListDoesNotExist = 3001 + +// HTTPError holds the http error description +func (err ErrListDoesNotExist) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListDoesNotExist, Message: "This list does not exist."} +} + // ErrNeedToBeListAdmin represents an error, where the user is not the owner of that list (used i.e. when deleting a list) type ErrNeedToBeListAdmin struct { ListID int64 @@ -159,6 +246,14 @@ func (err ErrNeedToBeListAdmin) Error() string { return fmt.Sprintf("You need to be list owner to do that [ListID: %d, UserID: %d]", err.ListID, err.UserID) } +// ErrCodeNeedToBeListAdmin holds the unique world-error code of this error +const ErrCodeNeedToBeListAdmin = 3002 + +// HTTPError holds the http error description +func (err ErrNeedToBeListAdmin) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToBeListAdmin, Message: "You need to be the list admin of that list."} +} + // ErrNeedToBeListWriter represents an error, where the user is not the owner of that list (used i.e. when deleting a list) type ErrNeedToBeListWriter struct { ListID int64 @@ -175,6 +270,14 @@ func (err ErrNeedToBeListWriter) Error() string { return fmt.Sprintf("You need to have write acces to the list to do that [ListID: %d, UserID: %d]", err.ListID, err.UserID) } +// ErrCodeNeedToBeListWriter holds the unique world-error code of this error +const ErrCodeNeedToBeListWriter = 3003 + +// HTTPError holds the http error description +func (err ErrNeedToBeListWriter) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToBeListWriter, Message: "You need to have write access on that list."} +} + // ErrNeedToHaveListReadAccess represents an error, where the user dont has read access to that List type ErrNeedToHaveListReadAccess struct { ListID int64 @@ -191,6 +294,14 @@ func (err ErrNeedToHaveListReadAccess) Error() string { return fmt.Sprintf("You need to be List owner to do that [ListID: %d, UserID: %d]", err.ListID, err.UserID) } +// ErrCodeNeedToHaveListReadAccess holds the unique world-error code of this error +const ErrCodeNeedToHaveListReadAccess = 3004 + +// HTTPError holds the http error description +func (err ErrNeedToHaveListReadAccess) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveListReadAccess, Message: "You need to have read access to this list."} +} + // ErrListTitleCannotBeEmpty represents a "ErrListTitleCannotBeEmpty" kind of error. Used if the list does not exist. type ErrListTitleCannotBeEmpty struct{} @@ -204,6 +315,14 @@ func (err ErrListTitleCannotBeEmpty) Error() string { return fmt.Sprintf("List task text cannot be empty.") } +// ErrCodeListTitleCannotBeEmpty holds the unique world-error code of this error +const ErrCodeListTitleCannotBeEmpty = 3005 + +// HTTPError holds the http error description +func (err ErrListTitleCannotBeEmpty) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."} +} + // ================ // List task errors // ================ @@ -221,6 +340,14 @@ func (err ErrListTaskCannotBeEmpty) Error() string { return fmt.Sprintf("List task text cannot be empty.") } +// ErrCodeListTaskCannotBeEmpty holds the unique world-error code of this error +const ErrCodeListTaskCannotBeEmpty = 4001 + +// HTTPError holds the http error description +func (err ErrListTaskCannotBeEmpty) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTaskCannotBeEmpty, Message: "You must provide at least a list task text."} +} + // ErrListTaskDoesNotExist represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist. type ErrListTaskDoesNotExist struct { ID int64 @@ -236,6 +363,14 @@ func (err ErrListTaskDoesNotExist) Error() string { return fmt.Sprintf("List task does not exist. [ID: %d]", err.ID) } +// ErrCodeListTaskDoesNotExist holds the unique world-error code of this error +const ErrCodeListTaskDoesNotExist = 4002 + +// HTTPError holds the http error description +func (err ErrListTaskDoesNotExist) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListTaskDoesNotExist, Message: "This list task does not exist"} +} + // ErrNeedToBeTaskOwner represents an error, where the user is not the owner of that task (used i.e. when deleting a list) type ErrNeedToBeTaskOwner struct { TaskID int64 @@ -252,6 +387,14 @@ func (err ErrNeedToBeTaskOwner) Error() string { return fmt.Sprintf("You need to be task owner to do that [TaskID: %d, UserID: %d]", err.TaskID, err.UserID) } +// ErrCodeNeedToBeTaskOwner holds the unique world-error code of this error +const ErrCodeNeedToBeTaskOwner = 4003 + +// HTTPError holds the http error description +func (err ErrNeedToBeTaskOwner) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToBeTaskOwner, Message: "You need to be task owner to do that."} +} + // ================= // Namespace errors // ================= @@ -271,6 +414,14 @@ func (err ErrNamespaceDoesNotExist) Error() string { return fmt.Sprintf("Namespace does not exist [ID: %d]", err.ID) } +// ErrCodeNamespaceDoesNotExist holds the unique world-error code of this error +const ErrCodeNamespaceDoesNotExist = 5001 + +// HTTPError holds the http error description +func (err ErrNamespaceDoesNotExist) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeNamespaceDoesNotExist, Message: "Namespace not found."} +} + // ErrNeedToBeNamespaceOwner represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) type ErrNeedToBeNamespaceOwner struct { NamespaceID int64 @@ -287,6 +438,14 @@ func (err ErrNeedToBeNamespaceOwner) Error() string { return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeNeedToBeNamespaceOwner holds the unique world-error code of this error +const ErrCodeNeedToBeNamespaceOwner = 5002 + +// HTTPError holds the http error description +func (err ErrNeedToBeNamespaceOwner) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToBeNamespaceOwner, Message: "You need to be namespace owner to do this."} +} + // ErrUserDoesNotHaveAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) type ErrUserDoesNotHaveAccessToNamespace struct { NamespaceID int64 @@ -303,6 +462,14 @@ func (err ErrUserDoesNotHaveAccessToNamespace) Error() string { return fmt.Sprintf("You need to have access to this namespace to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeUserDoesNotHaveAccessToNamespace holds the unique world-error code of this error +const ErrCodeUserDoesNotHaveAccessToNamespace = 5003 + +// HTTPError holds the http error description +func (err ErrUserDoesNotHaveAccessToNamespace) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToNamespace, Message: "This user does not have access to the namespace."} +} + // ErrUserNeedsToBeNamespaceAdmin represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) type ErrUserNeedsToBeNamespaceAdmin struct { NamespaceID int64 @@ -319,6 +486,14 @@ func (err ErrUserNeedsToBeNamespaceAdmin) Error() string { return fmt.Sprintf("You need to be namespace admin to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeUserNeedsToBeNamespaceAdmin holds the unique world-error code of this error +const ErrCodeUserNeedsToBeNamespaceAdmin = 5004 + +// HTTPError holds the http error description +func (err ErrUserNeedsToBeNamespaceAdmin) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserNeedsToBeNamespaceAdmin, Message: "You need to be namespace admin to do this."} +} + // ErrUserDoesNotHaveWriteAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) type ErrUserDoesNotHaveWriteAccessToNamespace struct { NamespaceID int64 @@ -335,6 +510,14 @@ func (err ErrUserDoesNotHaveWriteAccessToNamespace) Error() string { return fmt.Sprintf("You need to have write access to this namespace to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeUserDoesNotHaveWriteAccessToNamespace holds the unique world-error code of this error +const ErrCodeUserDoesNotHaveWriteAccessToNamespace = 5005 + +// HTTPError holds the http error description +func (err ErrUserDoesNotHaveWriteAccessToNamespace) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveWriteAccessToNamespace, Message: "You need to have write access to this namespace to do this."} +} + // ErrNamespaceNameCannotBeEmpty represents an error, where a namespace name is empty. type ErrNamespaceNameCannotBeEmpty struct { NamespaceID int64 @@ -351,6 +534,14 @@ func (err ErrNamespaceNameCannotBeEmpty) Error() string { return fmt.Sprintf("Namespace name cannot be empty [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeNamespaceNameCannotBeEmpty holds the unique world-error code of this error +const ErrCodeNamespaceNameCannotBeEmpty = 5006 + +// HTTPError holds the http error description +func (err ErrNamespaceNameCannotBeEmpty) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceNameCannotBeEmpty, Message: "The namespace name cannot be empty."} +} + // ErrNamespaceOwnerCannotBeEmpty represents an error, where a namespace owner is empty. type ErrNamespaceOwnerCannotBeEmpty struct { NamespaceID int64 @@ -367,6 +558,14 @@ func (err ErrNamespaceOwnerCannotBeEmpty) Error() string { return fmt.Sprintf("Namespace owner cannot be empty [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeNamespaceOwnerCannotBeEmpty holds the unique world-error code of this error +const ErrCodeNamespaceOwnerCannotBeEmpty = 5007 + +// HTTPError holds the http error description +func (err ErrNamespaceOwnerCannotBeEmpty) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceOwnerCannotBeEmpty, Message: "The namespace owner cannot be empty."} +} + // ErrNeedToBeNamespaceAdmin represents an error, where the user is not the admin of that namespace (used i.e. when deleting a namespace) type ErrNeedToBeNamespaceAdmin struct { NamespaceID int64 @@ -383,6 +582,14 @@ func (err ErrNeedToBeNamespaceAdmin) Error() string { return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrCodeNeedToBeNamespaceAdmin holds the unique world-error code of this error +const ErrCodeNeedToBeNamespaceAdmin = 5008 + +// HTTPError holds the http error description +func (err ErrNeedToBeNamespaceAdmin) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToBeNamespaceAdmin, Message: "You need to be namespace owner to do this."} +} + // ErrNeedToHaveNamespaceReadAccess represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) type ErrNeedToHaveNamespaceReadAccess struct { NamespaceID int64 @@ -396,7 +603,15 @@ func IsErrNeedToHaveNamespaceReadAccess(err error) bool { } func (err ErrNeedToHaveNamespaceReadAccess) Error() string { - return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) + return fmt.Sprintf("You need to have namespace read access to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) +} + +// ErrCodeNeedToHaveNamespaceReadAccess holds the unique world-error code of this error +const ErrCodeNeedToHaveNamespaceReadAccess = 5009 + +// HTTPError holds the http error description +func (err ErrNeedToHaveNamespaceReadAccess) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveNamespaceReadAccess, Message: "You need to have namespace read access to do this."} } // ErrTeamDoesNotHaveAccessToNamespace represents an error, where the Team is not the owner of that namespace (used i.e. when deleting a namespace) @@ -415,6 +630,14 @@ func (err ErrTeamDoesNotHaveAccessToNamespace) Error() string { return fmt.Sprintf("You need to have access to this namespace to do that [NamespaceID: %d, TeamID: %d]", err.NamespaceID, err.TeamID) } +// ErrCodeTeamDoesNotHaveAccessToNamespace holds the unique world-error code of this error +const ErrCodeTeamDoesNotHaveAccessToNamespace = 5010 + +// HTTPError holds the http error description +func (err ErrTeamDoesNotHaveAccessToNamespace) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToNamespace, Message: "You need to have access to this namespace to do this."} +} + // ErrUserAlreadyHasNamespaceAccess represents an error where a user already has access to a namespace type ErrUserAlreadyHasNamespaceAccess struct { UserID int64 @@ -431,6 +654,14 @@ func (err ErrUserAlreadyHasNamespaceAccess) Error() string { return fmt.Sprintf("This user already has access to that namespace. [User ID: %d, Namespace ID: %d]", err.UserID, err.NamespaceID) } +// ErrCodeUserAlreadyHasNamespaceAccess holds the unique world-error code of this error +const ErrCodeUserAlreadyHasNamespaceAccess = 5011 + +// HTTPError holds the http error description +func (err ErrUserAlreadyHasNamespaceAccess) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasNamespaceAccess, Message: "This user already has access to this namespace."} +} + // ============ // Team errors // ============ @@ -450,6 +681,14 @@ func (err ErrTeamNameCannotBeEmpty) Error() string { return fmt.Sprintf("Team name cannot be empty [Team ID: %d]", err.TeamID) } +// ErrCodeTeamNameCannotBeEmpty holds the unique world-error code of this error +const ErrCodeTeamNameCannotBeEmpty = 6001 + +// HTTPError holds the http error description +func (err ErrTeamNameCannotBeEmpty) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"} +} + // ErrTeamDoesNotExist represents an error where a team does not exist type ErrTeamDoesNotExist struct { TeamID int64 @@ -465,6 +704,14 @@ func (err ErrTeamDoesNotExist) Error() string { return fmt.Sprintf("Team does not exist [Team ID: %d]", err.TeamID) } +// ErrCodeTeamDoesNotExist holds the unique world-error code of this error +const ErrCodeTeamDoesNotExist = 6002 + +// HTTPError holds the http error description +func (err ErrTeamDoesNotExist) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."} +} + // ErrInvalidTeamRight represents an error where a team right is invalid type ErrInvalidTeamRight struct { Right TeamRight @@ -480,6 +727,14 @@ func (err ErrInvalidTeamRight) Error() string { return fmt.Sprintf("The right is invalid [Right: %d]", err.Right) } +// ErrCodeInvalidTeamRight holds the unique world-error code of this error +const ErrCodeInvalidTeamRight = 6003 + +// HTTPError holds the http error description +func (err ErrInvalidTeamRight) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."} +} + // ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace type ErrTeamAlreadyHasAccess struct { TeamID int64 @@ -496,6 +751,14 @@ func (err ErrTeamAlreadyHasAccess) Error() string { return fmt.Sprintf("This team already has access. [Team ID: %d, ID: %d]", err.TeamID, err.ID) } +// ErrCodeTeamAlreadyHasAccess holds the unique world-error code of this error +const ErrCodeTeamAlreadyHasAccess = 6004 + +// HTTPError holds the http error description +func (err ErrTeamAlreadyHasAccess) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamAlreadyHasAccess, Message: "This team already has access."} +} + // ErrUserIsMemberOfTeam represents an error where a user is already member of a team. type ErrUserIsMemberOfTeam struct { TeamID int64 @@ -512,6 +775,14 @@ func (err ErrUserIsMemberOfTeam) Error() string { return fmt.Sprintf("This user is already a member of that team. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID) } +// ErrCodeUserIsMemberOfTeam holds the unique world-error code of this error +const ErrCodeUserIsMemberOfTeam = 6005 + +// HTTPError holds the http error description +func (err ErrUserIsMemberOfTeam) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserIsMemberOfTeam, Message: "This user is already a member of that team."} +} + // ErrCannotDeleteLastTeamMember represents an error where a user wants to delete the last member of a team (probably himself) type ErrCannotDeleteLastTeamMember struct { TeamID int64 @@ -525,7 +796,15 @@ func IsErrCannotDeleteLastTeamMember(err error) bool { } func (err ErrCannotDeleteLastTeamMember) Error() string { - return fmt.Sprintf("This user is already a member of that team. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID) + return fmt.Sprintf("Cannot delete last team member. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID) +} + +// ErrCodeCannotDeleteLastTeamMember holds the unique world-error code of this error +const ErrCodeCannotDeleteLastTeamMember = 6006 + +// HTTPError holds the http error description +func (err ErrCannotDeleteLastTeamMember) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCannotDeleteLastTeamMember, Message: "You cannot delete the last member of a team."} } // ErrTeamDoesNotHaveAccessToList represents an error, where the Team is not the owner of that List (used i.e. when deleting a List) @@ -544,6 +823,14 @@ func (err ErrTeamDoesNotHaveAccessToList) Error() string { return fmt.Sprintf("You need to have access to this List to do that [ListID: %d, TeamID: %d]", err.ListID, err.TeamID) } +// ErrCodeTeamDoesNotHaveAccessToList holds the unique world-error code of this error +const ErrCodeTeamDoesNotHaveAccessToList = 6007 + +// HTTPError holds the http error description +func (err ErrTeamDoesNotHaveAccessToList) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."} +} + // ==================== // User <-> List errors // ==================== @@ -563,6 +850,14 @@ func (err ErrInvalidUserRight) Error() string { return fmt.Sprintf("The right is invalid [Right: %d]", err.Right) } +// ErrCodeInvalidUserRight holds the unique world-error code of this error +const ErrCodeInvalidUserRight = 7001 + +// HTTPError holds the http error description +func (err ErrInvalidUserRight) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."} +} + // ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace type ErrUserAlreadyHasAccess struct { UserID int64 @@ -579,6 +874,14 @@ func (err ErrUserAlreadyHasAccess) Error() string { return fmt.Sprintf("This user already has access to that list. [User ID: %d, List ID: %d]", err.UserID, err.ListID) } +// ErrCodeUserAlreadyHasAccess holds the unique world-error code of this error +const ErrCodeUserAlreadyHasAccess = 7002 + +// HTTPError holds the http error description +func (err ErrUserAlreadyHasAccess) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasAccess, Message: "This user already has access to this list."} +} + // ErrUserDoesNotHaveAccessToList represents an error, where the user is not the owner of that List (used i.e. when deleting a List) type ErrUserDoesNotHaveAccessToList struct { ListID int64 @@ -594,3 +897,11 @@ func IsErrUserDoesNotHaveAccessToList(err error) bool { func (err ErrUserDoesNotHaveAccessToList) Error() string { return fmt.Sprintf("You need to have access to this List to do that [ListID: %d, UserID: %d]", err.ListID, err.UserID) } + +// ErrCodeUserDoesNotHaveAccessToList holds the unique world-error code of this error +const ErrCodeUserDoesNotHaveAccessToList = 7003 + +// HTTPError holds the http error description +func (err ErrUserDoesNotHaveAccessToList) HTTPError() HTTPError { + return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToList, Message: "This user does not have access to the list."} +} diff --git a/routes/api/v1/user_add_update.go b/routes/api/v1/user_add_update.go index 6ab74dd7e0..8fb339664c 100644 --- a/routes/api/v1/user_add_update.go +++ b/routes/api/v1/user_add_update.go @@ -2,6 +2,7 @@ package v1 import ( "code.vikunja.io/api/models" + "code.vikunja.io/api/routes/crud" "github.com/labstack/echo" "net/http" "strconv" @@ -77,32 +78,7 @@ func userAddOrUpdate(c echo.Context) error { } if err != nil { - // Check for user already exists - if models.IsErrUsernameExists(err) { - return c.JSON(http.StatusBadRequest, models.Message{"A user with this username already exists."}) - } - - // Check for user with that email already exists - if models.IsErrUserEmailExists(err) { - return c.JSON(http.StatusBadRequest, models.Message{"A user with this email address already exists."}) - } - - // Check for no username provided - if models.IsErrNoUsername(err) { - return c.JSON(http.StatusBadRequest, models.Message{"Please specify a username."}) - } - - // Check for no username or password provided - if models.IsErrNoUsernamePassword(err) { - return c.JSON(http.StatusBadRequest, models.Message{"Please specify a username and a password."}) - } - - // Check for user does not exist - if models.IsErrUserDoesNotExist(err) { - return c.JSON(http.StatusBadRequest, models.Message{"The user does not exist."}) - } - - return c.JSON(http.StatusInternalServerError, models.Message{"Error"}) + return crud.HandleHTTPError(err) } return c.JSON(http.StatusOK, newUser) diff --git a/routes/api/v1/user_delete.go b/routes/api/v1/user_delete.go index b62c9f72d1..f3d2656822 100644 --- a/routes/api/v1/user_delete.go +++ b/routes/api/v1/user_delete.go @@ -2,6 +2,7 @@ package v1 import ( "code.vikunja.io/api/models" + "code.vikunja.io/api/routes/crud" "github.com/labstack/echo" "net/http" "strconv" @@ -41,15 +42,7 @@ func UserDelete(c echo.Context) error { err = models.DeleteUserByID(userID, &doer) if err != nil { - if models.IsErrIDCannotBeZero(err) { - return c.JSON(http.StatusBadRequest, models.Message{"Id cannot be 0"}) - } - - if models.IsErrCannotDeleteLastUser(err) { - return c.JSON(http.StatusBadRequest, models.Message{"Cannot delete last user."}) - } - - return c.JSON(http.StatusInternalServerError, models.Message{"Could not delete user."}) + return crud.HandleHTTPError(err) } return c.JSON(http.StatusOK, models.Message{"success"}) diff --git a/routes/api/v1/user_list.go b/routes/api/v1/user_list.go index 8dec513de7..e86080e044 100644 --- a/routes/api/v1/user_list.go +++ b/routes/api/v1/user_list.go @@ -2,6 +2,7 @@ package v1 import ( "code.vikunja.io/api/models" + "code.vikunja.io/api/routes/crud" "github.com/labstack/echo" "net/http" ) @@ -31,8 +32,7 @@ func UserList(c echo.Context) error { s := c.QueryParam("s") users, err := models.ListUsers(s) if err != nil { - models.Log.Error(err.Error()) - return echo.NewHTTPError(http.StatusInternalServerError, "An error occurred.") + return crud.HandleHTTPError(err) } // Obfuscate the mailadresses diff --git a/routes/api/v1/user_show.go b/routes/api/v1/user_show.go index 382f159540..4c591ab646 100644 --- a/routes/api/v1/user_show.go +++ b/routes/api/v1/user_show.go @@ -2,6 +2,7 @@ package v1 import ( "code.vikunja.io/api/models" + "code.vikunja.io/api/routes/crud" "github.com/labstack/echo" "net/http" ) @@ -25,18 +26,12 @@ func UserShow(c echo.Context) error { userInfos, err := models.GetCurrentUser(c) if err != nil { - if models.IsErrUserDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "The user does not exist.") - } - return echo.NewHTTPError(http.StatusInternalServerError, "Error getting user infos.") + return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.") } user, err := models.GetUserByID(userInfos.ID) if err != nil { - if models.IsErrUserDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "The user does not exist.") - } - return echo.NewHTTPError(http.StatusInternalServerError, "Error getting user infos.") + return crud.HandleHTTPError(err) } return c.JSON(http.StatusOK, user) diff --git a/routes/api/v1/user_update_password.go b/routes/api/v1/user_update_password.go index c93e2ea034..9c0b23ac14 100644 --- a/routes/api/v1/user_update_password.go +++ b/routes/api/v1/user_update_password.go @@ -2,6 +2,7 @@ package v1 import ( "code.vikunja.io/api/models" + "code.vikunja.io/api/routes/crud" "github.com/labstack/echo" "net/http" ) @@ -50,20 +51,12 @@ func UserChangePassword(c echo.Context) error { // Check the current password if _, err = models.CheckUserCredentials(&models.UserLogin{Username: doer.Username, Password: newPW.OldPassword}); err != nil { - if models.IsErrUserDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "The user does not exist.") - } - return c.JSON(http.StatusUnauthorized, models.Message{"Wrong password."}) + return crud.HandleHTTPError(err) } // Update the password if err = models.UpdateUserPassword(&doer, newPW.NewPassword); err != nil { - if models.IsErrUserDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "The user does not exist.") - } - - models.Log.Error("Error updating a users password, user: %d, err: %s", doer.ID, err) - return echo.NewHTTPError(http.StatusInternalServerError, "An error occurred.") + return crud.HandleHTTPError(err) } return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."}) diff --git a/routes/crud/create.go b/routes/crud/create.go index f38a0d4f73..43aa3b046d 100644 --- a/routes/crud/create.go +++ b/routes/crud/create.go @@ -33,50 +33,7 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error { // Create err = c.CObject.Create(¤tUser) if err != nil { - models.Log.Error(err.Error()) - - if models.IsErrListDoesNotExist(err) { - return echo.NewHTTPError(http.StatusBadRequest, "The list does not exist.") - } - if models.IsErrListTitleCannotBeEmpty(err) { - return echo.NewHTTPError(http.StatusBadRequest, "You must provide at least a list title.") - } - if models.IsErrListTaskCannotBeEmpty(err) { - return echo.NewHTTPError(http.StatusBadRequest, "You must provide at least a list task text.") - } - if models.IsErrUserDoesNotExist(err) { - return echo.NewHTTPError(http.StatusBadRequest, "The user does not exist.") - } - if models.IsErrNeedToBeListWriter(err) { - return echo.NewHTTPError(http.StatusForbidden, "You need to have write access on that list.") - } - - if models.IsErrNamespaceNameCannotBeEmpty(err) { - return echo.NewHTTPError(http.StatusNotFound, "The namespace name cannot be empty.") - } - - if models.IsErrTeamNameCannotBeEmpty(err) { - return echo.NewHTTPError(http.StatusBadRequest, "The team name cannot be empty.") - } - - if models.IsErrTeamAlreadyHasAccess(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This team already has access.") - } - if models.IsErrUserIsMemberOfTeam(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This user is already a member of that team.") - } - - if models.IsErrUserAlreadyHasAccess(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This user already has access to this list.") - } - if models.IsErrUserAlreadyHasNamespaceAccess(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This user already has access to this namespace.") - } - if models.IsErrInvalidUserRight(err) { - return echo.NewHTTPError(http.StatusBadRequest, "The right is invalid.") - } - - return echo.NewHTTPError(http.StatusInternalServerError) + return HandleHTTPError(err) } return ctx.JSON(http.StatusCreated, c.CObject) diff --git a/routes/crud/delete.go b/routes/crud/delete.go index 587fa4442e..ddd476ef75 100644 --- a/routes/crud/delete.go +++ b/routes/crud/delete.go @@ -25,36 +25,7 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error { err = c.CObject.Delete() if err != nil { - models.Log.Error(err.Error()) - - if models.IsErrNeedToBeListAdmin(err) { - return echo.NewHTTPError(http.StatusForbidden, "You need to be the list admin to delete a list.") - } - - if models.IsErrListDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "This list does not exist.") - } - if models.IsErrTeamDoesNotHaveAccessToList(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This team does not have access to the list.") - } - - if models.IsErrTeamDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "This team does not exist.") - } - - if models.IsErrCannotDeleteLastTeamMember(err) { - return echo.NewHTTPError(http.StatusBadRequest, "You cannot delete the last member of a team.") - } - - if models.IsErrUserDoesNotHaveAccessToList(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This user does not have access to the list.") - } - - if models.IsErrUserDoesNotHaveAccessToNamespace(err) { - return echo.NewHTTPError(http.StatusBadRequest, "This user does not have access to the namespace.") - } - - return echo.NewHTTPError(http.StatusInternalServerError) + return HandleHTTPError(err) } return ctx.JSON(http.StatusOK, models.Message{"Successfully deleted."}) diff --git a/routes/crud/helper.go b/routes/crud/helper.go index 67d46995ac..e3f08f5ab7 100644 --- a/routes/crud/helper.go +++ b/routes/crud/helper.go @@ -2,6 +2,8 @@ package crud import ( "code.vikunja.io/api/models" + "github.com/labstack/echo" + "net/http" ) // WebHandler defines the webhandler object @@ -12,3 +14,13 @@ type WebHandler struct { models.Rights } } + +// HandleHTTPError does what it says +func HandleHTTPError(err error) *echo.HTTPError { + if a, has := err.(models.HTTPErrorProcessor); has { + errDetails := a.HTTPError() + return echo.NewHTTPError(errDetails.Code, errDetails) + } + models.Log.Error(err.Error()) + return echo.NewHTTPError(http.StatusInternalServerError) +} diff --git a/routes/crud/read_all.go b/routes/crud/read_all.go index 02b06d7483..26c0e24808 100644 --- a/routes/crud/read_all.go +++ b/routes/crud/read_all.go @@ -20,17 +20,7 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error { lists, err := c.CObject.ReadAll(¤tUser) if err != nil { - models.Log.Error(err.Error()) - - if models.IsErrNeedToHaveListReadAccess(err) { - return echo.NewHTTPError(http.StatusForbidden, "You need to have read access to this list.") - } - - if models.IsErrNamespaceDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "This namespace does not exist.") - } - - return echo.NewHTTPError(http.StatusInternalServerError, "An error occurred.") + return HandleHTTPError(err) } return ctx.JSON(http.StatusOK, lists) diff --git a/routes/crud/read_one.go b/routes/crud/read_one.go index 652d2dd092..881d293f57 100644 --- a/routes/crud/read_one.go +++ b/routes/crud/read_one.go @@ -17,21 +17,7 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error { // Get our object err := c.CObject.ReadOne() if err != nil { - models.Log.Error(err.Error()) - - if models.IsErrListDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound) - } - - if models.IsErrNamespaceDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound) - } - - if models.IsErrTeamDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound) - } - - return echo.NewHTTPError(http.StatusInternalServerError, "An error occurred.") + return HandleHTTPError(err) } // Check rights diff --git a/routes/crud/update.go b/routes/crud/update.go index 0912438cb1..608e64dfa8 100644 --- a/routes/crud/update.go +++ b/routes/crud/update.go @@ -31,23 +31,7 @@ func (c *WebHandler) UpdateWeb(ctx echo.Context) error { // Do the update err = c.CObject.Update() if err != nil { - models.Log.Error(err.Error()) - - if models.IsErrNeedToBeListAdmin(err) { - return echo.NewHTTPError(http.StatusForbidden, "You need to be list admin to do that.") - } - - if models.IsErrNamespaceDoesNotExist(err) { - return echo.NewHTTPError(http.StatusNotFound, "The namespace does not exist.") - } - if models.IsErrNamespaceNameCannotBeEmpty(err) { - return echo.NewHTTPError(http.StatusBadRequest, "The namespace name cannot be empty.") - } - if models.IsErrNamespaceOwnerCannotBeEmpty(err) { - return echo.NewHTTPError(http.StatusBadRequest, "The namespace owner cannot be empty.") - } - - return echo.NewHTTPError(http.StatusInternalServerError) + return HandleHTTPError(err) } return ctx.JSON(http.StatusOK, c.CObject)