chore: logging

This commit is contained in:
Gabriel Santana de Menezes 2026-05-10 12:07:35 -04:00
parent 96e35dc21d
commit 6537e96e46
37 changed files with 2497 additions and 51 deletions

View file

@ -7,6 +7,7 @@ import net.beetechgroup.beetask.entities.organization.Organization;
import net.beetechgroup.beetask.frameworks.persistence.entities.OrganizationEntity; import net.beetechgroup.beetask.frameworks.persistence.entities.OrganizationEntity;
import net.beetechgroup.beetask.frameworks.persistence.mapper.OrganizationEntityMapper; import net.beetechgroup.beetask.frameworks.persistence.mapper.OrganizationEntityMapper;
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -14,6 +15,7 @@ import java.util.Optional;
@ApplicationScoped @ApplicationScoped
public class OrganizationRepositoryImpl implements OrganizationRepository, PanacheRepository<OrganizationEntity> { public class OrganizationRepositoryImpl implements OrganizationRepository, PanacheRepository<OrganizationEntity> {
private static final Logger LOGGER = Logger.getLogger(OrganizationRepositoryImpl.class);
@Override @Override
@Transactional @Transactional
@ -24,25 +26,35 @@ public class OrganizationRepositoryImpl implements OrganizationRepository, Panac
} else { } else {
entity = getEntityManager().merge(entity); entity = getEntityManager().merge(entity);
} }
return OrganizationEntityMapper.toDomain(entity); Organization savedOrganization = OrganizationEntityMapper.toDomain(entity);
LOGGER.infof("Persisted organization %d with name '%s'", savedOrganization.getId(), savedOrganization.getName());
return savedOrganization;
} }
@Override @Override
public List<Organization> search(String query) { public List<Organization> search(String query) {
if (Objects.isNull(query) || query.isBlank()) { if (Objects.isNull(query) || query.isBlank()) {
return findAll().list().stream() List<Organization> organizations = findAll().list().stream()
.map(OrganizationEntityMapper::toDomain) .map(OrganizationEntityMapper::toDomain)
.toList(); .toList();
LOGGER.infof("Loaded %d organizations with empty search query", organizations.size());
return organizations;
} }
return find("name like ?1", "%" + query + "%").stream() List<Organization> organizations = find("name like ?1", "%" + query + "%").stream()
.map(OrganizationEntityMapper::toDomain) .map(OrganizationEntityMapper::toDomain)
.toList(); .toList();
LOGGER.infof("Loaded %d organizations for search query '%s'", organizations.size(), query);
return organizations;
} }
@Override @Override
public Optional<Organization> findOrganizationById(Long id) { public Optional<Organization> findOrganizationById(Long id) {
return findByIdOptional(id) Optional<Organization> organization = findByIdOptional(id)
.map(OrganizationEntityMapper::toDomain); .map(OrganizationEntityMapper::toDomain);
if (organization.isEmpty()) {
LOGGER.warnf("Organization %d was not found", id);
}
return organization;
} }
} }

View file

@ -7,6 +7,7 @@ import net.beetechgroup.beetask.entities.Project;
import net.beetechgroup.beetask.frameworks.persistence.entities.ProjectEntity; import net.beetechgroup.beetask.frameworks.persistence.entities.ProjectEntity;
import net.beetechgroup.beetask.frameworks.persistence.mapper.ProjectEntityMapper; import net.beetechgroup.beetask.frameworks.persistence.mapper.ProjectEntityMapper;
import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -14,6 +15,7 @@ import java.util.Optional;
@ApplicationScoped @ApplicationScoped
public class ProjectRepositoryImpl implements ProjectRepository, PanacheRepository<ProjectEntity> { public class ProjectRepositoryImpl implements ProjectRepository, PanacheRepository<ProjectEntity> {
private static final Logger LOGGER = Logger.getLogger(ProjectRepositoryImpl.class);
@Override @Override
@Transactional @Transactional
@ -24,19 +26,27 @@ public class ProjectRepositoryImpl implements ProjectRepository, PanacheReposito
} else { } else {
entity = getEntityManager().merge(entity); entity = getEntityManager().merge(entity);
} }
return ProjectEntityMapper.toDomain(entity); Project savedProject = ProjectEntityMapper.toDomain(entity);
LOGGER.infof("Persisted project %d for organization %d", savedProject.getId(), savedProject.getOrganization().getId());
return savedProject;
} }
@Override @Override
public Optional<Project> findProjectById(Long id) { public Optional<Project> findProjectById(Long id) {
return findByIdOptional(id) Optional<Project> project = findByIdOptional(id)
.map(ProjectEntityMapper::toDomain); .map(ProjectEntityMapper::toDomain);
if (project.isEmpty()) {
LOGGER.warnf("Project %d was not found", id);
}
return project;
} }
@Override @Override
public List<Project> findByOrganizationId(Long organizationId) { public List<Project> findByOrganizationId(Long organizationId) {
return find("organization.id", organizationId).stream() List<Project> projects = find("organization.id", organizationId).stream()
.map(ProjectEntityMapper::toDomain) .map(ProjectEntityMapper::toDomain)
.toList(); .toList();
LOGGER.infof("Loaded %d projects for organization %d from database", projects.size(), organizationId);
return projects;
} }
} }

View file

@ -13,9 +13,11 @@ import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity;
import net.beetechgroup.beetask.frameworks.persistence.mapper.TaskEntityMapper; import net.beetechgroup.beetask.frameworks.persistence.mapper.TaskEntityMapper;
import net.beetechgroup.beetask.usecase.exceptions.TaskNotFoundException; import net.beetechgroup.beetask.usecase.exceptions.TaskNotFoundException;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import org.jboss.logging.Logger;
@ApplicationScoped @ApplicationScoped
public class TaskRepositoryImpl implements TaskRepository, PanacheRepository<TaskEntity> { public class TaskRepositoryImpl implements TaskRepository, PanacheRepository<TaskEntity> {
private static final Logger LOGGER = Logger.getLogger(TaskRepositoryImpl.class);
@Override @Override
@Transactional @Transactional
@ -26,19 +28,26 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository<Tas
} else { } else {
entity = getEntityManager().merge(entity); entity = getEntityManager().merge(entity);
} }
return TaskEntityMapper.toDomain(entity); Task savedTask = TaskEntityMapper.toDomain(entity);
LOGGER.infof("Persisted task %d with status %s", savedTask.getId(), savedTask.getStatus());
return savedTask;
} }
@Override @Override
public Task findTaskById(Long id) { public Task findTaskById(Long id) {
TaskEntity entity = find("id = ?1", id).firstResultOptional() TaskEntity entity = find("id = ?1", id).firstResultOptional()
.orElseThrow(() -> new TaskNotFoundException(id)); .orElseThrow(() -> {
LOGGER.warnf("Task %d was not found in repository", id);
return new TaskNotFoundException(id);
});
return TaskEntityMapper.toDomain(entity); return TaskEntityMapper.toDomain(entity);
} }
@Override @Override
public List<Task> findAllTasks() { public List<Task> findAllTasks() {
return findAll().list().stream().map(TaskEntityMapper::toDomain).toList(); List<Task> tasks = findAll().list().stream().map(TaskEntityMapper::toDomain).toList();
LOGGER.infof("Loaded %d tasks from database", tasks.size());
return tasks;
} }
@Override @Override
@ -61,7 +70,9 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository<Tas
@Override @Override
public List<Task> findTasksByUser(String email) { public List<Task> findTasksByUser(String email) {
return find("user.email = ?1", email).list().stream().map(TaskEntityMapper::toDomain).toList(); List<Task> tasks = find("user.email = ?1", email).list().stream().map(TaskEntityMapper::toDomain).toList();
LOGGER.infof("Loaded %d tasks for user %s", tasks.size(), email);
return tasks;
} }
@Override @Override
@ -100,7 +111,10 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository<Tas
@Override @Override
public List<Task> findTasksWorkedByUserEmailsInPeriod(List<String> emails, LocalDateTime start, LocalDateTime end) { public List<Task> findTasksWorkedByUserEmailsInPeriod(List<String> emails, LocalDateTime start, LocalDateTime end) {
if (emails.isEmpty()) return List.of(); if (emails.isEmpty()) {
LOGGER.warn("Task search by user emails skipped because the email list is empty");
return List.of();
}
return find("user.email in ?1", emails).list().stream() return find("user.email in ?1", emails).list().stream()
.filter(entity -> entity.getHistory().stream().anyMatch(h -> .filter(entity -> entity.getHistory().stream().anyMatch(h ->
Objects.nonNull(h.getStartAt()) && Objects.nonNull(h.getStartAt()) &&
@ -112,7 +126,10 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository<Tas
@Override @Override
public List<Task> findTasksFinishedByUserEmailsInPeriod(List<String> emails, LocalDateTime start, LocalDateTime end) { public List<Task> findTasksFinishedByUserEmailsInPeriod(List<String> emails, LocalDateTime start, LocalDateTime end) {
if (emails.isEmpty()) return List.of(); if (emails.isEmpty()) {
LOGGER.warn("Finished task search by user emails skipped because the email list is empty");
return List.of();
}
return find("user.email in ?1 and status = ?2 and finishedAt >= ?3 and finishedAt <= ?4", return find("user.email in ?1 and status = ?2 and finishedAt >= ?3 and finishedAt <= ?4",
emails, TaskStatus.COMPLETED, start, end) emails, TaskStatus.COMPLETED, start, end)
.list().stream().map(TaskEntityMapper::toDomain).toList(); .list().stream().map(TaskEntityMapper::toDomain).toList();

View file

@ -8,6 +8,7 @@ import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity; import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity;
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserOrganizationEntityMapper; import net.beetechgroup.beetask.frameworks.persistence.mapper.UserOrganizationEntityMapper;
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -15,33 +16,44 @@ import java.util.Optional;
@ApplicationScoped @ApplicationScoped
public class UserOrganizationRepositoryImpl implements UserOrganizationRepository, PanacheRepository<UserOrganizationEntity> { public class UserOrganizationRepositoryImpl implements UserOrganizationRepository, PanacheRepository<UserOrganizationEntity> {
private static final Logger LOGGER = Logger.getLogger(UserOrganizationRepositoryImpl.class);
@Override @Override
public List<UserOrganization> findByUserId(Long userId) { public List<UserOrganization> findByUserId(Long userId) {
return find("user.id", userId).stream() List<UserOrganization> organizations = find("user.id", userId).stream()
.map(UserOrganizationEntityMapper::toDomain) .map(UserOrganizationEntityMapper::toDomain)
.toList(); .toList();
LOGGER.infof("Loaded %d organization memberships for user %d", organizations.size(), userId);
return organizations;
} }
@Override @Override
public List<UserOrganization> findByUserIdAndStatus(Long userId, UserOrganizationStatus status) { public List<UserOrganization> findByUserIdAndStatus(Long userId, UserOrganizationStatus status) {
return find("user.id = ?1 and status = ?2", userId, status).stream() List<UserOrganization> organizations = find("user.id = ?1 and status = ?2", userId, status).stream()
.map(UserOrganizationEntityMapper::toDomain) .map(UserOrganizationEntityMapper::toDomain)
.toList(); .toList();
LOGGER.infof("Loaded %d organization memberships for user %d with status %s", organizations.size(), userId, status);
return organizations;
} }
@Override @Override
public List<UserOrganization> findByOrganizationIdAndStatus(Long organizationId, UserOrganizationStatus status) { public List<UserOrganization> findByOrganizationIdAndStatus(Long organizationId, UserOrganizationStatus status) {
return find("organization.id = ?1 and status = ?2", organizationId, status).stream() List<UserOrganization> organizations = find("organization.id = ?1 and status = ?2", organizationId, status).stream()
.map(UserOrganizationEntityMapper::toDomain) .map(UserOrganizationEntityMapper::toDomain)
.toList(); .toList();
LOGGER.infof("Loaded %d memberships for organization %d with status %s", organizations.size(), organizationId, status);
return organizations;
} }
@Override @Override
public Optional<UserOrganization> findByUserAndOrganization(Long userId, Long organizationId) { public Optional<UserOrganization> findByUserAndOrganization(Long userId, Long organizationId) {
return find("user.id = ?1 and organization.id = ?2", userId, organizationId) Optional<UserOrganization> association = find("user.id = ?1 and organization.id = ?2", userId, organizationId)
.firstResultOptional() .firstResultOptional()
.map(UserOrganizationEntityMapper::toDomain); .map(UserOrganizationEntityMapper::toDomain);
if (association.isEmpty()) {
LOGGER.warnf("Membership for user %d and organization %d was not found", userId, organizationId);
}
return association;
} }
@Override @Override
@ -56,9 +68,13 @@ public class UserOrganizationRepositoryImpl implements UserOrganizationRepositor
existingEntity.setRole(entity.getRole()); existingEntity.setRole(entity.getRole());
existingEntity.setStatus(entity.getStatus()); existingEntity.setStatus(entity.getStatus());
getEntityManager().merge(existingEntity); getEntityManager().merge(existingEntity);
LOGGER.infof("Updated membership for user %d in organization %d to role %s and status %s",
entity.getUser().getId(), entity.getOrganization().getId(), entity.getRole(), entity.getStatus());
return; return;
} }
} }
persist(entity); persist(entity);
LOGGER.infof("Created membership for user %d in organization %d with role %s and status %s",
entity.getUser().getId(), entity.getOrganization().getId(), entity.getRole(), entity.getStatus());
} }
} }

View file

@ -12,12 +12,14 @@ import net.beetechgroup.beetask.entities.organization.UserOrganization;
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus; import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ApplicationScoped @ApplicationScoped
public class UserRepositoryImpl implements UserRepository, PanacheRepository<UserEntity> { public class UserRepositoryImpl implements UserRepository, PanacheRepository<UserEntity> {
private static final Logger LOGGER = Logger.getLogger(UserRepositoryImpl.class);
@Inject @Inject
UserOrganizationRepository userOrganizationRepository; UserOrganizationRepository userOrganizationRepository;
@ -31,23 +33,35 @@ public class UserRepositoryImpl implements UserRepository, PanacheRepository<Use
} else { } else {
entity = getEntityManager().merge(entity); entity = getEntityManager().merge(entity);
} }
return UserEntityMapper.toDomain(entity); User savedUser = UserEntityMapper.toDomain(entity);
LOGGER.infof("Persisted user %d with email %s", savedUser.getId(), savedUser.getEmail());
return savedUser;
} }
@Override @Override
public Optional<User> findByEmail(String email) { public Optional<User> findByEmail(String email) {
return find("email", email).firstResultOptional() Optional<User> user = find("email", email).firstResultOptional()
.map(UserEntityMapper::toDomain); .map(UserEntityMapper::toDomain);
if (user.isEmpty()) {
LOGGER.warnf("User with email %s was not found", email);
}
return user;
} }
@Override @Override
public Optional<User> findUserById(Long id) { public Optional<User> findUserById(Long id) {
return findByIdOptional(id) Optional<User> user = findByIdOptional(id)
.map(UserEntityMapper::toDomain); .map(UserEntityMapper::toDomain);
if (user.isEmpty()) {
LOGGER.warnf("User with id %d was not found", id);
}
return user;
} }
@Override @Override
public List<UserOrganization> findUserOrganizations(Long userId) { public List<UserOrganization> findUserOrganizations(Long userId) {
return userOrganizationRepository.findByUserIdAndStatus(userId, UserOrganizationStatus.ACTIVE); List<UserOrganization> organizations = userOrganizationRepository.findByUserIdAndStatus(userId, UserOrganizationStatus.ACTIVE);
LOGGER.infof("Loaded %d active organizations for user %d", organizations.size(), userId);
return organizations;
} }
} }

View file

@ -27,12 +27,14 @@ import io.quarkus.security.identity.SecurityIdentity;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import net.beetechgroup.beetask.usecase.user.profile.GetUserProfileUseCase; import net.beetechgroup.beetask.usecase.user.profile.GetUserProfileUseCase;
import net.beetechgroup.beetask.usecase.user.profile.UserProfileOutput; import net.beetechgroup.beetask.usecase.user.profile.UserProfileOutput;
import org.jboss.logging.Logger;
@Path("/auth") @Path("/auth")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Authentication", description = "Authentication operations") @Tag(name = "Authentication", description = "Authentication operations")
public class AuthController { public class AuthController {
private static final Logger LOGGER = Logger.getLogger(AuthController.class);
@Inject @Inject
CreateUserUseCase createUserUseCase; CreateUserUseCase createUserUseCase;
@ -55,6 +57,7 @@ public class AuthController {
@Operation(summary = "Get current user profile", description = "Returns details of the logged in user") @Operation(summary = "Get current user profile", description = "Returns details of the logged in user")
public UserProfileOutput me() { public UserProfileOutput me() {
String email = securityIdentity.getPrincipal().getName(); String email = securityIdentity.getPrincipal().getName();
LOGGER.infof("Profile requested for authenticated user %s", email);
return getUserProfileUseCase.execute(email); return getUserProfileUseCase.execute(email);
} }
@ -62,8 +65,10 @@ public class AuthController {
@POST @POST
@Operation(summary = "Register", description = "Creates a new user account") @Operation(summary = "Register", description = "Creates a new user account")
public CreateAccountResponse register(CreateAccountRequest request) { public CreateAccountResponse register(CreateAccountRequest request) {
LOGGER.infof("Registration requested for email %s", request.email());
CreateUserInput input = AuthControllerMapper.toCreateUserInput(request); CreateUserInput input = AuthControllerMapper.toCreateUserInput(request);
CreateUserOutput output = createUserUseCase.execute(input); CreateUserOutput output = createUserUseCase.execute(input);
LOGGER.infof("User account created successfully for email %s", output.email());
return AuthControllerMapper.toCreateAccountResponse(output); return AuthControllerMapper.toCreateAccountResponse(output);
} }
@ -71,8 +76,10 @@ public class AuthController {
@POST @POST
@Operation(summary = "Login", description = "Login that returns real user data and JWT") @Operation(summary = "Login", description = "Login that returns real user data and JWT")
public LoginResponse login(LoginRequest request) { public LoginResponse login(LoginRequest request) {
LOGGER.infof("Login requested for email %s", request.email());
LoginInput input = AuthControllerMapper.toLoginInput(request); LoginInput input = AuthControllerMapper.toLoginInput(request);
LoginOutput output = loginUseCase.execute(input); LoginOutput output = loginUseCase.execute(input);
LOGGER.infof("Login succeeded for email %s", output.email());
return AuthControllerMapper.toLoginResponse(output); return AuthControllerMapper.toLoginResponse(output);
} }
@ -80,8 +87,10 @@ public class AuthController {
@POST @POST
@Operation(summary = "Refresh token", description = "Token refresh using refresh token") @Operation(summary = "Refresh token", description = "Token refresh using refresh token")
public LoginResponse refresh(RefreshTokenRequest request) { public LoginResponse refresh(RefreshTokenRequest request) {
LOGGER.info("Refresh token requested");
RefreshTokenInput input = AuthControllerMapper.toRefreshTokenInput(request); RefreshTokenInput input = AuthControllerMapper.toRefreshTokenInput(request);
LoginOutput output = refreshTokenUseCase.execute(input); LoginOutput output = refreshTokenUseCase.execute(input);
LOGGER.infof("Refresh token succeeded for email %s", output.email());
return AuthControllerMapper.toLoginResponse(output); return AuthControllerMapper.toLoginResponse(output);
} }
} }

View file

@ -11,6 +11,7 @@ import jakarta.ws.rs.core.MediaType;
import net.beetechgroup.beetask.usecase.dashboard.DashboardInput; import net.beetechgroup.beetask.usecase.dashboard.DashboardInput;
import net.beetechgroup.beetask.usecase.dashboard.DashboardOutput; import net.beetechgroup.beetask.usecase.dashboard.DashboardOutput;
import net.beetechgroup.beetask.usecase.dashboard.DashboardUseCase; import net.beetechgroup.beetask.usecase.dashboard.DashboardUseCase;
import org.jboss.logging.Logger;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -19,6 +20,7 @@ import java.time.format.DateTimeFormatter;
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Authenticated @Authenticated
public class DashboardController { public class DashboardController {
private static final Logger LOGGER = Logger.getLogger(DashboardController.class);
@Inject @Inject
DashboardUseCase dashboardUseCase; DashboardUseCase dashboardUseCase;
@ -46,6 +48,7 @@ public class DashboardController {
} }
DashboardInput input = new DashboardInput(email, start, end); DashboardInput input = new DashboardInput(email, start, end);
LOGGER.infof("Dashboard requested by user %s for period %s to %s", email, start, end);
return dashboardUseCase.execute(input); return dashboardUseCase.execute(input);
} }
} }

View file

@ -21,11 +21,13 @@ import net.beetechgroup.beetask.usecase.organization.requests.ListUserJoinReques
import net.beetechgroup.beetask.usecase.organization.requests.UserJoinRequestOutput; import net.beetechgroup.beetask.usecase.organization.requests.UserJoinRequestOutput;
import net.beetechgroup.beetask.usecase.organization.requests.JoinRequestOutput; import net.beetechgroup.beetask.usecase.organization.requests.JoinRequestOutput;
import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase; import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
@Path("/organizations") @Path("/organizations")
public class OrganizationController { public class OrganizationController {
private static final Logger LOGGER = Logger.getLogger(OrganizationController.class);
@Inject @Inject
CreateOrganizationUseCase createOrganizationUseCase; CreateOrganizationUseCase createOrganizationUseCase;
@ -53,8 +55,10 @@ public class OrganizationController {
@Operation(summary = "Create organization", description = "Creates a new organization") @Operation(summary = "Create organization", description = "Creates a new organization")
public CreateOrganizationResponse createOrganization(CreateOrganizationRequest request) { public CreateOrganizationResponse createOrganization(CreateOrganizationRequest request) {
String userEmail = securityIdentity.getPrincipal().getName(); String userEmail = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested organization creation with name '%s'", userEmail, request.name());
CreateOrganizationInput input = OrganizationControllerMapper.toCreateOrganizationInput(request, userEmail); CreateOrganizationInput input = OrganizationControllerMapper.toCreateOrganizationInput(request, userEmail);
CreateOrganizationOutput output = createOrganizationUseCase.execute(input); CreateOrganizationOutput output = createOrganizationUseCase.execute(input);
LOGGER.infof("Organization %d created successfully by user %s", output.id(), userEmail);
return OrganizationControllerMapper.toCreateOrganizationResponse(output); return OrganizationControllerMapper.toCreateOrganizationResponse(output);
} }
@ -63,7 +67,9 @@ public class OrganizationController {
@Authenticated @Authenticated
@Operation(summary = "Search organizations", description = "Search for organizations by name") @Operation(summary = "Search organizations", description = "Search for organizations by name")
public List<SearchOrganizationOutput> search(@QueryParam("q") String query) { public List<SearchOrganizationOutput> search(@QueryParam("q") String query) {
return searchOrganizationsUseCase.execute(query); List<SearchOrganizationOutput> organizations = searchOrganizationsUseCase.execute(query);
LOGGER.infof("Organization search executed with query '%s' and returned %d results", query, organizations.size());
return organizations;
} }
@POST @POST
@ -72,7 +78,9 @@ public class OrganizationController {
@Operation(summary = "Request to join", description = "Request to join an organization") @Operation(summary = "Request to join", description = "Request to join an organization")
public void requestJoin(@PathParam("id") Long organizationId) { public void requestJoin(@PathParam("id") Long organizationId) {
String userEmail = securityIdentity.getPrincipal().getName(); String userEmail = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested to join organization %d", userEmail, organizationId);
requestJoinOrganizationUseCase.execute(organizationId, userEmail); requestJoinOrganizationUseCase.execute(organizationId, userEmail);
LOGGER.infof("Join request created for user %s in organization %d", userEmail, organizationId);
} }
@GET @GET
@ -81,7 +89,9 @@ public class OrganizationController {
@Operation(summary = "List pending requests", description = "List all pending join requests for an organization (Owner only)") @Operation(summary = "List pending requests", description = "List all pending join requests for an organization (Owner only)")
public List<JoinRequestOutput> listRequests(@PathParam("id") Long organizationId) { public List<JoinRequestOutput> listRequests(@PathParam("id") Long organizationId) {
String userEmail = securityIdentity.getPrincipal().getName(); String userEmail = securityIdentity.getPrincipal().getName();
return listPendingRequestsUseCase.execute(userEmail, organizationId, null); List<JoinRequestOutput> requests = listPendingRequestsUseCase.execute(userEmail, organizationId, null);
LOGGER.infof("User %s listed %d pending requests for organization %d", userEmail, requests.size(), organizationId);
return requests;
} }
@PATCH @PATCH
@ -90,7 +100,9 @@ public class OrganizationController {
@Operation(summary = "Approve request", description = "Approve a join request (Owner only)") @Operation(summary = "Approve request", description = "Approve a join request (Owner only)")
public void approveRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) { public void approveRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) {
String userEmail = securityIdentity.getPrincipal().getName(); String userEmail = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested approval for user %d in organization %d", userEmail, userId, organizationId);
handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, true)); handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, true));
LOGGER.infof("Join request approved for user %d in organization %d by %s", userId, organizationId, userEmail);
} }
@PATCH @PATCH
@ -99,7 +111,9 @@ public class OrganizationController {
@Operation(summary = "Reject request", description = "Reject a join request (Owner only)") @Operation(summary = "Reject request", description = "Reject a join request (Owner only)")
public void rejectRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) { public void rejectRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) {
String userEmail = securityIdentity.getPrincipal().getName(); String userEmail = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested rejection for user %d in organization %d", userEmail, userId, organizationId);
handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, false)); handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, false));
LOGGER.infof("Join request rejected for user %d in organization %d by %s", userId, organizationId, userEmail);
} }
@GET @GET
@ -108,6 +122,8 @@ public class OrganizationController {
@Operation(summary = "List my requests", description = "List all join requests made by the current user") @Operation(summary = "List my requests", description = "List all join requests made by the current user")
public List<UserJoinRequestOutput> listMyRequests() { public List<UserJoinRequestOutput> listMyRequests() {
String userEmail = securityIdentity.getPrincipal().getName(); String userEmail = securityIdentity.getPrincipal().getName();
return listUserJoinRequestsUseCase.execute(userEmail); List<UserJoinRequestOutput> requests = listUserJoinRequestsUseCase.execute(userEmail);
LOGGER.infof("User %s listed %d organization join requests", userEmail, requests.size());
return requests;
} }
} }

View file

@ -11,6 +11,7 @@ import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import net.beetechgroup.beetask.usecase.orgdashboard.memberdetail.MemberDetailInput; import net.beetechgroup.beetask.usecase.orgdashboard.memberdetail.MemberDetailInput;
import net.beetechgroup.beetask.usecase.orgdashboard.memberdetail.MemberDetailUseCase; import net.beetechgroup.beetask.usecase.orgdashboard.memberdetail.MemberDetailUseCase;
import org.jboss.logging.Logger;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -19,6 +20,7 @@ import java.time.format.DateTimeFormatter;
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Authenticated @Authenticated
public class MemberDetailController { public class MemberDetailController {
private static final Logger LOGGER = Logger.getLogger(MemberDetailController.class);
@Inject @Inject
MemberDetailUseCase memberDetailUseCase; MemberDetailUseCase memberDetailUseCase;
@ -47,6 +49,8 @@ public class MemberDetailController {
end = end.with(java.time.LocalTime.MAX); end = end.with(java.time.LocalTime.MAX);
} }
LOGGER.infof("Member stats requested by user %s for organization %d member %d and period %s to %s",
email, organizationId, memberId, start, end);
return MemberDetailControllerMapper.toResponse( return MemberDetailControllerMapper.toResponse(
memberDetailUseCase.execute(new MemberDetailInput(email, organizationId, memberId, start, end)) memberDetailUseCase.execute(new MemberDetailInput(email, organizationId, memberId, start, end))
); );

View file

@ -11,6 +11,7 @@ import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import net.beetechgroup.beetask.usecase.orgdashboard.OrgDashboardInput; import net.beetechgroup.beetask.usecase.orgdashboard.OrgDashboardInput;
import net.beetechgroup.beetask.usecase.orgdashboard.OrgDashboardUseCase; import net.beetechgroup.beetask.usecase.orgdashboard.OrgDashboardUseCase;
import org.jboss.logging.Logger;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -19,6 +20,7 @@ import java.time.format.DateTimeFormatter;
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Authenticated @Authenticated
public class OrgDashboardController { public class OrgDashboardController {
private static final Logger LOGGER = Logger.getLogger(OrgDashboardController.class);
@Inject @Inject
OrgDashboardUseCase orgDashboardUseCase; OrgDashboardUseCase orgDashboardUseCase;
@ -46,6 +48,8 @@ public class OrgDashboardController {
end = end.with(java.time.LocalTime.MAX); end = end.with(java.time.LocalTime.MAX);
} }
LOGGER.infof("Organization dashboard requested by user %s for organization %d and period %s to %s",
email, organizationId, start, end);
return OrgDashboardControllerMapper.toResponse( return OrgDashboardControllerMapper.toResponse(
orgDashboardUseCase.execute(new OrgDashboardInput(email, organizationId, start, end)) orgDashboardUseCase.execute(new OrgDashboardInput(email, organizationId, start, end))
); );

View file

@ -12,6 +12,7 @@ import jakarta.ws.rs.core.MediaType;
import net.beetechgroup.beetask.entities.Project; import net.beetechgroup.beetask.entities.Project;
import net.beetechgroup.beetask.usecase.project.create.CreateProjectUseCase; import net.beetechgroup.beetask.usecase.project.create.CreateProjectUseCase;
import net.beetechgroup.beetask.usecase.project.list.ListProjectsUseCase; import net.beetechgroup.beetask.usecase.project.list.ListProjectsUseCase;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
@ -19,6 +20,7 @@ import java.util.List;
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public class ProjectController { public class ProjectController {
private static final Logger LOGGER = Logger.getLogger(ProjectController.class);
@Inject @Inject
CreateProjectUseCase createProjectUseCase; CreateProjectUseCase createProjectUseCase;
@ -29,15 +31,19 @@ public class ProjectController {
@GET @GET
@Authenticated @Authenticated
public List<ProjectResponse> listProjects(@PathParam("orgId") Long orgId) { public List<ProjectResponse> listProjects(@PathParam("orgId") Long orgId) {
return listProjectsUseCase.execute(orgId).stream() List<ProjectResponse> projects = listProjectsUseCase.execute(orgId).stream()
.map(p -> new ProjectResponse(p.getId(), p.getName())) .map(p -> new ProjectResponse(p.getId(), p.getName()))
.toList(); .toList();
LOGGER.infof("Listed %d projects for organization %d", projects.size(), orgId);
return projects;
} }
@POST @POST
@Authenticated @Authenticated
public ProjectResponse createProject(@PathParam("orgId") Long orgId, CreateProjectRequest request) { public ProjectResponse createProject(@PathParam("orgId") Long orgId, CreateProjectRequest request) {
LOGGER.infof("Project creation requested for organization %d with name '%s'", orgId, request.name());
Project project = createProjectUseCase.execute(orgId, request.name()); Project project = createProjectUseCase.execute(orgId, request.name());
LOGGER.infof("Project %d created successfully for organization %d", project.getId(), orgId);
return new ProjectResponse(project.getId(), project.getName()); return new ProjectResponse(project.getId(), project.getName());
} }

View file

@ -18,6 +18,7 @@ import java.util.List;
import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import net.beetechgroup.beetask.entities.task.TaskStatus; import net.beetechgroup.beetask.entities.task.TaskStatus;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskInput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskInput;
@ -36,6 +37,7 @@ import net.beetechgroup.beetask.usecase.task.get.GetTaskUseCase;
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Tasks", description = "Task management operations") @Tag(name = "Tasks", description = "Task management operations")
public class TaskController { public class TaskController {
private static final Logger LOGGER = Logger.getLogger(TaskController.class);
@Inject @Inject
CreateTaskUseCase createTaskUseCase; CreateTaskUseCase createTaskUseCase;
@ -69,8 +71,10 @@ public class TaskController {
@APIResponse(responseCode = "201", description = "Task created successfully") @APIResponse(responseCode = "201", description = "Task created successfully")
public CreateTaskResponse createTask(CreateTaskRequest request) { public CreateTaskResponse createTask(CreateTaskRequest request) {
String email = securityIdentity.getPrincipal().getName(); String email = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested task creation with title '%s'", email, request.title());
CreateTaskInput input = TaskControllerMapper.toCreateTaskInput(request, email); CreateTaskInput input = TaskControllerMapper.toCreateTaskInput(request, email);
CreateTaskOutput output = createTaskUseCase.execute(input); CreateTaskOutput output = createTaskUseCase.execute(input);
LOGGER.infof("Task %d created successfully by user %s", output.id(), email);
return TaskControllerMapper.toCreateTaskResponse(output); return TaskControllerMapper.toCreateTaskResponse(output);
} }
@ -80,7 +84,9 @@ public class TaskController {
@APIResponse(responseCode = "200", description = "Task updated successfully") @APIResponse(responseCode = "200", description = "Task updated successfully")
public CreateTaskResponse updateTask(@PathParam("id") Long id, CreateTaskRequest request) { public CreateTaskResponse updateTask(@PathParam("id") Long id, CreateTaskRequest request) {
String email = securityIdentity.getPrincipal().getName(); String email = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested update for task %d", email, id);
CreateTaskOutput output = updateTaskUseCase.execute(TaskControllerMapper.toUpdateTaskInput(id, request, email)); CreateTaskOutput output = updateTaskUseCase.execute(TaskControllerMapper.toUpdateTaskInput(id, request, email));
LOGGER.infof("Task %d updated successfully by user %s", id, email);
return TaskControllerMapper.toCreateTaskResponse(output); return TaskControllerMapper.toCreateTaskResponse(output);
} }
@ -89,6 +95,7 @@ public class TaskController {
@Operation(summary = "Get a task", description = "Gets a specific task by ID") @Operation(summary = "Get a task", description = "Gets a specific task by ID")
@APIResponse(responseCode = "200", description = "Task retrieved successfully") @APIResponse(responseCode = "200", description = "Task retrieved successfully")
public CreateTaskResponse getTask(@PathParam("id") Long id) { public CreateTaskResponse getTask(@PathParam("id") Long id) {
LOGGER.infof("Task details requested for task %d", id);
return TaskControllerMapper.toCreateTaskResponse(getTaskUseCase.execute(id)); return TaskControllerMapper.toCreateTaskResponse(getTaskUseCase.execute(id));
} }
@ -98,7 +105,9 @@ public class TaskController {
@APIResponse(responseCode = "200", description = "Task started successfully") @APIResponse(responseCode = "200", description = "Task started successfully")
public CreateTaskResponse startTask(@PathParam("id") Long id) { public CreateTaskResponse startTask(@PathParam("id") Long id) {
String email = securityIdentity.getPrincipal().getName(); String email = securityIdentity.getPrincipal().getName();
LOGGER.infof("User %s requested start for task %d", email, id);
CreateTaskOutput output = startTaskUseCase.execute(new StartTaskInput(id, email)); CreateTaskOutput output = startTaskUseCase.execute(new StartTaskInput(id, email));
LOGGER.infof("Task %d started successfully by user %s", id, email);
return TaskControllerMapper.toCreateTaskResponse(output); return TaskControllerMapper.toCreateTaskResponse(output);
} }
@ -107,7 +116,9 @@ public class TaskController {
@Operation(summary = "Stop a task", description = "Stops a task in the system") @Operation(summary = "Stop a task", description = "Stops a task in the system")
@APIResponse(responseCode = "200", description = "Task stopped successfully") @APIResponse(responseCode = "200", description = "Task stopped successfully")
public CreateTaskResponse stopTask(@PathParam("id") Long id) { public CreateTaskResponse stopTask(@PathParam("id") Long id) {
LOGGER.infof("Stop requested for task %d", id);
CreateTaskOutput output = stopTaskUseCase.execute(TaskControllerMapper.toStopTaskInput(id)); CreateTaskOutput output = stopTaskUseCase.execute(TaskControllerMapper.toStopTaskInput(id));
LOGGER.infof("Task %d stopped successfully", id);
return TaskControllerMapper.toCreateTaskResponse(output); return TaskControllerMapper.toCreateTaskResponse(output);
} }
@ -116,7 +127,9 @@ public class TaskController {
@Operation(summary = "Update task status", description = "Updates the status of a task") @Operation(summary = "Update task status", description = "Updates the status of a task")
@APIResponse(responseCode = "200", description = "Task status updated successfully") @APIResponse(responseCode = "200", description = "Task status updated successfully")
public CreateTaskResponse updateTaskStatus(@PathParam("id") Long id, TaskStatus status) { public CreateTaskResponse updateTaskStatus(@PathParam("id") Long id, TaskStatus status) {
LOGGER.infof("Status update requested for task %d to %s", id, status);
CreateTaskOutput output = updateTaskStatusUseCase.execute(id, status); CreateTaskOutput output = updateTaskStatusUseCase.execute(id, status);
LOGGER.infof("Task %d status updated successfully to %s", id, status);
return TaskControllerMapper.toCreateTaskResponse(output); return TaskControllerMapper.toCreateTaskResponse(output);
} }
@ -124,7 +137,9 @@ public class TaskController {
@Operation(summary = "List all tasks", description = "Lists all tasks in the system") @Operation(summary = "List all tasks", description = "Lists all tasks in the system")
@APIResponse(responseCode = "200", description = "Tasks listed successfully") @APIResponse(responseCode = "200", description = "Tasks listed successfully")
public List<CreateTaskResponse> listTasks() { public List<CreateTaskResponse> listTasks() {
return listAllTasksUseCase.execute().stream().map(TaskControllerMapper::toCreateTaskResponse).toList(); List<CreateTaskResponse> tasks = listAllTasksUseCase.execute().stream().map(TaskControllerMapper::toCreateTaskResponse).toList();
LOGGER.infof("Listed %d tasks", tasks.size());
return tasks;
} }
@GET @GET
@ -133,7 +148,9 @@ public class TaskController {
@APIResponse(responseCode = "200", description = "Tasks listed successfully") @APIResponse(responseCode = "200", description = "Tasks listed successfully")
public List<CreateTaskResponse> listMyTasks() { public List<CreateTaskResponse> listMyTasks() {
String email = securityIdentity.getPrincipal().getName(); String email = securityIdentity.getPrincipal().getName();
return listMyTasksUseCase.execute(email).stream().map(TaskControllerMapper::toCreateTaskResponse).toList(); List<CreateTaskResponse> tasks = listMyTasksUseCase.execute(email).stream().map(TaskControllerMapper::toCreateTaskResponse).toList();
LOGGER.infof("Listed %d tasks for user %s", tasks.size(), email);
return tasks;
} }
} }

View file

@ -12,8 +12,10 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jboss.logging.Logger;
public class LoginUseCase { public class LoginUseCase {
private static final Logger LOGGER = Logger.getLogger(LoginUseCase.class);
private final UserRepository userRepository; private final UserRepository userRepository;
private final String issuer; private final String issuer;
@ -27,6 +29,7 @@ public class LoginUseCase {
Optional<User> userOptional = userRepository.findByEmail(input.email()); Optional<User> userOptional = userRepository.findByEmail(input.email());
if (userOptional.isEmpty()) { if (userOptional.isEmpty()) {
LOGGER.warnf("Login failed because email %s was not found", input.email());
throw new RuntimeException("Invalid credentials"); throw new RuntimeException("Invalid credentials");
} }
@ -34,9 +37,11 @@ public class LoginUseCase {
// Simple password check for now (should use Bcrypt) // Simple password check for now (should use Bcrypt)
if (!user.getPassword().equals(input.password())) { if (!user.getPassword().equals(input.password())) {
LOGGER.warnf("Login failed because password did not match for email %s", input.email());
throw new RuntimeException("Invalid credentials"); throw new RuntimeException("Invalid credentials");
} }
LOGGER.infof("Login authenticated for user %s", input.email());
List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId()); List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId());
Set<String> groups = new HashSet<>(); Set<String> groups = new HashSet<>();
@ -67,6 +72,7 @@ public class LoginUseCase {
.expiresIn(86400) // 24 hours .expiresIn(86400) // 24 hours
.sign(); .sign();
LOGGER.infof("Login completed for user %s with %d organizations", user.getEmail(), orgs.size());
return new LoginOutput( return new LoginOutput(
user.getName(), user.getName(),
user.getEmail(), user.getEmail(),

View file

@ -16,8 +16,10 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jboss.logging.Logger;
public class RefreshTokenUseCase { public class RefreshTokenUseCase {
private static final Logger LOGGER = Logger.getLogger(RefreshTokenUseCase.class);
private final UserRepository userRepository; private final UserRepository userRepository;
private final JWTParser jwtParser; private final JWTParser jwtParser;
@ -35,11 +37,14 @@ public class RefreshTokenUseCase {
String email = token.getSubject(); // Or token.getName() / token.getClaim(Claims.upn.name()) String email = token.getSubject(); // Or token.getName() / token.getClaim(Claims.upn.name())
if (Objects.isNull(email)) { if (Objects.isNull(email)) {
LOGGER.warn("Refresh token rejected because subject/email was not present");
throw new RuntimeException("Invalid refresh token: email not found"); throw new RuntimeException("Invalid refresh token: email not found");
} }
LOGGER.infof("Refreshing authentication token for user %s", email);
Optional<User> userOptional = userRepository.findByEmail(email); Optional<User> userOptional = userRepository.findByEmail(email);
if (userOptional.isEmpty()) { if (userOptional.isEmpty()) {
LOGGER.warnf("Refresh token rejected because user %s was not found", email);
throw new RuntimeException("User not found for the provided refresh token"); throw new RuntimeException("User not found for the provided refresh token");
} }
@ -75,6 +80,7 @@ public class RefreshTokenUseCase {
}) })
.toList(); .toList();
LOGGER.infof("Refresh token completed for user %s with %d organizations", user.getEmail(), orgs.size());
return new LoginOutput( return new LoginOutput(
user.getName(), user.getName(),
user.getEmail(), user.getEmail(),
@ -86,6 +92,7 @@ public class RefreshTokenUseCase {
orgs orgs
); );
} catch (ParseException e) { } catch (ParseException e) {
LOGGER.error("Refresh token parsing failed", e);
throw new RuntimeException("Invalid refresh token: " + e.getMessage()); throw new RuntimeException("Invalid refresh token: " + e.getMessage());
} }
} }

View file

@ -13,8 +13,10 @@ import net.beetechgroup.beetask.entities.task.Task;
import net.beetechgroup.beetask.entities.task.TaskHistoryItem; import net.beetechgroup.beetask.entities.task.TaskHistoryItem;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import org.jboss.logging.Logger;
public class DashboardUseCase { public class DashboardUseCase {
private static final Logger LOGGER = Logger.getLogger(DashboardUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
@ -23,6 +25,8 @@ public class DashboardUseCase {
} }
public DashboardOutput execute(DashboardInput input) { public DashboardOutput execute(DashboardInput input) {
LOGGER.infof("Calculating dashboard for user %s in period %s to %s",
input.userEmail(), input.startDate(), input.endDate());
List<Task> workedTasks = taskRepository.findTasksWorkedByUserInPeriod( List<Task> workedTasks = taskRepository.findTasksWorkedByUserInPeriod(
input.userEmail(), input.startDate(), input.endDate() input.userEmail(), input.startDate(), input.endDate()
); );
@ -61,12 +65,15 @@ public class DashboardUseCase {
} }
} }
return new DashboardOutput( DashboardOutput output = new DashboardOutput(
totalMinutes, totalMinutes,
new ArrayList<>(projectStatsMap.values()), new ArrayList<>(projectStatsMap.values()),
yesterdayTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList(), yesterdayTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList(),
finishedTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList() finishedTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList()
); );
LOGGER.infof("Dashboard calculated for user %s with %d worked tasks, %d finished tasks and %d total minutes",
input.userEmail(), workedTasks.size(), finishedTasks.size(), totalMinutes);
return output;
} }
private boolean isWithinPeriod(TaskHistoryItem item, LocalDateTime start, LocalDateTime end) { private boolean isWithinPeriod(TaskHistoryItem item, LocalDateTime start, LocalDateTime end) {

View file

@ -7,8 +7,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import java.util.Optional; import java.util.Optional;
import org.jboss.logging.Logger;
public class AuthorizeOrganizationAdminUseCase { public class AuthorizeOrganizationAdminUseCase {
private static final Logger LOGGER = Logger.getLogger(AuthorizeOrganizationAdminUseCase.class);
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
@ -21,6 +23,7 @@ public class AuthorizeOrganizationAdminUseCase {
public void execute(String userEmail, Long organizationId) { public void execute(String userEmail, Long organizationId) {
Optional<User> userOptional = userRepository.findByEmail(userEmail); Optional<User> userOptional = userRepository.findByEmail(userEmail);
if (userOptional.isEmpty()) { if (userOptional.isEmpty()) {
LOGGER.warnf("Organization admin authorization failed because user %s was not found", userEmail);
throw new RuntimeException("Unauthorized: User not found"); throw new RuntimeException("Unauthorized: User not found");
} }
@ -28,12 +31,17 @@ public class AuthorizeOrganizationAdminUseCase {
Optional<UserOrganization> association = userOrganizationRepository.findByUserAndOrganization(user.getId(), organizationId); Optional<UserOrganization> association = userOrganizationRepository.findByUserAndOrganization(user.getId(), organizationId);
if (association.isEmpty()) { if (association.isEmpty()) {
LOGGER.warnf("Organization admin authorization failed because user %s is not a member of organization %d",
userEmail, organizationId);
throw new RuntimeException("Forbidden: Not a member of this organization"); throw new RuntimeException("Forbidden: Not a member of this organization");
} }
UserOrganization uo = association.get(); UserOrganization uo = association.get();
if (uo.getRole() != UserOrganizationRole.OWNER && uo.getRole() != UserOrganizationRole.ADMIN) { if (uo.getRole() != UserOrganizationRole.OWNER && uo.getRole() != UserOrganizationRole.ADMIN) {
LOGGER.warnf("Organization admin authorization failed because user %s has role %s in organization %d",
userEmail, uo.getRole(), organizationId);
throw new RuntimeException("Forbidden: Only owners or admins can perform this action"); throw new RuntimeException("Forbidden: Only owners or admins can perform this action");
} }
LOGGER.infof("Organization admin authorization granted to user %s for organization %d", userEmail, organizationId);
} }
} }

View file

@ -10,8 +10,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import java.util.Optional; import java.util.Optional;
import org.jboss.logging.Logger;
public class CreateOrganizationUseCase { public class CreateOrganizationUseCase {
private static final Logger LOGGER = Logger.getLogger(CreateOrganizationUseCase.class);
private final OrganizationRepository organizationRepository; private final OrganizationRepository organizationRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
@ -27,10 +29,12 @@ public class CreateOrganizationUseCase {
public CreateOrganizationOutput execute(CreateOrganizationInput input) { public CreateOrganizationOutput execute(CreateOrganizationInput input) {
Optional<User> userOptional = userRepository.findByEmail(input.userEmail()); Optional<User> userOptional = userRepository.findByEmail(input.userEmail());
if (userOptional.isEmpty()) { if (userOptional.isEmpty()) {
LOGGER.warnf("Organization creation failed because user %s was not found", input.userEmail());
throw new RuntimeException("User not found: " + input.userEmail()); throw new RuntimeException("User not found: " + input.userEmail());
} }
User user = userOptional.get(); User user = userOptional.get();
LOGGER.infof("Creating organization '%s' for owner %s", input.name(), input.userEmail());
Organization organization = new Organization(); Organization organization = new Organization();
organization.setName(input.name()); organization.setName(input.name());
organization = organizationRepository.saveOrganization(organization); organization = organizationRepository.saveOrganization(organization);
@ -41,6 +45,7 @@ public class CreateOrganizationUseCase {
userOrganization.setRole(UserOrganizationRole.OWNER); userOrganization.setRole(UserOrganizationRole.OWNER);
userOrganization.setStatus(UserOrganizationStatus.ACTIVE); userOrganization.setStatus(UserOrganizationStatus.ACTIVE);
userOrganizationRepository.save(userOrganization); userOrganizationRepository.save(userOrganization);
LOGGER.infof("Organization %d created successfully with owner user %d", organization.getId(), user.getId());
return CreateOrganizationMapper.toCreateOrganizationOutput(organization); return CreateOrganizationMapper.toCreateOrganizationOutput(organization);
} }

View file

@ -10,8 +10,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import java.util.Optional; import java.util.Optional;
import org.jboss.logging.Logger;
public class RequestJoinOrganizationUseCase { public class RequestJoinOrganizationUseCase {
private static final Logger LOGGER = Logger.getLogger(RequestJoinOrganizationUseCase.class);
private final OrganizationRepository organizationRepository; private final OrganizationRepository organizationRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
@ -29,6 +31,7 @@ public class RequestJoinOrganizationUseCase {
Optional<Organization> orgOptional = organizationRepository.findOrganizationById(organizationId); Optional<Organization> orgOptional = organizationRepository.findOrganizationById(organizationId);
if (userOptional.isEmpty() || orgOptional.isEmpty()) { if (userOptional.isEmpty() || orgOptional.isEmpty()) {
LOGGER.warnf("Join request failed because user %s or organization %d was not found", userEmail, organizationId);
throw new RuntimeException("User or Organization not found"); throw new RuntimeException("User or Organization not found");
} }
@ -38,8 +41,10 @@ public class RequestJoinOrganizationUseCase {
Optional<UserOrganization> existing = userOrganizationRepository.findByUserAndOrganization(user.getId(), organization.getId()); Optional<UserOrganization> existing = userOrganizationRepository.findByUserAndOrganization(user.getId(), organization.getId());
if (existing.isPresent()) { if (existing.isPresent()) {
if (existing.get().getStatus() == UserOrganizationStatus.ACTIVE) { if (existing.get().getStatus() == UserOrganizationStatus.ACTIVE) {
LOGGER.warnf("Join request ignored because user %s is already active in organization %d", userEmail, organizationId);
throw new RuntimeException("User is already a member of this organization"); throw new RuntimeException("User is already a member of this organization");
} else if (existing.get().getStatus() == UserOrganizationStatus.PENDING) { } else if (existing.get().getStatus() == UserOrganizationStatus.PENDING) {
LOGGER.warnf("Join request ignored because user %s already has a pending request for organization %d", userEmail, organizationId);
throw new RuntimeException("A join request is already pending for this organization"); throw new RuntimeException("A join request is already pending for this organization");
} }
// If REJECTED, we allow a new request (or we could just update the existing one to PENDING) // If REJECTED, we allow a new request (or we could just update the existing one to PENDING)
@ -52,5 +57,6 @@ public class RequestJoinOrganizationUseCase {
request.setStatus(UserOrganizationStatus.PENDING); request.setStatus(UserOrganizationStatus.PENDING);
userOrganizationRepository.save(request); userOrganizationRepository.save(request);
LOGGER.infof("Join request created for user %s in organization %d", userEmail, organizationId);
} }
} }

View file

@ -7,8 +7,10 @@ import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationA
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import java.util.Optional; import java.util.Optional;
import org.jboss.logging.Logger;
public class HandleJoinRequestUseCase extends AuthenticatedOrganizationUseCase<HandleJoinRequestUseCase.Input, Void> { public class HandleJoinRequestUseCase extends AuthenticatedOrganizationUseCase<HandleJoinRequestUseCase.Input, Void> {
private static final Logger LOGGER = Logger.getLogger(HandleJoinRequestUseCase.class);
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
public HandleJoinRequestUseCase(AuthorizeOrganizationAdminUseCase authorizer, public HandleJoinRequestUseCase(AuthorizeOrganizationAdminUseCase authorizer,
@ -22,16 +24,22 @@ public class HandleJoinRequestUseCase extends AuthenticatedOrganizationUseCase<H
Optional<UserOrganization> associationOptional = userOrganizationRepository.findByUserAndOrganization(input.userId(), organizationId); Optional<UserOrganization> associationOptional = userOrganizationRepository.findByUserAndOrganization(input.userId(), organizationId);
if (associationOptional.isEmpty()) { if (associationOptional.isEmpty()) {
LOGGER.warnf("Join request handling failed because request for user %d in organization %d was not found",
input.userId(), organizationId);
throw new RuntimeException("Request not found"); throw new RuntimeException("Request not found");
} }
UserOrganization association = associationOptional.get(); UserOrganization association = associationOptional.get();
if (association.getStatus() != UserOrganizationStatus.PENDING) { if (association.getStatus() != UserOrganizationStatus.PENDING) {
LOGGER.warnf("Join request handling failed because request for user %d in organization %d is %s",
input.userId(), organizationId, association.getStatus());
throw new RuntimeException("Request is not pending"); throw new RuntimeException("Request is not pending");
} }
association.setStatus(input.approved() ? UserOrganizationStatus.ACTIVE : UserOrganizationStatus.REJECTED); association.setStatus(input.approved() ? UserOrganizationStatus.ACTIVE : UserOrganizationStatus.REJECTED);
userOrganizationRepository.save(association); userOrganizationRepository.save(association);
LOGGER.infof("Join request for user %d in organization %d was %s",
input.userId(), organizationId, input.approved() ? "approved" : "rejected");
return null; return null;
} }

View file

@ -8,8 +8,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jboss.logging.Logger;
public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase<Void, List<JoinRequestOutput>> { public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase<Void, List<JoinRequestOutput>> {
private static final Logger LOGGER = Logger.getLogger(ListPendingRequestsUseCase.class);
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
public ListPendingRequestsUseCase(AuthorizeOrganizationAdminUseCase authorizer, public ListPendingRequestsUseCase(AuthorizeOrganizationAdminUseCase authorizer,
@ -21,7 +23,7 @@ public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase
@Override @Override
protected List<JoinRequestOutput> doExecute(Long organizationId, Void input) { protected List<JoinRequestOutput> doExecute(Long organizationId, Void input) {
List<UserOrganization> pending = userOrganizationRepository.findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.PENDING); List<UserOrganization> pending = userOrganizationRepository.findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.PENDING);
return pending.stream() List<JoinRequestOutput> requests = pending.stream()
.map(uo -> new JoinRequestOutput( .map(uo -> new JoinRequestOutput(
uo.getUser().getId(), uo.getUser().getId(),
uo.getUser().getName(), uo.getUser().getName(),
@ -29,5 +31,7 @@ public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase
uo.getUser().getPhoto() uo.getUser().getPhoto()
)) ))
.collect(Collectors.toList()); .collect(Collectors.toList());
LOGGER.infof("Loaded %d pending requests for organization %d", requests.size(), organizationId);
return requests;
} }
} }

View file

@ -5,8 +5,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger;
public class ListUserJoinRequestsUseCase { public class ListUserJoinRequestsUseCase {
private static final Logger LOGGER = Logger.getLogger(ListUserJoinRequestsUseCase.class);
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
@ -17,14 +19,19 @@ public class ListUserJoinRequestsUseCase {
public List<UserJoinRequestOutput> execute(String userEmail) { public List<UserJoinRequestOutput> execute(String userEmail) {
User user = userRepository.findByEmail(userEmail) User user = userRepository.findByEmail(userEmail)
.orElseThrow(() -> new RuntimeException("User not found")); .orElseThrow(() -> {
LOGGER.warnf("Join request listing failed because user %s was not found", userEmail);
return new RuntimeException("User not found");
});
return userOrganizationRepository.findByUserId(user.getId()).stream() List<UserJoinRequestOutput> requests = userOrganizationRepository.findByUserId(user.getId()).stream()
.map(uo -> new UserJoinRequestOutput( .map(uo -> new UserJoinRequestOutput(
uo.getOrganization().getId(), uo.getOrganization().getId(),
uo.getOrganization().getName(), uo.getOrganization().getName(),
uo.getStatus() uo.getStatus()
)) ))
.toList(); .toList();
LOGGER.infof("Loaded %d join requests for user %s", requests.size(), userEmail);
return requests;
} }
} }

View file

@ -3,8 +3,10 @@ package net.beetechgroup.beetask.usecase.organization.search;
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger;
public class SearchOrganizationsUseCase { public class SearchOrganizationsUseCase {
private static final Logger LOGGER = Logger.getLogger(SearchOrganizationsUseCase.class);
private final OrganizationRepository organizationRepository; private final OrganizationRepository organizationRepository;
public SearchOrganizationsUseCase(OrganizationRepository organizationRepository) { public SearchOrganizationsUseCase(OrganizationRepository organizationRepository) {
@ -12,8 +14,10 @@ public class SearchOrganizationsUseCase {
} }
public List<SearchOrganizationOutput> execute(String query) { public List<SearchOrganizationOutput> execute(String query) {
return organizationRepository.search(query).stream() List<SearchOrganizationOutput> organizations = organizationRepository.search(query).stream()
.map(org -> new SearchOrganizationOutput(org.getId(), org.getName())) .map(org -> new SearchOrganizationOutput(org.getId(), org.getName()))
.toList(); .toList();
LOGGER.infof("Organization search for query '%s' returned %d results", query, organizations.size());
return organizations;
} }
} }

View file

@ -16,8 +16,10 @@ import net.beetechgroup.beetask.entities.task.TaskHistoryItem;
import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase; import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
import org.jboss.logging.Logger;
public class OrgDashboardUseCase { public class OrgDashboardUseCase {
private static final Logger LOGGER = Logger.getLogger(OrgDashboardUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
@ -32,6 +34,8 @@ public class OrgDashboardUseCase {
} }
public OrgDashboardOutput execute(OrgDashboardInput input) { public OrgDashboardOutput execute(OrgDashboardInput input) {
LOGGER.infof("Calculating organization dashboard for org %d requested by %s in period %s to %s",
input.organizationId(), input.userEmail(), input.startDate(), input.endDate());
authorizer.execute(input.userEmail(), input.organizationId()); authorizer.execute(input.userEmail(), input.organizationId());
List<UserOrganization> members = userOrganizationRepository.findByOrganizationIdAndStatus( List<UserOrganization> members = userOrganizationRepository.findByOrganizationIdAndStatus(
@ -98,11 +102,14 @@ public class OrgDashboardUseCase {
.sorted(Comparator.comparingLong(OrgMemberStats::totalMinutesWorked).reversed()) .sorted(Comparator.comparingLong(OrgMemberStats::totalMinutesWorked).reversed())
.toList(); .toList();
return new OrgDashboardOutput( OrgDashboardOutput output = new OrgDashboardOutput(
new ArrayList<>(projectStatsMap.values()), new ArrayList<>(projectStatsMap.values()),
topTasks, topTasks,
memberStats memberStats
); );
LOGGER.infof("Organization dashboard calculated for org %d with %d members, %d worked tasks and %d finished tasks",
input.organizationId(), members.size(), workedTasks.size(), finishedTasks.size());
return output;
} }
private long calculateMinutesInPeriod(Task task, LocalDateTime start, LocalDateTime end) { private long calculateMinutesInPeriod(Task task, LocalDateTime start, LocalDateTime end) {

View file

@ -17,8 +17,10 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import org.jboss.logging.Logger;
public class MemberDetailUseCase { public class MemberDetailUseCase {
private static final Logger LOGGER = Logger.getLogger(MemberDetailUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
private final UserOrganizationRepository userOrganizationRepository; private final UserOrganizationRepository userOrganizationRepository;
@ -33,11 +35,17 @@ public class MemberDetailUseCase {
} }
public MemberDetailOutput execute(MemberDetailInput input) { public MemberDetailOutput execute(MemberDetailInput input) {
LOGGER.infof("Calculating member detail for member %d in organization %d requested by %s from %s to %s",
input.memberId(), input.organizationId(), input.userEmail(), input.startDate(), input.endDate());
authorizer.execute(input.userEmail(), input.organizationId()); authorizer.execute(input.userEmail(), input.organizationId());
UserOrganization member = userOrganizationRepository UserOrganization member = userOrganizationRepository
.findByUserAndOrganization(input.memberId(), input.organizationId()) .findByUserAndOrganization(input.memberId(), input.organizationId())
.orElseThrow(() -> new IllegalArgumentException("Member not found in organization")); .orElseThrow(() -> {
LOGGER.warnf("Member detail failed because member %d was not found in organization %d",
input.memberId(), input.organizationId());
return new IllegalArgumentException("Member not found in organization");
});
LocalDateTime start = input.startDate(); LocalDateTime start = input.startDate();
LocalDateTime end = input.endDate(); LocalDateTime end = input.endDate();
@ -114,8 +122,11 @@ public class MemberDetailUseCase {
} }
} }
return MemberDetailMapper.toOutput(member, groupByDay ? "DAY" : "MONTH", periodStats, MemberDetailOutput output = MemberDetailMapper.toOutput(member, groupByDay ? "DAY" : "MONTH", periodStats,
new ArrayList<>(projectStatsMap.values())); new ArrayList<>(projectStatsMap.values()));
LOGGER.infof("Member detail calculated for member %d in organization %d with %d worked tasks and %d finished tasks",
input.memberId(), input.organizationId(), workedTasks.size(), finishedTasks.size());
return output;
} }
private boolean isWithinPeriod(TaskHistoryItem item, LocalDateTime start, LocalDateTime end) { private boolean isWithinPeriod(TaskHistoryItem item, LocalDateTime start, LocalDateTime end) {

View file

@ -4,8 +4,10 @@ import net.beetechgroup.beetask.entities.Project;
import net.beetechgroup.beetask.entities.organization.Organization; import net.beetechgroup.beetask.entities.organization.Organization;
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository;
import org.jboss.logging.Logger;
public class CreateProjectUseCase { public class CreateProjectUseCase {
private static final Logger LOGGER = Logger.getLogger(CreateProjectUseCase.class);
private final ProjectRepository projectRepository; private final ProjectRepository projectRepository;
private final OrganizationRepository organizationRepository; private final OrganizationRepository organizationRepository;
@ -16,12 +18,18 @@ public class CreateProjectUseCase {
public Project execute(Long organizationId, String projectName) { public Project execute(Long organizationId, String projectName) {
Organization organization = organizationRepository.findOrganizationById(organizationId) Organization organization = organizationRepository.findOrganizationById(organizationId)
.orElseThrow(() -> new RuntimeException("Organization not found")); .orElseThrow(() -> {
LOGGER.warnf("Project creation failed because organization %d was not found", organizationId);
return new RuntimeException("Organization not found");
});
LOGGER.infof("Creating project '%s' in organization %d", projectName, organizationId);
Project project = new Project(); Project project = new Project();
project.setName(projectName); project.setName(projectName);
project.setOrganization(organization); project.setOrganization(organization);
return projectRepository.saveProject(project); Project savedProject = projectRepository.saveProject(project);
LOGGER.infof("Project %d created in organization %d", savedProject.getId(), organizationId);
return savedProject;
} }
} }

View file

@ -4,8 +4,10 @@ import net.beetechgroup.beetask.entities.Project;
import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger;
public class ListProjectsUseCase { public class ListProjectsUseCase {
private static final Logger LOGGER = Logger.getLogger(ListProjectsUseCase.class);
private final ProjectRepository projectRepository; private final ProjectRepository projectRepository;
public ListProjectsUseCase(ProjectRepository projectRepository) { public ListProjectsUseCase(ProjectRepository projectRepository) {
@ -13,6 +15,8 @@ public class ListProjectsUseCase {
} }
public List<Project> execute(Long organizationId) { public List<Project> execute(Long organizationId) {
return projectRepository.findByOrganizationId(organizationId); List<Project> projects = projectRepository.findByOrganizationId(organizationId);
LOGGER.infof("Loaded %d projects for organization %d", projects.size(), organizationId);
return projects;
} }
} }

View file

@ -6,8 +6,10 @@ import net.beetechgroup.beetask.entities.task.Task;
import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import org.jboss.logging.Logger;
public class CreateTaskUseCase { public class CreateTaskUseCase {
private static final Logger LOGGER = Logger.getLogger(CreateTaskUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
private final ProjectRepository projectRepository; private final ProjectRepository projectRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
@ -19,6 +21,7 @@ public class CreateTaskUseCase {
} }
public CreateTaskOutput execute(CreateTaskInput input) { public CreateTaskOutput execute(CreateTaskInput input) {
LOGGER.infof("Creating task '%s' for user %s", input.title(), input.userEmail());
Task task = new Task(); Task task = new Task();
task.setTitle(input.title()); task.setTitle(input.title());
task.setDescription(input.description()); task.setDescription(input.description());
@ -26,13 +29,21 @@ public class CreateTaskUseCase {
if (Objects.nonNull(input.projectId())) { if (Objects.nonNull(input.projectId())) {
Project project = projectRepository.findProjectById(input.projectId()) Project project = projectRepository.findProjectById(input.projectId())
.orElseThrow(() -> new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId())); .orElseThrow(() -> {
LOGGER.warnf("Task creation failed because project %d was not found", input.projectId());
return new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId());
});
task.setProject(project); task.setProject(project);
} }
task.setUser(userRepository.findByEmail(input.userEmail()) task.setUser(userRepository.findByEmail(input.userEmail())
.orElseThrow(() -> new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()))); .orElseThrow(() -> {
LOGGER.warnf("Task creation failed because user %s was not found", input.userEmail());
return new IllegalArgumentException("Usuário não encontrado: " + input.userEmail());
}));
return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task));
LOGGER.infof("Task %d created for user %s", output.id(), input.userEmail());
return output;
} }
} }

View file

@ -3,8 +3,10 @@ package net.beetechgroup.beetask.usecase.task.get;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
public class GetTaskUseCase { public class GetTaskUseCase {
private static final Logger LOGGER = Logger.getLogger(GetTaskUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
public GetTaskUseCase(TaskRepository taskRepository) { public GetTaskUseCase(TaskRepository taskRepository) {
@ -13,6 +15,7 @@ public class GetTaskUseCase {
public CreateTaskOutput execute(Long id) { public CreateTaskOutput execute(Long id) {
//TODO deve validar se o usuario logado é o dono da task //TODO deve validar se o usuario logado é o dono da task
LOGGER.infof("Fetching task %d", id);
return CreateTaskMapper.toCreateTaskOutput(taskRepository.findTaskById(id)); return CreateTaskMapper.toCreateTaskOutput(taskRepository.findTaskById(id));
} }
} }

View file

@ -3,10 +3,12 @@ package net.beetechgroup.beetask.usecase.task.listall;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
public class ListAllTasksUseCase { public class ListAllTasksUseCase {
private static final Logger LOGGER = Logger.getLogger(ListAllTasksUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
@ -15,6 +17,8 @@ public class ListAllTasksUseCase {
} }
public List<CreateTaskOutput> execute() { public List<CreateTaskOutput> execute() {
return taskRepository.findAllTasks().stream().map(CreateTaskMapper::toCreateTaskOutput).toList(); List<CreateTaskOutput> tasks = taskRepository.findAllTasks().stream().map(CreateTaskMapper::toCreateTaskOutput).toList();
LOGGER.infof("Loaded %d tasks from repository", tasks.size());
return tasks;
} }
} }

View file

@ -3,10 +3,12 @@ package net.beetechgroup.beetask.usecase.task.listall;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
public class ListMyTasksUseCase { public class ListMyTasksUseCase {
private static final Logger LOGGER = Logger.getLogger(ListMyTasksUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
@ -15,6 +17,8 @@ public class ListMyTasksUseCase {
} }
public List<CreateTaskOutput> execute(String email) { public List<CreateTaskOutput> execute(String email) {
return taskRepository.findTasksByUser(email).stream().map(CreateTaskMapper::toCreateTaskOutput).toList(); List<CreateTaskOutput> tasks = taskRepository.findTasksByUser(email).stream().map(CreateTaskMapper::toCreateTaskOutput).toList();
LOGGER.infof("Loaded %d tasks for user %s", tasks.size(), email);
return tasks;
} }
} }

View file

@ -6,8 +6,10 @@ import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
public class StartTaskUseCase { public class StartTaskUseCase {
private static final Logger LOGGER = Logger.getLogger(StartTaskUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
@ -18,13 +20,18 @@ public class StartTaskUseCase {
} }
public CreateTaskOutput execute(StartTaskInput input) { public CreateTaskOutput execute(StartTaskInput input) {
LOGGER.infof("Starting task %d for user %s", input.id(), input.userEmail());
Task task = taskRepository.findTaskById(input.id()); Task task = taskRepository.findTaskById(input.id());
if (Objects.nonNull(task.getUser()) && !task.getUser().getEmail().equals(input.userEmail())) { if (Objects.nonNull(task.getUser()) && !task.getUser().getEmail().equals(input.userEmail())) {
LOGGER.warnf("User %s attempted to start task %d owned by %s",
input.userEmail(), input.id(), task.getUser().getEmail());
throw new SecurityException("Você não tem permissão para iniciar esta tarefa."); throw new SecurityException("Você não tem permissão para iniciar esta tarefa.");
} }
task.start(); task.start();
return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task));
LOGGER.infof("Task %d started successfully", input.id());
return output;
} }
} }

View file

@ -4,8 +4,10 @@ import net.beetechgroup.beetask.entities.task.Task;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
public class StopTaskUseCase { public class StopTaskUseCase {
private static final Logger LOGGER = Logger.getLogger(StopTaskUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
@ -14,8 +16,11 @@ public class StopTaskUseCase {
} }
public CreateTaskOutput execute(StopTaskInput input) { public CreateTaskOutput execute(StopTaskInput input) {
LOGGER.infof("Stopping task %d", input.id());
Task task = taskRepository.findTaskById(input.id()); Task task = taskRepository.findTaskById(input.id());
task.stop(); task.stop();
return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task));
LOGGER.infof("Task %d stopped successfully", input.id());
return output;
} }
} }

View file

@ -7,8 +7,10 @@ import net.beetechgroup.beetask.entities.task.TaskStatus;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
public class UpdateTaskStatusUseCase { public class UpdateTaskStatusUseCase {
private static final Logger LOGGER = Logger.getLogger(UpdateTaskStatusUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
@ -17,10 +19,13 @@ public class UpdateTaskStatusUseCase {
} }
public CreateTaskOutput execute(Long id, TaskStatus newStatus) { public CreateTaskOutput execute(Long id, TaskStatus newStatus) {
LOGGER.infof("Updating status for task %d to %s", id, newStatus);
Task task = taskRepository.findTaskById(id); Task task = taskRepository.findTaskById(id);
task.setStatus(newStatus); task.setStatus(newStatus);
return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task));
LOGGER.infof("Status updated for task %d to %s", id, newStatus);
return output;
} }
} }

View file

@ -10,8 +10,10 @@ import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput;
import org.jboss.logging.Logger;
public class UpdateTaskUseCase { public class UpdateTaskUseCase {
private static final Logger LOGGER = Logger.getLogger(UpdateTaskUseCase.class);
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
private final ProjectRepository projectRepository; private final ProjectRepository projectRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
@ -23,11 +25,15 @@ public class UpdateTaskUseCase {
} }
public CreateTaskOutput execute(UpdateTaskInput input) { public CreateTaskOutput execute(UpdateTaskInput input) {
LOGGER.infof("Updating task %d requested by user %s", input.id(), input.userEmail());
Task task = taskRepository.findTaskById(input.id()); Task task = taskRepository.findTaskById(input.id());
if (Objects.isNull(task.getUser())) { if (Objects.isNull(task.getUser())) {
task.setUser(userRepository.findByEmail(input.userEmail()) task.setUser(userRepository.findByEmail(input.userEmail())
.orElseThrow(() -> new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()))); .orElseThrow(() -> {
LOGGER.warnf("Task update failed because user %s was not found", input.userEmail());
return new IllegalArgumentException("Usuário não encontrado: " + input.userEmail());
}));
} }
task.setTitle(input.title()); task.setTitle(input.title());
@ -36,7 +42,10 @@ public class UpdateTaskUseCase {
if (Objects.nonNull(input.projectId())) { if (Objects.nonNull(input.projectId())) {
Project project = projectRepository.findProjectById(input.projectId()) Project project = projectRepository.findProjectById(input.projectId())
.orElseThrow(() -> new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId())); .orElseThrow(() -> {
LOGGER.warnf("Task update failed because project %d was not found", input.projectId());
return new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId());
});
task.setProject(project); task.setProject(project);
} else { } else {
task.setProject(null); task.setProject(null);
@ -69,8 +78,11 @@ public class UpdateTaskUseCase {
task.getHistory().clear(); task.getHistory().clear();
task.getHistory().addAll(updatedHistory); task.getHistory().addAll(updatedHistory);
LOGGER.infof("Task %d history updated with %d entries", input.id(), updatedHistory.size());
} }
return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task));
LOGGER.infof("Task %d updated successfully", input.id());
return output;
} }
} }

View file

@ -2,8 +2,10 @@ package net.beetechgroup.beetask.usecase.user.create;
import net.beetechgroup.beetask.entities.User; import net.beetechgroup.beetask.entities.User;
import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository;
import org.jboss.logging.Logger;
public class CreateUserUseCase { public class CreateUserUseCase {
private static final Logger LOGGER = Logger.getLogger(CreateUserUseCase.class);
private final UserRepository userRepository; private final UserRepository userRepository;
@ -13,11 +15,14 @@ public class CreateUserUseCase {
public CreateUserOutput execute(CreateUserInput input) { public CreateUserOutput execute(CreateUserInput input) {
if (userRepository.findByEmail(input.email()).isPresent()) { if (userRepository.findByEmail(input.email()).isPresent()) {
LOGGER.warnf("User creation rejected because email %s already exists", input.email());
throw new RuntimeException("User already exists with this email"); throw new RuntimeException("User already exists with this email");
} }
LOGGER.infof("Creating user with email %s", input.email());
User user = CreateUserMapper.toUser(input); User user = CreateUserMapper.toUser(input);
User savedUser = userRepository.save(user); User savedUser = userRepository.save(user);
LOGGER.infof("User %d created successfully with email %s", savedUser.getId(), savedUser.getEmail());
return CreateUserMapper.toCreateUserOutput(savedUser); return CreateUserMapper.toCreateUserOutput(savedUser);
} }

View file

@ -8,8 +8,10 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jboss.logging.Logger;
public class GetUserProfileUseCase { public class GetUserProfileUseCase {
private static final Logger LOGGER = Logger.getLogger(GetUserProfileUseCase.class);
private final UserRepository userRepository; private final UserRepository userRepository;
@ -21,11 +23,13 @@ public class GetUserProfileUseCase {
Optional<User> userOptional = userRepository.findByEmail(email); Optional<User> userOptional = userRepository.findByEmail(email);
if (userOptional.isEmpty()) { if (userOptional.isEmpty()) {
LOGGER.warnf("Profile lookup failed because user %s was not found", email);
throw new RuntimeException("User not found"); throw new RuntimeException("User not found");
} }
User user = userOptional.get(); User user = userOptional.get();
List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId()); List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId());
LOGGER.infof("Loaded profile for user %s with %d active organizations", email, userOrganizations.size());
List<UserProfileOutput.OrganizationProfileOutput> orgs = userOrganizations.stream() List<UserProfileOutput.OrganizationProfileOutput> orgs = userOrganizations.stream()
.collect(Collectors.groupingBy(uo -> uo.getOrganization().getId())) .collect(Collectors.groupingBy(uo -> uo.getOrganization().getId()))

2166
frontend/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff