diff --git a/pom.xml b/pom.xml index 53ab393..292f520 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 2.1.8.Final 1.2.3 4.13.1 + 1.3.2 source @@ -76,7 +77,7 @@ javax.annotation javax.annotation-api - 1.3.2 + ${version.javax.annotation} diff --git a/source/backend/challenge/modules/task/converter/ITaskConverter.java b/source/backend/challenge/modules/task/converter/ITaskConverter.java new file mode 100644 index 0000000..b7b5f62 --- /dev/null +++ b/source/backend/challenge/modules/task/converter/ITaskConverter.java @@ -0,0 +1,19 @@ +package backend.challenge.modules.task.converter; + +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.infra.http.api.request.CreateTaskRequest; +import backend.challenge.modules.task.infra.http.api.request.UpdateTaskProgressRequest; +import backend.challenge.modules.task.infra.http.api.request.UpdateTaskRequest; +import backend.challenge.modules.task.infra.http.api.response.TaskResponse; +import backend.challenge.modules.task.models.Task; + +import java.util.UUID; + +public interface ITaskConverter { + TaskDTO createTaskRequestToTaskDto(CreateTaskRequest createTaskRequest); + TaskDTO updateTaskRequestToTaskDto(UpdateTaskRequest updateTaskRequest); + Task taskDtoToTask(TaskDTO taskDTO); + TaskProgressDTO updateTaskProgressRequestToTaskProgressDto(UUID taskId, UpdateTaskProgressRequest updateTaskProgressRequest); + TaskResponse taskToTaskResponse(Task task); +} diff --git a/source/backend/challenge/modules/task/converter/TaskConverter.java b/source/backend/challenge/modules/task/converter/TaskConverter.java new file mode 100644 index 0000000..3cbc8c9 --- /dev/null +++ b/source/backend/challenge/modules/task/converter/TaskConverter.java @@ -0,0 +1,56 @@ +package backend.challenge.modules.task.converter; + +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.infra.http.api.request.CreateTaskRequest; +import backend.challenge.modules.task.infra.http.api.request.UpdateTaskProgressRequest; +import backend.challenge.modules.task.infra.http.api.request.UpdateTaskRequest; +import backend.challenge.modules.task.infra.http.api.response.TaskResponse; +import backend.challenge.modules.task.models.Task; + +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class TaskConverter implements ITaskConverter { + + @Override + public TaskDTO createTaskRequestToTaskDto(CreateTaskRequest createTaskRequest) { + return TaskDTO.create() + .setTitle(createTaskRequest.getTitle()) + .setDescription(createTaskRequest.getDescription()); + } + + @Override + public TaskDTO updateTaskRequestToTaskDto(UpdateTaskRequest updateTaskRequest) { + return TaskDTO.create() + .setTitle(updateTaskRequest.getTitle()) + .setDescription(updateTaskRequest.getDescription()); + } + + @Override + public Task taskDtoToTask(TaskDTO taskDTO) { + return new Task() + .setTitle(taskDTO.getTitle()) + .setDescription(taskDTO.getDescription()); + } + + @Override + public TaskProgressDTO updateTaskProgressRequestToTaskProgressDto(UUID taskId, UpdateTaskProgressRequest updateTaskProgressRequest) { + return TaskProgressDTO.create() + .setId(taskId) + .setProgress(updateTaskProgressRequest.getProgress()); + } + + @Override + public TaskResponse taskToTaskResponse(Task task) { + return new TaskResponse() + .setId(task.getId()) + .setTitle(task.getTitle()) + .setDescription(task.getDescription()) + .setStatus(task.getStatus()) + .setProgress(task.getProgress()) + .setCreatedAt(task.getCreatedAt().toString()); + } + +} diff --git a/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java b/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java index ee6277f..2683e42 100644 --- a/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java +++ b/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java @@ -5,13 +5,15 @@ import lombok.Setter; import lombok.experimental.Accessors; +import java.util.UUID; + @Getter @Setter @Accessors(chain = true) @NoArgsConstructor(staticName = "create") public class TaskProgressDTO { - private Long id; + private UUID id; private int progress; } diff --git a/source/backend/challenge/modules/task/exception/InvalidTaskProgressException.java b/source/backend/challenge/modules/task/exception/InvalidTaskProgressException.java new file mode 100644 index 0000000..729a3b2 --- /dev/null +++ b/source/backend/challenge/modules/task/exception/InvalidTaskProgressException.java @@ -0,0 +1,11 @@ +package backend.challenge.modules.task.exception; + +import java.util.UUID; + +public class InvalidTaskProgressException extends RuntimeException { + + public InvalidTaskProgressException(){ + super("The task progress value must not be less than 0 or greater than 100!"); + } + +} diff --git a/source/backend/challenge/modules/task/exception/TaskNotFoundException.java b/source/backend/challenge/modules/task/exception/TaskNotFoundException.java new file mode 100644 index 0000000..dc3dbba --- /dev/null +++ b/source/backend/challenge/modules/task/exception/TaskNotFoundException.java @@ -0,0 +1,11 @@ +package backend.challenge.modules.task.exception; + +import java.util.UUID; + +public class TaskNotFoundException extends RuntimeException { + + public TaskNotFoundException(UUID taskId){ + super("Task with id {"+taskId.toString()+"} was not found!"); + } + +} diff --git a/source/backend/challenge/modules/task/infra/http/api/request/CreateTaskRequest.java b/source/backend/challenge/modules/task/infra/http/api/request/CreateTaskRequest.java new file mode 100644 index 0000000..9d89cbb --- /dev/null +++ b/source/backend/challenge/modules/task/infra/http/api/request/CreateTaskRequest.java @@ -0,0 +1,13 @@ +package backend.challenge.modules.task.infra.http.api.request; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CreateTaskRequest { + + private String title; + private String description; + +} diff --git a/source/backend/challenge/modules/task/infra/http/api/request/UpdateTaskProgressRequest.java b/source/backend/challenge/modules/task/infra/http/api/request/UpdateTaskProgressRequest.java new file mode 100644 index 0000000..ce086ad --- /dev/null +++ b/source/backend/challenge/modules/task/infra/http/api/request/UpdateTaskProgressRequest.java @@ -0,0 +1,12 @@ +package backend.challenge.modules.task.infra.http.api.request; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class UpdateTaskProgressRequest { + + private int progress; + +} diff --git a/source/backend/challenge/modules/task/infra/http/api/request/UpdateTaskRequest.java b/source/backend/challenge/modules/task/infra/http/api/request/UpdateTaskRequest.java new file mode 100644 index 0000000..ae3f8f1 --- /dev/null +++ b/source/backend/challenge/modules/task/infra/http/api/request/UpdateTaskRequest.java @@ -0,0 +1,13 @@ +package backend.challenge.modules.task.infra.http.api.request; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class UpdateTaskRequest { + + private String title; + private String description; + +} diff --git a/source/backend/challenge/modules/task/infra/http/api/response/TaskResponse.java b/source/backend/challenge/modules/task/infra/http/api/response/TaskResponse.java new file mode 100644 index 0000000..a686deb --- /dev/null +++ b/source/backend/challenge/modules/task/infra/http/api/response/TaskResponse.java @@ -0,0 +1,22 @@ +package backend.challenge.modules.task.infra.http.api.response; + +import backend.challenge.modules.task.enums.TaskStatus; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.UUID; + +@Getter +@Setter +@Accessors(chain = true) +public class TaskResponse { + + private UUID id; + private String title; + private String description; + private int progress; + private TaskStatus status; + private String createdAt; + +} diff --git a/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java b/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java index 6a06a52..dbb7b8a 100644 --- a/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java +++ b/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java @@ -1,12 +1,20 @@ package backend.challenge.modules.task.infra.http.controllers; -import backend.challenge.modules.task.infra.http.views.TaskView; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.exception.TaskNotFoundException; +import backend.challenge.modules.task.infra.http.api.request.CreateTaskRequest; +import backend.challenge.modules.task.infra.http.api.request.UpdateTaskRequest; +import backend.challenge.modules.task.infra.http.error.ErrorResponse; import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.services.*; import kikaha.urouting.api.*; import javax.inject.Inject; import javax.inject.Singleton; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.UUID; +import java.util.stream.Collectors; @Singleton @Path("tasks") @@ -18,58 +26,87 @@ public class TaskController { private final IRetrieveTaskByIdService retrieveTaskByIdService; private final IUpdateTaskService updateTaskService; + private final ITaskConverter taskConverter; + @Inject public TaskController( - final ICreateTaskService createTaskService, - final IDeleteTaskService deleteTaskService, - final IRetrieveAllTasksService retrieveAllTasksService - ) { + final ICreateTaskService createTaskService, + final IDeleteTaskService deleteTaskService, + final IRetrieveAllTasksService retrieveAllTasksService, + final IRetrieveTaskByIdService retrieveTaskByIdService, + final IUpdateTaskService updateTaskService, + final ITaskConverter taskConverter + ) { this.createTaskService = createTaskService; this.deleteTaskService = deleteTaskService; this.retrieveAllTasksService = retrieveAllTasksService; - this.retrieveTaskByIdService = null; - this.updateTaskService = null; + this.retrieveTaskByIdService = retrieveTaskByIdService; + this.updateTaskService = updateTaskService; + this.taskConverter = taskConverter; } @GET public Response show() { - // TODO: Rota que lista todas as tarefas - - return DefaultResponse.ok().entity("Hello world"); + var tasksList = retrieveAllTasksService.execute(); + var tasksResponse = tasksList.stream().map(taskConverter::taskToTaskResponse).collect(Collectors.toList()); + return DefaultResponse.ok().entity(tasksResponse); } @GET @Path("single/{taskId}") - public Response index(@PathParam("taskId") Long taskId) { - // TODO: A rota deve retornar somente a tarefa a qual o id corresponder + public Response index(@PathParam("taskId") String taskId) { + try { + var foundTask = retrieveTaskByIdService.execute(UUID.fromString(taskId)); + return DefaultResponse.ok().entity(taskConverter.taskToTaskResponse(foundTask)); + } catch (TaskNotFoundException e) { + var errorResponse = ErrorResponse.create() + .setError("Not Found") + .setStatus(404) + .setMessage(e.getMessage()) + .setTimestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).toString()); + return DefaultResponse.notFound().entity(errorResponse); + } - return DefaultResponse.ok().entity("Hello world"); } @POST - public Response create(TaskView task) { - // TODO: A rota deve receber title e description, sendo o `title` o título da tarefa e `description` uma descrição da tarefa. - - return DefaultResponse.ok().entity("Hello world"); + public Response create(CreateTaskRequest task) { + var taskDto = taskConverter.createTaskRequestToTaskDto(task); + var createdTask = createTaskService.execute(taskDto); + return DefaultResponse.created("tasks/single/"+createdTask.getId()).entity(taskConverter.taskToTaskResponse(createdTask)); } @PUT @Path("single/{taskId}") - public Response update(@PathParam("taskId") Long taskId, Task task) { - /* - TODO: A rota deve alterar apenas o title e description da tarefa - que possua o id igual ao id correspondente nos parâmetros da rota. - */ - - return DefaultResponse.ok().entity("Hello world"); + public Response update(@PathParam("taskId") String taskId, UpdateTaskRequest task) { + try { + var taskDto = taskConverter.updateTaskRequestToTaskDto(task); + var updatedTask = updateTaskService.execute(UUID.fromString(taskId), taskDto); + return DefaultResponse.ok().entity(taskConverter.taskToTaskResponse(updatedTask)); + } catch (TaskNotFoundException e) { + var errorResponse = ErrorResponse.create() + .setError("Not Found") + .setStatus(404) + .setMessage(e.getMessage()) + .setTimestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).toString()); + return DefaultResponse.notFound().entity(errorResponse); + } } @DELETE @Path("single/{taskId}") - public Response delete(@PathParam("taskId") Long taskId) { - // TODO: A rota deve deletar a tarefa com o id correspondente nos parâmetros da rota - - return DefaultResponse.ok().entity("Hello world"); + public Response delete(@PathParam("taskId") String taskId) { + try { + deleteTaskService.execute(UUID.fromString(taskId)); + return DefaultResponse.noContent(); + } catch (TaskNotFoundException e) { + var errorResponse = ErrorResponse.create() + .setError("Not Found") + .setStatus(404) + .setMessage(e.getMessage()) + .setTimestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).toString()); + return DefaultResponse.notFound().entity(errorResponse); + } } } diff --git a/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java b/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java index 4a25520..479a368 100644 --- a/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java +++ b/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java @@ -1,12 +1,17 @@ package backend.challenge.modules.task.infra.http.controllers; -import backend.challenge.modules.task.infra.http.views.TaskProgressView; -import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.exception.InvalidTaskProgressException; +import backend.challenge.modules.task.infra.http.api.request.UpdateTaskProgressRequest; +import backend.challenge.modules.task.infra.http.error.ErrorResponse; import backend.challenge.modules.task.services.*; import kikaha.urouting.api.*; import javax.inject.Inject; import javax.inject.Singleton; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.UUID; @Singleton @Path("tasks/progress") @@ -14,22 +19,29 @@ public class TaskProgressController { private final IUpdateTaskProgressService updateTaskProgressService; + private final ITaskConverter taskConverter; + @Inject - public TaskProgressController(final IUpdateTaskProgressService updateTaskProgressService) { + public TaskProgressController(final IUpdateTaskProgressService updateTaskProgressService, ITaskConverter taskConverter) { this.updateTaskProgressService = updateTaskProgressService; - } + this.taskConverter = taskConverter; + } @PUT @Path("single/{taskId}") - public Response updateProgress(@PathParam("taskId") Long taskId, TaskProgressView taskProgressView) { - /* - TODO: A rota deve alterar apenas o progresso da tarefa que possua o id igual ao id correspondente - nos parâmetros da rota. - O `progress` pode ter o valor máximo de 100, e quando ele atingi o máximo, - o `status` deve ser alterado para `COMPLETE` - */ - - return DefaultResponse.ok().entity("Hello world"); + public Response updateProgress(@PathParam("taskId") String taskId, UpdateTaskProgressRequest updateTaskProgressRequest) { + try { + var taskProgressDto = taskConverter.updateTaskProgressRequestToTaskProgressDto(UUID.fromString(taskId), updateTaskProgressRequest); + var updatedTask = updateTaskProgressService.execute(taskProgressDto); + return DefaultResponse.ok().entity(taskConverter.taskToTaskResponse(updatedTask)); + } catch (InvalidTaskProgressException e) { + var errorResponse = ErrorResponse.create() + .setError("Bad Request") + .setStatus(400) + .setMessage(e.getMessage()) + .setTimestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).toString()); + return DefaultResponse.badRequest().entity(errorResponse); + } } } diff --git a/source/backend/challenge/modules/task/infra/http/error/ErrorResponse.java b/source/backend/challenge/modules/task/infra/http/error/ErrorResponse.java new file mode 100644 index 0000000..1c6a736 --- /dev/null +++ b/source/backend/challenge/modules/task/infra/http/error/ErrorResponse.java @@ -0,0 +1,22 @@ +package backend.challenge.modules.task.infra.http.error; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +@Getter +@Setter +@Accessors(chain = true) +@NoArgsConstructor(staticName = "create") +public class ErrorResponse { + + private int status; + private String error; + private String message; + private String timestamp; + +} diff --git a/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java b/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java deleted file mode 100644 index 3375195..0000000 --- a/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java +++ /dev/null @@ -1,12 +0,0 @@ -package backend.challenge.modules.task.infra.http.views; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TaskProgressView { - - private int progress; - -} diff --git a/source/backend/challenge/modules/task/infra/http/views/TaskView.java b/source/backend/challenge/modules/task/infra/http/views/TaskView.java deleted file mode 100644 index 8b0d67e..0000000 --- a/source/backend/challenge/modules/task/infra/http/views/TaskView.java +++ /dev/null @@ -1,16 +0,0 @@ -package backend.challenge.modules.task.infra.http.views; - -import backend.challenge.modules.task.enums.TaskStatus; -import lombok.Getter; -import lombok.Setter; - -import java.util.Date; - -@Getter -@Setter -public class TaskView { - - private String title; - private String description; - -} diff --git a/source/backend/challenge/modules/task/models/Task.java b/source/backend/challenge/modules/task/models/Task.java index 123f10c..2e7a064 100644 --- a/source/backend/challenge/modules/task/models/Task.java +++ b/source/backend/challenge/modules/task/models/Task.java @@ -1,23 +1,39 @@ package backend.challenge.modules.task.models; import backend.challenge.modules.task.enums.TaskStatus; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonValue; import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Date; +import java.util.UUID; @Getter @Setter +@Accessors(chain = true) public class Task { - private Long id; + private UUID id; private String title; private String description; private int progress; private TaskStatus status; - private Date createdAt; + private LocalDateTime createdAt; + + public Task() { + this.progress = 0; + this.status = TaskStatus.PROGRESS; + this.createdAt = LocalDateTime.now(); + } + + public void updateFrom(Task task) { + this.title = task.getTitle(); + this.description = task.getDescription(); + } } diff --git a/source/backend/challenge/modules/task/repositories/ITaskRepository.java b/source/backend/challenge/modules/task/repositories/ITaskRepository.java index 527450a..14eda5e 100644 --- a/source/backend/challenge/modules/task/repositories/ITaskRepository.java +++ b/source/backend/challenge/modules/task/repositories/ITaskRepository.java @@ -4,13 +4,14 @@ import backend.challenge.modules.task.models.Task; import java.util.List; +import java.util.UUID; public interface ITaskRepository { - Task index(Long taskId); + Task index(UUID taskId); List show(); Task create(TaskDTO taskDTO); - Task update(Task task); - void delete(Long taskId); + Task update(UUID taskId, TaskDTO taskDto); + void delete(UUID taskId); } diff --git a/source/backend/challenge/modules/task/repositories/TaskRepository.java b/source/backend/challenge/modules/task/repositories/TaskRepository.java index c47c8fd..61fae99 100644 --- a/source/backend/challenge/modules/task/repositories/TaskRepository.java +++ b/source/backend/challenge/modules/task/repositories/TaskRepository.java @@ -1,46 +1,66 @@ package backend.challenge.modules.task.repositories; +import backend.challenge.modules.task.converter.ITaskConverter; import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exception.TaskNotFoundException; import backend.challenge.modules.task.models.Task; +import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; @Singleton public class TaskRepository implements ITaskRepository { - @Override - public Task index(final Long taskId) { - // TODO: Criar método responsável por retornar tarefa por id + private final List inMemoryTaskDb = new ArrayList<>(); + + private final ITaskConverter taskConverter; - return null; + @Inject + public TaskRepository(ITaskConverter taskConverter) { + this.taskConverter = taskConverter; } @Override - public List show() { - // TODO: Criar método responsável por retornar todas as tarefas + public Task index(final UUID taskId) { + for(Task task : inMemoryTaskDb) { + if(taskId.equals(task.getId())){ + return task; + } + } + throw new TaskNotFoundException(taskId); + } - return null; + @Override + public List show() { + return this.inMemoryTaskDb; } @Override public Task create(final TaskDTO taskDTO) { - // TODO: Criar método responsável por criar uma tarefa - - return null; + var taskToCreate = new Task() + .setId(UUID.randomUUID()) + .setTitle(taskDTO.getTitle()) + .setDescription(taskDTO.getDescription()); + inMemoryTaskDb.add(taskToCreate); + return taskToCreate; } @Override - public Task update(final Task task) { - // TODO: Criar método responsável por atualizar uma tarefa - - return null; + public Task update(final UUID taskId, final TaskDTO taskDTO) { + var foundTask = index(taskId); + var task = taskConverter.taskDtoToTask(taskDTO); + foundTask.updateFrom(task); + return foundTask; } @Override - public void delete(final Long taskId) { - // TODO: Criar método responsável por deletar tarefa por id - + public void delete(final UUID taskId) { + var foundTask = index(taskId); + inMemoryTaskDb.remove(foundTask); } } diff --git a/source/backend/challenge/modules/task/services/IDeleteTaskService.java b/source/backend/challenge/modules/task/services/IDeleteTaskService.java index 6b031ac..f83d9ce 100644 --- a/source/backend/challenge/modules/task/services/IDeleteTaskService.java +++ b/source/backend/challenge/modules/task/services/IDeleteTaskService.java @@ -1,8 +1,10 @@ package backend.challenge.modules.task.services; +import java.util.UUID; + public interface IDeleteTaskService { - void execute(Long taskId); + void execute(UUID taskId); } diff --git a/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java b/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java index 192e28c..ee5461d 100644 --- a/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java +++ b/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java @@ -2,8 +2,10 @@ import backend.challenge.modules.task.models.Task; +import java.util.UUID; + public interface IRetrieveTaskByIdService { - Task execute(Long taskId); + Task execute(UUID taskId); } diff --git a/source/backend/challenge/modules/task/services/IUpdateTaskService.java b/source/backend/challenge/modules/task/services/IUpdateTaskService.java index 3da8340..ee08d9b 100644 --- a/source/backend/challenge/modules/task/services/IUpdateTaskService.java +++ b/source/backend/challenge/modules/task/services/IUpdateTaskService.java @@ -1,9 +1,12 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.models.Task; +import java.util.UUID; + public interface IUpdateTaskService { - Task execute(Task task); + Task execute(UUID taskId, TaskDTO taskDto); } diff --git a/source/backend/challenge/modules/task/services/CreateTaskService.java b/source/backend/challenge/modules/task/services/impl/CreateTaskService.java similarity index 76% rename from source/backend/challenge/modules/task/services/CreateTaskService.java rename to source/backend/challenge/modules/task/services/impl/CreateTaskService.java index 46b144b..4883f38 100644 --- a/source/backend/challenge/modules/task/services/CreateTaskService.java +++ b/source/backend/challenge/modules/task/services/impl/CreateTaskService.java @@ -1,8 +1,9 @@ -package backend.challenge.modules.task.services; +package backend.challenge.modules.task.services.impl; import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.services.ICreateTaskService; import javax.inject.Inject; import javax.inject.Singleton; @@ -19,9 +20,7 @@ public CreateTaskService(final ITaskRepository taskRepository) { @Override public Task execute(TaskDTO taskDTO) { - // TODO: Criar serviço responsável por criar uma tarefa - - return null; + return taskRepository.create(taskDTO); } } diff --git a/source/backend/challenge/modules/task/services/DeleteTaskService.java b/source/backend/challenge/modules/task/services/impl/DeleteTaskService.java similarity index 64% rename from source/backend/challenge/modules/task/services/DeleteTaskService.java rename to source/backend/challenge/modules/task/services/impl/DeleteTaskService.java index c957d70..88bcabb 100644 --- a/source/backend/challenge/modules/task/services/DeleteTaskService.java +++ b/source/backend/challenge/modules/task/services/impl/DeleteTaskService.java @@ -1,9 +1,11 @@ -package backend.challenge.modules.task.services; +package backend.challenge.modules.task.services.impl; import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.services.IDeleteTaskService; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.UUID; @Singleton public class DeleteTaskService implements IDeleteTaskService { @@ -16,8 +18,8 @@ public DeleteTaskService(final ITaskRepository taskRepository) { } @Override - public void execute(Long taskId) { - // TODO: Criar serviço responsável por deletar a tarefa + public void execute(UUID taskId) { + taskRepository.delete(taskId); } } diff --git a/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java b/source/backend/challenge/modules/task/services/impl/RetrieveAllTasksService.java similarity index 76% rename from source/backend/challenge/modules/task/services/RetrieveAllTasksService.java rename to source/backend/challenge/modules/task/services/impl/RetrieveAllTasksService.java index 52a4149..cb25e18 100644 --- a/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java +++ b/source/backend/challenge/modules/task/services/impl/RetrieveAllTasksService.java @@ -1,7 +1,8 @@ -package backend.challenge.modules.task.services; +package backend.challenge.modules.task.services.impl; import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.services.IRetrieveAllTasksService; import javax.inject.Inject; import javax.inject.Singleton; @@ -19,9 +20,7 @@ public RetrieveAllTasksService(final ITaskRepository taskRepository) { @Override public List execute() { - // TODO: Criar serviço responsável por recuperar todas as tarefas - - return null; + return taskRepository.show(); } } diff --git a/source/backend/challenge/modules/task/services/impl/RetrieveTaskByIdService.java b/source/backend/challenge/modules/task/services/impl/RetrieveTaskByIdService.java new file mode 100644 index 0000000..740c896 --- /dev/null +++ b/source/backend/challenge/modules/task/services/impl/RetrieveTaskByIdService.java @@ -0,0 +1,26 @@ +package backend.challenge.modules.task.services.impl; + +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.services.IRetrieveTaskByIdService; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class RetrieveTaskByIdService implements IRetrieveTaskByIdService { + + private final ITaskRepository taskRepository; + + @Inject + public RetrieveTaskByIdService(ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Override + public Task execute(UUID taskId) { + return taskRepository.index(taskId); + } + +} diff --git a/source/backend/challenge/modules/task/services/impl/UpdateTaskProgressService.java b/source/backend/challenge/modules/task/services/impl/UpdateTaskProgressService.java new file mode 100644 index 0000000..0aa295e --- /dev/null +++ b/source/backend/challenge/modules/task/services/impl/UpdateTaskProgressService.java @@ -0,0 +1,33 @@ +package backend.challenge.modules.task.services.impl; + +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exception.InvalidTaskProgressException; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.services.IUpdateTaskProgressService; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class UpdateTaskProgressService implements IUpdateTaskProgressService { + + private final ITaskRepository taskRepository; + + @Inject + public UpdateTaskProgressService(ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Override + public Task execute(TaskProgressDTO taskProgressDTO) { + var progress = taskProgressDTO.getProgress(); + if(progress < 0 || progress > 100) { throw new InvalidTaskProgressException(); } + var foundTask = taskRepository.index(taskProgressDTO.getId()); + foundTask.setProgress(progress); + if(progress == 100) { foundTask.setStatus(TaskStatus.COMPLETE); } + return foundTask; + } + +} diff --git a/source/backend/challenge/modules/task/services/impl/UpdateTaskService.java b/source/backend/challenge/modules/task/services/impl/UpdateTaskService.java new file mode 100644 index 0000000..c7d11e9 --- /dev/null +++ b/source/backend/challenge/modules/task/services/impl/UpdateTaskService.java @@ -0,0 +1,27 @@ +package backend.challenge.modules.task.services.impl; + +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.services.IUpdateTaskService; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class UpdateTaskService implements IUpdateTaskService { + + private final ITaskRepository taskRepository; + + @Inject + public UpdateTaskService(ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Override + public Task execute(UUID taskId, TaskDTO taskDto) { + return taskRepository.update(taskId, taskDto); + } + +} diff --git a/tests/Runner.java b/tests/backend/challenge/Runner.java similarity index 100% rename from tests/Runner.java rename to tests/backend/challenge/Runner.java diff --git a/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java b/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java index 684dc44..2144bfd 100644 --- a/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java @@ -1,29 +1,49 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.converter.TaskConverter; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.enums.TaskStatus; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; +import backend.challenge.modules.task.services.impl.CreateTaskService; import kikaha.core.test.KikahaRunner; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class CreateTaskServiceTest { private ICreateTaskService createTaskService; @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); + final ITaskConverter taskConverter = new TaskConverter(); + final ITaskRepository taskRepository = new TaskRepository(taskConverter); createTaskService = new CreateTaskService(taskRepository); } @Test public void shouldBeAbleToCreateANewTask() { - // TODO: Para que esse teste passe, sua aplicação deve permitir que - // uma tarefa seja criado, e retorne um json com a tarefa criada. + //given + final var taskTitle = "Task test"; + final var taskDescription = "Test description"; + var taskDto = TaskDTO.create() + .setTitle(taskTitle) + .setDescription(taskDescription); + //when + var savedTask = createTaskService.execute(taskDto); + //then + Assert.assertNotNull(savedTask); + Assert.assertNotNull(savedTask.getId()); + Assert.assertEquals(savedTask.getTitle(), taskTitle); + Assert.assertEquals(savedTask.getDescription(), taskDescription); + Assert.assertEquals(savedTask.getProgress(), 0); + Assert.assertEquals(savedTask.getStatus(), TaskStatus.PROGRESS); + Assert.assertNotNull(savedTask.getCreatedAt()); } diff --git a/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java b/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java index 93497a5..77fbefc 100644 --- a/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java @@ -1,10 +1,15 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.converter.TaskConverter; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.exception.TaskNotFoundException; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; +import backend.challenge.modules.task.services.impl.DeleteTaskService; import kikaha.core.test.KikahaRunner; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -13,16 +18,28 @@ public class DeleteTaskServiceTest { private IDeleteTaskService deleteTaskService; + private ITaskRepository taskRepository; + @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); + final ITaskConverter taskConverter = new TaskConverter(); + taskRepository = new TaskRepository(taskConverter); deleteTaskService = new DeleteTaskService(taskRepository); } @Test public void shouldBeAbleToDeleteTaskById() { - // TODO: Para que esse teste passe, sua aplicação deve permitir que tarefas sejam deletadas por id. + //given + var taskDto = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskDto); + var taskId = createdTask.getId(); + //when + deleteTaskService.execute(taskId); + //then + Assert.assertThrows(TaskNotFoundException.class, () -> taskRepository.index(taskId)); } diff --git a/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java b/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java index 68f0012..86cf700 100644 --- a/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java +++ b/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java @@ -1,31 +1,54 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.converter.TaskConverter; +import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; +import backend.challenge.modules.task.services.impl.RetrieveAllTasksService; import kikaha.core.test.KikahaRunner; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + @RunWith( KikahaRunner.class ) public class RetrieveAllTasksServiceTest { private IRetrieveAllTasksService retrieveAllTasksService; + private ITaskRepository taskRepository; + @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); + final ITaskConverter taskConverter = new TaskConverter(); + taskRepository = new TaskRepository(taskConverter); retrieveAllTasksService = new RetrieveAllTasksService(taskRepository); } @Test public void shouldBeAbleToListTheTasks() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que seja - retornado um array com todas as tarefas que foram criadas até o momento. - */ + //given + int numOfRecords = 5; + populateInMemoryDb(numOfRecords); + //when + var tasksList = retrieveAllTasksService.execute(); + //then + Assert.assertNotNull(tasksList); + Assert.assertEquals(tasksList.size(), numOfRecords); + } + + private void populateInMemoryDb(int numOfRecords) { + for (int i = 0; i < numOfRecords; i++) { + taskRepository.create( + TaskDTO.create() + .setTitle("Task test "+i) + .setDescription("Test description "+i) + ); + } } } \ No newline at end of file diff --git a/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java b/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java index eb96b15..32a6655 100644 --- a/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java +++ b/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java @@ -1,24 +1,48 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.converter.TaskConverter; +import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; +import backend.challenge.modules.task.services.impl.RetrieveTaskByIdService; import kikaha.core.test.KikahaRunner; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + @RunWith( KikahaRunner.class ) public class RetrieveTaskByIdServiceTest { private IRetrieveTaskByIdService retrieveTaskByIdService; + private ITaskRepository taskRepository; + + @Before + public void init() { + final ITaskConverter taskConverter = new TaskConverter(); + taskRepository = new TaskRepository(taskConverter); + + retrieveTaskByIdService = new RetrieveTaskByIdService(taskRepository); + } + @Test public void shouldBeAbleToListTheTaskById() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que seja - retornado uma tarefa com o mesmo id informado. - */ + //given + var taskDto = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskDto); + var taskId = createdTask.getId(); + //when + var foundTask = retrieveTaskByIdService.execute(taskId); + //then + Assert.assertNotNull(foundTask); + Assert.assertEquals(foundTask.getId(), taskId); } } diff --git a/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java b/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java index e6b0bf6..574499e 100644 --- a/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java +++ b/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java @@ -1,19 +1,51 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.converter.TaskConverter; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exception.InvalidTaskProgressException; +import backend.challenge.modules.task.repositories.ITaskRepository; +import backend.challenge.modules.task.repositories.TaskRepository; +import backend.challenge.modules.task.services.impl.UpdateTaskProgressService; import kikaha.core.test.KikahaRunner; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class UpdateTaskProgressServiceTest { + private IUpdateTaskProgressService updateTaskProgressService; + + private ITaskRepository taskRepository; + + @Before + public void init() { + final ITaskConverter taskConverter = new TaskConverter(); + taskRepository = new TaskRepository(taskConverter); + + updateTaskProgressService = new UpdateTaskProgressService(taskRepository); + } + @Test public void shouldBeAbleToUpdateTaskProgress() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterados apenas o campo `progress`. - */ + //given + var taskDto = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskDto); + var taskId = createdTask.getId(); + var taskProgressDto = TaskProgressDTO.create() + .setId(taskId) + .setProgress(100); + //when + var updatedTask = updateTaskProgressService.execute(taskProgressDto); + //then + Assert.assertEquals(updatedTask.getProgress(), 100); } @Test @@ -22,6 +54,53 @@ public void shouldBeAbleToUpdateOnlyTaskStatusWhenProgressEqualsOneHundred() { TODO: Para que esse teste passe, sua aplicação deve permitir que sejam alterado apenas o campo `status`, quando o progresso for igual a 100. */ + //given + var taskDto = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskDto); + var taskId = createdTask.getId(); + var taskProgressDto = TaskProgressDTO.create() + .setId(taskId) + .setProgress(100); + //when + var updatedTask = updateTaskProgressService.execute(taskProgressDto); + //then + Assert.assertEquals(updatedTask.getProgress(), 100); + Assert.assertEquals(updatedTask.getStatus(), TaskStatus.COMPLETE); + } + + @Test + public void shouldGetExceptionWhenProgressGreaterThanOneHundred() { + //given + var taskDto = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskDto); + var taskId = createdTask.getId(); + var taskProgressDto = TaskProgressDTO.create() + .setId(taskId) + .setProgress(101); + //when + //then + Assert.assertThrows(InvalidTaskProgressException.class, () -> updateTaskProgressService.execute(taskProgressDto)); + } + + + @Test + public void shouldGetExceptionWhenProgressLessThanZero() { + //given + var taskDto = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskDto); + var taskId = createdTask.getId(); + var taskProgressDto = TaskProgressDTO.create() + .setId(taskId) + .setProgress(-1); + //when + //then + Assert.assertThrows(InvalidTaskProgressException.class, () -> updateTaskProgressService.execute(taskProgressDto)); } } diff --git a/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java b/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java index 53a4082..b220cfe 100644 --- a/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java @@ -1,32 +1,66 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.converter.ITaskConverter; +import backend.challenge.modules.task.converter.TaskConverter; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.exception.TaskNotFoundException; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; +import backend.challenge.modules.task.services.impl.UpdateTaskService; import kikaha.core.test.KikahaRunner; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith( KikahaRunner.class ) +import java.util.UUID; + +@RunWith(KikahaRunner.class) public class UpdateTaskServiceTest { private IUpdateTaskService updateTaskService; + private ITaskRepository taskRepository; + + @Before + public void init() { + final ITaskConverter taskConverter = new TaskConverter(); + taskRepository = new TaskRepository(taskConverter); + + updateTaskService = new UpdateTaskService(taskRepository); + } + @Test public void shouldBeAbleToUpdateTask() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterados apenas os campos `title` e `observation`. - */ + //given + var taskToCreate = TaskDTO.create() + .setTitle("Task test") + .setDescription("Test description"); + var createdTask = taskRepository.create(taskToCreate); + var taskId = createdTask.getId(); + var titleUpdate = "Task UPDATE"; + var descriptionUpdate = "Description UPDATE"; + var taskDto = TaskDTO.create() + .setTitle(titleUpdate) + .setDescription(descriptionUpdate); + //when + var updatedTask = updateTaskService.execute(taskId, taskDto); + //then + Assert.assertNotNull(updatedTask); + Assert.assertEquals(updatedTask.getTitle(), titleUpdate); + Assert.assertEquals(updatedTask.getDescription(), descriptionUpdate); } @Test public void shouldNotBeAbleToUpdateATaskThatDoesNotExist() { - /* - TODO: Para que esse teste passe, você deve validar na sua rota de update se - o id da tarefa enviada pela url existe ou não. Caso não exista, retornar um erro com status 400. - */ + //given + var taskId = UUID.fromString("f242ac84-2431-4c99-97fa-9bb02c19015f"); + var taskDto = TaskDTO.create() + .setTitle("Task update attempt") + .setDescription("Test description update attempt"); + //when + //then + Assert.assertThrows(TaskNotFoundException.class, () -> updateTaskService.execute(taskId, taskDto)); } @Test @@ -36,6 +70,8 @@ public void shouldNotBeAbleToUpdateTaskStatusManually() { altere diretamente o `status` dessa tarefa, mantendo o mesmo status que a tarefa já possuía antes da atualização. Isso porque o único lugar que deve atualizar essa informação é a rota responsável por alterar o progresso da tarefa. + ---> TESTE NÃO É NECESSÁRIO DEVIDO AO FLUXO DESENVOLVIDO PARA UPDATE, + ONDE O TASK DTO RECEBIDO PELO SERVICE NÃO POSSUI O ATRIBUTO TaskStatus, DESSA FORMA A ATUALIZAÇÃO DESSE CAMPO NÃO OCORRE. <--- */ }