This may have caught a few. Next I need to actually work on the development/testing/production environment setup.
153 lines
3.9 KiB
Go
153 lines
3.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
domain "github.com/haydenhargreaves/Potion/internal/domain/user"
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
type UserRepository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// Compile-time check to ensure the UserRepository implements domain.UserRepository
|
|
var _ domain.UserRepository = (*UserRepository)(nil)
|
|
|
|
// NewUserRepository creates a user repository object which is used by the user service to access
|
|
// the database. Any user related database operations will take place in this repository.
|
|
func NewUserRepository(db *sql.DB) domain.UserRepository {
|
|
return &UserRepository{db: db}
|
|
}
|
|
|
|
// CreateGoogleUser creates a user entry in the users table. The refresh token is required along
|
|
// with the Google user info, in order to complete the database schema. Currently, Google login is
|
|
// the only support method of authentication, if this changes, this repository may need updates to
|
|
// match an updated table schema.
|
|
//
|
|
// This function will NOT check if the user already exists, if they do, it will return an error. For
|
|
// best results, pair this function with the GetGoogleUser which will return the user if it can find
|
|
// it.
|
|
func (r *UserRepository) CreateGoogleUser(googleUserInfo *domain.GoogleUserInfo, googleRefreshToken string) (domain.User, error) {
|
|
tx, err := r.db.Begin()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return domain.User{}, err
|
|
}
|
|
|
|
if googleUserInfo == nil {
|
|
return domain.User{}, fmt.Errorf("Google user info provided was nil")
|
|
}
|
|
|
|
var user domain.User
|
|
query := `INSERT INTO users
|
|
(GoogleId, Name, Email, ImageUrl, GoogleRefreshToken)
|
|
VALUES ($1, $2, $3, $4, $5) RETURNING *;`
|
|
|
|
if err := tx.QueryRow(
|
|
query,
|
|
googleUserInfo.Id,
|
|
googleUserInfo.Name,
|
|
googleUserInfo.Email,
|
|
googleUserInfo.Picture,
|
|
googleRefreshToken,
|
|
).Scan(
|
|
&user.Id,
|
|
&user.GoogleId,
|
|
&user.Name,
|
|
&user.Email,
|
|
&user.ImageUrl,
|
|
&user.GoogleRefreshToken,
|
|
&user.Created,
|
|
); err != nil {
|
|
tx.Rollback()
|
|
return domain.User{}, err
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
tx.Rollback()
|
|
return domain.User{}, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
// GetGoogleUser attempts to find a user in the database via its Google ID, not the database ID. This
|
|
// function is used when a user logs in with Google to prevent duplicate entries from being made. If
|
|
// no user is found, this function will return a null pointer but not an error.
|
|
func (r *UserRepository) GetGoogleUser(googleId string) (*domain.User, error) {
|
|
tx, err := r.db.Begin()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return nil, err
|
|
}
|
|
|
|
var user domain.User
|
|
query := `SELECT * FROM users WHERE GoogleId = $1`
|
|
|
|
if err := tx.QueryRow(query, googleId).Scan(
|
|
&user.Id,
|
|
&user.GoogleId,
|
|
&user.Name,
|
|
&user.Email,
|
|
&user.ImageUrl,
|
|
&user.GoogleRefreshToken,
|
|
&user.Created,
|
|
); err != nil {
|
|
// If no user was found, don't error, just return
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
tx.Rollback()
|
|
return nil, err
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
tx.Rollback()
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// GetUser gets a user from the database via its ID. The operation is wrapped in a transaction
|
|
// for added safety. The repository will not check for a nil result, instead the service will.
|
|
// Callers are responsible for protecting against double nil results. Any errors will be bubbled
|
|
// to the caller.
|
|
func (r *UserRepository) GetUser(id int) (*domain.User, error) {
|
|
tx, err := r.db.Begin()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return nil, err
|
|
}
|
|
|
|
query := "SELECT * FROM users WHERE id = $1"
|
|
|
|
var user domain.User
|
|
if err := tx.QueryRow(query, id).Scan(
|
|
&user.Id,
|
|
&user.GoogleId,
|
|
&user.Name,
|
|
&user.Email,
|
|
&user.ImageUrl,
|
|
&user.GoogleRefreshToken,
|
|
&user.Created,
|
|
); err != nil {
|
|
// If no user was found, don't error, just return
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
tx.Rollback()
|
|
return nil, err
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
tx.Rollback()
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
|
|
}
|