Skip to content

Commit 9ca9d44

Browse files
vdusekclaude
andauthored
fix: correct boolean parsing of pagination desc header in list_items (#621)
## Summary - `bool(response.headers['x-apify-pagination-desc'])` always returns `True` for any non-empty string, including `"false"` - The `desc` field in `ListPage` from `list_items` was always `True` regardless of the actual API response - Fixed by using explicit string comparison: `.lower() == 'true'` (both sync and async variants) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 58fd8c8 commit 9ca9d44

File tree

2 files changed

+91
-2
lines changed

2 files changed

+91
-2
lines changed

src/apify_client/clients/resource_clients/dataset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def list_items(
156156
'limit': int(
157157
response.headers['x-apify-pagination-limit']
158158
), # API returns 999999999999 when no limit is used
159-
'desc': bool(response.headers['x-apify-pagination-desc']),
159+
'desc': response.headers['x-apify-pagination-desc'].lower() == 'true',
160160
}
161161
)
162162

@@ -769,7 +769,7 @@ async def list_items(
769769
'limit': int(
770770
response.headers['x-apify-pagination-limit']
771771
), # API returns 999999999999 when no limit is used
772-
'desc': bool(response.headers['x-apify-pagination-desc']),
772+
'desc': response.headers['x-apify-pagination-desc'].lower() == 'true',
773773
}
774774
)
775775

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from typing import TYPE_CHECKING
5+
6+
import pytest
7+
from werkzeug import Response
8+
9+
from apify_client import ApifyClient, ApifyClientAsync
10+
11+
if TYPE_CHECKING:
12+
from collections.abc import Callable
13+
14+
from pytest_httpserver import HTTPServer
15+
16+
DATASET_ID = 'test-dataset-id'
17+
ITEMS_PATH = f'/v2/datasets/{DATASET_ID}/items'
18+
19+
20+
def _make_list_items_handler(*, desc_header_value: str) -> Callable:
21+
"""Create a handler that returns a list_items response with the given desc header value."""
22+
23+
def handler(_request: object) -> Response:
24+
return Response(
25+
status=200,
26+
headers={
27+
'x-apify-pagination-total': '2',
28+
'x-apify-pagination-offset': '0',
29+
'x-apify-pagination-count': '2',
30+
'x-apify-pagination-limit': '999999999999',
31+
'x-apify-pagination-desc': desc_header_value,
32+
'content-type': 'application/json',
33+
},
34+
response=json.dumps([{'id': 1}, {'id': 2}]),
35+
)
36+
37+
return handler
38+
39+
40+
@pytest.mark.parametrize('desc_header_value', ['false', 'False', 'FALSE'])
41+
def test_list_items_desc_false_sync(httpserver: HTTPServer, desc_header_value: str) -> None:
42+
httpserver.expect_request(ITEMS_PATH).respond_with_handler(
43+
_make_list_items_handler(desc_header_value=desc_header_value),
44+
)
45+
api_url = httpserver.url_for('/').removesuffix('/')
46+
47+
client = ApifyClient(token='test-token', api_url=api_url)
48+
result = client.dataset(DATASET_ID).list_items()
49+
50+
assert result.desc is False
51+
52+
53+
@pytest.mark.parametrize('desc_header_value', ['true', 'True', 'TRUE'])
54+
def test_list_items_desc_true_sync(httpserver: HTTPServer, desc_header_value: str) -> None:
55+
httpserver.expect_request(ITEMS_PATH).respond_with_handler(
56+
_make_list_items_handler(desc_header_value=desc_header_value),
57+
)
58+
api_url = httpserver.url_for('/').removesuffix('/')
59+
60+
client = ApifyClient(token='test-token', api_url=api_url)
61+
result = client.dataset(DATASET_ID).list_items()
62+
63+
assert result.desc is True
64+
65+
66+
@pytest.mark.parametrize('desc_header_value', ['false', 'False', 'FALSE'])
67+
async def test_list_items_desc_false_async(httpserver: HTTPServer, desc_header_value: str) -> None:
68+
httpserver.expect_request(ITEMS_PATH).respond_with_handler(
69+
_make_list_items_handler(desc_header_value=desc_header_value),
70+
)
71+
api_url = httpserver.url_for('/').removesuffix('/')
72+
73+
client = ApifyClientAsync(token='test-token', api_url=api_url)
74+
result = await client.dataset(DATASET_ID).list_items()
75+
76+
assert result.desc is False
77+
78+
79+
@pytest.mark.parametrize('desc_header_value', ['true', 'True', 'TRUE'])
80+
async def test_list_items_desc_true_async(httpserver: HTTPServer, desc_header_value: str) -> None:
81+
httpserver.expect_request(ITEMS_PATH).respond_with_handler(
82+
_make_list_items_handler(desc_header_value=desc_header_value),
83+
)
84+
api_url = httpserver.url_for('/').removesuffix('/')
85+
86+
client = ApifyClientAsync(token='test-token', api_url=api_url)
87+
result = await client.dataset(DATASET_ID).list_items()
88+
89+
assert result.desc is True

0 commit comments

Comments
 (0)