Skip to content

Commit 0a4bc9c

Browse files
fix: list_published queries drafts endpoint instead of published posts
list_published() was calling get_drafts() and filtering by post_date, which returns 0 results because the drafts API only returns unpublished content. Fixed by adding get_published_posts() to APIWrapper (wrapping the library's get_published_posts method, handling its dict response format) and updating list_published() to call it directly. Added two unit tests to prevent regression. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ebffaf9 commit 0a4bc9c

3 files changed

Lines changed: 51 additions & 12 deletions

File tree

src/handlers/post_handler.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,7 @@ async def list_published(self, limit: int = 10) -> List[Dict[str, Any]]:
369369
if limit < 1 or limit > 25:
370370
raise ValueError("limit must be between 1 and 25")
371371

372-
# Get all posts and filter for published only
373-
all_posts = self.client.get_drafts(limit=min(limit, 25))
374-
published = []
375-
376-
for post in all_posts:
377-
# Check if it's published (has a post_date)
378-
if post.get("post_date"):
379-
published.append(post)
380-
if len(published) >= limit:
381-
break
382-
383-
return published
372+
return self.client.get_published_posts(limit=limit)
384373

385374
async def get_post(self, post_id: str) -> Dict[str, Any]:
386375
"""Get a specific post by ID

src/utils/api_wrapper.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,31 @@ def get_draft(self, post_id: str) -> Dict[str, Any]:
152152
)
153153
raise SubstackAPIError(f"Failed to get post {post_id}: {str(e)}")
154154

155+
def get_published_posts(self, limit: int = 10) -> List[Dict[str, Any]]:
156+
"""Get published posts with error handling"""
157+
try:
158+
result = self.client.get_published_posts(limit=limit)
159+
# get_published_posts returns {'posts': [...]} not a bare list
160+
if isinstance(result, dict) and "posts" in result:
161+
items = result["posts"]
162+
elif isinstance(result, list):
163+
items = result
164+
else:
165+
logger.warning(
166+
f"Unexpected get_published_posts response type: {type(result)}"
167+
)
168+
return []
169+
170+
posts = []
171+
for item in items:
172+
checked = self._handle_response(item, "get_published_posts[item]")
173+
if isinstance(checked, dict):
174+
posts.append(checked)
175+
return posts
176+
except Exception as e:
177+
logger.error(f"get_published_posts error: {type(e).__name__}: {str(e)}")
178+
return []
179+
155180
def get_drafts(self, limit: int = 10) -> List[Dict[str, Any]]:
156181
"""Get drafts with error handling"""
157182
try:

tests/unit/test_post_handler.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,31 @@ async def test_invalid_content_type(self):
243243
title="Test", content="Content", content_type="invalid"
244244
)
245245

246+
@pytest.mark.asyncio
247+
async def test_list_published_uses_published_endpoint(self):
248+
"""list_published must call get_published_posts, not get_drafts"""
249+
mock_posts = [
250+
{"id": "post-1", "title": "Published 1", "post_date": "2026-04-08T12:00:00Z"},
251+
{"id": "post-2", "title": "Published 2", "post_date": "2026-03-23T12:00:00Z"},
252+
]
253+
self.mock_client.get_published_posts = Mock(return_value=mock_posts)
254+
255+
result = await self.handler.list_published(limit=10)
256+
257+
assert len(result) == 2
258+
assert result[0]["title"] == "Published 1"
259+
self.mock_client.get_published_posts.assert_called_once_with(limit=10)
260+
self.mock_client.get_drafts.assert_not_called()
261+
262+
@pytest.mark.asyncio
263+
async def test_list_published_respects_limit(self):
264+
"""list_published passes limit to get_published_posts"""
265+
self.mock_client.get_published_posts = Mock(return_value=[])
266+
267+
await self.handler.list_published(limit=5)
268+
269+
self.mock_client.get_published_posts.assert_called_once_with(limit=5)
270+
246271
@pytest.mark.asyncio
247272
async def test_error_handling(self):
248273
"""Test error handling when API calls fail"""

0 commit comments

Comments
 (0)