131 lines
3.9 KiB
Go
131 lines
3.9 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/google/uuid"
|
|
|
|
"git.threesix.ai/jordan/slack5-1770603014/pkg/app"
|
|
"git.threesix.ai/jordan/slack5-1770603014/pkg/httperror"
|
|
"git.threesix.ai/jordan/slack5-1770603014/pkg/httpresponse"
|
|
"git.threesix.ai/jordan/slack5-1770603014/pkg/logging"
|
|
"git.threesix.ai/jordan/slack5-1770603014/services/preferences-api/internal/domain"
|
|
"git.threesix.ai/jordan/slack5-1770603014/services/preferences-api/internal/service"
|
|
)
|
|
|
|
// Preferences handles HTTP requests for user preferences.
|
|
type Preferences struct {
|
|
svc *service.PreferencesService
|
|
logger *logging.Logger
|
|
}
|
|
|
|
// NewPreferences creates a new Preferences handler with injected dependencies.
|
|
func NewPreferences(svc *service.PreferencesService, logger *logging.Logger) *Preferences {
|
|
return &Preferences{
|
|
svc: svc,
|
|
logger: logger.WithComponent("PreferencesHandler"),
|
|
}
|
|
}
|
|
|
|
// UpdatePreferencesRequest is the request body for updating preferences.
|
|
type UpdatePreferencesRequest struct {
|
|
Preferences PreferencesInput `json:"preferences" validate:"required"`
|
|
}
|
|
|
|
// PreferencesInput uses pointers to distinguish "not provided" from zero values.
|
|
type PreferencesInput struct {
|
|
Theme *string `json:"theme,omitempty"`
|
|
Language *string `json:"language,omitempty"`
|
|
Notifications *NotificationsInput `json:"notifications,omitempty"`
|
|
}
|
|
|
|
// NotificationsInput uses pointers for partial update semantics.
|
|
type NotificationsInput struct {
|
|
Email *bool `json:"email,omitempty"`
|
|
Push *bool `json:"push,omitempty"`
|
|
Digest *string `json:"digest,omitempty"`
|
|
}
|
|
|
|
// PreferencesResponse is the response for preferences endpoints.
|
|
type PreferencesResponse struct {
|
|
UserID string `json:"user_id"`
|
|
Preferences domain.Preferences `json:"preferences"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
// toPreferencesResponse converts a domain UserPreferences to an API response.
|
|
func toPreferencesResponse(p *domain.UserPreferences) PreferencesResponse {
|
|
return PreferencesResponse{
|
|
UserID: p.UserID.String(),
|
|
Preferences: p.Preferences,
|
|
UpdatedAt: p.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
|
}
|
|
}
|
|
|
|
// Get returns preferences for a user.
|
|
func (h *Preferences) 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.GetPreferences(r.Context(), domain.UserID(userID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
httpresponse.OK(w, r, toPreferencesResponse(prefs))
|
|
return nil
|
|
}
|
|
|
|
// Upsert creates or updates preferences for a user.
|
|
func (h *Preferences) 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.BindAndValidate(r, &req); err != nil {
|
|
return err
|
|
}
|
|
|
|
input := service.UpdateInput{
|
|
Theme: req.Preferences.Theme,
|
|
Language: req.Preferences.Language,
|
|
}
|
|
if req.Preferences.Notifications != nil {
|
|
input.Notifications = &service.NotificationsInput{
|
|
Email: req.Preferences.Notifications.Email,
|
|
Push: req.Preferences.Notifications.Push,
|
|
Digest: req.Preferences.Notifications.Digest,
|
|
}
|
|
}
|
|
|
|
prefs, err := h.svc.UpdatePreferences(r.Context(), domain.UserID(userID), input)
|
|
if err != nil {
|
|
return mapPreferencesDomainError(err)
|
|
}
|
|
|
|
httpresponse.OK(w, r, toPreferencesResponse(prefs))
|
|
return nil
|
|
}
|
|
|
|
// mapPreferencesDomainError converts domain errors to HTTP errors.
|
|
func mapPreferencesDomainError(err error) error {
|
|
switch {
|
|
case errors.Is(err, domain.ErrInvalidTheme):
|
|
return httperror.BadRequest(err.Error())
|
|
case errors.Is(err, domain.ErrInvalidLanguage):
|
|
return httperror.BadRequest(err.Error())
|
|
case errors.Is(err, domain.ErrInvalidDigest):
|
|
return httperror.BadRequest(err.Error())
|
|
default:
|
|
return err
|
|
}
|
|
}
|