diff --git a/src/main/java/com/soupulsar/modulith/auth/api/controllers/AuthController.java b/src/main/java/com/soupulsar/modulith/auth/api/controllers/AuthController.java index a580bb5..2300b8d 100644 --- a/src/main/java/com/soupulsar/modulith/auth/api/controllers/AuthController.java +++ b/src/main/java/com/soupulsar/modulith/auth/api/controllers/AuthController.java @@ -1,10 +1,12 @@ package com.soupulsar.modulith.auth.api.controllers; import com.soupulsar.modulith.auth.application.dto.AuthUserRequest; -import com.soupulsar.modulith.auth.application.dto.CreateUserRequest; -import com.soupulsar.modulith.auth.application.dto.CreateUserResponse; +import com.soupulsar.modulith.auth.application.dto.AuthUserResponse; +import com.soupulsar.modulith.auth.application.dto.RegistrationRequest; +import com.soupulsar.modulith.auth.application.dto.RegistrationResponse; import com.soupulsar.modulith.auth.application.usecase.AuthenticateUserUseCase; -import com.soupulsar.modulith.auth.application.usecase.RegisterUserUseCase; +import com.soupulsar.modulith.auth.application.usecase.RegistrationUseCase; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -12,25 +14,25 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.net.URI; + @RestController @RequestMapping("/api/auth") @RequiredArgsConstructor public class AuthController { private final AuthenticateUserUseCase authenticateUserUseCase; - private final RegisterUserUseCase registerUserUseCase; + private final RegistrationUseCase registrationUseCase; @PostMapping(value = {"/login", "/signin"}) - public ResponseEntity login(@RequestBody AuthUserRequest request) { - String token = authenticateUserUseCase.execute(request); - return ResponseEntity.ok(token); + public ResponseEntity login(@RequestBody @Valid AuthUserRequest request) { + return ResponseEntity.ok(authenticateUserUseCase.execute(request)); } @PostMapping(value = {"/register", "/signup"}) - public ResponseEntity register(@RequestBody CreateUserRequest request) { - var response = registerUserUseCase.execute(request); - return ResponseEntity.ok(response); - + public ResponseEntity register(@RequestBody @Valid RegistrationRequest request) { + var response = registrationUseCase.execute(request); + return ResponseEntity.created(URI.create("/api/users/" + response.userId())).body(response); } } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/config/AuthUseCaseConfig.java b/src/main/java/com/soupulsar/modulith/auth/application/config/AuthUseCaseConfig.java index 6eac44b..fe3a955 100644 --- a/src/main/java/com/soupulsar/modulith/auth/application/config/AuthUseCaseConfig.java +++ b/src/main/java/com/soupulsar/modulith/auth/application/config/AuthUseCaseConfig.java @@ -3,7 +3,9 @@ import com.soupulsar.modulith.auth.application.security.JwtService; import com.soupulsar.modulith.auth.application.security.PasswordHasher; import com.soupulsar.modulith.auth.application.usecase.AuthenticateUserUseCase; -import com.soupulsar.modulith.auth.application.usecase.RegisterUserUseCase; +import com.soupulsar.modulith.auth.application.usecase.RegistrationUseCase; +import com.soupulsar.modulith.auth.domain.repository.ClientProfileRepository; +import com.soupulsar.modulith.auth.domain.repository.SpecialistProfileRepository; import com.soupulsar.modulith.auth.domain.repository.UserRepository; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -17,8 +19,9 @@ public AuthenticateUserUseCase authenticateUserUseCase(UserRepository userReposi } @Bean - public RegisterUserUseCase registerUserUseCase(UserRepository userRepository, PasswordHasher passwordHasher) { - return new RegisterUserUseCase(userRepository, passwordHasher); + public RegistrationUseCase registrationUseCase(UserRepository userRepository, ClientProfileRepository clientProfileRepository, + SpecialistProfileRepository specialistProfileRepository, PasswordHasher passwordHasher) { + return new RegistrationUseCase(userRepository, clientProfileRepository, specialistProfileRepository,passwordHasher); } diff --git a/src/main/java/com/soupulsar/modulith/auth/application/dto/AuthUserResponse.java b/src/main/java/com/soupulsar/modulith/auth/application/dto/AuthUserResponse.java new file mode 100644 index 0000000..1eeed24 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/application/dto/AuthUserResponse.java @@ -0,0 +1,13 @@ +package com.soupulsar.modulith.auth.application.dto; + +import java.util.Date; + +public record AuthUserResponse( + + String accessToken, + String tokenType, + String subject, + Date issuedAt, + Long expiresIn +) { +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/dto/CreateUserRequest.java b/src/main/java/com/soupulsar/modulith/auth/application/dto/CreateUserRequest.java deleted file mode 100644 index 3f55bc0..0000000 --- a/src/main/java/com/soupulsar/modulith/auth/application/dto/CreateUserRequest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.soupulsar.modulith.auth.application.dto; - -import com.soupulsar.modulith.auth.domain.model.enums.UserRole; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import org.hibernate.validator.constraints.br.CPF; - -public record CreateUserRequest( - - @NotBlank - String name, - @Email - String email, - @CPF - String cpf, - @NotBlank - String telephone, - @NotBlank - String password, - @NotBlank - UserRole role - -) { -} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/dto/RegistrationRequest.java b/src/main/java/com/soupulsar/modulith/auth/application/dto/RegistrationRequest.java new file mode 100644 index 0000000..55562d1 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/application/dto/RegistrationRequest.java @@ -0,0 +1,44 @@ +package com.soupulsar.modulith.auth.application.dto; + +import com.soupulsar.modulith.auth.domain.model.enums.UserRole; +import com.soupulsar.modulith.auth.domain.model.vo.Address; +import com.soupulsar.modulith.auth.domain.model.vo.EmergencyContact; +import com.soupulsar.modulith.auth.domain.model.vo.Presentation; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import org.hibernate.validator.constraints.br.CPF; + +import java.util.Date; +import java.util.List; + +public record RegistrationRequest( + + // User info + @NotBlank + String name, + @NotBlank + @CPF + String cpf, + @NotBlank + String telephone, + @NotBlank + String email, + @NotBlank + String password, + @NotNull + UserRole role, + @NotNull + Address address, + + // Client info + Date dateOfBirth, + EmergencyContact emergencyContact, + + // Specialist info + String registrationNumber, + Presentation presentation, + List formations, + List specialties, + List approaches +) { +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/dto/CreateUserResponse.java b/src/main/java/com/soupulsar/modulith/auth/application/dto/RegistrationResponse.java similarity index 73% rename from src/main/java/com/soupulsar/modulith/auth/application/dto/CreateUserResponse.java rename to src/main/java/com/soupulsar/modulith/auth/application/dto/RegistrationResponse.java index 5c8e151..800c398 100644 --- a/src/main/java/com/soupulsar/modulith/auth/application/dto/CreateUserResponse.java +++ b/src/main/java/com/soupulsar/modulith/auth/application/dto/RegistrationResponse.java @@ -2,7 +2,8 @@ import java.util.UUID; -public record CreateUserResponse( +public record RegistrationResponse( + UUID userId ) { } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCase.java b/src/main/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCase.java index 927b489..2ebe357 100644 --- a/src/main/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCase.java +++ b/src/main/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCase.java @@ -1,13 +1,17 @@ package com.soupulsar.modulith.auth.application.usecase; import com.soupulsar.modulith.auth.application.dto.AuthUserRequest; +import com.soupulsar.modulith.auth.application.dto.AuthUserResponse; import com.soupulsar.modulith.auth.application.security.JwtService; import com.soupulsar.modulith.auth.application.security.PasswordHasher; import com.soupulsar.modulith.auth.domain.model.User; import com.soupulsar.modulith.auth.domain.model.enums.UserStatus; import com.soupulsar.modulith.auth.domain.repository.UserRepository; +import io.jsonwebtoken.Claims; import lombok.RequiredArgsConstructor; +import java.util.Date; + @RequiredArgsConstructor public class AuthenticateUserUseCase { @@ -15,7 +19,7 @@ public class AuthenticateUserUseCase { private final PasswordHasher passwordHasher; private final JwtService jwtService; - public String execute(AuthUserRequest request) { + public AuthUserResponse execute(AuthUserRequest request) { User user = userRepository.findByEmail(request.email()) .orElseThrow(() -> new IllegalArgumentException("Invalid email or password")); @@ -28,7 +32,17 @@ public String execute(AuthUserRequest request) { throw new IllegalArgumentException("Invalid email or password"); } - return jwtService.generateToken(user); - + var token = jwtService.generateToken(user); + Date issuedAt = jwtService.extractClaim(token, Claims::getIssuedAt); + String subject = jwtService.extractClaim(token, Claims::getSubject); + Long expiresIn = (jwtService.extractClaim(token, Claims::getExpiration).getTime() - System.currentTimeMillis()) / 1000; + + return new AuthUserResponse( + token, + "Bearer", + subject, + issuedAt, + expiresIn + ); } } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/usecase/RegisterUserUseCase.java b/src/main/java/com/soupulsar/modulith/auth/application/usecase/RegisterUserUseCase.java deleted file mode 100644 index 73f3554..0000000 --- a/src/main/java/com/soupulsar/modulith/auth/application/usecase/RegisterUserUseCase.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.soupulsar.modulith.auth.application.usecase; - -import com.soupulsar.modulith.auth.application.dto.CreateUserRequest; -import com.soupulsar.modulith.auth.application.dto.CreateUserResponse; -import com.soupulsar.modulith.auth.application.security.PasswordHasher; -import com.soupulsar.modulith.auth.domain.model.User; -import com.soupulsar.modulith.auth.domain.repository.UserRepository; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class RegisterUserUseCase { - - private final UserRepository userRepository; - private final PasswordHasher passwordEncoder; - - public CreateUserResponse execute (CreateUserRequest userRequest) { - if (userRepository.existsByEmail(userRequest.email())) { - throw new IllegalArgumentException("Email already in use"); - } - - User user = User.create( - userRequest.name(), - userRequest.cpf(), - userRequest.telephone(), - userRequest.email(), - passwordEncoder.hash(userRequest.password()), - userRequest.role() - ); - - userRepository.save(user); - - return new CreateUserResponse(user.getUserId()); - - } -} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/application/usecase/RegistrationUseCase.java b/src/main/java/com/soupulsar/modulith/auth/application/usecase/RegistrationUseCase.java new file mode 100644 index 0000000..a1abb6c --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/application/usecase/RegistrationUseCase.java @@ -0,0 +1,74 @@ +package com.soupulsar.modulith.auth.application.usecase; + +import com.soupulsar.modulith.auth.application.dto.RegistrationRequest; +import com.soupulsar.modulith.auth.application.dto.RegistrationResponse; +import com.soupulsar.modulith.auth.application.security.PasswordHasher; +import com.soupulsar.modulith.auth.domain.model.ClientProfile; +import com.soupulsar.modulith.auth.domain.model.SpecialistProfile; +import com.soupulsar.modulith.auth.domain.model.User; +import com.soupulsar.modulith.auth.domain.model.enums.UserRole; +import com.soupulsar.modulith.auth.domain.repository.ClientProfileRepository; +import com.soupulsar.modulith.auth.domain.repository.SpecialistProfileRepository; +import com.soupulsar.modulith.auth.domain.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + + +@RequiredArgsConstructor +public class RegistrationUseCase { + + private final UserRepository userRepository; + private final ClientProfileRepository clientProfileRepository; + private final SpecialistProfileRepository specialistProfileRepository; + private final PasswordHasher passwordHasher; + + @Transactional + public RegistrationResponse execute(RegistrationRequest request) { + + if(userRepository.existsByEmail(request.email()) || userRepository.existsByCpf(request.cpf())) { + throw new IllegalArgumentException("Email or CPF already exists"); + } + + User user = User.create( + request.name(), + normalizeDigits(request.cpf()), + normalizeDigits(request.telephone()), + request.email(), + passwordHasher.hash(request.password()), + request.role(), + request.address().withZipCode(normalizeDigits(request.address().getZipCode())) + ); + userRepository.save(user); + + if (request.role() == UserRole.CLIENT) { + ClientProfile clientProfile = ClientProfile.create( + user.getUserId(), + request.dateOfBirth(), + request.emergencyContact().withPhoneNumber(normalizeDigits(request.emergencyContact().getPhoneNumber())) + ); + clientProfileRepository.save(clientProfile); + + } else if (request.role() == UserRole.SPECIALIST) { + SpecialistProfile specialistProfile = SpecialistProfile.create( + user.getUserId(), + request.registrationNumber(), + request.presentation(), + request.formations(), + request.specialties(), + request.approaches() + ); + specialistProfileRepository.save(specialistProfile); + } else { + throw new IllegalArgumentException("Invalid user role"); + } + + return new RegistrationResponse(user.getUserId()); + + } + + + private String normalizeDigits(String input) { + return input != null ? input.replaceAll("\\D", "") : null; + } + +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/ClientProfile.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/ClientProfile.java new file mode 100644 index 0000000..b293243 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/ClientProfile.java @@ -0,0 +1,43 @@ +package com.soupulsar.modulith.auth.domain.model; + +import com.soupulsar.modulith.auth.domain.model.vo.EmergencyContact; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Date; +import java.util.UUID; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +@Builder +public class ClientProfile { + + private final UUID profileId; + private final UUID userId; + private final Date dateOfBirth; + private final EmergencyContact emergencyContact; + + + public static ClientProfile create(UUID userId, Date dateOfBirth, EmergencyContact emergencyContact) { + if (userId == null) throw new IllegalArgumentException("User ID cannot be null"); + if (dateOfBirth == null) throw new IllegalArgumentException("Date of birth cannot be null"); + if (emergencyContact == null) throw new IllegalArgumentException("Emergency contact cannot be null"); + return ClientProfile.builder() + .profileId(UUID.randomUUID()) + .userId(userId) + .dateOfBirth(dateOfBirth) + .emergencyContact(emergencyContact) + .build(); + } + + public static ClientProfile restore(UUID profileId, UUID userId, Date dateOfBirth, EmergencyContact emergencyContact) { + return ClientProfile.builder() + .profileId(profileId) + .userId(userId) + .dateOfBirth(dateOfBirth) + .emergencyContact(emergencyContact) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/SpecialistProfile.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/SpecialistProfile.java new file mode 100644 index 0000000..f0235c4 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/SpecialistProfile.java @@ -0,0 +1,53 @@ +package com.soupulsar.modulith.auth.domain.model; + +import com.soupulsar.modulith.auth.domain.model.vo.Presentation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; + +import java.util.List; +import java.util.UUID; + +@AllArgsConstructor(access = lombok.AccessLevel.PRIVATE) +@Getter +@Builder +public class SpecialistProfile { + + private final UUID profileId; + private final UUID userId; + private final String registrationNumber; + private Presentation presentation; + @Singular("formation") + private List formations; + @Singular("specialty") + private List specialties; + @Singular("approach") + private List approaches; + + + public static SpecialistProfile create(UUID userId, String registrationNumber, Presentation presentation, List formation, List specialties, List approaches) { + if (userId == null) throw new IllegalArgumentException("User ID cannot be null"); + return SpecialistProfile.builder() + .profileId(UUID.randomUUID()) + .userId(userId) + .registrationNumber(registrationNumber) + .presentation(presentation) + .formations(formation) + .specialties(specialties) + .approaches(approaches) + .build(); + } + + public static SpecialistProfile restore(UUID profileId, UUID userId, String registrationNumber ,Presentation presentation, List formation, List specialties, List approaches) { + return SpecialistProfile.builder() + .profileId(profileId) + .userId(userId) + .registrationNumber(registrationNumber) + .presentation(presentation) + .formations(formation) + .specialties(specialties) + .approaches(approaches) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/User.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/User.java index cf69a92..983fd7e 100644 --- a/src/main/java/com/soupulsar/modulith/auth/domain/model/User.java +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/User.java @@ -2,14 +2,17 @@ import com.soupulsar.modulith.auth.domain.model.enums.UserRole; import com.soupulsar.modulith.auth.domain.model.enums.UserStatus; +import com.soupulsar.modulith.auth.domain.model.vo.Address; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import java.util.UUID; @AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter +@Builder public class User { private final UUID userId; @@ -18,24 +21,44 @@ public class User { private String telephone; private String email; private String passwordHash; + private Address address; private final UserRole role; private UserStatus status; - public static User create(String name, String cpf, String telephone, String email, String passwordHash, UserRole role) { + public static User create(String name, String cpf, String telephone, String email, String passwordHash, UserRole role, Address address) { if (name == null || name.isBlank()) throw new IllegalArgumentException("Name cannot be null or blank"); if (cpf == null || cpf.isBlank()) throw new IllegalArgumentException("CPF cannot be null or blank"); if (telephone == null || telephone.isBlank()) throw new IllegalArgumentException("Telephone cannot be null or blank"); if (email == null || email.isBlank()) throw new IllegalArgumentException("Email cannot be null or blank"); if (passwordHash == null || passwordHash.isBlank()) throw new IllegalArgumentException("Password cannot be null or blank"); - return new User(UUID.randomUUID(), name, cpf, telephone, email, passwordHash, role, UserStatus.ACTIVE); + return new User.UserBuilder() + .userId(UUID.randomUUID()) + .name(name) + .cpf(cpf) + .telephone(telephone) + .email(email) + .passwordHash(passwordHash) + .role(role) + .status(UserStatus.ACTIVE) + .address(address) + .build(); } - public static User restore(UUID userId, String name, String cpf, String telephone, String email, String passwordHash, UserRole role, UserStatus status) { - return new User(userId, name, cpf, telephone, email, passwordHash, role, status); + public static User restore(UUID userId, String name, String cpf, String telephone, String email, String passwordHash, UserRole role, UserStatus status, Address address) { + return new UserBuilder() + .userId(userId) + .name(name) + .cpf(cpf) + .telephone(telephone) + .email(email) + .passwordHash(passwordHash) + .role(role) + .status(status) + .address(address) + .build(); } - public void activate() { if (this.status == UserStatus.ACTIVE) { throw new IllegalStateException("User is already active."); @@ -49,8 +72,4 @@ public void deactivate() { } this.status = UserStatus.INACTIVE; } - - - - } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/enums/RelationshipDegree.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/enums/RelationshipDegree.java new file mode 100644 index 0000000..5a70e09 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/enums/RelationshipDegree.java @@ -0,0 +1,21 @@ +package com.soupulsar.modulith.auth.domain.model.enums; + +public enum RelationshipDegree { + + CONJUGE, + FILHO, + IRMAO, + MAE, + NETO, + PAI, + SOBRINHO, + TIO, + AMIGO, + CUNHADO, + GENRO, + NORA, + SOGRO, + AVO, + COMPANHEIRO, + OUTRO +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/Address.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/Address.java new file mode 100644 index 0000000..d6a13df --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/Address.java @@ -0,0 +1,25 @@ +package com.soupulsar.modulith.auth.domain.model.vo; + +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class Address { + + String street; + String city; + String state; + String zipCode; + String neighbourhood; + + public Address withZipCode(String newZipCode) { + return Address.builder() + .street(this.street) + .city(this.city) + .state(this.state) + .zipCode(newZipCode) + .neighbourhood(this.neighbourhood) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/EmergencyContact.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/EmergencyContact.java new file mode 100644 index 0000000..b7561b1 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/EmergencyContact.java @@ -0,0 +1,22 @@ +package com.soupulsar.modulith.auth.domain.model.vo; + +import com.soupulsar.modulith.auth.domain.model.enums.RelationshipDegree; +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class EmergencyContact { + + String name; + String phoneNumber; + RelationshipDegree relationshipDegree; + + public EmergencyContact withPhoneNumber(String newPhoneNumber) { + return EmergencyContact.builder() + .name(this.name) + .phoneNumber(newPhoneNumber) + .relationshipDegree(this.relationshipDegree) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/Presentation.java b/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/Presentation.java new file mode 100644 index 0000000..bffbf3b --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/model/vo/Presentation.java @@ -0,0 +1,14 @@ +package com.soupulsar.modulith.auth.domain.model.vo; + +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class Presentation { + + String about; + String personalDescription; + String presentationVideoUrl; + String base64Image; +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/repository/ClientProfileRepository.java b/src/main/java/com/soupulsar/modulith/auth/domain/repository/ClientProfileRepository.java new file mode 100644 index 0000000..290d589 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/repository/ClientProfileRepository.java @@ -0,0 +1,14 @@ +package com.soupulsar.modulith.auth.domain.repository; + +import com.soupulsar.modulith.auth.domain.model.ClientProfile; + +import java.util.Optional; +import java.util.UUID; + +public interface ClientProfileRepository { + + ClientProfile save(ClientProfile profile); + + Optional findById(UUID id); + +} diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/repository/SpecialistProfileRepository.java b/src/main/java/com/soupulsar/modulith/auth/domain/repository/SpecialistProfileRepository.java new file mode 100644 index 0000000..e8af6ee --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/domain/repository/SpecialistProfileRepository.java @@ -0,0 +1,13 @@ +package com.soupulsar.modulith.auth.domain.repository; + +import com.soupulsar.modulith.auth.domain.model.SpecialistProfile; + +import java.util.Optional; +import java.util.UUID; + +public interface SpecialistProfileRepository { + + SpecialistProfile save(SpecialistProfile profile); + Optional findById(UUID id); + +} diff --git a/src/main/java/com/soupulsar/modulith/auth/domain/repository/UserRepository.java b/src/main/java/com/soupulsar/modulith/auth/domain/repository/UserRepository.java index 202cd9b..df05b89 100644 --- a/src/main/java/com/soupulsar/modulith/auth/domain/repository/UserRepository.java +++ b/src/main/java/com/soupulsar/modulith/auth/domain/repository/UserRepository.java @@ -11,4 +11,6 @@ public interface UserRepository { Optional findByEmail(String email); Optional findById(UUID userId); boolean existsByEmail(String email); + boolean existsByCpf(String cpf); + boolean existsById(UUID userId); } diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/AddressEmbeddable.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/AddressEmbeddable.java new file mode 100644 index 0000000..6e53661 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/AddressEmbeddable.java @@ -0,0 +1,22 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.entity; + +import jakarta.persistence.Embeddable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +public final class AddressEmbeddable { + + private String street; + private String city; + private String state; + private String zipCode; + private String neighbourhood; + +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/ClientProfileEntity.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/ClientProfileEntity.java new file mode 100644 index 0000000..1149a1f --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/ClientProfileEntity.java @@ -0,0 +1,32 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.entity; + +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "client_profiles") +@Getter +@Setter +public class ClientProfileEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private UUID profileId; + private UUID userId; + private Date dateOfBirth; + + @Embedded + private EmergencyContactEmbeddable emergencyContact; + +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/EmergencyContactEmbeddable.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/EmergencyContactEmbeddable.java new file mode 100644 index 0000000..f4dd622 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/EmergencyContactEmbeddable.java @@ -0,0 +1,24 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.entity; + +import com.soupulsar.modulith.auth.domain.model.enums.RelationshipDegree; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +public final class EmergencyContactEmbeddable { + + private String name; + private String phoneNumber; + @Enumerated(EnumType.STRING) + private RelationshipDegree relationshipDegree; + +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/PresentationEmbeddable.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/PresentationEmbeddable.java new file mode 100644 index 0000000..b30d2bf --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/PresentationEmbeddable.java @@ -0,0 +1,26 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Lob; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +public final class PresentationEmbeddable { + + private String about; + private String personalDescription; + private String presentationVideoUrl; + + @Lob + @Column(columnDefinition = "TEXT") + private String base64Image; + +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/SpecialistProfileEntity.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/SpecialistProfileEntity.java new file mode 100644 index 0000000..422b240 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/SpecialistProfileEntity.java @@ -0,0 +1,51 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.entity; + +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Entity +@Table(name = "specialist_profiles") +@Getter +@Setter +public class SpecialistProfileEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private UUID profileId; + private UUID userId; + private String registrationNumber; + + @Embedded + private PresentationEmbeddable presentation; + + @ElementCollection + @CollectionTable(name = "specialist_formation", joinColumns = @JoinColumn(name = "specialist_id")) + @Column(name = "formation") + private List formations = new ArrayList<>(); + + @ElementCollection + @CollectionTable(name = "specialist_specialties", joinColumns = @JoinColumn(name = "specialist_id")) + @Column(name = "specialty") + private List specialties = new ArrayList<>(); + + @ElementCollection + @CollectionTable(name = "specialist_approaches", joinColumns = @JoinColumn(name = "specialist_id")) + @Column(name = "approach") + private List approaches = new ArrayList<>(); +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/UserEntity.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/UserEntity.java index 53414bc..ac0c372 100644 --- a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/UserEntity.java +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/entity/UserEntity.java @@ -2,7 +2,15 @@ import com.soupulsar.modulith.auth.domain.model.enums.UserRole; import com.soupulsar.modulith.auth.domain.model.enums.UserStatus; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; @@ -43,4 +51,7 @@ public class UserEntity { @Column(nullable = false) @Enumerated(EnumType.STRING) private UserStatus status; + + @Embedded + private AddressEmbeddable address; } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/AddressMapper.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/AddressMapper.java new file mode 100644 index 0000000..754472b --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/AddressMapper.java @@ -0,0 +1,35 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.mapper; + +import com.soupulsar.modulith.auth.domain.model.vo.Address; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.AddressEmbeddable; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class AddressMapper { + + public static AddressEmbeddable toEmbeddable(Address address) { + if (address == null) return null; + + return AddressEmbeddable.builder() + .street(address.getStreet()) + .city(address.getCity()) + .state(address.getState()) + .zipCode(address.getZipCode()) + .neighbourhood(address.getNeighbourhood()) + .build(); + } + + public static Address toValueObject(AddressEmbeddable embeddable) { + if (embeddable == null) return null; + + return Address.builder() + .street(embeddable.getStreet()) + .city(embeddable.getCity()) + .state(embeddable.getState()) + .zipCode(embeddable.getZipCode()) + .neighbourhood(embeddable.getNeighbourhood()) + .build(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/ClientProfileMapper.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/ClientProfileMapper.java new file mode 100644 index 0000000..5eac8f6 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/ClientProfileMapper.java @@ -0,0 +1,35 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.mapper; + +import com.soupulsar.modulith.auth.domain.model.ClientProfile; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.ClientProfileEntity; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ClientProfileMapper { + + public static ClientProfileEntity toEntity(ClientProfile clientProfile) { + if (clientProfile == null) return null; + + ClientProfileEntity entity = new ClientProfileEntity(); + entity.setProfileId(clientProfile.getProfileId()); + entity.setUserId(clientProfile.getUserId()); + entity.setDateOfBirth(clientProfile.getDateOfBirth()); + entity.setEmergencyContact(EmergencyContactMapper.toEmbeddable(clientProfile.getEmergencyContact())); + return entity; + } + + public static ClientProfile toModel(ClientProfileEntity entity) { + if (entity == null) return null; + + return ClientProfile.builder() + .profileId(entity.getProfileId()) + .userId(entity.getUserId()) + .dateOfBirth(entity.getDateOfBirth()) + .emergencyContact(EmergencyContactMapper.toValueObject(entity.getEmergencyContact())) + .build(); + + + } + +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/EmergencyContactMapper.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/EmergencyContactMapper.java new file mode 100644 index 0000000..280ec8c --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/EmergencyContactMapper.java @@ -0,0 +1,30 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.mapper; + +import com.soupulsar.modulith.auth.domain.model.vo.EmergencyContact; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.EmergencyContactEmbeddable; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class EmergencyContactMapper { + + public static EmergencyContactEmbeddable toEmbeddable(EmergencyContact emergencyContact) { + if (emergencyContact == null) return null; + + return EmergencyContactEmbeddable.builder() + .name(emergencyContact.getName()) + .phoneNumber(emergencyContact.getPhoneNumber()) + .relationshipDegree(emergencyContact.getRelationshipDegree()) + .build(); + } + + public static EmergencyContact toValueObject(EmergencyContactEmbeddable emergencyContact) { + if (emergencyContact == null) return null; + + return EmergencyContact.builder() + .name(emergencyContact.getName()) + .phoneNumber(emergencyContact.getPhoneNumber()) + .relationshipDegree(emergencyContact.getRelationshipDegree()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/PresentationMapper.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/PresentationMapper.java new file mode 100644 index 0000000..88a5930 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/PresentationMapper.java @@ -0,0 +1,33 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.mapper; + +import com.soupulsar.modulith.auth.domain.model.vo.Presentation; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.PresentationEmbeddable; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PresentationMapper { + + public static PresentationEmbeddable toEmbeddable(Presentation presentation) { + if (presentation == null) return null; + + + return PresentationEmbeddable.builder() + .personalDescription(presentation.getPersonalDescription()) + .about(presentation.getAbout()) + .presentationVideoUrl(presentation.getPresentationVideoUrl()) + .base64Image(presentation.getBase64Image()) + .build(); + } + + public static Presentation toValueObject(PresentationEmbeddable embeddable) { + if (embeddable == null) return null; + + return Presentation.builder() + .personalDescription(embeddable.getPersonalDescription()) + .about(embeddable.getAbout()) + .presentationVideoUrl(embeddable.getPresentationVideoUrl()) + .base64Image(embeddable.getBase64Image()) + .build(); + } +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/SpecialistProfileMapper.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/SpecialistProfileMapper.java new file mode 100644 index 0000000..fcb1ce2 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/SpecialistProfileMapper.java @@ -0,0 +1,41 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.mapper; + +import com.soupulsar.modulith.auth.domain.model.SpecialistProfile; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.SpecialistProfileEntity; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SpecialistProfileMapper { + + public static SpecialistProfileEntity toEntity(SpecialistProfile specialistProfile) { + if (specialistProfile == null) return null; + + SpecialistProfileEntity entity = new SpecialistProfileEntity(); + entity.setProfileId(specialistProfile.getProfileId()); + entity.setUserId(specialistProfile.getUserId()); + entity.setRegistrationNumber(specialistProfile.getRegistrationNumber()); + entity.setApproaches(specialistProfile.getApproaches()); + entity.setFormations(specialistProfile.getFormations()); + entity.setSpecialties(specialistProfile.getSpecialties()); + entity.setPresentation(PresentationMapper.toEmbeddable(specialistProfile.getPresentation())); + + return entity; + } + + + public static SpecialistProfile toModel(SpecialistProfileEntity entity) { + if (entity == null) return null; + + return SpecialistProfile.builder() + .profileId(entity.getProfileId()) + .userId(entity.getUserId()) + .registrationNumber(entity.getRegistrationNumber()) + .formations(entity.getFormations()) + .approaches(entity.getApproaches()) + .specialties(entity.getSpecialties()) + .presentation(PresentationMapper.toValueObject(entity.getPresentation())) + .build(); + } + +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/UserMapper.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/UserMapper.java index 6f41663..5513c5c 100644 --- a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/UserMapper.java +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/mapper/UserMapper.java @@ -18,19 +18,21 @@ public static UserEntity toEntity(User user){ entity.setPassword(user.getPasswordHash()); entity.setStatus(user.getStatus()); entity.setRole(user.getRole()); + entity.setAddress(AddressMapper.toEmbeddable(user.getAddress())); return entity; } public static User toModel(UserEntity entity){ - return User.restore( - entity.getUserId(), - entity.getName(), - entity.getCpf(), - entity.getTelephone(), - entity.getEmail(), - entity.getPassword(), - entity.getRole(), - entity.getStatus() - ); + return User.builder() + .userId(entity.getUserId()) + .name(entity.getName()) + .cpf(entity.getCpf()) + .telephone(entity.getTelephone()) + .email(entity.getEmail()) + .passwordHash(entity.getPassword()) + .role(entity.getRole()) + .status(entity.getStatus()) + .address(AddressMapper.toValueObject(entity.getAddress())) + .build(); } } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/ClientProfileJpaRepository.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/ClientProfileJpaRepository.java new file mode 100644 index 0000000..b2062fe --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/ClientProfileJpaRepository.java @@ -0,0 +1,9 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.repository; + +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.ClientProfileEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface ClientProfileJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/ClientProfileRepositoryImpl.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/ClientProfileRepositoryImpl.java new file mode 100644 index 0000000..76dd2f4 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/ClientProfileRepositoryImpl.java @@ -0,0 +1,31 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.repository; + +import com.soupulsar.modulith.auth.domain.model.ClientProfile; +import com.soupulsar.modulith.auth.domain.repository.ClientProfileRepository; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.ClientProfileEntity; +import com.soupulsar.modulith.auth.infrastructure.persistence.mapper.ClientProfileMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +@RequiredArgsConstructor +public class ClientProfileRepositoryImpl implements ClientProfileRepository { + + private final ClientProfileJpaRepository jpaRepository; + + @Override + public ClientProfile save(ClientProfile profile) { + ClientProfileEntity entity = ClientProfileMapper.toEntity(profile); + ClientProfileEntity saved = jpaRepository.save(entity); + + return ClientProfileMapper.toModel(saved); + } + + @Override + public Optional findById(UUID id) { + return jpaRepository.findById(id).map(ClientProfileMapper::toModel); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/SpecialistProfileJpaRepository.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/SpecialistProfileJpaRepository.java new file mode 100644 index 0000000..5c5bc20 --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/SpecialistProfileJpaRepository.java @@ -0,0 +1,9 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.repository; + +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.SpecialistProfileEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface SpecialistProfileJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/SpecialistProfileRepositoryImpl.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/SpecialistProfileRepositoryImpl.java new file mode 100644 index 0000000..9209fee --- /dev/null +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/SpecialistProfileRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.soupulsar.modulith.auth.infrastructure.persistence.repository; + +import com.soupulsar.modulith.auth.domain.model.SpecialistProfile; +import com.soupulsar.modulith.auth.domain.repository.SpecialistProfileRepository; +import com.soupulsar.modulith.auth.infrastructure.persistence.entity.SpecialistProfileEntity; +import com.soupulsar.modulith.auth.infrastructure.persistence.mapper.SpecialistProfileMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +@RequiredArgsConstructor +public class SpecialistProfileRepositoryImpl implements SpecialistProfileRepository { + + private final SpecialistProfileJpaRepository jpaRepository; + + @Override + public SpecialistProfile save(SpecialistProfile profile) { + SpecialistProfileEntity entity = SpecialistProfileMapper.toEntity(profile); + SpecialistProfileEntity saved = jpaRepository.save(entity); + return SpecialistProfileMapper.toModel(saved); + } + + @Override + public Optional findById(UUID id) { + return Optional.empty(); + } +} diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserJpaRepository.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserJpaRepository.java index 8fda46b..8644063 100644 --- a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserJpaRepository.java +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserJpaRepository.java @@ -10,5 +10,6 @@ public interface UserJpaRepository extends JpaRepository { Optional findByEmail(String email); boolean existsByEmail(String email); + boolean existsByCpf(String cpf); } diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserRepositoryImpl.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserRepositoryImpl.java index b6a6850..4514784 100644 --- a/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserRepositoryImpl.java +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/persistence/repository/UserRepositoryImpl.java @@ -38,4 +38,14 @@ public Optional findById(UUID userId) { public boolean existsByEmail(String email) { return jpaRepository.existsByEmail(email); } + + @Override + public boolean existsByCpf(String cpf) { + return jpaRepository.existsByCpf(cpf); + } + + @Override + public boolean existsById(UUID userId) { + return jpaRepository.existsById(userId); + } } diff --git a/src/main/java/com/soupulsar/modulith/auth/infrastructure/security/SecurityConfig.java b/src/main/java/com/soupulsar/modulith/auth/infrastructure/security/SecurityConfig.java index f5ec906..6494333 100644 --- a/src/main/java/com/soupulsar/modulith/auth/infrastructure/security/SecurityConfig.java +++ b/src/main/java/com/soupulsar/modulith/auth/infrastructure/security/SecurityConfig.java @@ -10,6 +10,7 @@ import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -27,8 +28,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti return http .csrf(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.POST, "/api/auth/register", "/api/auth/login").permitAll() + .requestMatchers("/h2-console/**").permitAll() .anyRequest().authenticated() ) .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) diff --git a/src/main/java/com/soupulsar/modulith/scheduling/domain/model/Session.java b/src/main/java/com/soupulsar/modulith/scheduling/domain/model/Session.java index 9b731d4..a706c9e 100644 --- a/src/main/java/com/soupulsar/modulith/scheduling/domain/model/Session.java +++ b/src/main/java/com/soupulsar/modulith/scheduling/domain/model/Session.java @@ -3,6 +3,7 @@ import com.soupulsar.modulith.scheduling.domain.model.enums.SessionStatus; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import java.time.LocalDateTime; @@ -14,6 +15,7 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter +@Builder public class Session { private final UUID sessionId; @@ -27,7 +29,14 @@ public class Session { public static Session scheduleSession(UUID specialistId, UUID clientId, LocalDateTime startAt, LocalDateTime endAt) { - return new Session(UUID.randomUUID(), specialistId, clientId, startAt, endAt, SessionStatus.SCHEDULING); + return new SessionBuilder() + .sessionId(UUID.randomUUID()) + .specialistId(specialistId) + .clientId(clientId) + .startAt(startAt) + .endAt(endAt) + .status(SessionStatus.AWAITING_PAYMENT) + .build(); } public static Session restore(UUID sessionId, UUID specialistId, UUID clientId, LocalDateTime startAt, LocalDateTime endAt, SessionStatus status) { diff --git a/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/entity/SessionEntity.java b/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/entity/SessionEntity.java index f96fcf8..443eef4 100644 --- a/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/entity/SessionEntity.java +++ b/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/entity/SessionEntity.java @@ -3,6 +3,8 @@ import com.soupulsar.modulith.scheduling.domain.model.enums.SessionStatus; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -39,6 +41,7 @@ public class SessionEntity { private LocalDateTime endAt; @Column(nullable = false) + @Enumerated(EnumType.STRING) private SessionStatus status; } diff --git a/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/mapper/SessionMapper.java b/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/mapper/SessionMapper.java index 93717b8..191187b 100644 --- a/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/mapper/SessionMapper.java +++ b/src/main/java/com/soupulsar/modulith/scheduling/infratructure/persistence/mapper/SessionMapper.java @@ -15,16 +15,19 @@ public static SessionEntity toEntity(Session session) { entity.setClientId(session.getClientId()); entity.setStartAt(session.getStartAt()); entity.setEndAt(session.getEndAt()); + entity.setStatus(session.getStatus()); return entity; } public static Session toModel(SessionEntity entity) { - return Session.restore(entity.getSessionId(), - entity.getSpecialistId(), - entity.getClientId(), - entity.getStartAt(), - entity.getEndAt(), - entity.getStatus()); + return Session.builder() + .sessionId(entity.getSessionId()) + .specialistId(entity.getSpecialistId()) + .clientId(entity.getClientId()) + .startAt(entity.getStartAt()) + .endAt(entity.getEndAt()) + .status(entity.getStatus()) + .build(); } } diff --git a/src/test/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCaseTest.java b/src/test/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCaseTest.java index fdb016b..23e012c 100644 --- a/src/test/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCaseTest.java +++ b/src/test/java/com/soupulsar/modulith/auth/application/usecase/AuthenticateUserUseCaseTest.java @@ -1,20 +1,30 @@ package com.soupulsar.modulith.auth.application.usecase; import com.soupulsar.modulith.auth.application.dto.AuthUserRequest; +import com.soupulsar.modulith.auth.application.dto.AuthUserResponse; import com.soupulsar.modulith.auth.application.security.JwtService; import com.soupulsar.modulith.auth.application.security.PasswordHasher; import com.soupulsar.modulith.auth.domain.model.User; import com.soupulsar.modulith.auth.domain.model.enums.UserRole; import com.soupulsar.modulith.auth.domain.model.enums.UserStatus; +import com.soupulsar.modulith.auth.domain.model.vo.Address; import com.soupulsar.modulith.auth.domain.repository.UserRepository; +import io.jsonwebtoken.Claims; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Date; import java.util.Optional; import java.util.UUID; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class AuthenticateUserUseCaseTest { @@ -22,12 +32,14 @@ class AuthenticateUserUseCaseTest { private PasswordHasher passwordHasher; private AuthenticateUserUseCase useCase; private JwtService jwtService; + private Address address; @BeforeEach void setUp() { userRepository = mock(UserRepository.class); passwordHasher = mock(PasswordHasher.class); jwtService = mock(JwtService.class); + address = mock(Address.class); useCase = new AuthenticateUserUseCase(userRepository, passwordHasher, jwtService); } @@ -36,16 +48,34 @@ void shouldAuthenticateActiveUserWithCorrectPassword() { AuthUserRequest request = new AuthUserRequest("user@email.com", "password"); User user = User.restore( UUID.randomUUID(), "Test User", "12345678900", "999999999", "user@email.com", - "hashedPassword", UserRole.CLIENT, UserStatus.ACTIVE + "hashedPassword", UserRole.CLIENT, UserStatus.ACTIVE, address ); when(userRepository.findByEmail(request.email())).thenReturn(Optional.of(user)); when(passwordHasher.matches(request.password(), user.getPasswordHash())).thenReturn(true); when(jwtService.generateToken(user)).thenReturn("JWT-TOKEN"); - String token = useCase.execute(request); + Date now = new Date(); + Date expiration = new Date(now.getTime() + 3600_000); - assertEquals("JWT-TOKEN", token); + when(jwtService.extractClaim(eq("JWT-TOKEN"), any())).thenAnswer(invocation -> { + var resolver = invocation.>getArgument(1); + + Claims fakeClaims = mock(Claims.class); + when(fakeClaims.getIssuedAt()).thenReturn(now); + when(fakeClaims.getSubject()).thenReturn(user.getEmail()); + when(fakeClaims.getExpiration()).thenReturn(expiration); + + return resolver.apply(fakeClaims); + }); + + + AuthUserResponse token = useCase.execute(request); + + assertEquals("JWT-TOKEN", token.accessToken()); + assertEquals("Bearer", token.tokenType()); + assertEquals(user.getEmail(), token.subject()); + assertEquals(expiration.getTime() - now.getTime(), token.expiresIn() * 1000, 5000); verify(jwtService).generateToken(user); } @@ -54,7 +84,7 @@ void shouldThrowWhenUserIsInactive() { AuthUserRequest request = new AuthUserRequest("user@email.com", "password"); User user = User.restore( UUID.randomUUID(), "Test User", "12345678900", "999999999", "user@email.com", - "hashedPassword", UserRole.CLIENT, UserStatus.INACTIVE + "hashedPassword", UserRole.CLIENT, UserStatus.INACTIVE, address ); when(userRepository.findByEmail(request.email())).thenReturn(Optional.of(user)); @@ -69,7 +99,7 @@ void shouldThrowWhenPasswordIsInvalid() { AuthUserRequest request = new AuthUserRequest("user@email.com", "wrong"); User user = User.restore( UUID.randomUUID(), "Test User", "12345678900", "999999999", "user@email.com", - "hashedPassword", UserRole.CLIENT, UserStatus.ACTIVE + "hashedPassword", UserRole.CLIENT, UserStatus.ACTIVE, address ); when(userRepository.findByEmail(request.email())).thenReturn(Optional.of(user));