Repository: gtalarico/pyairtable
Pull request #417 (2024-12-06 19:09:34)

Release 2.3.7
Backports a fix for #415 to the 2.x branch. See #416.


Patch:
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index 39c6d46..62e0791 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -2,6 +2,14 @@
 Changelog
 =========
 
+2.3.7 (2024-12-06)
+------------------------
+
+* Fix for `#415 <https://github.com/gtalarico/pyairtable/issues/415>`_
+  which caused an endless loop when making a request via `POST /listRecords`.
+  - `PR #416 <https://github.com/gtalarico/pyairtable/pull/416>`_,
+    `PR #417 <https://github.com/gtalarico/pyairtable/pull/417>`_
+
 2.3.6 (2024-11-11)
 ------------------------
 
diff --git a/pyairtable/__init__.py b/pyairtable/__init__.py
index 6437f63..bbb8fda 100644
--- a/pyairtable/__init__.py
+++ b/pyairtable/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "2.3.6"
+__version__ = "2.3.7"
 
 from .api import Api, Base, Table
 from .api.enterprise import Enterprise
diff --git a/pyairtable/api/api.py b/pyairtable/api/api.py
index b6fb0c1..a4d9811 100644
--- a/pyairtable/api/api.py
+++ b/pyairtable/api/api.py
@@ -372,7 +372,7 @@ class Api:
                 return
             if not (offset := _get_offset_field(response)):
                 return
-            params = {**params, offset_field: offset}
+            options = {**options, offset_field: offset}
 
     def chunked(self, iterable: Sequence[T]) -> Iterator[Sequence[T]]:
         """
diff --git a/pyairtable/api/params.py b/pyairtable/api/params.py
index 82e2293..8f0e029 100644
--- a/pyairtable/api/params.py
+++ b/pyairtable/api/params.py
@@ -78,6 +78,9 @@ OPTIONS_TO_PARAMETERS = {
     # get webhook payloads
     "limit": "limit",
     "cursor": "cursor",
+    # get audit log events
+    "next": "next",
+    "previous": "previous",
 }
 
 


Test patch:
diff --git a/tests/test_api_api.py b/tests/test_api_api.py
index 6e5f32f..22ceb95 100644
--- a/tests/test_api_api.py
+++ b/tests/test_api_api.py
@@ -116,6 +116,42 @@ def test_iterate_requests(api: Api, requests_mock):
     assert responses == [response["json"] for response in response_list]
 
 
+def test_iterate_requests__post(api: Api, requests_mock):
+    url = "https://example.com"
+    # prepare a few pages of dummy responses
+    response_list = [
+        {"json": {"page": 0, "offset": 1}},
+        {"json": {"page": 1, "offset": 2}},
+        {"json": {"page": 2}},
+    ]
+    m = requests_mock.post(url, response_list=response_list)
+    # construct a request that will get converted from GET to POST
+    formula = "X" * (api.MAX_URL_LENGTH + 1)
+    pages = list(
+        api.iterate_requests(
+            "GET",
+            url=url,
+            fallback=("POST", url),
+            options={"formula": formula},
+        )
+    )
+    # ensure we got the responses we expected
+    assert pages == [
+        {"page": 0, "offset": 1},
+        {"page": 1, "offset": 2},
+        {"page": 2},
+    ]
+    # ensure we made three POST requests
+    assert m.call_count == 3
+    assert len(pages) == 3
+    requests = [r.json() for r in m.request_history]
+    assert requests == [
+        {"filterByFormula": formula},
+        {"filterByFormula": formula, "offset": "1"},
+        {"filterByFormula": formula, "offset": "2"},
+    ]
+
+
 def test_iterate_requests__invalid_type(api: Api, requests_mock):
     url = "https://example.com"
     response_list = [{"json": {"page": n, "offset": n + 1}} for n in range(1, 3)]