feat(orion): add rest api for waitlist
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package waitlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
var errNotFound = errors.New("not found")
|
||||
var errAlreadyExists = errors.New("already exists")
|
||||
|
||||
type repository interface {
|
||||
create(ctx context.Context, email string, metadata map[string]string) (*WaitlistEntry, error)
|
||||
getAll(ctx context.Context, filter GetWaitlistFilter) ([]*WaitlistEntry, error)
|
||||
getByEmail(ctx context.Context, email string) (*WaitlistEntry, error)
|
||||
markInvited(ctx context.Context, email string) error
|
||||
}
|
||||
|
||||
type repositoryImpl struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func newRepository(pool *pgxpool.Pool) repository {
|
||||
return &repositoryImpl{pool: pool}
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) create(ctx context.Context, email string, metadata map[string]string) (*WaitlistEntry, error) {
|
||||
metadataJSON, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var entry WaitlistEntry
|
||||
var metadataBytes []byte
|
||||
err = r.pool.QueryRow(ctx,
|
||||
`INSERT INTO waitlist_entries (email, metadata)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, email, metadata, created_at, invited_at`,
|
||||
email, metadataJSON,
|
||||
).Scan(&entry.Id, &entry.Email, &metadataBytes, &entry.CreatedAt, &entry.InvitedAt)
|
||||
if err != nil {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
|
||||
return nil, errAlreadyExists
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(metadataBytes, &entry.Metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) getAll(ctx context.Context, filter GetWaitlistFilter) ([]*WaitlistEntry, error) {
|
||||
query := `SELECT id, email, metadata, created_at, invited_at FROM waitlist_entries`
|
||||
switch filter {
|
||||
case GetWaitlistFilterInvitedOnly:
|
||||
query += ` WHERE invited_at IS NOT NULL`
|
||||
case GetWaitlistFilterUninvitedOnly:
|
||||
query += ` WHERE invited_at IS NULL`
|
||||
}
|
||||
query += ` ORDER BY created_at DESC`
|
||||
|
||||
rows, err := r.pool.Query(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var entries []*WaitlistEntry
|
||||
for rows.Next() {
|
||||
var entry WaitlistEntry
|
||||
var metadataBytes []byte
|
||||
if err := rows.Scan(&entry.Id, &entry.Email, &metadataBytes, &entry.CreatedAt, &entry.InvitedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(metadataBytes, &entry.Metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &entry)
|
||||
}
|
||||
|
||||
return entries, rows.Err()
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) getByEmail(ctx context.Context, email string) (*WaitlistEntry, error) {
|
||||
var entry WaitlistEntry
|
||||
var metadataBytes []byte
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, email, metadata, created_at, invited_at FROM waitlist_entries WHERE email = $1`,
|
||||
email,
|
||||
).Scan(&entry.Id, &entry.Email, &metadataBytes, &entry.CreatedAt, &entry.InvitedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(metadataBytes, &entry.Metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) markInvited(ctx context.Context, email string) error {
|
||||
result, err := r.pool.Exec(ctx,
|
||||
`UPDATE waitlist_entries SET invited_at = NOW() WHERE email = $1`,
|
||||
email,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.RowsAffected() == 0 {
|
||||
return errNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user