fix(news): revert get_item() to use get_items() + filter

Reverts the "perf(news): use direct API endpoint for get_item()" change
from commit 92c4bf3 which incorrectly assumed GET /items/{itemId} exists.

The News API (v1-2, v1-3, v2) does not provide a direct endpoint to
retrieve individual items. The only /items/{itemId} routes are POST
operations for marking items read/unread/starred.

Changes:
- Restore original get_item() implementation that fetches all items
  and filters in Python
- Update exception from HTTPStatusError to ValueError
- Restore documentation explaining API limitation
- Update unit tests to mock get_items() instead of _make_request()
- Add test for ValueError when item not found

Fixes vector processor 405 errors when indexing news items.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2025-12-13 15:47:27 +01:00
parent 5faf7cf45f
commit d61e33113c
2 changed files with 38 additions and 10 deletions
+12 -3
View File
@@ -228,6 +228,10 @@ class NewsClient(BaseNextcloudClient):
async def get_item(self, item_id: int) -> dict[str, Any]:
"""Get a specific item by ID.
Note: The News API doesn't have a direct single-item endpoint,
so we fetch all items and filter. For efficiency, consider
caching or using get_items with specific feed if known.
Args:
item_id: Item ID
@@ -235,10 +239,15 @@ class NewsClient(BaseNextcloudClient):
Item data
Raises:
HTTPStatusError: 404 if item not found
ValueError: If item not found
"""
response = await self._make_request("GET", f"{self.API_BASE}/items/{item_id}")
return response.json()
# Fetch all items and find the one we need
# This is inefficient but the API doesn't provide a direct endpoint
items = await self.get_items(batch_size=-1, get_read=True)
for item in items:
if item.get("id") == item_id:
return item
raise ValueError(f"Item {item_id} not found")
async def get_updated_items(
self,
+26 -7
View File
@@ -310,14 +310,16 @@ async def test_news_api_get_items_unread_only(mocker):
async def test_news_api_get_item(mocker):
"""Test that get_item fetches a single item by ID."""
item = create_mock_news_item(item_id=123, title="Single Item")
mock_response = create_mock_response(status_code=200, json_data=item)
"""Test that get_item fetches all items and filters for the requested ID."""
# Create multiple items, only one should be returned
items = [
create_mock_news_item(item_id=100, title="Other Item 1"),
create_mock_news_item(item_id=123, title="Single Item"),
create_mock_news_item(item_id=200, title="Other Item 2"),
]
mock_client = mocker.AsyncMock(spec=httpx.AsyncClient)
mock_make_request = mocker.patch.object(
NewsClient, "_make_request", return_value=mock_response
)
mock_get_items = mocker.patch.object(NewsClient, "get_items", return_value=items)
client = NewsClient(mock_client, "testuser")
result = await client.get_item(item_id=123)
@@ -325,7 +327,24 @@ async def test_news_api_get_item(mocker):
assert result["id"] == 123
assert result["title"] == "Single Item"
mock_make_request.assert_called_once_with("GET", "/apps/news/api/v1-3/items/123")
# Verify it fetched all items with correct params
mock_get_items.assert_called_once_with(batch_size=-1, get_read=True)
async def test_news_api_get_item_not_found(mocker):
"""Test that get_item raises ValueError when item not found."""
items = [
create_mock_news_item(item_id=100, title="Item 1"),
create_mock_news_item(item_id=200, title="Item 2"),
]
mock_client = mocker.AsyncMock(spec=httpx.AsyncClient)
mocker.patch.object(NewsClient, "get_items", return_value=items)
client = NewsClient(mock_client, "testuser")
with pytest.raises(ValueError, match="Item 999 not found"):
await client.get_item(item_id=999)
async def test_news_api_get_updated_items(mocker):