Documentation ¶
Index ¶
- Constants
- Variables
- func ValidateEmail(v *validator.Validator, email string)
- func ValidateFilters(v *validator.Validator, f Filters)
- func ValidateMovie(v *validator.Validator, movie *Movie)
- func ValidatePasswordPlaintext(v *validator.Validator, password string)
- func ValidateTokenPlaintext(v *validator.Validator, tokenPlaintext string)
- func ValidateUser(v *validator.Validator, user *User)
- type Filters
- type Metadata
- type MockMovieModel
- func (m MockMovieModel) Delete(id int64) error
- func (m MockMovieModel) Get(id int64) (*Movie, error)
- func (m MockMovieModel) GetAll(title string, genres []string, filters Filters) ([]*Movie, Metadata, error)
- func (m MockMovieModel) Insert(movie *Movie) error
- func (m MockMovieModel) Update(movie *Movie) error
- type MockPermissionModel
- type MockTokenModel
- type MockUserModel
- type Models
- type Movie
- type MovieModel
- type PermissionModel
- type Permissions
- type Runtime
- type Token
- type TokenModel
- type User
- type UserModel
Constants ¶
const ( ScopeActivation = "activation" ScopeAuthentication = "authentication" ScopePasswordReset = "password-reset" )
Define constants for the token scope. For now we just define the scope "activation" but we'll add additional scopes later.
Variables ¶
var ( ErrRecordNotFound = errors.New("record not found") ErrEditConflict = errors.New("edit conflict") )
Define a custom ErrRecordNotFound error. We'll return this from our Get() method when looking up a movie that doesn't exist in our database.
var AnonymousUser = &User{}
var (
ErrDuplicateEmail = errors.New("duplicate email")
)
var ErrInvalidRuntimeFormat = errors.New("invalid runtime format")
Define an error that our UnmarshalJSON() method can return if we're unable to parse or convert the JSON string successfully.
Functions ¶
func ValidateEmail ¶
func ValidateFilters ¶
func ValidateMovie ¶
func ValidateTokenPlaintext ¶
ValidateTokenPlaintext checks that the plaintext token has been provided and is exactly 26 bytes long.
func ValidateUser ¶
Types ¶
type Metadata ¶
type Metadata struct { CurrentPage int `json:"current_page,omitempty"` PageSize int `json:"page_size,omitempty"` FirstPage int `json:"first_page,omitempty"` LastPage int `json:"last_page,omitempty"` TotalRecords int `json:"total_records,omitempty"` }
Metadata holds the pagination metadata.
type MockMovieModel ¶
type MockMovieModel struct{}
func (MockMovieModel) Delete ¶
func (m MockMovieModel) Delete(id int64) error
Delete deletes the existing mockMovie.
func (MockMovieModel) Get ¶
func (m MockMovieModel) Get(id int64) (*Movie, error)
Get gets the mockMovie.
func (MockMovieModel) GetAll ¶
func (m MockMovieModel) GetAll(title string, genres []string, filters Filters) ([]*Movie, Metadata, error)
GetAll filters and returns a slice of movies and pagination metadata.
func (MockMovieModel) Insert ¶
func (m MockMovieModel) Insert(movie *Movie) error
Insert inserts a new movie record. Note that this movie must not be the same as the mocMovie.
func (MockMovieModel) Update ¶
func (m MockMovieModel) Update(movie *Movie) error
Update updates the mockMovie.
type MockPermissionModel ¶
type MockPermissionModel struct{}
func (MockPermissionModel) AddForUser ¶
func (m MockPermissionModel) AddForUser(userID int64, codes ...string) error
AddForUser adds the provided permission codes for a specific user.
func (MockPermissionModel) GetAllForUser ¶
func (m MockPermissionModel) GetAllForUser(userID int64) (Permissions, error)
GetAllForUser returns all mock permission codes for a specific user.
type MockTokenModel ¶
type MockTokenModel struct{}
func (MockTokenModel) DeleteAllForUser ¶
func (m MockTokenModel) DeleteAllForUser(scope string, userID int64) error
DeleteAllForUser ...
func (MockTokenModel) Insert ¶
func (m MockTokenModel) Insert(token *Token) error
Insert inserts the mock token data.
type MockUserModel ¶
type MockUserModel struct{}
func (MockUserModel) GetByEmail ¶
func (m MockUserModel) GetByEmail(email string) (*User, error)
GetByEmail gets the mockUser.
func (MockUserModel) GetForToken ¶
func (m MockUserModel) GetForToken(tokenScope, tokenPlaintext string) (*User, error)
GetForToken retrieves the details of the mockUser associated with a particular activation token.
func (MockUserModel) Insert ¶
func (m MockUserModel) Insert(user *User) error
Insert inserts a new user record. Note that this user must not be the same as the mockUser.
func (MockUserModel) Update ¶
func (m MockUserModel) Update(user *User) error
Update updates the mockUser.
type Models ¶
type Models struct { // Set the Movies field to be an interface containing the methods that both // the 'real' model and mock model need to support. Movies interface { Insert(movie *Movie) error Get(id int64) (*Movie, error) Update(movie *Movie) error Delete(id int64) error GetAll(title string, genres []string, filters Filters) ([]*Movie, Metadata, error) } Users interface { Insert(user *User) error GetByEmail(email string) (*User, error) Update(user *User) error GetForToken(tokenScope, tokenPlaintext string) (*User, error) } Tokens interface { New(userID int64, ttl time.Duration, scope string) (*Token, error) Insert(token *Token) error DeleteAllForUser(scope string, userID int64) error } Permissions interface { GetAllForUser(userID int64) (Permissions, error) AddForUser(userID int64, codes ...string) error } }
Create a Models struct which wraps the MovieModel. We'll add other models to this, like a UserModel and PermissionModel, as our build progresses.
func NewMockModels ¶
func NewMockModels() Models
Create a helper function which returns a Models instance containing the mock models only.
type Movie ¶
type Movie struct { ID int64 `json:"id"` // Unique integer ID for the movie CreatedAt time.Time `json:"-"` // Timestamp for when the movie is added to our database Title string `json:"title"` // Movie title Year int32 `json:"year,omitempty"` // Movie release year // Use the Runtime type instead of int32. Note that the omitempty directive // will still work on this: if the Runtime field has the underlying value 0, // then it will be considered empty and omitted -- and the MarshalJSON() // method we just made won't be called at all. Runtime Runtime `json:"runtime,omitempty"` // Movie runtime (in minutes) Genres []string `json:"genres,omitempty"` // Slice of genres for the movie (romance, comedy, etc.) Version int32 `json:"version"` // The version number starts at 1 and will be incremented each time the movie information is updated }
Annotate the Movie struct with struct tags to control how the keys appear in the JSON-encoded output.
type MovieModel ¶
Define a MovieModel struct type which wraps a sql.DB connection pool.
func (MovieModel) Delete ¶
func (m MovieModel) Delete(id int64) error
Delete deletes a specific record from the movies table.
func (MovieModel) Get ¶
func (m MovieModel) Get(id int64) (*Movie, error)
Get fetches a specific record from the movies table.
func (MovieModel) GetAll ¶
func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*Movie, Metadata, error)
GetAll method returns a slice of movies and pagination metadata. We've set this up to accept the various filter parameters as arguments.
func (MovieModel) Insert ¶
func (m MovieModel) Insert(movie *Movie) error
The Insert() method accepts a pointer to a movie struct, which should contain the data for the new record.
func (MovieModel) Update ¶
func (m MovieModel) Update(movie *Movie) error
Update updates a specific record in the movies table.
type PermissionModel ¶
PermissionModel struct type which wraps a sql.DB connection pool.
func (PermissionModel) AddForUser ¶
func (m PermissionModel) AddForUser(userID int64, codes ...string) error
AddForUser adds the provided permission codes for a specific user. Notice that we're using a variadic parameter for the codes so that we can assign multiple permissions in a single call.
func (PermissionModel) GetAllForUser ¶
func (m PermissionModel) GetAllForUser(userID int64) (Permissions, error)
GetAllForUser method returns all permission codes for a specific user in a Permissions slice.
type Permissions ¶
type Permissions []string
Define a Permissions slice, which we will use to will hold the permission codes (like "movies:read" and "movies:write") for a single user.
func (Permissions) Include ¶
func (p Permissions) Include(code string) bool
Add a helper method to check whether the Permissions slice contains a specific permission code.
type Runtime ¶
type Runtime int32
Declare a custom Runtime type, which has the underlying type int32 (the same as our Movie struct field).
func (Runtime) MarshalJSON ¶
Implement a MarshalJSON() method on the Runtime type so that it satisfies the json.Marshaler interface. This should return the JSON-encoded value for the movie runtime (in our case, it will return a string in the format '<runtime> mins').
func (*Runtime) UnmarshalJSON ¶
Implement a UnmarshalJSON() method on the Runtime type so that it satisfies the json.Unmarshaler interface. IMPORTANT: Because UnmarshalJSON() needs to modify the receiver (our Runtime type), we must use a pointer receiver for this to work correctly. Otherwise, we will only be modifying a copy (which is then discarded when this method returns).
type Token ¶
type Token struct { Plaintext string `json:"token"` Hash []byte `json:"-"` UserID int64 `json:"-"` Expiry time.Time `json:"expiry"` Scope string `json:"-"` }
Token holds the data for an individual token. This includes the plaintext and hashed versions of the token, associated user ID, expiry time and scope.
type TokenModel ¶
TokenModel struct wraps the connection pool.
func (TokenModel) DeleteAllForUser ¶
func (m TokenModel) DeleteAllForUser(scope string, userID int64) error
DeleteAllForUser deletes all tokens with a specific scope for a specific user.
func (TokenModel) Insert ¶
func (m TokenModel) Insert(token *Token) error
Insert adds the data for a specific token to the tokens table.
type User ¶
type User struct { ID int64 `json:"id"` CreatedAt time.Time `json:"created_at"` Name string `json:"name"` Email string `json:"email"` Password password `json:"-"` Activated bool `json:"activated"` Version int `json:"-"` }
User struct represents an individual user. Importantly, notice how we are using the json:'-' struct tag to prevent the Password and Version fields appearing in any output when we encode it to JSON. Also notice that the Password field uses the custom password type defined below.
func (*User) IsAnonymous ¶
IsAnonymous checks if a User instance is the AnonymousUser.
type UserModel ¶
UserModel struct wraps the connection pool.
func (UserModel) GetByEmail ¶
Retrieve the User details from the database based on the user's email address. Because we have a UNIQUE constraint on the email column, this SQL query will only return one record (or none at all, in which case we return a ErrRecordNotFound error).
func (UserModel) GetForToken ¶
GetForToken method retrieves the details of the user associated with a particular activation token. If there is no matching token found, or it has expired, this returns a `ErrRecordNotFound` error instead.
func (UserModel) Insert ¶
Insert a new record in the database for the user. Note that the id, created_at and version fields are all automatically generated by our database, so we use the RETURNING clause to read them into the User struct after the insert, in the same way that we did when creating a movie.
func (UserModel) Update ¶
Update the details for a specific user. Notice that we check against the version field to help prevent any race conditions during the request cycle, just like we did when updating a movie. And we also check for a violation of the 'users_email_key' constraint when performing the update, just like we did when inserting the user record originally.