diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 067a53d..5950fd2 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,16 +1,12 @@ package org.example import kotlinx.coroutines.runBlocking -import logic.usecase.auth.CreateDefaultAdminUseCase -import org.example.data.DataProvider import org.example.di.appModule import org.example.di.mongoModule import org.example.di.uiModule import org.example.di.useCaseModule -import org.example.entity.UserEntity import org.example.presentation.PlanMateConsoleUI import org.koin.core.context.startKoin -import org.koin.core.qualifier.named import org.koin.java.KoinJavaComponent.getKoin @@ -24,14 +20,7 @@ fun main() { ) } val console: PlanMateConsoleUI = getKoin().get() - val usersData: DataProvider = getKoin().get(named("userDataProviderMongo")) - val createDefaultAdminUseCase = CreateDefaultAdminUseCase(usersData) runBlocking { - try { - createDefaultAdminUseCase.invoke() - } catch (e: Exception) { - e.printStackTrace() - } console.start() } } \ No newline at end of file diff --git a/src/main/kotlin/data/mongo/AuditLogMongoDbImpl.kt b/src/main/kotlin/data/mongo/AuditLogMongoDbImpl.kt index 7fae266..b5e7337 100644 --- a/src/main/kotlin/data/mongo/AuditLogMongoDbImpl.kt +++ b/src/main/kotlin/data/mongo/AuditLogMongoDbImpl.kt @@ -15,13 +15,13 @@ import org.example.utils.PlanMateException import java.util.* class AuditLogMongoDbImpl( - private val mongoDBClient: MongoDBClient + mongoDBClient: MongoDBClient ) : DataProvider { private val auditLogCollection = mongoDBClient.getDatabase().getCollection("audit_log") - override suspend fun add(auditLog: AuditLogEntity) { + override suspend fun add(item: AuditLogEntity) { MongoExceptionHandler.handleOperation("adding audit log") { - val document = toDocument(auditLog) + val document = toDocument(item) auditLogCollection.insertOne(document) } } diff --git a/src/main/kotlin/data/mongo/AuthMongoImpl.kt b/src/main/kotlin/data/mongo/AuthMongoImpl.kt index ae59788..f30f4f1 100644 --- a/src/main/kotlin/data/mongo/AuthMongoImpl.kt +++ b/src/main/kotlin/data/mongo/AuthMongoImpl.kt @@ -10,16 +10,14 @@ import org.example.utils.PlanMateException import java.util.* class AuthMongoImpl( - private val mongoDBClient: MongoDBClient + mongoDBClient: MongoDBClient ) : AuthProvider { private val currentUserCollection = mongoDBClient.getDatabase().getCollection("current_users") override suspend fun addCurrentUser(user: UserEntity) { MongoExceptionHandler.handleOperation("adding current user") { - // First delete any existing current user deleteCurrentUser() - // Then add the new user val document = toDocument(user) currentUserCollection.insertOne(document) } diff --git a/src/main/kotlin/data/mongo/MongoDBClient.kt b/src/main/kotlin/data/mongo/MongoDBClient.kt index c512996..db8eba7 100644 --- a/src/main/kotlin/data/mongo/MongoDBClient.kt +++ b/src/main/kotlin/data/mongo/MongoDBClient.kt @@ -7,10 +7,10 @@ import com.mongodb.kotlin.client.coroutine.MongoDatabase import org.bson.UuidRepresentation class MongoDBClient( - private val username: String, - private val password: String, - private val clusterUrl: String = "cluster0.watzb0c.mongodb.net", - private val databaseName: String = "PlanMate" + username: String, + password: String, + clusterUrl: String = "cluster0.watzb0c.mongodb.net", + databaseName: String = "PlanMate" ) { private val client: MongoClient diff --git a/src/main/kotlin/data/mongo/StateMongoDBImpl.kt b/src/main/kotlin/data/mongo/StateMongoDBImpl.kt index 5c9d055..6fd83a0 100644 --- a/src/main/kotlin/data/mongo/StateMongoDBImpl.kt +++ b/src/main/kotlin/data/mongo/StateMongoDBImpl.kt @@ -32,7 +32,7 @@ class StateMongoDBImpl( } } - override suspend fun getById(id: UUID): StateEntity? { + override suspend fun getById(id: UUID): StateEntity { return MongoExceptionHandler.handleOperation("fetching state by ID") { val filter = Filters.eq("id", id.toString()) val document = collection.find(filter).firstOrNull() diff --git a/src/main/kotlin/data/mongo/UsersMongoImpl.kt b/src/main/kotlin/data/mongo/UsersMongoImpl.kt index 6ac3121..8abfe1d 100644 --- a/src/main/kotlin/data/mongo/UsersMongoImpl.kt +++ b/src/main/kotlin/data/mongo/UsersMongoImpl.kt @@ -2,7 +2,7 @@ package org.example.data.mongo import com.mongodb.client.model.Filters import com.mongodb.client.model.Updates -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import org.bson.Document @@ -14,7 +14,7 @@ import org.example.utils.PlanMateException import java.util.* class UsersMongoImpl( - private val mongoDBClient: MongoDBClient + mongoDBClient: MongoDBClient ) : DataProvider { private val usersCollection = mongoDBClient.getDatabase().getCollection("users") @@ -33,10 +33,10 @@ class UsersMongoImpl( } } - override suspend fun getById(id: UUID): UserEntity? { + override suspend fun getById(id: UUID): UserEntity { return MongoExceptionHandler.handleOperation("fetching user by ID") { val filter = Filters.eq("id", id.toString()) - val document = usersCollection.find(filter).first() + val document = usersCollection.find(filter).firstOrNull() document?.let { fromDocument(it) } ?: throw PlanMateException.ItemNotFoundException("User not found with id: $id") } diff --git a/src/main/kotlin/data/mongo/mongo impl goes here b/src/main/kotlin/data/mongo/mongo impl goes here deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/kotlin/data/repository/AuditLogRepositoryImpl.kt b/src/main/kotlin/data/repository/AuditLogRepositoryImpl.kt index 35ac862..388e929 100644 --- a/src/main/kotlin/data/repository/AuditLogRepositoryImpl.kt +++ b/src/main/kotlin/data/repository/AuditLogRepositoryImpl.kt @@ -13,7 +13,7 @@ class AuditLogRepositoryImpl( override suspend fun addAudit(auditLogEntity: AuditLogEntity) { try { dataProvider.add(auditLogEntity) - }catch (e: PlanMateException.FileWriteException) { + } catch (e: PlanMateException.FileWriteException) { throw PlanMateException.FileWriteException("Failed to add audit log due to invalid state.") } } @@ -21,11 +21,9 @@ class AuditLogRepositoryImpl( override suspend fun getProjectHistory(projectId: UUID): List = getEntityHistory(projectId, AuditedEntityType.PROJECT) - override suspend fun getTaskHistory(taskId: UUID): List = getEntityHistory(taskId, AuditedEntityType.TASK) - private suspend fun getEntityHistory(entityId: UUID, entityType: AuditedEntityType): List { val history = dataProvider.get().filter { it.entityType == entityType && it.entityId == entityId diff --git a/src/main/kotlin/data/repository/AuthenticationRepositoryImpl.kt b/src/main/kotlin/data/repository/AuthenticationRepositoryImpl.kt index d7a6511..9b8029f 100644 --- a/src/main/kotlin/data/repository/AuthenticationRepositoryImpl.kt +++ b/src/main/kotlin/data/repository/AuthenticationRepositoryImpl.kt @@ -6,20 +6,21 @@ import org.example.entity.UserEntity import org.example.logic.repository.AuthenticationRepository import org.example.logic.repository.UserRepository import org.example.utils.PlanMateException +import org.example.utils.hasher.PasswordHasher class AuthenticationRepositoryImpl( private val authenticationProvider: AuthProvider, private val userRepository: UserRepository, - private val dataProvider: DataProvider + private val dataProvider: DataProvider, + private val passwordHasher: PasswordHasher ) : AuthenticationRepository { override suspend fun login(username: String, password: String) { val user = userRepository.getUserByUsername(username) - if (user.password != password) { + if (!isPasswordValid(user, password)) { throw PlanMateException.ValidationException("Password is not correct.") } - authenticationProvider.addCurrentUser(user) } @@ -28,7 +29,6 @@ class AuthenticationRepositoryImpl( userRepository.getUserByUsername(newUser.username) throw PlanMateException.ValidationException("A user with that username already exists.") } catch (e: PlanMateException.ItemNotFoundException) { - // User doesn't exist, we can proceed dataProvider.add(newUser) } } @@ -44,6 +44,14 @@ class AuthenticationRepositoryImpl( null } } -} + private fun isPasswordValid(user: UserEntity, inputPassword: String): Boolean { + return try { + val hashedInput = passwordHasher.hash(inputPassword) + user.password == hashedInput + } catch (e: Exception) { + false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/data/repository/ProjectRepositoryImpl.kt b/src/main/kotlin/data/repository/ProjectRepositoryImpl.kt index 7246db4..26d1bdd 100644 --- a/src/main/kotlin/data/repository/ProjectRepositoryImpl.kt +++ b/src/main/kotlin/data/repository/ProjectRepositoryImpl.kt @@ -1,12 +1,9 @@ package org.example.data.repository -import kotlinx.datetime.Clock -import kotlinx.datetime.TimeZone.Companion.UTC -import kotlinx.datetime.toLocalDateTime import org.example.data.DataProvider +import org.example.data.logAudit import org.example.entity.AuditAction import org.example.entity.AuditLogEntity -import org.example.entity.AuditedEntityType import org.example.entity.ProjectEntity import org.example.logic.repository.ProjectRepository import java.util.* @@ -20,15 +17,12 @@ class ProjectRepositoryImpl( require(project.name.isNotBlank()) { "Project name cannot be blank" } projectDataProvider.add(project) - auditDataProvider.add( - AuditLogEntity( - userId = project.createdByAdminId, - entityType = AuditedEntityType.PROJECT, - entityId = project.id, - action = AuditAction.CREATE, - changeDetails = "Created project: ${project.name}", - timestamp = Clock.System.now().toLocalDateTime(UTC) - ) + logAudit( + userId = project.createdByAdminId, + entityId = project.id, + action = AuditAction.CREATE, + changeDetails = "Created project: ${project.name}", + auditDataProvider ) return project } @@ -36,41 +30,33 @@ class ProjectRepositoryImpl( override suspend fun updateProject(project: ProjectEntity, currentUserId: UUID): ProjectEntity { projectDataProvider.update(project) - auditDataProvider.add( - AuditLogEntity( - userId = currentUserId, - entityType = AuditedEntityType.PROJECT, - entityId = project.id, - action = AuditAction.UPDATE, - changeDetails = "Updated project: ${project.name}", - timestamp = Clock.System.now().toLocalDateTime(UTC) - ) + logAudit( + userId = currentUserId, + entityId = project.id, + action = AuditAction.UPDATE, + changeDetails = "Updated project: ${project.name}", auditDataProvider ) return project } - override suspend fun deleteProject(projectId: UUID, currentUserId: UUID) { val project = projectDataProvider.getById(projectId) ?: throw NoSuchElementException("Project not found") projectDataProvider.delete(projectId) - auditDataProvider.add( - AuditLogEntity( - userId = currentUserId, - entityType = AuditedEntityType.PROJECT, - entityId = projectId, - action = AuditAction.DELETE, - changeDetails = "Deleted project: ${project.name}", - timestamp = Clock.System.now().toLocalDateTime(UTC) - ) + logAudit( + userId = currentUserId, + entityId = projectId, + action = AuditAction.DELETE, + changeDetails = "Deleted project: ${project.name}", + auditDataProvider ) } override suspend fun getAllProjects(): List = projectDataProvider.get() - override suspend fun getProjectById(projectId: String): ProjectEntity { - return projectDataProvider.getById(UUID.fromString(projectId)) + override suspend fun getProjectById(projectId: UUID): ProjectEntity { + return projectDataProvider.getById(projectId) ?: throw NoSuchElementException("Project not found") } } \ No newline at end of file diff --git a/src/main/kotlin/data/repository/TaskRepositoryImpl.kt b/src/main/kotlin/data/repository/TaskRepositoryImpl.kt index bc9e818..e0447e0 100644 --- a/src/main/kotlin/data/repository/TaskRepositoryImpl.kt +++ b/src/main/kotlin/data/repository/TaskRepositoryImpl.kt @@ -57,10 +57,10 @@ class TaskRepositoryImpl( dataProvider.getById(id) ?: throw PlanMateException.ItemNotFoundException("Task $id not found") - override suspend fun getTasksByProjectId(projectId: UUID): List = - dataProvider.get().filter { it.projectId == projectId } + override suspend fun getTasksByProjectId(id: UUID): List = + dataProvider.get().filter { it.projectId == id } .takeIf { it.isNotEmpty() } - ?: throw PlanMateException.ItemNotFoundException("Project $projectId not found") + ?: throw PlanMateException.ItemNotFoundException("Project $id not found") private suspend fun generateUpdateDetails( @@ -99,7 +99,6 @@ class TaskRepositoryImpl( ) } - private suspend fun audit( userId: UUID, entityId: UUID, @@ -114,5 +113,4 @@ class TaskRepositoryImpl( changeDetails = details ) ) -} - +} \ No newline at end of file diff --git a/src/main/kotlin/data/utils.kt b/src/main/kotlin/data/utils.kt new file mode 100644 index 0000000..7297841 --- /dev/null +++ b/src/main/kotlin/data/utils.kt @@ -0,0 +1,28 @@ +package org.example.data + +import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone.Companion.UTC +import kotlinx.datetime.toLocalDateTime +import org.example.entity.AuditAction +import org.example.entity.AuditLogEntity +import org.example.entity.AuditedEntityType +import java.util.* + +suspend fun logAudit( + userId: UUID, + entityId: UUID, + action: AuditAction, + changeDetails: String, + auditDataProvider:DataProvider +) { + auditDataProvider.add( + AuditLogEntity( + userId = userId, + entityType = AuditedEntityType.PROJECT, + entityId = entityId, + action = action, + changeDetails = changeDetails, + timestamp = Clock.System.now().toLocalDateTime(UTC) + ) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/di/AppModule.kt b/src/main/kotlin/di/AppModule.kt index efbdcbb..3ef4658 100644 --- a/src/main/kotlin/di/AppModule.kt +++ b/src/main/kotlin/di/AppModule.kt @@ -8,10 +8,14 @@ import org.example.data.mongo.* import org.example.data.repository.* import org.example.entity.* import org.example.logic.repository.* +import org.example.utils.hasher.PasswordHasher +import org.example.utils.hasher.PasswordMD5HasherImpl import org.koin.core.qualifier.named import org.koin.dsl.module val appModule = module { + single { PasswordMD5HasherImpl() } + single(named("projects")) { "projects.csv" } single(named("states")) { "states.csv" } single(named("tasks")) { "tasks.csv" } @@ -39,7 +43,8 @@ val appModule = module { AuthenticationRepositoryImpl( get(named("authProviderMongo")), get(), - get(qualifier = named("userDataProviderMongo")) + get(qualifier = named("userDataProviderMongo")), + get() ) } single { diff --git a/src/main/kotlin/di/UseCaseModule.kt b/src/main/kotlin/di/UseCaseModule.kt index 8be6315..f2e05df 100644 --- a/src/main/kotlin/di/UseCaseModule.kt +++ b/src/main/kotlin/di/UseCaseModule.kt @@ -1,7 +1,5 @@ package org.example.di -import logic.usecase.auth.CreateDefaultAdminUseCase -import org.example.logic.usecase.audit.AddAuditLogUseCase import org.example.logic.usecase.audit.GetAuditLogUseCase import org.example.logic.usecase.auth.GetCurrentUserUseCase import org.example.logic.usecase.auth.LoginUseCase @@ -23,7 +21,6 @@ val useCaseModule = module { single { GetUserByIdUseCase(get()) } single { GetUserByUsernameUseCase(get()) } single { GetAuditLogUseCase(get()) } - single { AddAuditLogUseCase(get()) } single { AddTaskUseCase(get()) } single { DeleteTaskUseCase(get()) } single { GetTaskByIdUseCase(get()) } @@ -42,5 +39,4 @@ val useCaseModule = module { single { UpdateStateUseCase(get()) } single { AddStateUseCase(get()) } single { DeleteStateUseCase(get()) } - single { CreateDefaultAdminUseCase(get()) } } \ No newline at end of file diff --git a/src/main/kotlin/logic/repository/ProjectRepository.kt b/src/main/kotlin/logic/repository/ProjectRepository.kt index bc19b75..1816f9a 100644 --- a/src/main/kotlin/logic/repository/ProjectRepository.kt +++ b/src/main/kotlin/logic/repository/ProjectRepository.kt @@ -13,5 +13,5 @@ interface ProjectRepository { suspend fun getAllProjects(): List - suspend fun getProjectById(projectId: String): ProjectEntity + suspend fun getProjectById(projectId: UUID): ProjectEntity } \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/audit/AddAuditLogUseCase.kt b/src/main/kotlin/logic/usecase/audit/AddAuditLogUseCase.kt deleted file mode 100644 index 6df3ec2..0000000 --- a/src/main/kotlin/logic/usecase/audit/AddAuditLogUseCase.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.example.logic.usecase.audit - -import org.example.entity.AuditLogEntity -import org.example.logic.repository.AuditLogRepository -import org.example.utils.PlanMateException - -class AddAuditLogUseCase( - private val auditLogRepository: AuditLogRepository -) { - suspend operator fun invoke(auditLogEntity: AuditLogEntity): Boolean { - return try { - auditLogRepository.addAudit(auditLogEntity) - return true - } catch (e: PlanMateException.FileWriteException) { - return false - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/audit/GetAuditLogUseCase.kt b/src/main/kotlin/logic/usecase/audit/GetAuditLogUseCase.kt index 075da7a..ff44549 100644 --- a/src/main/kotlin/logic/usecase/audit/GetAuditLogUseCase.kt +++ b/src/main/kotlin/logic/usecase/audit/GetAuditLogUseCase.kt @@ -3,25 +3,13 @@ package org.example.logic.usecase.audit import org.example.entity.AuditLogEntity import org.example.entity.AuditedEntityType import org.example.logic.repository.AuditLogRepository -import org.example.utils.PlanMateException.InvalidStateIdException -import java.util.* +import java.util.UUID class GetAuditLogUseCase( private val auditLogRepository: AuditLogRepository ) { - - suspend operator fun invoke(id: UUID, entityType: AuditedEntityType): List { - return getAuditLogs(id,entityType) - } - - private suspend fun getAuditLogs(id: UUID, entityType: AuditedEntityType): List { - return try { - when (entityType) { - AuditedEntityType.PROJECT -> (auditLogRepository.getProjectHistory(id)) - AuditedEntityType.TASK -> (auditLogRepository.getTaskHistory(id)) - } - } catch (e: InvalidStateIdException) { - return emptyList() - } + suspend operator fun invoke(id: UUID, entityType: AuditedEntityType): List = when (entityType) { + AuditedEntityType.PROJECT -> (auditLogRepository.getProjectHistory(id)) + AuditedEntityType.TASK -> (auditLogRepository.getTaskHistory(id)) } } \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/auth/CreateDefaultAdminUseCase.kt b/src/main/kotlin/logic/usecase/auth/CreateDefaultAdminUseCase.kt deleted file mode 100644 index 0d97518..0000000 --- a/src/main/kotlin/logic/usecase/auth/CreateDefaultAdminUseCase.kt +++ /dev/null @@ -1,39 +0,0 @@ -package logic.usecase.auth - -import org.example.data.DataProvider -import org.example.entity.UserEntity -import org.example.entity.UserType -import java.util.* - -class CreateDefaultAdminUseCase( - private val userRepository: DataProvider -) { - suspend operator fun invoke() { - try { - // Check if admin already exists - val existingAdmin = userRepository.get().find { it.type == UserType.ADMIN } - - if (existingAdmin != null) { - println("Admin user already exists") - return - } - - // Create new admin if none exists - val adminUser = UserEntity( - id = UUID.randomUUID(), - username = "admin", - password = "admin123", // You should hash this password - type = UserType.ADMIN, - ) - - userRepository.add(adminUser) - println("Admin user created successfully") - - - } catch (e: Exception) { - println("Failed to initialize admin user: ${e.message}") - throw e - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/auth/LoginUseCase.kt b/src/main/kotlin/logic/usecase/auth/LoginUseCase.kt index 25109dd..fa021e4 100644 --- a/src/main/kotlin/logic/usecase/auth/LoginUseCase.kt +++ b/src/main/kotlin/logic/usecase/auth/LoginUseCase.kt @@ -1,6 +1,5 @@ package org.example.logic.usecase.auth -import org.example.entity.UserEntity import org.example.logic.repository.AuthenticationRepository import org.example.utils.PlanMateException @@ -8,9 +7,7 @@ class LoginUseCase( private val authRepository: AuthenticationRepository ) { suspend operator fun invoke(username: String, password: String) { - if (username.isBlank()) { - throw PlanMateException.ValidationException("Username cannot be empty") - } + if (username.isBlank()) throw PlanMateException.ValidationException("Username cannot be empty") authRepository.login(username, password) } diff --git a/src/main/kotlin/logic/usecase/auth/RegisterUseCase.kt b/src/main/kotlin/logic/usecase/auth/RegisterUseCase.kt index 5890d0b..f463dcd 100644 --- a/src/main/kotlin/logic/usecase/auth/RegisterUseCase.kt +++ b/src/main/kotlin/logic/usecase/auth/RegisterUseCase.kt @@ -13,9 +13,7 @@ class RegisterUseCase( throw PlanMateException.UserActionNotAllowedException("MATE users cannot create new users.") } - if (newUser.username.isBlank()) { - throw PlanMateException.ValidationException("Username cannot be empty") - } + if (newUser.username.isBlank()) throw PlanMateException.ValidationException("Username cannot be empty") authRepository.register(newUser, currentUser) } diff --git a/src/main/kotlin/logic/usecase/project/AddProjectUseCase.kt b/src/main/kotlin/logic/usecase/project/AddProjectUseCase.kt index c3da565..68cd7db 100644 --- a/src/main/kotlin/logic/usecase/project/AddProjectUseCase.kt +++ b/src/main/kotlin/logic/usecase/project/AddProjectUseCase.kt @@ -13,6 +13,7 @@ class AddProjectUseCase( projectEntity: ProjectEntity, currentUser: UserEntity ): ProjectEntity { + if (currentUser.type != UserType.ADMIN) { throw PlanMateException.UserActionNotAllowedException( "User ${currentUser.id} is not authorized to create projects" diff --git a/src/main/kotlin/logic/usecase/project/DeleteProjectUseCase.kt b/src/main/kotlin/logic/usecase/project/DeleteProjectUseCase.kt index 39416e7..7cbeda3 100644 --- a/src/main/kotlin/logic/usecase/project/DeleteProjectUseCase.kt +++ b/src/main/kotlin/logic/usecase/project/DeleteProjectUseCase.kt @@ -10,7 +10,7 @@ class DeleteProjectUseCase( projectId: UUID, currentUser: UUID ) { - projectRepository.getProjectById(projectId.toString()) + projectRepository.getProjectById(projectId) projectRepository.deleteProject(projectId, currentUser) } } \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/project/GetProjectUseCase.kt b/src/main/kotlin/logic/usecase/project/GetProjectUseCase.kt index d0555ef..a75f1e0 100644 --- a/src/main/kotlin/logic/usecase/project/GetProjectUseCase.kt +++ b/src/main/kotlin/logic/usecase/project/GetProjectUseCase.kt @@ -8,6 +8,6 @@ class GetProjectUseCase( private val projectRepository: ProjectRepository ) { suspend operator fun invoke(projectId: UUID): ProjectEntity { - return projectRepository.getProjectById(projectId.toString()) + return projectRepository.getProjectById(projectId) } } \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/user/GetUserByUsernameUseCase.kt b/src/main/kotlin/logic/usecase/user/GetUserByUsernameUseCase.kt index 29082d9..6942276 100644 --- a/src/main/kotlin/logic/usecase/user/GetUserByUsernameUseCase.kt +++ b/src/main/kotlin/logic/usecase/user/GetUserByUsernameUseCase.kt @@ -8,11 +8,9 @@ class GetUserByUsernameUseCase( private val userRepository: UserRepository ) { suspend operator fun invoke(username: String): UserEntity { - // Input validation if (username.isBlank()) { throw PlanMateException.ValidationException("Username cannot be empty") } - return userRepository.getUserByUsername(username) } } \ No newline at end of file diff --git a/src/main/kotlin/logic/usecase/user/UpdateUserUseCase.kt b/src/main/kotlin/logic/usecase/user/UpdateUserUseCase.kt index 38f5e6e..1831ada 100644 --- a/src/main/kotlin/logic/usecase/user/UpdateUserUseCase.kt +++ b/src/main/kotlin/logic/usecase/user/UpdateUserUseCase.kt @@ -9,23 +9,20 @@ class UpdateUserUseCase( private val userRepository: UserRepository ) { suspend operator fun invoke(user: UserEntity, currentUser: UserEntity) { - // Authorization check + if (currentUser.type != UserType.ADMIN) { throw PlanMateException.UserActionNotAllowedException( "${currentUser.type} users are not allowed to update users" ) } - // Data validation - if (user.username.isBlank()) { - throw PlanMateException.ValidationException("Username cannot be empty") - } + validationInput(user) + userRepository.update(user) + } - // Additional validations as needed - if (user.password.isBlank()) { - throw PlanMateException.ValidationException("Password cannot be empty") - } + private fun validationInput(user: UserEntity) { + if (user.username.isBlank()) throw PlanMateException.ValidationException("Username cannot be empty") - userRepository.update(user) + if (user.password.isBlank()) throw PlanMateException.ValidationException("Password cannot be empty") } } \ No newline at end of file diff --git a/src/main/kotlin/utils/PlanMateException.kt b/src/main/kotlin/utils/PlanMateException.kt index dc62f26..d7e3e63 100644 --- a/src/main/kotlin/utils/PlanMateException.kt +++ b/src/main/kotlin/utils/PlanMateException.kt @@ -12,10 +12,6 @@ open class PlanMateException(message: String) : Exception(message) { class ValidationException(message: String = "Validation failed.") : PlanMateException(message) - class DatabaseException(message: String = "Error adding project") : PlanMateException(message) - - class AuthenticationException(message: String = "Problem in User/Password input") : PlanMateException(message) - class HashingException(message: String = "Failed to hash") : PlanMateException(message) class UserActionNotAllowedException( @@ -25,11 +21,6 @@ open class PlanMateException(message: String) : Exception(message) { class InvalidStateIdException(message: String = "Invalid state id, no audit logs found.") : PlanMateException(message) - // MongoDB Exceptions - class DatabaseConnectionException( - message: String = "Failed to connect to database." - ) : PlanMateException(message) - class DatabaseOperationException( message: String = "Database operation failed." ) : PlanMateException(message) @@ -45,8 +36,4 @@ open class PlanMateException(message: String) : Exception(message) { class DatabaseAuthenticationException( message: String = "Database authentication failed." ) : PlanMateException(message) - - class DatabaseTransactionException( - message: String = "Database transaction failed." - ) : PlanMateException(message) } \ No newline at end of file diff --git a/src/test/kotlin/logic/usecase/audit/AddAuditLogUseCaseTest.kt b/src/test/kotlin/logic/usecase/audit/AddAuditLogUseCaseTest.kt deleted file mode 100644 index 5b4ee5a..0000000 --- a/src/test/kotlin/logic/usecase/audit/AddAuditLogUseCaseTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package logic.usecase.audit - -import com.google.common.truth.Truth.assertThat -import fakeData.createAuditLogEntity -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.test.runTest -import org.example.entity.AuditAction -import org.example.entity.AuditedEntityType -import org.example.logic.repository.AuditLogRepository -import org.example.logic.usecase.audit.AddAuditLogUseCase -import org.example.utils.PlanMateException.FileWriteException -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class AddAuditLogUseCaseTest { - private lateinit var addAuditLogUseCase: AddAuditLogUseCase - private val auditLogRepository: AuditLogRepository = mockk(relaxed = true) - - @BeforeEach - fun setUp() { - addAuditLogUseCase = AddAuditLogUseCase(auditLogRepository) - } - - @Test - fun `should call repository to add audit log and return true on success`() = runTest{ - // Given - val auditLogEntity = createAuditLogEntity( - entityType = AuditedEntityType.TASK, - action = AuditAction.CREATE, - changeDetails = "Task created" - ) - // When - coEvery { auditLogRepository.addAudit(auditLogEntity) } returns Unit - val result = addAuditLogUseCase.invoke(auditLogEntity) - - // Then - coVerify(exactly = 1) { auditLogRepository.addAudit(auditLogEntity) } - assertThat(result).isTrue() - } - - @Test - fun `should call repository and return false when FileWriteException is thrown by repository`() = runTest{ - // Given - val auditLogEntity = createAuditLogEntity( - entityType = AuditedEntityType.PROJECT, - action = AuditAction.UPDATE, - changeDetails = "Project updated" - ) - val exception = FileWriteException("Error writing audit log to file.") - coEvery { auditLogRepository.addAudit(auditLogEntity) } throws exception - - // When - val result = addAuditLogUseCase.invoke(auditLogEntity) - - // Then - coVerify(exactly = 1) { auditLogRepository.addAudit(auditLogEntity) } - assertThat(result).isFalse() - } -} \ No newline at end of file diff --git a/src/test/kotlin/logic/usecase/audit/GetAuditLogUseCaseTest.kt b/src/test/kotlin/logic/usecase/audit/GetAuditLogUseCaseTest.kt index accb136..b47484e 100644 --- a/src/test/kotlin/logic/usecase/audit/GetAuditLogUseCaseTest.kt +++ b/src/test/kotlin/logic/usecase/audit/GetAuditLogUseCaseTest.kt @@ -3,7 +3,6 @@ package logic.usecase.audit import com.google.common.truth.Truth.assertThat import fakeData.createAuditLogEntity import io.mockk.coEvery -import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.example.entity.AuditAction @@ -11,11 +10,10 @@ import org.example.entity.AuditedEntityType import org.example.logic.repository.AuditLogRepository import org.example.logic.usecase.audit.GetAuditLogUseCase import org.example.utils.PlanMateException.InvalidStateIdException -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import java.util.* -import kotlin.test.assertEquals class GetAuditLogUseCaseTest { @@ -29,7 +27,7 @@ class GetAuditLogUseCaseTest { @Test - fun `should retrieve audit logs filtered by project ID when provided ID is valid`() = runTest{ + fun `should retrieve audit logs filtered by project ID when provided ID is valid`() = runTest { // Given val projectUUID = "0f89e958-d40d-4b57-a8e6-75ad7ac0f679" val projectId = UUID.fromString(projectUUID) @@ -57,23 +55,6 @@ class GetAuditLogUseCaseTest { assertThat(result).isEqualTo(expectedLogs) } - @Test - fun `should return failure when an exception occurs while retrieving project audit logs`() = runTest { - // Given - val projectUUID = "0f89e958-d40d-4b57-a8e6-75ad7ac0f679" - val projectId = UUID.fromString(projectUUID) - val exception = InvalidStateIdException() - - coEvery { auditLogRepository.getProjectHistory(projectId) } throws exception - - // When - val result = getAuditLogsUseCase.invoke(projectId, AuditedEntityType.PROJECT) - - // Then - assertThat(result).isEmpty() - - } - @Test fun `should retrieve audit logs filtered by task ID when provided ID is valid`() = runTest { // Given @@ -103,17 +84,13 @@ class GetAuditLogUseCaseTest { } @Test - fun `should throw InvalidStateIdException when using invalid ID`() = runTest{ + fun `should throw InvalidStateIdException when using invalid ID`() = runTest { // Given - val taskUUID = "0f89e958-d40d-4b57-a8e6-75ad7ac0f679" - val taskId = UUID.fromString(taskUUID) + val taskId = UUID.randomUUID() val exception = InvalidStateIdException() coEvery { auditLogRepository.getTaskHistory(taskId) } throws exception - // When - val result = getAuditLogsUseCase.invoke(UUID.fromString(taskUUID), AuditedEntityType.TASK) - - // Then - assertThat(result).isEmpty() + // When & then + assertThrows { getAuditLogsUseCase.invoke(taskId, AuditedEntityType.TASK) } } } \ No newline at end of file diff --git a/src/test/kotlin/logic/usecase/project/DeleteProjectUseCaseTest.kt b/src/test/kotlin/logic/usecase/project/DeleteProjectUseCaseTest.kt index dc26b5f..c1183dc 100644 --- a/src/test/kotlin/logic/usecase/project/DeleteProjectUseCaseTest.kt +++ b/src/test/kotlin/logic/usecase/project/DeleteProjectUseCaseTest.kt @@ -22,10 +22,10 @@ class DeleteProjectUseCaseTest { coEvery { mockRepo.getProjectById(any()) } returns testProject coEvery { mockRepo.deleteProject(any(), any()) } just Runs - useCase(testProjectId, testUserId) // Should not throw + useCase(testProjectId, testUserId) coVerify { - mockRepo.getProjectById(testProjectId.toString()) + mockRepo.getProjectById(testProjectId) mockRepo.deleteProject(testProjectId, testUserId) } } diff --git a/src/test/kotlin/logic/usecase/project/GetProjectUseCaseTest.kt b/src/test/kotlin/logic/usecase/project/GetProjectUseCaseTest.kt index dad1d51..fc587de 100644 --- a/src/test/kotlin/logic/usecase/project/GetProjectUseCaseTest.kt +++ b/src/test/kotlin/logic/usecase/project/GetProjectUseCaseTest.kt @@ -19,7 +19,7 @@ class GetProjectUseCaseTest { @Test fun `should return project when found`() = runTest { val expectedProject = mockk() - coEvery { mockRepo.getProjectById(testProjectId.toString()) } returns expectedProject + coEvery { mockRepo.getProjectById(testProjectId) } returns expectedProject val result = useCase(testProjectId)