Repository: plone/plone.restapi (language: python)

Issue:
Regressions in `@querystringsearch` GET endpoint param handling
The GET version of the `@querystringsearch` endpoint added in #1616 has some regressions in handling query parameters:
- `b_start` and `b_size` are not in the request for ZCatalog to use
- The fields used by the summary serializer are not where it is looking


Discussion / hints:

Fix patch:
diff --git a/news/1621.bugfix b/news/1621.bugfix
new file mode 100644
index 0000000000..7878c27ea5
--- /dev/null
+++ b/news/1621.bugfix
@@ -0,0 +1,1 @@
+Fix bugs in handling parameters when the `@querystringsearch` endpoint is called with the GET method. @davisagli
diff --git a/src/plone/restapi/services/querystringsearch/get.py b/src/plone/restapi/services/querystringsearch/get.py
index 79de69accd..3683dbd161 100644
--- a/src/plone/restapi/services/querystringsearch/get.py
+++ b/src/plone/restapi/services/querystringsearch/get.py
@@ -7,8 +7,6 @@
 from urllib import parse
 from zope.component import getMultiAdapter
 
-import json
-
 
 zcatalog_version = get_distribution("Products.ZCatalog").version
 if parse_version(zcatalog_version) >= parse_version("5.1"):
@@ -24,7 +22,8 @@ def __init__(self, context, request):
         self.context = context
         self.request = request
 
-    def __call__(self, data):
+    def __call__(self):
+        data = json_body(self.request)
         query = data.get("query", None)
         b_start = int(data.get("b_start", 0))
         b_size = int(data.get("b_size", 25))
@@ -73,14 +72,16 @@ class QuerystringSearchPost(Service):
 
     def reply(self):
         querystring_search = QuerystringSearch(self.context, self.request)
-        return querystring_search(data=json_body(self.request))
+        return querystring_search()
 
 
 class QuerystringSearchGet(Service):
     """Returns the querystring search results given a p.a.querystring data."""
 
     def reply(self):
+        # We need to copy the JSON query parameters from the querystring
+        # into the request body, because that's where other code expects to find them
+        self.request["BODY"] = parse.unquote(self.request.form.get("query", "{}"))
+
         querystring_search = QuerystringSearch(self.context, self.request)
-        return querystring_search(
-            data=json.loads(parse.unquote(self.request.form.get("query", "{}")))
-        )
+        return querystring_search()


Test patch:
diff --git a/src/plone/restapi/tests/test_services_querystringsearch.py b/src/plone/restapi/tests/test_services_querystringsearch.py
index 69f777a3bd..b199fbc46a 100644
--- a/src/plone/restapi/tests/test_services_querystringsearch.py
+++ b/src/plone/restapi/tests/test_services_querystringsearch.py
@@ -53,14 +53,17 @@ def test_querystringsearch_basic(self):
         self.assertNotIn("effective", response.json()["items"][0])
 
     def test_querystringsearch_basic_get(self):
+        self.portal.invokeFactory("Document", "doc2", title="Test Document 2")
+        transaction.commit()
+
         response = self.api_session.get(
-            "/@querystring-search?query=%7B%22query%22%3A%5B%7B%22i%22%3A%22portal_type%22%2C%22o%22%3A%20%22plone.app.querystring.operation.selection.any%22%2C%22v%22%3A%5B%22Document%22%5D%7D%5D%7D"
+            "/@querystring-search?query=%7B%22query%22%3A%20%5B%7B%22i%22%3A%20%22portal_type%22%2C%20%22o%22%3A%20%22plone.app.querystring.operation.selection.any%22%2C%20%22v%22%3A%20%5B%22Document%22%5D%7D%5D%2C%20%22b_size%22%3A%201%7D"
         )
 
         self.assertEqual(response.status_code, 200)
         self.assertIn("items", response.json())
         self.assertIn("items_total", response.json())
-        self.assertEqual(response.json()["items_total"], 1)
+        self.assertEqual(response.json()["items_total"], 2)
         self.assertEqual(len(response.json()["items"]), 1)
         self.assertNotIn("effective", response.json()["items"][0])