ms-auth/internal/users/repository/pg_repository.go
Vyacheslav1557 93eaf89d78 refactor:
2024-10-09 22:07:38 +05:00

196 lines
4.4 KiB
Go

package repository
import (
"context"
"errors"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
"time"
)
type UsersRepository struct {
db *sqlx.DB
logger *zap.Logger
}
func NewUserRepository(db *sqlx.DB, logger *zap.Logger) *UsersRepository {
return &UsersRepository{
db: db,
logger: logger,
}
}
const year = time.Hour * 24 * 365
const createUser = `
INSERT INTO users
(username, hashed_pwd, email, expires_at, role)
VALUES (?, ?, ?, ?, ?)
RETURNING id
`
func (storage *UsersRepository) CreateUser(ctx context.Context, user *models.User) (int32, error) {
if err := user.ValidUsername(); err != nil {
return 0, err
}
if err := user.ValidPassword(); err != nil {
return 0, err
}
if err := user.ValidEmail(); err != nil {
return 0, err
}
if err := user.ValidRole(); err != nil {
return 0, err
}
if err := user.HashPassword(); err != nil { // FIXME: get rid of mutation
return 0, err
}
user.ExpiresAt = utils.AsTimeP(time.Now().Add(100 * year)) // FIXME: get rid of mutation
query := storage.db.Rebind(createUser)
rows, err := storage.db.QueryxContext(
ctx,
query,
user.Username,
user.Password,
user.Email,
user.ExpiresAt,
user.Role,
)
if err != nil {
return 0, storage.handlePgErr(err)
}
defer rows.Close()
var id int32
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, storage.handlePgErr(err)
}
return id, nil
}
const readUserByEmail = "SELECT * from users WHERE email=? LIMIT 1"
func (storage *UsersRepository) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
var user models.User
query := storage.db.Rebind(readUserByEmail)
err := storage.db.GetContext(ctx, &user, query, email)
if err != nil {
return nil, storage.handlePgErr(err)
}
return &user, nil
}
const readUserByUsername = "SELECT * from users WHERE username=? LIMIT 1"
func (storage *UsersRepository) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
var user models.User
query := storage.db.Rebind(readUserByUsername)
err := storage.db.GetContext(ctx, &user, query, username)
if err != nil {
return nil, storage.handlePgErr(err)
}
return &user, nil
}
const readUserById = "SELECT * from users WHERE id=? LIMIT 1"
func (storage *UsersRepository) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
var user models.User
query := storage.db.Rebind(readUserById)
err := storage.db.GetContext(ctx, &user, query, id)
if err != nil {
return nil, storage.handlePgErr(err)
}
return &user, nil
}
const updateUser = `
UPDATE users
SET username = COALESCE(?, username),
hashed_pwd = COALESCE(?, hashed_pwd),
email = COALESCE(?, email),
expires_at = COALESCE(?, expires_at),
role = COALESCE(?, role)
WHERE id = ?
`
func (storage *UsersRepository) UpdateUser(ctx context.Context, user *models.User) error {
var err error
if user.Username != nil {
if err = user.ValidUsername(); err != nil {
return err
}
}
if user.Password != nil {
if err = user.ValidPassword(); err != nil {
return err
}
if err = user.HashPassword(); err != nil {
return err
}
}
if user.Email != nil {
if err = utils.ValidEmail(*user.Email); err != nil {
return err
}
}
if user.Role != nil {
if err = user.Role.Valid(); err != nil {
return err
}
}
query := storage.db.Rebind(updateUser)
_, err = storage.db.ExecContext(
ctx,
query,
user.Username,
user.Password,
user.Email,
user.ExpiresAt,
user.Role,
user.Id,
)
if err != nil {
return storage.handlePgErr(err)
}
return nil
}
const deleteUser = "UPDATE users SET expired_at=now() WHERE id = ?"
func (storage *UsersRepository) DeleteUser(ctx context.Context, id int32) error {
query := storage.db.Rebind(deleteUser)
_, err := storage.db.ExecContext(ctx, query, id)
if err != nil {
return storage.handlePgErr(err)
}
return nil
}
func (storage *UsersRepository) handlePgErr(err error) error {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error()))
return utils.ErrUnexpected
}
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
return errors.New("unique key violation") // FIXME
}
storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error()))
return utils.ErrInternal
}