111 lines
2.7 KiB
Go
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
|
|
}
|