Compare commits
2 Commits
0e5a46f050
...
eb2f42d50a
Author | SHA1 | Date |
---|---|---|
Drew Bednar | eb2f42d50a | 1 week ago |
Drew Bednar | f8ccbe90b4 | 1 week ago |
@ -0,0 +1,68 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const INTERNAL_ERROR_MESSAGE = "internal error"
|
||||
|
||||
// Application error codes.
|
||||
//
|
||||
// Note: these are generic codes but map well to HTTP status codes.
|
||||
const (
|
||||
EINTERNAL = "internal"
|
||||
ENOTFOUND = "not_found"
|
||||
)
|
||||
|
||||
// Error respresents an application specific-error. Application errors can be
|
||||
// unwrapped by the caller to extract out the code and message
|
||||
//
|
||||
// Any non-application error (like disk error) should be reported as an EINTERNAL
|
||||
// error and the human user should only see "Internal error" as the message.
|
||||
// These low-level internal error details should only be logged and reported to
|
||||
// the operation of the application, not the end user.
|
||||
type Error struct {
|
||||
// Machine-readable error code.
|
||||
Code string
|
||||
|
||||
// Human-readable error message.
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error implements the error interface. Not used by the application otherwise.
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("ratchet error: code=%s message=%s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// ErrorCode unwraps an application error and returns its code.
|
||||
// Non-application errors always return EINTERNAL.
|
||||
func ErrorCode(err error) string {
|
||||
var e *Error
|
||||
if err == nil {
|
||||
return ""
|
||||
} else if errors.As(err, &e) {
|
||||
return e.Code
|
||||
}
|
||||
return EINTERNAL
|
||||
}
|
||||
|
||||
// ErrorMessage unwraps an application error and returns it's message.
|
||||
// Non-application errors always return "Internal error".
|
||||
func ErrorMessage(err error) string {
|
||||
var e *Error
|
||||
if err == nil {
|
||||
return ""
|
||||
} else if errors.As(err, &e) {
|
||||
return e.Message
|
||||
}
|
||||
return INTERNAL_ERROR_MESSAGE
|
||||
}
|
||||
|
||||
// Errorf is a helper function to return an Error with a given code and format
|
||||
func Errorf(code string, format string, args ...interface{}) *Error {
|
||||
return &Error{
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func AssertErrorString(t *testing.T, got, want string) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
t.Errorf("Incorrect error code/message got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorCode(t *testing.T) {
|
||||
|
||||
t.Run("should return empty", func(t *testing.T) {
|
||||
got := ErrorCode(nil)
|
||||
|
||||
AssertErrorString(t, got, "")
|
||||
})
|
||||
|
||||
t.Run("should return internal error", func(t *testing.T) {
|
||||
want := EINTERNAL
|
||||
got := ErrorCode(errors.New("Mock disk error"))
|
||||
|
||||
AssertErrorString(t, got, want)
|
||||
})
|
||||
|
||||
t.Run("should return my code", func(t *testing.T) {
|
||||
e := &Error{Code: "my_code", Message: "my_message"}
|
||||
got := ErrorCode(e)
|
||||
|
||||
AssertErrorString(t, got, e.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorMessage(t *testing.T) {
|
||||
t.Run("empty error should return empty string", func(t *testing.T) {
|
||||
got := ErrorMessage(nil)
|
||||
|
||||
AssertErrorString(t, got, "")
|
||||
})
|
||||
|
||||
t.Run("should return internal error message", func(t *testing.T) {
|
||||
got := ErrorMessage(errors.New("Mock disk error"))
|
||||
|
||||
AssertErrorString(t, got, INTERNAL_ERROR_MESSAGE)
|
||||
})
|
||||
|
||||
t.Run("should return application error message", func(t *testing.T) {
|
||||
e := Errorf(ENOTFOUND, "Entity %s not found", "dirp")
|
||||
got := ErrorMessage(e)
|
||||
|
||||
AssertErrorString(t, got, e.Message)
|
||||
})
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
// User prefered name and email
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
|
||||
// Randomly generated API key for use with the API
|
||||
// "-" omits the key from serialization
|
||||
APIKey string `json:"-"`
|
||||
|
||||
// Timestamps for user creatation and last update
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
|
||||
// List of associated Oauth authentication Objects
|
||||
// Not yet implemented
|
||||
// Auths []*Auth `json:"auths"`
|
||||
}
|
||||
|
||||
// UserService represents a service for managing users.
|
||||
type UserService interface {
|
||||
// Retrieves a user by ID along with their associated auth objects
|
||||
// Returns ENOTFOUND if user does not exist.
|
||||
FindUserByID(ctx context.Context, id int) (*User, error)
|
||||
}
|
Loading…
Reference in New Issue