Skip to content

Commit

Permalink
[fc] Repository: plone.restapi
Browse files Browse the repository at this point in the history
Branch: refs/heads/main
Date: 2025-01-21T19:14:39-08:00
Author: Mauro Amico (mamico) <mauro.amico@gmail.com>
Commit: plone/plone.restapi@5a120c4

fix: contextnavigation when a file has an unknown type (#1864)

* fix: contextnavigation when a file has an unknown type

* changelog

* Update news/1864.bugfix

---------

Co-authored-by: David Glick &lt;david@glicksoftware.com&gt;

Files changed:
A news/1864.bugfix
M src/plone/restapi/services/contextnavigation/get.py
M src/plone/restapi/tests/test_services_contextnavigation.py
  • Loading branch information
davisagli committed Jan 22, 2025
1 parent 08d2943 commit 9726a8d
Showing 1 changed file with 11 additions and 30 deletions.
41 changes: 11 additions & 30 deletions last_commit.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,26 @@ Repository: plone.restapi


Branch: refs/heads/main
Date: 2025-01-21T19:01:56-08:00
Author: Faakhir Zahid (Faakhir30) <110815427+Faakhir30@users.noreply.github.com>
Commit: https://github.com/plone/plone.restapi/commit/88f2bc9cba9d2f3057813f8e0b4bc029ddcee117
Date: 2025-01-21T19:14:39-08:00
Author: Mauro Amico (mamico) <mauro.amico@gmail.com>
Commit: https://github.com/plone/plone.restapi/commit/5a120c416db8bbd6168fc8c10fdf1ef75e5eac62

add query param to search registry records. (#1861)
fix: contextnavigation when a file has an unknown type (#1864)

* add query param to search registry records.
* fix: contextnavigation when a file has an unknown type

* refactor serializer.
* changelog

* format using black.

* use tmp registry isntead of seperate serializer class.

* udpate http resp files

* update docs.

* update docs.

* version added

* Update docs/source/endpoints/registry.md

* Apply suggestions from code review
* Update news/1864.bugfix

---------

Co-authored-by: Steve Piercy &lt;web@stevepiercy.com&gt;
Co-authored-by: David Glick &lt;david@glicksoftware.com&gt;

Files changed:
A news/1861.feature
A src/plone/restapi/tests/http-examples/registry_get_list_filtered.req
A src/plone/restapi/tests/http-examples/registry_get_list_filtered.resp
M docs/source/endpoints/registry.md
M src/plone/restapi/services/registry/get.py
M src/plone/restapi/tests/test_documentation.py
M src/plone/restapi/tests/test_registry.py
A news/1864.bugfix
M src/plone/restapi/services/contextnavigation/get.py
M src/plone/restapi/tests/test_services_contextnavigation.py

b'diff --git a/docs/source/endpoints/registry.md b/docs/source/endpoints/registry.md\nindex 57fb9f3426..1a5e0a9c7f 100644\n--- a/docs/source/endpoints/registry.md\n+++ b/docs/source/endpoints/registry.md\n@@ -52,6 +52,25 @@ Example response:\n :language: http\n ```\n \n+## Filter list of registry records\n+\n+```{versionadded} plone.restapi 9.10.0\n+```\n+\n+You can filter a list of registry records and batch the results.\n+To do so, append a query string to the listing endpoint with a `q` parameter and its value set to the prefix of the desired record name.\n+See {doc}`../usage/batching` for details of how to work with batched results.\n+\n+```{eval-rst}\n+.. http:example:: curl httpie python-requests\n+ :request: ../../../src/plone/restapi/tests/http-examples/registry_get_list_filtered.req\n+```\n+\n+Example response:\n+\n+```{literalinclude} ../../../src/plone/restapi/tests/http-examples/registry_get_list_filtered.resp\n+:language: http\n+```\n \n ## Updating registry records\n \ndiff --git a/news/1861.feature b/news/1861.feature\nnew file mode 100644\nindex 0000000000..5e3538d612\n--- /dev/null\n+++ b/news/1861.feature\n@@ -0,0 +1 @@\n+In the `@registry` endpoint, added support for filtering the list of registry records. @Faakhir30\n\\ No newline at end of file\ndiff --git a/src/plone/restapi/services/registry/get.py b/src/plone/restapi/services/registry/get.py\nindex b75ecc07a4..689d712da3 100644\n--- a/src/plone/restapi/services/registry/get.py\n+++ b/src/plone/restapi/services/registry/get.py\n@@ -1,3 +1,4 @@\n+from plone.registry import Registry\n from plone.registry.interfaces import IRegistry\n from plone.restapi.interfaces import ISerializeToJson\n from plone.restapi.serializer.converters import json_compatible\n@@ -35,5 +36,15 @@ def reply(self):\n value = registry[self._get_record_name]\n return json_compatible(value)\n else: # batched listing\n- serializer = getMultiAdapter((registry, self.request), ISerializeToJson)\n+ if q := self.request.form.get("q"):\n+\n+ tmp_registry = Registry()\n+ for key in registry.records.keys():\n+ if key.startswith(q):\n+ tmp_registry.records[key] = registry.records[key]\n+ registry = tmp_registry\n+ serializer = getMultiAdapter(\n+ (registry, self.request),\n+ ISerializeToJson,\n+ )\n return serializer()\ndiff --git a/src/plone/restapi/tests/http-examples/registry_get_list_filtered.req b/src/plone/restapi/tests/http-examples/registry_get_list_filtered.req\nnew file mode 100644\nindex 0000000000..f9cfd8b3c8\n--- /dev/null\n+++ b/src/plone/restapi/tests/http-examples/registry_get_list_filtered.req\n@@ -0,0 +1,3 @@\n+GET /plone/@registry?q=Products.CMFPlone HTTP/1.1\n+Accept: application/json\n+Authorization: Basic YWRtaW46c2VjcmV0\ndiff --git a/src/plone/restapi/tests/http-examples/registry_get_list_filtered.resp b/src/plone/restapi/tests/http-examples/registry_get_list_filtered.resp\nnew file mode 100644\nindex 0000000000..8962fb5d70\n--- /dev/null\n+++ b/src/plone/restapi/tests/http-examples/registry_get_list_filtered.resp\n@@ -0,0 +1,57 @@\n+HTTP/1.1 200 OK\n+Content-Type: application/json\n+\n+{\n+ "@id": "http://localhost:55001/plone/@registry?q=Products.CMFPlone",\n+ "items": [\n+ {\n+ "name": "Products.CMFPlone.i18nl10n.override_dateformat.Enabled",\n+ "schema": {\n+ "properties": {\n+ "description": "Override the translation machinery",\n+ "factory": "Yes/No",\n+ "title": "Enabled",\n+ "type": "boolean"\n+ }\n+ },\n+ "value": false\n+ },\n+ {\n+ "name": "Products.CMFPlone.i18nl10n.override_dateformat.date_format_long",\n+ "schema": {\n+ "properties": {\n+ "description": "Default value: %Y-%m-%d %H:%M (2038-01-19 03:14)",\n+ "factory": "Text line (String)",\n+ "title": "old ZMI property: localLongTimeFormat",\n+ "type": "string"\n+ }\n+ },\n+ "value": "%Y-%m-%d %H:%M"\n+ },\n+ {\n+ "name": "Products.CMFPlone.i18nl10n.override_dateformat.date_format_short",\n+ "schema": {\n+ "properties": {\n+ "description": "Default value: %Y-%m-%d (2038-01-19)",\n+ "factory": "Text line (String)",\n+ "title": "old ZMI property: localTimeFormat",\n+ "type": "string"\n+ }\n+ },\n+ "value": "%Y-%m-%d"\n+ },\n+ {\n+ "name": "Products.CMFPlone.i18nl10n.override_dateformat.time_format",\n+ "schema": {\n+ "properties": {\n+ "description": "Default value: %H:%M (03:14)",\n+ "factory": "Text line (String)",\n+ "title": "old ZMI property: localTimeOnlyFormat",\n+ "type": "string"\n+ }\n+ },\n+ "value": "%H:%M"\n+ }\n+ ],\n+ "items_total": 4\n+}\ndiff --git a/src/plone/restapi/tests/test_documentation.py b/src/plone/restapi/tests/test_documentation.py\nindex 574873ce6e..916f654b2a 100644\n--- a/src/plone/restapi/tests/test_documentation.py\n+++ b/src/plone/restapi/tests/test_documentation.py\n@@ -549,6 +549,10 @@ def test_documentation_registry_get_list(self):\n response = self.api_session.get("/@registry")\n save_request_and_response_for_docs("registry_get_list", response)\n \n+ def test_documentation_registry_get_list_filtered(self):\n+ response = self.api_session.get("/@registry?q=Products.CMFPlone")\n+ save_request_and_response_for_docs("registry_get_list_filtered", response)\n+\n def test_documentation_types(self):\n response = self.api_session.get("/@types")\n save_request_and_response_for_docs("types", response)\ndiff --git a/src/plone/restapi/tests/test_registry.py b/src/plone/restapi/tests/test_registry.py\nindex 61a266b644..069ca363cb 100644\n--- a/src/plone/restapi/tests/test_registry.py\n+++ b/src/plone/restapi/tests/test_registry.py\n@@ -107,3 +107,12 @@ def test_get_listing(self):\n self.assertIn("items", response)\n self.assertIn("batching", response)\n self.assertIn("next", response["batching"])\n+\n+ def test_get_filtered_listing(self):\n+ response = self.api_session.get("/@registry?q=foo.bar1")\n+ self.assertEqual(response.status_code, 200)\n+ response = response.json()\n+ # 10 records from foo.bar10 to foo.bar19 and 1 record foo.bar1\n+ self.assertEqual(len(response["items"]), 11)\n+ self.assertEqual(response["items"][0]["name"], "foo.bar1")\n+ self.assertEqual(response["items"][0]["value"], "Lorem Ipsum")\n'
b'diff --git a/news/1864.bugfix b/news/1864.bugfix\nnew file mode 100644\nindex 0000000000..9e63a64abd\n--- /dev/null\n+++ b/news/1864.bugfix\n@@ -0,0 +1 @@\n+In the `@contextnavigation` endpoint, return `"icon": null` for Files with a mimetype not found in the `content_type_registry`, instead of raising `TypeError`. @mamico\ndiff --git a/src/plone/restapi/services/contextnavigation/get.py b/src/plone/restapi/services/contextnavigation/get.py\nindex 0fe0d7a054..3f22602222 100644\n--- a/src/plone/restapi/services/contextnavigation/get.py\n+++ b/src/plone/restapi/services/contextnavigation/get.py\n@@ -357,7 +357,8 @@ def getMimeTypeIcon(self, node):\n mtt = getToolByName(self.context, "mimetypes_registry")\n if fileo.contentType:\n ctype = mtt.lookup(fileo.contentType)\n- return os.path.join(portal_url, guess_icon_path(ctype[0]))\n+ if ctype:\n+ return os.path.join(portal_url, guess_icon_path(ctype[0]))\n except AttributeError:\n pass\n \ndiff --git a/src/plone/restapi/tests/test_services_contextnavigation.py b/src/plone/restapi/tests/test_services_contextnavigation.py\nindex 9f8405e95a..05fe3c037e 100644\n--- a/src/plone/restapi/tests/test_services_contextnavigation.py\n+++ b/src/plone/restapi/tests/test_services_contextnavigation.py\n@@ -3,6 +3,7 @@\n from plone.app.testing import SITE_OWNER_NAME\n from plone.app.testing import SITE_OWNER_PASSWORD\n from plone.app.testing import TEST_USER_ID\n+from plone.namedfile.file import NamedBlobFile\n from plone.registry.interfaces import IRegistry\n from plone.restapi.services.contextnavigation.get import ContextNavigation\n from plone.restapi.testing import PLONE_RESTAPI_DX_FUNCTIONAL_TESTING\n@@ -99,6 +100,9 @@ def populateSite(self):\n folder2.invokeFactory("Document", "doc22")\n folder2.invokeFactory("Document", "doc23")\n folder2.invokeFactory("File", "file21")\n+ folder2.file21.file = NamedBlobFile(\n+ data="Hello World", contentType="text/plain", filename="file.txt"\n+ )\n folder2.invokeFactory("Folder", "folder21")\n folder21 = getattr(folder2, "folder21")\n folder21.invokeFactory("Document", "doc211")\n@@ -996,3 +1000,28 @@ def testContextNavigation(self):\n "/plone/folder1/doc11",\n )\n )\n+\n+ def testIcon(self):\n+ view = self.renderer(\n+ self.portal.folder2.file21,\n+ opts(root_path="/folder2", topLevel=0),\n+ )\n+ tree = view.getNavTree()\n+ self.assertTrue(tree)\n+ self.assertEqual(\n+ tree["items"][0]["icon"],\n+ "/plone/++resource++mimetype.icons/txt.png",\n+ )\n+\n+ def testIconNotRegisteredMimetype(self):\n+ self.portal.folder2.file21.file.contentType = "plain/x-text"\n+ view = self.renderer(\n+ self.portal.folder2.file21,\n+ opts(root_path="/folder2", topLevel=0),\n+ )\n+ tree = view.getNavTree()\n+ self.assertTrue(tree)\n+ self.assertEqual(\n+ tree["items"][0]["icon"],\n+ None,\n+ )\n'

0 comments on commit 9726a8d

Please sign in to comment.