From c3c96712e6a45fd59ec1717e71c586f3aeaca6fb Mon Sep 17 00:00:00 2001 From: George Antonopoulos Date: Thu, 28 Aug 2025 14:00:53 +0100 Subject: [PATCH] Enhance get_todos method to handle pagination in BasecampClient Updated the get_todos method to retrieve all todos from a todolist by handling pagination transparently. The method now aggregates todos across multiple pages and updates the README to reflect this change. --- README.md | 2 +- basecamp_client.py | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1862174..bf51af4 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ Once configured, you can use these tools in Cursor: - `get_projects` - Get all Basecamp projects - `get_project` - Get details for a specific project - `get_todolists` - Get todo lists for a project -- `get_todos` - Get todos from a todo list +- `get_todos` - Get todos from a todo list (returns all pages; handles Basecamp pagination transparently) - `search_basecamp` - Search across projects, todos, and messages - `get_comments` - Get comments for a Basecamp item - `get_campfire_lines` - Get recent messages from a Basecamp campfire diff --git a/basecamp_client.py b/basecamp_client.py index 612ac8b..7e2b27b 100644 --- a/basecamp_client.py +++ b/basecamp_client.py @@ -144,12 +144,36 @@ class BasecampClient: # To-do methods def get_todos(self, project_id, todolist_id): - """Get all todos in a todolist.""" - response = self.get(f'buckets/{project_id}/todolists/{todolist_id}/todos.json') - if response.status_code == 200: - return response.json() - else: - raise Exception(f"Failed to get todos: {response.status_code} - {response.text}") + """Get all todos in a todolist, handling pagination. + + Basecamp paginates list endpoints (commonly 15 items per page). This + implementation follows pagination via the `page` query parameter and + the HTTP `Link` header if present, aggregating all pages before + returning the combined list. + """ + endpoint = f'buckets/{project_id}/todolists/{todolist_id}/todos.json' + + all_todos = [] + page = 1 + + while True: + response = self.get(endpoint, params={"page": page}) + if response.status_code != 200: + raise Exception(f"Failed to get todos: {response.status_code} - {response.text}") + + page_items = response.json() or [] + all_todos.extend(page_items) + + # Check for next page using Link header or by empty result + link_header = response.headers.get("Link", "") + has_next = 'rel="next"' in link_header if link_header else False + + if not page_items or not has_next: + break + + page += 1 + + return all_todos def get_todo(self, todo_id): """Get a specific todo."""