slack5-1770606136/services/preferences-api/internal/api/handlers/preference.go
rdev-worker a0ff64af5e
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
build: /implement-feature user-preferences
2026-02-09 03:30:01 +00:00

123 lines
3.3 KiB
Go

package handlers
import (
"errors"
"net/http"
"time"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"git.threesix.ai/jordan/slack5-1770606136/pkg/app"
"git.threesix.ai/jordan/slack5-1770606136/pkg/httperror"
"git.threesix.ai/jordan/slack5-1770606136/pkg/httpresponse"
"git.threesix.ai/jordan/slack5-1770606136/pkg/logging"
"git.threesix.ai/jordan/slack5-1770606136/services/preferences-api/internal/domain"
"git.threesix.ai/jordan/slack5-1770606136/services/preferences-api/internal/service"
)
// Preference handles HTTP requests for user preference resources.
type Preference struct {
svc *service.PreferenceService
logger *logging.Logger
}
// NewPreference creates a new Preference handler with injected dependencies.
func NewPreference(svc *service.PreferenceService, logger *logging.Logger) *Preference {
return &Preference{
svc: svc,
logger: logger.WithComponent("PreferenceHandler"),
}
}
// UpdatePreferencesRequest is the request body for updating preferences.
type UpdatePreferencesRequest struct {
Preferences map[string]any `json:"preferences"`
}
// PreferenceResponse is the response for a preference resource.
type PreferenceResponse struct {
UserID string `json:"user_id"`
Preferences map[string]any `json:"preferences"`
UpdatedAt string `json:"updated_at"`
}
// Get returns preferences for a user.
func (h *Preference) Get(w http.ResponseWriter, r *http.Request) error {
userID := chi.URLParam(r, "user_id")
if _, err := uuid.Parse(userID); err != nil {
return httperror.BadRequest("invalid user_id format")
}
prefs, err := h.svc.Get(r.Context(), userID)
if err != nil {
return err
}
if prefs == nil {
httpresponse.OK(w, r, PreferenceResponse{
UserID: userID,
Preferences: map[string]any{},
UpdatedAt: time.Now().UTC().Format(time.RFC3339),
})
return nil
}
httpresponse.OK(w, r, toPreferenceResponse(prefs))
return nil
}
// Upsert creates or updates preferences for a user.
func (h *Preference) Upsert(w http.ResponseWriter, r *http.Request) error {
userID := chi.URLParam(r, "user_id")
if _, err := uuid.Parse(userID); err != nil {
return httperror.BadRequest("invalid user_id format")
}
var req UpdatePreferencesRequest
if err := app.Bind(r, &req); err != nil {
return err
}
if req.Preferences == nil {
return httperror.BadRequest("preferences field is required")
}
result, err := h.svc.Upsert(r.Context(), service.UpsertInput{
UserID: userID,
Preferences: req.Preferences,
})
if err != nil {
return mapPreferenceDomainError(err)
}
httpresponse.OK(w, r, toPreferenceResponse(result))
return nil
}
// toPreferenceResponse converts a domain UserPreferences to an API response.
func toPreferenceResponse(p *domain.UserPreferences) PreferenceResponse {
return PreferenceResponse{
UserID: p.UserID,
Preferences: p.Preferences,
UpdatedAt: p.UpdatedAt.Format(time.RFC3339),
}
}
// mapPreferenceDomainError converts domain errors to HTTP errors.
func mapPreferenceDomainError(err error) error {
if errors.Is(err, domain.ErrInvalidPreferenceValue) {
var valErr *service.ValidationError
if errors.As(err, &valErr) {
return httperror.WithDetails(
httperror.Validation("Invalid preference values"),
valErr.Details,
)
}
return httperror.BadRequest("invalid preference value")
}
return err
}