Cleaned up the architecture
Build, Test & Push Auth Service / quality-check (push) Failing after 30s
Build, Test & Push Auth Service / docker (push) Has been skipped

This commit is contained in:
lobo committed 2026-04-03 11:35:53 +02:00
1 parent be4d4df351
commit 2e8ff1b079
17 files changed
+297 -46

No files matched your search

@@ -1,13 +1,12 @@
package auth_service.domain.login.controller;
import auth_service.domain.login.controller.dtos.LoginRequest;
import auth_service.domain.login.controller.dtos.LoginResponse;
import auth_service.domain.login.repository.UserRepository;
import auth_service.domain.login.controller.dtos.request.LoginRequest;
import auth_service.domain.login.controller.dtos.response.LoginResponse;
import auth_service.domain.login.controller.mapper.ApiMapper;
import auth_service.domain.login.service.LoginService;
import io.micrometer.core.annotation.Timed;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -21,8 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class LoginController {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final LoginService loginService;
/**
* Authenticates a user based on email and password.
@@ -34,13 +32,8 @@ public class LoginController {
@Timed(value = "auth.login.requests.time", description = "Time taken to process login requests")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
return userRepository.findByEmail(request.email())
.filter(user -> passwordEncoder.matches(request.password(), user.getPassword()))
.map(user -> ResponseEntity.ok(
new LoginResponse("Login successful", user.getEmail())
))
.orElseGet(() -> ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(
new LoginResponse("Invalid credentials", null)
));
var user = ApiMapper.toModel(request);
loginService.login(user);
return ResponseEntity.ok(new LoginResponse("Login successful", user.getEmail()));
}
}
@@ -0,0 +1,48 @@
package auth_service.domain.login.controller;
import auth_service.domain.login.controller.dtos.response.ErrorResponse;
import auth_service.domain.login.exception.InvalidCredentialsException;
import auth_service.domain.login.exception.UserNotFoundException;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Order(value = 1)
@Slf4j
public class LoginExceptionController {
private static @NonNull ResponseEntity<ErrorResponse> response500(ErrorResponse body) {
log.error(body.message(), body.code());
return ResponseEntity.status(500)
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
.body(body);
}
private static @NonNull ResponseEntity<ErrorResponse> response400(ErrorResponse body) {
return ResponseEntity.status(400)
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
.body(body);
}
/*
######## CLIENT ERRORS (500-599)
*/
/*
######## CLIENT ERRORS (400-499)
*/
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> clientError(UserNotFoundException ex) {
var body = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
return response400(body);
}
@ExceptionHandler(InvalidCredentialsException.class)
public ResponseEntity<ErrorResponse> clientError(InvalidCredentialsException ex) {
var body = new ErrorResponse("INVALID_CREDENTIALS", ex.getMessage());
return response400(body);
}
}
@@ -1,7 +0,0 @@
package auth_service.domain.login.controller.dtos;
/**
* DTO (Data Transfer Object) representing the incoming login request.
*/
public record LoginRequest(String email, String password) {
}
@@ -0,0 +1,10 @@
package auth_service.domain.login.controller.dtos.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* DTO (Data Transfer Object) representing the incoming login request.
*/
public record LoginRequest(@NotNull String email, @NotNull @NotBlank String password) {
}
@@ -0,0 +1,4 @@
package auth_service.domain.login.controller.dtos.response;
public record ErrorResponse(String code, String message) {
}
@@ -1,4 +1,4 @@
package auth_service.domain.login.controller.dtos;
package auth_service.domain.login.controller.dtos.response;
/**
* DTO representing the outgoing login response.
@@ -0,0 +1,13 @@
package auth_service.domain.login.controller.mapper;
import auth_service.domain.login.controller.dtos.request.LoginRequest;
import auth_service.domain.login.model.User;
public abstract class ApiMapper {
private ApiMapper() {
}
public static User toModel(LoginRequest loginRequest) {
return User.builder().email(loginRequest.email()).encryptedPassword(loginRequest.password()).build();
}
}
@@ -0,0 +1,7 @@
package auth_service.domain.login.exception;
public class InvalidCredentialsException extends RuntimeException {
public InvalidCredentialsException(String message) {
super(message);
}
}
@@ -0,0 +1,7 @@
package auth_service.domain.login.exception;
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
@@ -0,0 +1,12 @@
package auth_service.domain.login.model;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
public class User {
private long id;
private String email;
private String encryptedPassword;
}
@@ -0,0 +1,18 @@
package auth_service.domain.login.persistence;
import auth_service.domain.login.model.User;
import java.util.Optional;
/**
* Repository for database operations regarding the User entity.
*/
public interface UserRepository {
/**
* Finds a user by their email address.
* * @param email The email to search for
*
* @return An Optional containing the user if found
*/
Optional<User> findByEmail(String email);
}
@@ -0,0 +1,36 @@
package auth_service.domain.login.persistence;
import auth_service.domain.login.model.User;
import auth_service.domain.login.persistence.entities.UserEntity;
import auth_service.domain.login.persistence.mapper.EntityMapper;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public class UserRepositoryImpl implements UserRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public Optional<User> findByEmail(String email) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<UserEntity> query = cb.createQuery(UserEntity.class);
Root<UserEntity> user = query.from(UserEntity.class);
// Build the query: SELECT u FROM UserEntity u WHERE u.email = :email
query.select(user).where(cb.equal(user.get("email"), email));
TypedQuery<UserEntity> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultStream()
.findFirst()
.map(EntityMapper::mapToModel);
}
}
@@ -1,4 +1,4 @@
package auth_service.domain.login.model;
package auth_service.domain.login.persistence.entities;
import jakarta.persistence.*;
import lombok.*;
@@ -0,0 +1,22 @@
package auth_service.domain.login.persistence.mapper;
import auth_service.domain.login.model.User;
import auth_service.domain.login.persistence.entities.UserEntity;
public abstract class EntityMapper {
private EntityMapper() {
}
public static User mapToModel(UserEntity entity) {
User model = User.builder().build();
if (entity != null) {
model.setId(entity.getId());
model.setEmail(entity.getEmail());
model.setEncryptedPassword(entity.getPassword());
}
return model;
}
}
@@ -1,22 +0,0 @@
package auth_service.domain.login.repository;
import auth_service.domain.login.model.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* Repository for database operations regarding the User entity.
*/
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
/**
* Finds a user by their email address.
* * @param email The email to search for
*
* @return An Optional containing the user if found
*/
Optional<UserEntity> findByEmail(String email);
}
@@ -0,0 +1,28 @@
package auth_service.domain.login.service;
import auth_service.domain.login.exception.InvalidCredentialsException;
import auth_service.domain.login.exception.UserNotFoundException;
import auth_service.domain.login.model.User;
import auth_service.domain.login.persistence.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class LoginService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public String login(User userToLogin) {
var userEntity = userRepository.findByEmail(userToLogin.getEmail()).orElseThrow(() -> new UserNotFoundException("User not found with given email"));
var loginSuccessful = passwordEncoder.matches(userToLogin.getEncryptedPassword(), userEntity.getEncryptedPassword());
if (!loginSuccessful) {
throw new InvalidCredentialsException("Invalid credentials");
}
return "";
}
}