package handlers import ( "errors" "net/http" "github.com/go-chi/chi/v5" "github.com/google/uuid" "git.threesix.ai/jordan/slate-test-1770505673/pkg/app" "git.threesix.ai/jordan/slate-test-1770505673/pkg/auth" "git.threesix.ai/jordan/slate-test-1770505673/pkg/httperror" "git.threesix.ai/jordan/slate-test-1770505673/pkg/httpresponse" "git.threesix.ai/jordan/slate-test-1770505673/pkg/logging" "git.threesix.ai/jordan/slate-test-1770505673/services/preferences-api/internal/domain" "git.threesix.ai/jordan/slate-test-1770505673/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"), } } // Get returns all 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") } if err := h.checkOwnership(r, userID); err != nil { return err } prefs, err := h.svc.Get(r.Context(), userID) if err != nil { return err } httpresponse.OK(w, r, prefs) return nil } // Update creates or updates preferences for a user. func (h *Preference) Update(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") } if err := h.checkOwnership(r, userID); err != nil { return err } var prefs map[string]string if err := app.Bind(r, &prefs); err != nil { return err } if len(prefs) == 0 { return httperror.BadRequest("request body is required") } result, err := h.svc.Upsert(r.Context(), userID, prefs) if err != nil { return mapDomainError(err) } httpresponse.OK(w, r, result) return nil } // checkOwnership verifies that the authenticated user matches the requested user_id. func (h *Preference) checkOwnership(r *http.Request, userID string) error { user := auth.GetUser(r.Context()) if user == nil { return httperror.Unauthorized("authentication required") } if user.ID != userID { return httperror.Forbidden("cannot access preferences for another user") } return nil } // mapDomainError converts domain errors to HTTP errors. func mapDomainError(err error) error { switch { case errors.Is(err, domain.ErrUnknownKey): return httperror.BadRequest(err.Error()) case errors.Is(err, domain.ErrInvalidValue): return httperror.BadRequest(err.Error()) case errors.Is(err, domain.ErrForbidden): return httperror.Forbidden(err.Error()) default: return err } }