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 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" )
Define constants for the token scope. For now we just define the scope "activation" but we'll add additional scope later in the book.
Variables ¶
var ( ErrRecordNotFound = errors.New("record not found") ErrEditConflict = errors.New("edit conflict") )
var AnonymousUser = &User{}
Declare a new AnonymousUser variable.
var (
ErrDuplicateEmail = errors.New("duplicate email")
)
Define a custom ErrDuplicateEmail error.
var ErrInvalidRuntimeFormat = errors.New("invalid runtime format")
Define an error that out UnmarshalJSON() method can return if we are unable to parse or convert the JSON string successfully.
Functions ¶
func ValidateEmail ¶
func ValidateFilters ¶
func ValidateMovie ¶
func ValidateTokenPlaintext ¶
Check that the plaintext token has been provided and it exactly 52 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"` }
Define a new Metadata struct for holding the pagination metadata.
type Models ¶
type Models struct { Movies MovieModel Users UserModel Tokens TokenModel Permissions PermissionModel }
Create a Models struct which wraps the MovieModel. We'll add other models to this, like a UserModel and PermissionModel.
type Movie ¶
type Movie struct { ID int64 `json:"id"` CreatedAt time.Time `json:"-"` // Timestamp for when the movie is added to our database Title string `json:"title"` Year int32 `json:"year,omitempty"` 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 will be incremented each time the information is updated. }
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 a specific record from the movies table.
func (MovieModel) Get ¶
func (m MovieModel) Get(id int64) (*Movie, error)
Get a specific record from the movies table.
func (MovieModel) Insert ¶
func (m MovieModel) Insert(movie *Movie) error
Insert a new record in the movies table. 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 a specific record in the movies table.
type PermissionModel ¶
func (PermissionModel) AddForUser ¶
func (m PermissionModel) AddForUser(userID int64, codes ...string) error
Add the provided permission codes for a specific user. Notice that we are 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)
type Permissions ¶
type Permissions []string
Define a Permissions slice, which we will use to hold the permission codes like "movie:read" and "movie: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.
func (Runtime) MarshalJSON ¶
Implement a MarshalJSON() method on the Runtime type so that it statisfies the json.Marshaler interface.
func (*Runtime) UnmarshalJSON ¶
Note that 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 is returned).
type Token ¶
type Token struct { Plaintext string `json:"token"` Hash []byte `json:"-"` UserID int64 `json:"-"` Expiry time.Time `json:"expiry"` Scope string `json:"-"` }
Add struct tag to control how the struct appears when encoded to JSON.
type TokenModel ¶
Define the TokenModel type.
func (TokenModel) DeleteAllForUser ¶
func (m TokenModel) DeleteAllForUser(scope string, userID int64) error
DeleteAllForUser() deletes all tokens for a specific user and scope.
func (TokenModel) Insert ¶
func (m TokenModel) Insert(token *Token) error
Insert() adds the data for a specific token to the token 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 string `json:"-"` }
Define a User struct to represent 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 ¶
Check if a User instance is the AnonymousUser.
type UserModel ¶
Create a UserModel struct which 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 query will only return one record (or none at all, in which case we return a ErrRecordNotFound error).
func (UserModel) GetForToken ¶
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 udpate, just like we did when inserting the user record originally.