Implement Get Task
Now that we can list and create tasks, let's implement the ability to retrieve a single task by its ID. This functionality is essential for viewing and editing individual tasks. We'll need to add methods to both our service and controller layers.
Updating the Service Interface
First, let's add the get method to our TaskService interface:
public interface TaskService {
List<Task> listTasks(UUID taskListId);
Task createTask(UUID taskListId, Task task);
Optional<Task> getTask(UUID taskListId, UUID taskId); // Add this method
}We're returning an Optional<Task> because the requested task might not exist.
Implementing the Service Method
Now let's implement the get 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 Optional<Task> getTask(UUID taskListId, UUID taskId) {
return taskRepository.findByTaskListIdAndId(taskListId, taskId);
}
}This implementation:
- Takes two parameters:
taskListIdto scope the search to a specific task listtaskIdto identify the specific task
- Delegates to our custom repository method
findByTaskListIdAndId - Returns an
Optional<Task>which will be empty if no matching task exists
Updating the Controller
Now let's add the get 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;
}
@GetMapping("/{task_id}")
public Optional<TaskDto> getTask(
@PathVariable("task_list_id") UUID taskListId,
@PathVariable("task_id") UUID taskId) {
return taskService.getTask(taskListId, taskId)
.map(taskMapper::toDto);
}
}Let's break down the key components:
-
URL Path:
@GetMapping("/{task_id}")maps this to GET requests with a task ID in the path- The path includes both task list ID and task ID
- Follows REST resource nesting pattern (/task-lists/{id}/tasks/{id})
-
Path Variables:
- Two
@PathVariableannotations extract both IDs from the URL - Spring automatically converts the string IDs to UUIDs
- Two
-
Implementation:
- Calls the service with both IDs to get the task
- Uses
mapto transform the Task to TaskDto if present - Returns an empty Optional if no task is found
How It Works Together
When a GET request arrives at /task-lists/{task_list_id}/tasks/{task_id}:
- Spring extracts both IDs from the URL path
- The controller calls
taskService.getTask() - The service delegates to
taskRepository.findByTaskListIdAndId() - If found:
- The repository returns the Task entity
- The controller converts it to a DTO
- Spring serializes the DTO to JSON
- If not found:
- An empty Optional is returned
- Spring converts this to a 200 OK with null body
For example, a GET request to /task-lists/123/tasks/789 might return:
{
"id": "789",
"title": "Complete Documentation",
"description": "Write API documentation for the task endpoints",
"dueDate": "2024-11-01T09:00:00",
"priority": "HIGH",
"status": "OPEN"
}Summary
- Added get functionality to the
TaskService - Implemented the get endpoint in the controller
- Used
Optionalto handle cases where the task isn't found - Added proper task list scoping to ensure tasks are only accessed in their correct context