View Javadoc
1   package fr.tiogars.inception.settings.user.services;
2   
3   import java.time.Clock;
4   import java.time.Instant;
5   import java.time.temporal.ChronoUnit;
6   import java.util.Optional;
7   
8   import org.springframework.beans.factory.annotation.Autowired;
9   import org.springframework.stereotype.Service;
10  import org.springframework.transaction.annotation.Transactional;
11  
12  import fr.tiogars.inception.domains.auth.role.services.RoleService;
13  import fr.tiogars.inception.domains.auth.user.entities.UserEntity;
14  import fr.tiogars.inception.domains.auth.user.repositories.UserRepository;
15  import fr.tiogars.inception.settings.user.entities.UserSettings;
16  import fr.tiogars.inception.settings.user.entities.UserSettingsEntity;
17  import fr.tiogars.inception.settings.user.models.UpdateUserSettingsRequest;
18  import fr.tiogars.inception.settings.user.repositories.UserSettingsRepository;
19  
20  /** Coordinates user settings, the ANONYMOUS role settings and their defaults. */
21  @Service
22  public class UserSettingsService {
23  
24      public static final String DEFAULT_VIEW_MODE = "system";
25  
26      private final Clock clock;
27      private final UserRepository userRepository;
28      private final UserSettingsRepository repository;
29  
30      @Autowired
31      public UserSettingsService(UserSettingsRepository repository, UserRepository userRepository) {
32          this(repository, userRepository, Clock.systemUTC());
33      }
34  
35      UserSettingsService(UserSettingsRepository repository, UserRepository userRepository, Clock clock) {
36          this.repository = repository;
37          this.userRepository = userRepository;
38          this.clock = clock;
39      }
40  
41      /** Returns the settings of the authenticated account, or empty when the account does not exist. */
42      @Transactional(readOnly = true)
43      public Optional<UserSettings> findByUsername(String username) {
44          return userRepository.findByUsername(username).map(this::readUserSettings);
45      }
46  
47      /** Updates the settings of the authenticated account, or returns empty when the account does not exist. */
48      @Transactional
49      public Optional<UserSettings> updateByUsername(String username, UpdateUserSettingsRequest request) {
50          return userRepository.findByUsername(username).map(user -> writeUserSettings(user, request));
51      }
52  
53      /** Returns the settings of an account holding the USER role, or empty otherwise. */
54      @Transactional(readOnly = true)
55      public Optional<UserSettings> findByUserId(Long userId) {
56          return findManagedUser(userId).map(this::readUserSettings);
57      }
58  
59      /** Updates the settings of an account holding the USER role, or returns empty otherwise. */
60      @Transactional
61      public Optional<UserSettings> updateByUserId(Long userId, UpdateUserSettingsRequest request) {
62          return findManagedUser(userId).map(user -> writeUserSettings(user, request));
63      }
64  
65      @Transactional(readOnly = true)
66      public UserSettings findAnonymous() {
67          return repository.findByRoleCode(RoleService.ANONYMOUS)
68                  .map(UserSettingsService::toRecord)
69                  .orElseGet(() -> defaults(null, RoleService.ANONYMOUS));
70      }
71  
72      @Transactional
73      public UserSettings updateAnonymous(UpdateUserSettingsRequest request) {
74          Instant timestamp = currentTimestamp();
75          UserSettingsEntity entity = repository.findByRoleCode(RoleService.ANONYMOUS)
76                  .map(existing -> {
77                      existing.update(request.viewMode(), timestamp);
78                      return existing;
79                  })
80                  .orElseGet(() -> UserSettingsEntity.forRole(RoleService.ANONYMOUS, request.viewMode(), timestamp));
81          return toRecord(repository.save(entity));
82      }
83  
84      private Optional<UserEntity> findManagedUser(Long userId) {
85          return userRepository.findById(userId)
86                  .filter(user -> user.getRoles().stream().anyMatch(role -> RoleService.USER.equals(role.getCode())));
87      }
88  
89      private UserSettings readUserSettings(UserEntity user) {
90          return repository.findByUserId(user.getId())
91                  .map(UserSettingsService::toRecord)
92                  .orElseGet(() -> defaults(user.getId(), null));
93      }
94  
95      private UserSettings writeUserSettings(UserEntity user, UpdateUserSettingsRequest request) {
96          Instant timestamp = currentTimestamp();
97          UserSettingsEntity entity = repository.findByUserId(user.getId())
98                  .map(existing -> {
99                      existing.update(request.viewMode(), timestamp);
100                     return existing;
101                 })
102                 .orElseGet(() -> UserSettingsEntity.forUser(user, request.viewMode(), timestamp));
103         return toRecord(repository.save(entity));
104     }
105 
106     private Instant currentTimestamp() {
107         return Instant.now(clock).truncatedTo(ChronoUnit.MICROS);
108     }
109 
110     private static UserSettings defaults(Long userId, String roleCode) {
111         return new UserSettings(userId, roleCode, DEFAULT_VIEW_MODE, null, null);
112     }
113 
114     public static UserSettings toRecord(UserSettingsEntity entity) {
115         return new UserSettings(
116                 entity.getUser() == null ? null : entity.getUser().getId(),
117                 entity.getRoleCode(),
118                 entity.getViewMode(),
119                 entity.getCreatedDate(),
120                 entity.getLastModifiedDate());
121     }
122 }