Implement Update Task
Now that we can create and retrieve tasks, let's implement the ability to update existing tasks. This will allow users to modify task details and mark tasks as complete. We'll need to add methods to both our service and controller layers.
Updating the Service Interface
First, let's add the update method to our TaskService interface:
public interface TaskService {
List<Task> listTasks(UUID taskListId);
Optional<Task> getTask(UUID taskListId, UUID taskId);
Task createTask(UUID taskListId, Task task);
Task updateTask(UUID taskListId, UUID taskId, Task task);
}Implementing the Service Method
Now let's implement the update functionality in TaskServiceImpl:
@Service
public class TaskServiceImpl implements TaskService {
private final TaskListRepository taskListRepository;
private final TaskRepository taskRepository;
public TaskServiceImpl(TaskListRepository taskListRepository,
TaskRepository taskRepository) {
this.taskListRepository = taskListRepository;
this.taskRepository = taskRepository;
}
@Override
public Task updateTask(UUID taskListId, UUID taskId, Task task) {
if(null == task.getId()) {
throw new IllegalArgumentException("Task must have ID!");
}
if(!Objects.equals(taskId, task.getId())) {
throw new IllegalArgumentException("Task IDs do not match!");
}
if(null == task.getPriority()) {
throw new IllegalArgumentException("Task must have a valid priority!");
}
if(null == task.getStatus()) {
throw new IllegalArgumentException("Task must have a valid status!");
}
Task existingTask = taskRepository.findByTaskListIdAndId(
taskListId, task.getId()
).orElseThrow(() -> new IllegalStateException("Task not found!"));
existingTask.setTitle(task.getTitle());
existingTask.setDescription(task.getDescription());
existingTask.setDueDate(task.getDueDate());
existingTask.setPriority(task.getPriority());
existingTask.setStatus(task.getStatus());
existingTask.setUpdated(LocalDateTime.now());
return taskRepository.save(existingTask);
}
}Let's examine the key aspects of this implementation:
-
Input Validation:
- Checks that the task has an ID
- Verifies the ID in the path matches the ID in the task
- Ensures priority and status are set
- Verifies the task exists in the specified task list
-
Update Logic:
- Updates all modifiable fields (title, description, dueDate, priority, status)
- Updates the 'updated' timestamp
- Preserves other fields like 'created' timestamp and task list reference
-
Error Handling:
- Throws IllegalArgumentException for invalid input
- Throws IllegalStateException if task doesn't exist
- These will be caught by our global exception handler
Updating the Controller
Now let's add the update endpoint to our TasksController:
@RestController
@RequestMapping(path = "/task-lists/{task_list_id}/tasks")
public class TasksController {
private final TaskService taskService;
private final TaskMapper taskMapper;
public TasksController(TaskService taskService, TaskMapper taskMapper) {
this.taskService = taskService;
this.taskMapper = taskMapper;
}
@PutMapping(path = "/{task_id}")
public TaskDto updateTask(
@PathVariable("task_list_id") UUID taskListId,
@PathVariable("task_id") UUID id,
@RequestBody TaskDto taskDto) {
Task updatedTask = taskService.updateTask(
taskListId,
id,
taskMapper.fromDto(taskDto)
);
return taskMapper.toDto(updatedTask);
}
}Let's break down the key components:
-
URL Mapping:
@PutMappingmaps this to HTTP PUT requests- Path variables capture both task list ID and task ID from the URL
- Maintains REST resource hierarchy (/task-lists/{task_list_id}/tasks/{task_id})
-
Parameters:
- Two
@PathVariableannotations extract IDs from the URL @RequestBodydeserializes the request body into a TaskDto
- Two
-
Implementation:
- Converts the incoming DTO to an entity
- Calls the service to update the task
- Converts the result back to a DTO for the response
How It Works Together
When a PUT request arrives at /task-lists/{task_list_id}/tasks/{task_id}:
- Spring extracts both IDs from the URL path
- The request body is deserialized into a TaskDto
- The controller converts the DTO to a Task entity
- The service:
- Validates the input
- Verifies the task exists in the specified task list
- Updates the task fields
- Updates the timestamp
- Saves the changes
- The controller converts the updated entity back to a DTO
- Spring serializes the DTO to JSON for the response
For example, a PUT request to /task-lists/123/tasks/789 might look like:
{
"id": "789",
"title": "Updated Task",
"description": "This task has been updated",
"dueDate": "2024-11-02T17:00:00",
"priority": "HIGH",
"status": "CLOSED"
}And receive a response like:
{
"id": "789",
"title": "Updated Task",
"description": "This task has been updated",
"dueDate": "2024-11-02T17:00:00",
"priority": "HIGH",
"status": "CLOSED"
}Summary
- Added update functionality to the
TaskService - Implemented comprehensive validation in the service layer
- Added the PUT endpoint to update tasks
- Maintained data integrity by validating IDs and task list ownership