Skip to content

Commit c52f236

Browse files
Update PaginatedList
Based on #605, `PaginatedList` could not process requests which return pagination info in the response body. This update checks for `Link` headers before checking the `meta` property in the response body.
1 parent ac2fc96 commit c52f236

File tree

3 files changed

+101
-4
lines changed

3 files changed

+101
-4
lines changed

canvasapi/paginated_list.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,23 @@ def _get_next_page(self):
5757
)
5858
data = response.json()
5959
self._next_url = None
60+
# Check the response headers first. This is the normal Canvas convention
61+
# for pagination, but there are endpoints which return a `meta` property
62+
# for pagination instead.
63+
# See https://github.com/ucfopen/canvasapi/discussions/605
64+
if response.links:
65+
next_link = response.links.get("next")
66+
elif type(data) is dict and data.get("meta") is not None:
67+
# requests parses headers into dicts, this returns the same
68+
# structure so the regex will still work.
69+
next_link = (
70+
{"url": data.get("meta").get("pagination").get("next"), "rel": "next"}
71+
if data["meta"]["pagination"]["next"]
72+
else None
73+
)
74+
else:
75+
next_link = None
6076

61-
next_link = response.links.get("next")
6277
regex = r"{}(.*)".format(re.escape(self._requester.base_url))
6378

6479
self._next_url = (
@@ -73,8 +88,9 @@ def _get_next_page(self):
7388
try:
7489
data = data[self._root]
7590
except KeyError:
76-
# TODO: Fix this message to make more sense to an end user.
77-
raise ValueError("Invalid root value specified.")
91+
raise ValueError(
92+
"The key <{}> does not exist in the response.".format(self._root)
93+
)
7894

7995
for element in data:
8096
if element is not None:

tests/fixtures/paginated_list.json

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,57 @@
114114
}
115115
],
116116
"status_code": 200
117-
}
117+
},
118+
"no_header_4_2_pages_p1": {
119+
"method": "ANY",
120+
"endpoint": "no_header_four_objects_two_pages",
121+
"data": {
122+
"assessments": [
123+
{
124+
"id": "1",
125+
"name": "object 1"
126+
},
127+
{
128+
"id": "2",
129+
"name": "object 2"
130+
}
131+
],
132+
"meta": {
133+
"pagination": {
134+
"next": "https://example.com/api/v1/no_header_four_objects_two_pages?page=2"
135+
}
136+
}
137+
},
138+
"status_code": 200
139+
},
140+
"no_header_4_2_pages_p2": {
141+
"method": "ANY",
142+
"endpoint": "no_header_four_objects_two_pages?page=2",
143+
"data": {
144+
"assessments": [
145+
{
146+
"id": "3",
147+
"name": "object 3"
148+
},
149+
{
150+
"id": "4",
151+
"name": "object 4"
152+
}
153+
]
154+
},
155+
"status_code": 200
156+
},
157+
"no_header_no_next_key": {
158+
"method": "ANY",
159+
"endpoint": "no_header_no_next_key",
160+
"data": {
161+
"assessments": [],
162+
"meta": {
163+
"pagination": {
164+
"prev": "https://example.com/api/v1/previous"
165+
}
166+
}
167+
}
168+
},
169+
"status_code": 200
118170
}

tests/test_paginated_list.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ def test_root_element_incorrect(self, m):
156156

157157
with self.assertRaises(ValueError):
158158
pag_list[0]
159+
self.assertEqual(
160+
pag_list[0], "The key <wrong> does not exist in the response."
161+
)
159162

160163
def test_root_element(self, m):
161164
register_uris({"account": ["get_enrollment_terms"]}, m)
@@ -202,3 +205,29 @@ def test_negative_index_for_slice_end(self, m):
202205

203206
with self.assertRaises(IndexError):
204207
pag_list[:-1]
208+
209+
def test_paginated_list_no_header(self, m):
210+
register_uris(
211+
{"paginated_list": ["no_header_4_2_pages_p1", "no_header_4_2_pages_p2"]}, m
212+
)
213+
214+
pag_list = PaginatedList(
215+
User,
216+
self.requester,
217+
"GET",
218+
"no_header_four_objects_two_pages",
219+
_root="assessments",
220+
)
221+
222+
self.assertIsInstance(pag_list, PaginatedList)
223+
self.assertEqual(len(list(pag_list)), 4)
224+
self.assertIsInstance(pag_list[0], User)
225+
226+
def test_paginated_list_no_header_no_next(self, m):
227+
register_uris({"paginated_list": ["no_header_no_next_key"]}, m)
228+
229+
pag_list = PaginatedList(
230+
User, self.requester, "GET", "no_header_no_next_key", _root="assessments"
231+
)
232+
233+
self.assertIsInstance(pag_list, PaginatedList)

0 commit comments

Comments
 (0)