91 lines
2.6 KiB
Go
91 lines
2.6 KiB
Go
// Package postgres provides PostgreSQL implementations of repository interfaces.
|
|
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
"git.threesix.ai/jordan/slate-test-1770505673/pkg/logging"
|
|
"git.threesix.ai/jordan/slate-test-1770505673/services/preferences-api/internal/port"
|
|
)
|
|
|
|
// Compile-time verification that PreferenceRepository implements port.PreferenceRepository.
|
|
var _ port.PreferenceRepository = (*PreferenceRepository)(nil)
|
|
|
|
// PreferenceRepository is a PostgreSQL implementation of port.PreferenceRepository.
|
|
type PreferenceRepository struct {
|
|
db *sqlx.DB
|
|
logger *logging.Logger
|
|
}
|
|
|
|
// NewPreferenceRepository creates a new PostgreSQL preference repository.
|
|
func NewPreferenceRepository(db *sqlx.DB, logger *logging.Logger) *PreferenceRepository {
|
|
return &PreferenceRepository{
|
|
db: db,
|
|
logger: logger.WithComponent("PreferenceRepository"),
|
|
}
|
|
}
|
|
|
|
// preferenceRow represents a row in the user_preferences table.
|
|
type preferenceRow struct {
|
|
Key string `db:"key"`
|
|
Value string `db:"value"`
|
|
}
|
|
|
|
// GetByUserID returns all preferences for a user as a map[key]value.
|
|
// Returns an empty map (not nil) if the user has no preferences.
|
|
func (r *PreferenceRepository) GetByUserID(ctx context.Context, userID string) (map[string]string, error) {
|
|
var rows []preferenceRow
|
|
err := r.db.SelectContext(ctx, &rows,
|
|
`SELECT key, value FROM user_preferences WHERE user_id = $1`, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("querying preferences: %w", err)
|
|
}
|
|
|
|
result := make(map[string]string, len(rows))
|
|
for _, row := range rows {
|
|
result[row.Key] = row.Value
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Upsert creates or updates preferences for a user within a single transaction.
|
|
// Only the provided keys are affected; existing keys not in the map are preserved.
|
|
func (r *PreferenceRepository) Upsert(ctx context.Context, userID string, prefs map[string]string) error {
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("beginning transaction: %w", err)
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
|
|
stmt, err := tx.PrepareContext(ctx, `
|
|
INSERT INTO user_preferences (user_id, key, value)
|
|
VALUES ($1, $2, $3)
|
|
ON CONFLICT (user_id, key)
|
|
DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()
|
|
`)
|
|
if err != nil {
|
|
return fmt.Errorf("preparing statement: %w", err)
|
|
}
|
|
defer closeStmt(stmt)
|
|
|
|
for key, value := range prefs {
|
|
if _, err := stmt.ExecContext(ctx, userID, key, value); err != nil {
|
|
return fmt.Errorf("upserting preference %s: %w", key, err)
|
|
}
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return fmt.Errorf("committing transaction: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func closeStmt(stmt *sql.Stmt) {
|
|
_ = stmt.Close()
|
|
}
|