1 package fr.tiogars.inception.domains.auth.role.services;
2
3 import java.time.Clock;
4 import java.time.Instant;
5 import java.time.temporal.ChronoUnit;
6 import java.util.List;
7 import java.util.Optional;
8 import java.util.Set;
9
10 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.stereotype.Service;
12 import org.springframework.transaction.annotation.Transactional;
13
14 import fr.tiogars.inception.domains.auth.role.entities.Role;
15 import fr.tiogars.inception.domains.auth.role.entities.RoleEntity;
16 import fr.tiogars.inception.domains.auth.role.models.CreateRoleRequest;
17 import fr.tiogars.inception.domains.auth.role.models.UpdateRoleRequest;
18 import fr.tiogars.inception.domains.auth.role.repositories.RoleRepository;
19
20
21 @Service
22 public class RoleService {
23
24 public static final String ANONYMOUS = "ANONYMOUS";
25 public static final String ADMIN = "ADMIN";
26 public static final String USER = "USER";
27
28 private static final Set<String> SYSTEM_ROLES = Set.of(ANONYMOUS, ADMIN, USER);
29
30 private final Clock clock;
31 private final RoleRepository repository;
32
33 @Autowired
34 public RoleService(RoleRepository repository) {
35 this(repository, Clock.systemUTC());
36 }
37
38 RoleService(RoleRepository repository, Clock clock) {
39 this.repository = repository;
40 this.clock = clock;
41 }
42
43 @Transactional
44 public Optional<Role> create(CreateRoleRequest request) {
45 if (repository.findByCode(request.code()).isPresent()) {
46 return Optional.empty();
47 }
48 Instant timestamp = currentTimestamp();
49 return Optional.of(toRecord(repository.save(new RoleEntity(request.code(), request.label(), timestamp, timestamp))));
50 }
51
52 @Transactional
53 public Optional<Role> update(Long id, UpdateRoleRequest request) {
54 return repository.findById(id)
55 .map(existing -> {
56 existing.update(request.label(), currentTimestamp());
57 return toRecord(repository.save(existing));
58 });
59 }
60
61 @Transactional
62 public boolean delete(Long id) {
63 return repository.findById(id)
64 .filter(existing -> !SYSTEM_ROLES.contains(existing.getCode()))
65 .map(existing -> {
66 repository.delete(existing);
67 return true;
68 })
69 .orElse(false);
70 }
71
72 public List<Role> findAll() {
73 return repository.findAllByOrderByCodeAsc().stream()
74 .map(RoleService::toRecord)
75 .toList();
76 }
77
78 public Optional<Role> findById(Long id) {
79 return repository.findById(id).map(RoleService::toRecord);
80 }
81
82 public Optional<RoleEntity> findEntityByCode(String code) {
83 return repository.findByCode(code);
84 }
85
86 @Transactional
87 public List<Role> seedDefaultRoles() {
88 return List.of(
89 seedSystemRole(ANONYMOUS, "Anonymous"),
90 seedSystemRole(ADMIN, "Administrator"),
91 seedSystemRole(USER, "User"));
92 }
93
94 private Role seedSystemRole(String code, String label) {
95 Instant timestamp = currentTimestamp();
96 RoleEntity entity = repository.findByCode(code)
97 .map(existing -> {
98 existing.update(label, timestamp);
99 return existing;
100 })
101 .orElseGet(() -> new RoleEntity(code, label, timestamp, timestamp));
102 return toRecord(repository.save(entity));
103 }
104
105 private Instant currentTimestamp() {
106 return Instant.now(clock).truncatedTo(ChronoUnit.MICROS);
107 }
108
109 public static Role toRecord(RoleEntity entity) {
110 return new Role(entity.getId(), entity.getCode(), entity.getLabel(), entity.getCreatedDate(), entity.getLastModifiedDate());
111 }
112 }