Files
llink/go/internal/human/repository.go
T
2026-06-11 13:47:34 -07:00

177 lines
4.7 KiB
Go

package human
import (
"context"
"errors"
"strings"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"go.jetify.com/typeid"
)
var errNotFound = errors.New("not found")
type humanIDPrefix struct{}
func (humanIDPrefix) Prefix() string { return "human" }
type humanID struct {
typeid.TypeID[humanIDPrefix]
}
func newHumanID() (humanID, error) {
return typeid.New[humanID]()
}
func emailPrefix(email string) string {
return strings.Split(email, "@")[0]
}
type repository interface {
getByEmail(ctx context.Context, email string) (*Human, error)
getByID(ctx context.Context, id string) (*Human, error)
create(ctx context.Context, email string) (*Human, error)
exists(ctx context.Context, email string) (bool, error)
listAll(ctx context.Context) ([]*Human, error)
updateEmailNotificationsEnabled(ctx context.Context, id string, enabled bool) error
updateLastEmailNotificationSentAt(ctx context.Context, id string, t time.Time) error
updateAvatarObjectID(ctx context.Context, id string, objectID *string) error
}
type repositoryImpl struct {
pool *pgxpool.Pool
}
func newRepository(pool *pgxpool.Pool) repository {
return &repositoryImpl{pool: pool}
}
func (r *repositoryImpl) getByEmail(ctx context.Context, email string) (*Human, error) {
var h Human
err := r.pool.QueryRow(ctx,
`SELECT id, email, email_notifications_enabled, last_email_notification_sent_at, created_at, avatar_object_id FROM humans WHERE email = $1`,
email,
).Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt, &h.AvatarObjectID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, errNotFound
}
return nil, err
}
h.EmailPrefix = emailPrefix(h.Email)
return &h, nil
}
func (r *repositoryImpl) getByID(ctx context.Context, id string) (*Human, error) {
var h Human
err := r.pool.QueryRow(ctx,
`SELECT id, email, email_notifications_enabled, last_email_notification_sent_at, created_at, avatar_object_id FROM humans WHERE id = $1`,
id,
).Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt, &h.AvatarObjectID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, errNotFound
}
return nil, err
}
h.EmailPrefix = emailPrefix(h.Email)
return &h, nil
}
func (r *repositoryImpl) create(ctx context.Context, email string) (*Human, error) {
id, err := newHumanID()
if err != nil {
return nil, err
}
var h Human
err = r.pool.QueryRow(ctx,
`INSERT INTO humans (id, email) VALUES ($1, $2)
RETURNING id, email, email_notifications_enabled, last_email_notification_sent_at, created_at`,
id.String(), email,
).Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt)
if err != nil {
return nil, err
}
h.EmailPrefix = emailPrefix(h.Email)
return &h, nil
}
func (r *repositoryImpl) exists(ctx context.Context, email string) (bool, error) {
var exists bool
err := r.pool.QueryRow(ctx,
`SELECT EXISTS(SELECT 1 FROM humans WHERE email = $1)`,
email,
).Scan(&exists)
if err != nil {
return false, err
}
return exists, nil
}
func (r *repositoryImpl) listAll(ctx context.Context) ([]*Human, error) {
rows, err := r.pool.Query(ctx,
`SELECT id, email, email_notifications_enabled, last_email_notification_sent_at, created_at, avatar_object_id FROM humans`,
)
if err != nil {
return nil, err
}
defer rows.Close()
var humans []*Human
for rows.Next() {
var h Human
if err := rows.Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt, &h.AvatarObjectID); err != nil {
return nil, err
}
h.EmailPrefix = emailPrefix(h.Email)
humans = append(humans, &h)
}
return humans, rows.Err()
}
func (r *repositoryImpl) updateEmailNotificationsEnabled(ctx context.Context, id string, enabled bool) error {
result, err := r.pool.Exec(ctx,
`UPDATE humans SET email_notifications_enabled = $2 WHERE id = $1`,
id, enabled,
)
if err != nil {
return err
}
if result.RowsAffected() == 0 {
return errNotFound
}
return nil
}
func (r *repositoryImpl) updateLastEmailNotificationSentAt(ctx context.Context, id string, t time.Time) error {
result, err := r.pool.Exec(ctx,
`UPDATE humans SET last_email_notification_sent_at = $2 WHERE id = $1`,
id, t,
)
if err != nil {
return err
}
if result.RowsAffected() == 0 {
return errNotFound
}
return nil
}
func (r *repositoryImpl) updateAvatarObjectID(ctx context.Context, id string, objectID *string) error {
result, err := r.pool.Exec(ctx,
`UPDATE humans SET avatar_object_id = $2 WHERE id = $1`,
id, objectID,
)
if err != nil {
return err
}
if result.RowsAffected() == 0 {
return errNotFound
}
return nil
}