// Package postgres provides PostgreSQL implementations of port interfaces. package postgres import ( "context" "database/sql" "encoding/json" "errors" "time" "github.com/jmoiron/sqlx" "git.threesix.ai/jordan/slack5-1770541397/pkg/logging" "git.threesix.ai/jordan/slack5-1770541397/services/preferences-api/internal/domain" "git.threesix.ai/jordan/slack5-1770541397/services/preferences-api/internal/port" ) // preferencesRow represents a row in the user_preferences table. type preferencesRow struct { UserID string `db:"user_id"` Preferences []byte `db:"preferences"` UpdatedAt time.Time `db:"updated_at"` } // PreferencesRepository implements port.PreferencesRepository using PostgreSQL. type PreferencesRepository struct { db *sqlx.DB logger *logging.Logger } var _ port.PreferencesRepository = (*PreferencesRepository)(nil) // NewPreferencesRepository creates a new PostgreSQL preferences repository. func NewPreferencesRepository(db *sqlx.DB, logger *logging.Logger) *PreferencesRepository { return &PreferencesRepository{ db: db, logger: logger.WithComponent("PreferencesRepository"), } } // Get retrieves preferences for a user by ID. // Returns domain.ErrPreferencesNotFound if no row exists. func (r *PreferencesRepository) Get(ctx context.Context, userID domain.UserID) (*domain.UserPreferences, error) { var row preferencesRow err := r.db.GetContext(ctx, &row, `SELECT user_id, preferences, updated_at FROM user_preferences WHERE user_id = $1`, string(userID), ) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, domain.ErrPreferencesNotFound } return nil, err } var prefs domain.Preferences if err := json.Unmarshal(row.Preferences, &prefs); err != nil { return nil, err } return &domain.UserPreferences{ UserID: domain.UserID(row.UserID), Preferences: prefs, UpdatedAt: row.UpdatedAt, }, nil } // Upsert creates or replaces preferences for a user. func (r *PreferencesRepository) Upsert(ctx context.Context, prefs *domain.UserPreferences) error { prefsJSON, err := json.Marshal(prefs.Preferences) if err != nil { return err } _, err = r.db.ExecContext(ctx, ` INSERT INTO user_preferences (user_id, preferences, updated_at) VALUES ($1, $2, $3) ON CONFLICT (user_id) DO UPDATE SET preferences = $2, updated_at = $3 `, string(prefs.UserID), prefsJSON, prefs.UpdatedAt) return err }