115 lines
3.0 KiB
Go
115 lines
3.0 KiB
Go
// Package service provides business logic / use cases for the application.
|
|
// Services orchestrate domain operations using port interfaces.
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"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/port"
|
|
)
|
|
|
|
// PreferencesService handles preferences-related business logic.
|
|
type PreferencesService struct {
|
|
repo port.PreferencesRepository
|
|
logger *logging.Logger
|
|
}
|
|
|
|
// NewPreferencesService creates a new preferences service.
|
|
func NewPreferencesService(repo port.PreferencesRepository, logger *logging.Logger) *PreferencesService {
|
|
return &PreferencesService{
|
|
repo: repo,
|
|
logger: logger.WithService("PreferencesService"),
|
|
}
|
|
}
|
|
|
|
// GetPreferences returns preferences for a user. Returns defaults if none are stored.
|
|
func (s *PreferencesService) GetPreferences(ctx context.Context, userID domain.UserID) (*domain.UserPreferences, error) {
|
|
prefs, err := s.repo.Get(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if prefs != nil {
|
|
return prefs, nil
|
|
}
|
|
|
|
// Return defaults for unknown users
|
|
return &domain.UserPreferences{
|
|
UserID: userID,
|
|
Preferences: domain.DefaultPreferences(),
|
|
UpdatedAt: time.Now().UTC(),
|
|
}, nil
|
|
}
|
|
|
|
// UpdateInput contains the partial preference data for an update.
|
|
// Pointer fields distinguish "not provided" from zero values.
|
|
type UpdateInput struct {
|
|
Theme *string
|
|
Language *string
|
|
Notifications *NotificationsInput
|
|
}
|
|
|
|
// NotificationsInput contains partial notification settings.
|
|
type NotificationsInput struct {
|
|
Email *bool
|
|
Push *bool
|
|
Digest *string
|
|
}
|
|
|
|
// UpdatePreferences merges partial input with existing preferences and persists.
|
|
func (s *PreferencesService) UpdatePreferences(ctx context.Context, userID domain.UserID, input UpdateInput) (*domain.UserPreferences, error) {
|
|
// Fetch existing or start from defaults
|
|
existing, err := s.repo.Get(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var merged domain.Preferences
|
|
if existing != nil {
|
|
merged = existing.Preferences
|
|
} else {
|
|
merged = domain.DefaultPreferences()
|
|
}
|
|
|
|
// Deep merge incoming fields
|
|
if input.Theme != nil {
|
|
merged.Theme = *input.Theme
|
|
}
|
|
if input.Language != nil {
|
|
merged.Language = *input.Language
|
|
}
|
|
if input.Notifications != nil {
|
|
if input.Notifications.Email != nil {
|
|
merged.Notifications.Email = *input.Notifications.Email
|
|
}
|
|
if input.Notifications.Push != nil {
|
|
merged.Notifications.Push = *input.Notifications.Push
|
|
}
|
|
if input.Notifications.Digest != nil {
|
|
merged.Notifications.Digest = *input.Notifications.Digest
|
|
}
|
|
}
|
|
|
|
// Validate merged result
|
|
if err := merged.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
prefs := &domain.UserPreferences{
|
|
UserID: userID,
|
|
Preferences: merged,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
if err := s.repo.Upsert(ctx, prefs); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("preferences updated", "user_id", userID)
|
|
return prefs, nil
|
|
}
|