mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-06 08:01:24 +00:00
Remove DynamoDB-backed storage paths, add Postgres/HTTP repositories for mailbox state maps, wire the internal message-map API, and add provisioning runner/migration plumbing.
85 lines
2.7 KiB
Go
85 lines
2.7 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/warmbly/warmbly/internal/infrastructure/db"
|
|
)
|
|
|
|
// pgEmailMessageMapRepository is the Postgres-backed implementation used by the
|
|
// backend, which serves it to workers over the internal API. The composite key
|
|
// (user_id, email_id, message_id) is canonical — see the 000003 migration.
|
|
type pgEmailMessageMapRepository struct {
|
|
db *db.DB
|
|
}
|
|
|
|
// NewEmailMessageMapRepository returns the Postgres-backed implementation.
|
|
func NewEmailMessageMapRepository(d *db.DB) EmailMessageMapRepository {
|
|
return &pgEmailMessageMapRepository{db: d}
|
|
}
|
|
|
|
func (r *pgEmailMessageMapRepository) Add(ctx context.Context, data EmailMessageData) error {
|
|
userID, err := uuid.Parse(data.UserID)
|
|
if err != nil {
|
|
return fmt.Errorf("email_message_map: invalid user_id: %w", err)
|
|
}
|
|
emailID, err := uuid.Parse(data.EmailID)
|
|
if err != nil {
|
|
return fmt.Errorf("email_message_map: invalid email_id: %w", err)
|
|
}
|
|
id, err := uuid.Parse(data.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("email_message_map: invalid id: %w", err)
|
|
}
|
|
|
|
// Upsert: the old DynamoDB PutItem overwrote unconditionally.
|
|
const q = `
|
|
INSERT INTO email_message_map (user_id, email_id, message_id, id, thread_id)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
ON CONFLICT (user_id, email_id, message_id)
|
|
DO UPDATE SET id = EXCLUDED.id, thread_id = EXCLUDED.thread_id
|
|
`
|
|
if _, err := r.db.Exec(ctx, q, userID, emailID, data.MessageID, id, data.ThreadID); err != nil {
|
|
return fmt.Errorf("email_message_map: add: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *pgEmailMessageMapRepository) Get(ctx context.Context, userID, emailID uuid.UUID, messageID string) (*EmailMessageData, error) {
|
|
const q = `
|
|
SELECT id, thread_id
|
|
FROM email_message_map
|
|
WHERE user_id = $1 AND email_id = $2 AND message_id = $3
|
|
`
|
|
var id uuid.UUID
|
|
var threadID string
|
|
err := r.db.QueryRow(ctx, q, userID, emailID, messageID).Scan(&id, &threadID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("email_message_map: get: %w", err)
|
|
}
|
|
return &EmailMessageData{
|
|
UserID: userID.String(),
|
|
EmailID: emailID.String(),
|
|
MessageID: messageID,
|
|
ID: id.String(),
|
|
ThreadID: threadID,
|
|
}, nil
|
|
}
|
|
|
|
func (r *pgEmailMessageMapRepository) Del(ctx context.Context, userID, emailID uuid.UUID, messageID string, _ uuid.UUID) error {
|
|
// (user_id, email_id, message_id) is the primary key, so the internal id
|
|
// is not needed to identify the row.
|
|
const q = `DELETE FROM email_message_map WHERE user_id = $1 AND email_id = $2 AND message_id = $3`
|
|
if _, err := r.db.Exec(ctx, q, userID, emailID, messageID); err != nil {
|
|
return fmt.Errorf("email_message_map: del: %w", err)
|
|
}
|
|
return nil
|
|
}
|