package user import ( "context" "database/sql" "errors" "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" ) type UserDB interface { AddUser(ctx context.Context, user *model.User) error GetUserBySubject(ctx context.Context, subject string) (*model.User, error) } var ErrNotFound = errors.New("not found") const getUserQuery = ` SELECT id, name, family_name, given_name, nickname, picture, updated_at, email, email_verified FROM user WHERE id = ? ` const insertUserQuery = ` INSERT OR REPLACE INTO user (id, name, family_name, given_name, nickname, picture, updated_at, email, email_verified) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ` type sqlUserDB struct { db *sql.DB } func (db *sqlUserDB) GetUserBySubject(ctx context.Context, subject string) (*model.User, error) { row := db.db.QueryRowContext(ctx, getUserQuery, subject) var res model.User if err := row.Scan(&res.Subject, &res.Name, &res.FamilyName, &res.GivenName, &res.Nickname, &res.Picture, &res.UpdatedAt, &res.Email, &res.EmailVerified); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, fmt.Errorf("failed to read result from DB: %w", err) } return &res, nil } func (db *sqlUserDB) AddUser(ctx context.Context, user *model.User) error { tx, err := db.db.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("failed to start transaction: %w", err) } defer func() { _ = tx.Rollback() }() if _, err := tx.ExecContext(ctx, insertUserQuery, user.Subject, user.Name, user.FamilyName, user.GivenName, user.Nickname, user.Picture, user.UpdatedAt, user.Email, user.EmailVerified); err != nil { return fmt.Errorf("failed to insert in DB: %w", err) } if err := tx.Commit(); err != nil { return fmt.Errorf("failed to commit transaction: %w", err) } return nil } func New(db *sql.DB) *sqlUserDB { return &sqlUserDB{db: db} }