Files
llink/go/internal/waitlist/service.go
T
2026-03-29 09:04:15 -07:00

111 lines
2.7 KiB
Go

package waitlist
import (
"context"
"errors"
"time"
"github.com/flowy-live/llink/internal/utils"
"github.com/jackc/pgx/v5/pgxpool"
)
type WaitlistEntry struct {
Id int `db:"id"`
Email string `db:"email"`
Metadata map[string]string `db:"metadata"`
CreatedAt time.Time `db:"created_at"`
InvitedAt *time.Time `db:"invited_at"`
}
var (
AlreadyInWaitlistError = errors.New("already in the waitlist")
EntryNotFoundError = errors.New("not found")
)
type GetWaitlistFilter string
const (
GetWaitlistFilterAll GetWaitlistFilter = "all"
GetWaitlistFilterInvitedOnly GetWaitlistFilter = "invited-only"
GetWaitlistFilterUninvitedOnly GetWaitlistFilter = "uninvited-only"
)
type Service interface {
// returns AlreadyInWaitlistError if already in the waitlist
// any other error is a failure
AddToWaitlist(ctx context.Context, email string, metadata map[string]string) error
GetWaitlist(ctx context.Context, filter GetWaitlistFilter) ([]*WaitlistEntry, error)
// returns error if not found
GetWaitlistEntryByEmail(ctx context.Context, email string) (*WaitlistEntry, error)
MarkWaitlistEntryInvited(ctx context.Context, email string) error
}
type serviceImpl struct {
repo repository
}
func NewService(pool *pgxpool.Pool) Service {
return &serviceImpl{
repo: newRepository(pool),
}
}
func (s *serviceImpl) AddToWaitlist(ctx context.Context, email string, metadata map[string]string) error {
normalizedEmail, err := utils.NormalizeEmail(email)
if err != nil {
return errors.New("invalid email")
}
if metadata == nil {
metadata = map[string]string{}
}
_, err = s.repo.create(ctx, normalizedEmail, metadata)
if err != nil {
if errors.Is(err, errAlreadyExists) {
return AlreadyInWaitlistError
}
return err
}
return nil
}
func (s *serviceImpl) GetWaitlist(ctx context.Context, filter GetWaitlistFilter) ([]*WaitlistEntry, error) {
return s.repo.getAll(ctx, filter)
}
func (s *serviceImpl) GetWaitlistEntryByEmail(ctx context.Context, email string) (*WaitlistEntry, error) {
normalizedEmail, err := utils.NormalizeEmail(email)
if err != nil {
return nil, errors.New("invalid email")
}
entry, err := s.repo.getByEmail(ctx, normalizedEmail)
if err != nil {
if errors.Is(err, errNotFound) {
return nil, EntryNotFoundError
}
return nil, err
}
return entry, nil
}
func (s *serviceImpl) MarkWaitlistEntryInvited(ctx context.Context, email string) error {
normalizedEmail, err := utils.NormalizeEmail(email)
if err != nil {
return errors.New("invalid email")
}
err = s.repo.markInvited(ctx, normalizedEmail)
if err != nil {
if errors.Is(err, errNotFound) {
return EntryNotFoundError
}
return err
}
return nil
}