diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java index 87d6749..77626af 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java @@ -1,17 +1,20 @@ package br.com.tasknoteapp.java_api.controller; import br.com.tasknoteapp.java_api.entity.TaskEntity; +import br.com.tasknoteapp.java_api.request.TaskPatchRequest; import br.com.tasknoteapp.java_api.request.TaskRequest; import br.com.tasknoteapp.java_api.response.TaskResponse; import br.com.tasknoteapp.java_api.service.TaskService; import jakarta.validation.Valid; +import java.util.List; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -24,13 +27,15 @@ public class TaskController { private final TaskService taskService; @GetMapping - public String tasks() { - return "Tasks"; + public List getAllTasks() { + return taskService.getAllTasks(); } - // https://restfulapi.net/rest-put-vs-post/ - @PutMapping - public void putTask() {} + @PatchMapping("/{id}") + public ResponseEntity putTask( + @PathVariable Long id, @RequestBody @Valid TaskPatchRequest taskRequest) { + return ResponseEntity.ok(taskService.patchTask(id, taskRequest)); + } // https://restfulapi.net/rest-put-vs-post/ @PostMapping diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskEntity.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskEntity.java index 5d1e461..5a3f3f4 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskEntity.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskEntity.java @@ -33,6 +33,6 @@ public class TaskEntity { @ManyToOne(fetch = FetchType.LAZY) private UserEntity user; - @OneToMany(mappedBy = "task") + @OneToMany(mappedBy = "task", fetch = FetchType.LAZY) private List urls; } diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskUrlEntity.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskUrlEntity.java index 3ea9352..108e954 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskUrlEntity.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/TaskUrlEntity.java @@ -15,7 +15,7 @@ import lombok.ToString; @Data @Entity @ToString -@Table(name = "task_links") +@Table(name = "task_url") @EqualsAndHashCode public class TaskUrlEntity { diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/exception/TaskNotFoundException.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/exception/TaskNotFoundException.java new file mode 100644 index 0000000..9e571c9 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/exception/TaskNotFoundException.java @@ -0,0 +1,13 @@ +package br.com.tasknoteapp.java_api.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.server.ResponseStatusException; + +@ResponseStatus(code = HttpStatus.NOT_FOUND) +public class TaskNotFoundException extends ResponseStatusException { + + public TaskNotFoundException() { + super(HttpStatus.NOT_FOUND, "Task not found"); + } +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskRepository.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskRepository.java index dbac006..b435cf7 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskRepository.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskRepository.java @@ -8,5 +8,5 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface TaskRepository extends JpaRepository { - List findAllByUserId(Long userId); + List findAllByUser_id(Long userId); } diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskUrlRepository.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskUrlRepository.java new file mode 100644 index 0000000..aa54506 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/TaskUrlRepository.java @@ -0,0 +1,10 @@ +package br.com.tasknoteapp.java_api.repository; + +import br.com.tasknoteapp.java_api.entity.TaskUrlEntity; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TaskUrlRepository extends JpaRepository { + + void deleteAllByIdIn(List ids); +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java new file mode 100644 index 0000000..b05fa84 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java @@ -0,0 +1,5 @@ +package br.com.tasknoteapp.java_api.request; + +import java.util.List; + +public record TaskPatchRequest(String description, Boolean done, List urls) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskUrlPatchRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskUrlPatchRequest.java new file mode 100644 index 0000000..fda9686 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskUrlPatchRequest.java @@ -0,0 +1,3 @@ +package br.com.tasknoteapp.java_api.request; + +public record TaskUrlPatchRequest(Long id, String url) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java index 724d7f4..b9c9e36 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java @@ -2,21 +2,25 @@ package br.com.tasknoteapp.java_api.response; import br.com.tasknoteapp.java_api.entity.TaskEntity; import br.com.tasknoteapp.java_api.entity.TaskUrlEntity; +import java.util.ArrayList; import java.util.List; import java.util.Objects; -public record TaskResponse(Long id, String description, Boolean done, List urls) { +public record TaskResponse(Long id, String description, Boolean done, List urls) { public static TaskResponse fromEntity(TaskEntity entity) { List urls = entity.getUrls(); + List urlsResponse = new ArrayList<>(); if (Objects.isNull(urls)) { urls = List.of(); + } else { + for (TaskUrlEntity url : urls) { + TaskUrlResponse urlResponse = new TaskUrlResponse(url.getId(), url.getUrl()); + urlsResponse.add(urlResponse); + } } return new TaskResponse( - entity.getId(), - entity.getDescription(), - entity.getDone(), - urls.stream().map(TaskUrlEntity::getUrl).toList()); + entity.getId(), entity.getDescription(), entity.getDone(), urlsResponse); } } diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java new file mode 100644 index 0000000..169e08f --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java @@ -0,0 +1,3 @@ +package br.com.tasknoteapp.java_api.response; + +public record TaskUrlResponse(Long id, String url) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java index 470fb21..2498fc3 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java @@ -1,17 +1,18 @@ package br.com.tasknoteapp.java_api.service; import br.com.tasknoteapp.java_api.entity.TaskEntity; +import br.com.tasknoteapp.java_api.request.TaskPatchRequest; import br.com.tasknoteapp.java_api.request.TaskRequest; import br.com.tasknoteapp.java_api.response.TaskResponse; import java.util.List; public interface TaskService { - public List getAllTasks(Long userId); + public List getAllTasks(); public TaskEntity createTask(TaskRequest taskRequest); - public void updateTask(TaskRequest taskRequest); + public TaskResponse patchTask(Long taskId, TaskPatchRequest taskRequest); public void updateTaskDone(Long taskId); diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java index b02bd87..3f23a8c 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java @@ -93,7 +93,7 @@ class AuthServiceImpl implements AuthService { */ @Override public String signin(LoginRequest login) { - log.info("Creating user! {}", login.email()); + log.info("Signing in user! {}", login.email()); Optional user = findByEmail(login.email()); if (user.isEmpty()) { diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java index 51c39f6..ef79705 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java @@ -1,14 +1,22 @@ package br.com.tasknoteapp.java_api.service.impl; import br.com.tasknoteapp.java_api.entity.TaskEntity; +import br.com.tasknoteapp.java_api.entity.TaskUrlEntity; import br.com.tasknoteapp.java_api.entity.UserEntity; +import br.com.tasknoteapp.java_api.exception.TaskNotFoundException; import br.com.tasknoteapp.java_api.repository.TaskRepository; +import br.com.tasknoteapp.java_api.repository.TaskUrlRepository; +import br.com.tasknoteapp.java_api.request.TaskPatchRequest; import br.com.tasknoteapp.java_api.request.TaskRequest; +import br.com.tasknoteapp.java_api.request.TaskUrlPatchRequest; import br.com.tasknoteapp.java_api.response.TaskResponse; import br.com.tasknoteapp.java_api.service.AuthService; import br.com.tasknoteapp.java_api.service.TaskService; import br.com.tasknoteapp.java_api.util.AuthUtil; +import jakarta.transaction.Transactional; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,11 +33,15 @@ class TaskServiceImpl implements TaskService { private final AuthUtil authUtil; - @Override - public List getAllTasks(Long userId) { - log.info("Get all tasks to user {}", userId); + private final TaskUrlRepository taskUrlRepository; - List tasks = taskRepository.findAllByUserId(userId); + @Override + public List getAllTasks() { + UserEntity user = getCurrentUser(); + + log.info("Get all tasks to user {}", user.getId()); + + List tasks = taskRepository.findAllByUser_id(user.getId()); log.info("{} tasks found!", tasks.size()); return tasks.stream().map(TaskResponse::fromEntity).toList(); @@ -37,15 +49,9 @@ class TaskServiceImpl implements TaskService { @Override public TaskEntity createTask(TaskRequest taskRequest) { - log.info("Creating task {}", taskRequest); + UserEntity user = getCurrentUser(); - // get user - Optional currentUserEmail = authUtil.getCurrentUserEmail(); - String email = currentUserEmail.orElseThrow(); - - UserEntity user = authService.findByEmail(email).orElseThrow(); - - // create task + log.info("Creating task to user {}", user.getId()); TaskEntity task = new TaskEntity(); task.setDescription(taskRequest.description()); @@ -53,14 +59,57 @@ class TaskServiceImpl implements TaskService { task.setUser(user); TaskEntity created = taskRepository.save(task); + if (Objects.isNull(taskRequest.urls())) { + List urls = saveUrls(task, taskRequest.urls()); + task.setUrls(urls); + } + log.info("Task created! Id {}", created.getId()); return created; } + @Transactional @Override - public void updateTask(TaskRequest taskRequest) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'updateTask'"); + public TaskResponse patchTask(Long taskId, TaskPatchRequest patch) { + UserEntity user = getCurrentUser(); + + log.info("Patching task {} to user {}", taskId, user.getId()); + + Optional task = taskRepository.findById(taskId); + if (task.isEmpty()) { + throw new TaskNotFoundException(); + } + + TaskEntity taskEntity = task.get(); + if (!Objects.isNull(patch.description()) && !patch.description().isBlank()) { + taskEntity.setDescription(patch.description()); + } + if (!Objects.isNull(patch.done())) { + taskEntity.setDone(patch.done()); + } + + if (!Objects.isNull(patch.urls())) { + List urlIds = + patch.urls().stream().filter(p -> p.id() != null).map(TaskUrlPatchRequest::id).toList(); + if (!urlIds.isEmpty()) { + taskUrlRepository.deleteAllByIdIn(urlIds); + log.info("Deleted {} urls from task {}", urlIds.size(), taskId); + } else { + log.info("No urls to patch for task {}", taskId); + } + + List urlsList = + patch.urls().stream().filter(p -> p.id() == null).map(TaskUrlPatchRequest::url).toList(); + List urls = saveUrls(taskEntity, urlsList); + + taskEntity.setUrls(urls); + } + + TaskEntity patchedTask = taskRepository.save(taskEntity); + + log.info("Task patched! Id {}", patchedTask.getId()); + + return TaskResponse.fromEntity(patchedTask); } @Override @@ -74,4 +123,25 @@ class TaskServiceImpl implements TaskService { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'deleteTask'"); } + + private UserEntity getCurrentUser() { + Optional currentUserEmail = authUtil.getCurrentUserEmail(); + String email = currentUserEmail.orElseThrow(); + return authService.findByEmail(email).orElseThrow(); + } + + private List saveUrls(TaskEntity taskEntity, List urls) { + List tasksUrl = new ArrayList<>(); + for (String url : urls) { + TaskUrlEntity taskUrl = new TaskUrlEntity(); + taskUrl.setUrl(url); + taskUrl.setTask(taskEntity); + tasksUrl.add(taskUrl); + } + + List savedUrls = taskUrlRepository.saveAll(tasksUrl); + log.info("Saved {} urls to task {}", savedUrls.size(), taskEntity.getId()); + + return savedUrls; + } }