From 5e989dd839c5e6f4db5ebb0a619f8d539a40dde5 Mon Sep 17 00:00:00 2001 From: Wesley Barroso Lopes Date: Tue, 30 Apr 2024 13:02:49 -0300 Subject: [PATCH] Returns an error message when an Invalid occurs when validating a controlpanel field (#1771) * Returns an error message when an Invalid occurs when validating a controlpanel field * Translates controlpanel validation error message * Does not return the "error" key as an object in controlpanel When deserializing the controlpanel, do not return the "error" key as an object, but rather as a string "ValidationError". On the front end, we are unable to convert an object to json. So we need to return a string. This is the same way content deserialization does. * Improves error handling --------- Co-authored-by: David Glick --- news/1771.bugfix | 1 + .../deserializer/controlpanels/__init__.py | 14 +++++++++++--- .../restapi/tests/test_services_controlpanels.py | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 news/1771.bugfix diff --git a/news/1771.bugfix b/news/1771.bugfix new file mode 100644 index 0000000000..55046bdae0 --- /dev/null +++ b/news/1771.bugfix @@ -0,0 +1 @@ +Returns an error message when an Invalid error occurs when validating a controlpanel field. Also translates the message. @wesleybl diff --git a/src/plone/restapi/deserializer/controlpanels/__init__.py b/src/plone/restapi/deserializer/controlpanels/__init__.py index 53b9a2a07c..f27b8fd4b8 100644 --- a/src/plone/restapi/deserializer/controlpanels/__init__.py +++ b/src/plone/restapi/deserializer/controlpanels/__init__.py @@ -9,7 +9,9 @@ from zope.component import adapter from zope.component import getUtility from zope.component import queryMultiAdapter +from zope.i18n import translate from zope.interface import implementer +from zope.interface.exceptions import Invalid from zope.schema import getFields from zope.schema.interfaces import ValidationError @@ -32,7 +34,7 @@ def __init__(self, controlpanel): self.context = self.controlpanel.context self.request = self.controlpanel.request - def __call__(self): + def __call__(self, mask_validation_errors=True): data = json_body(self.controlpanel.request) proxy = self.registry.forInterface(self.schema, prefix=self.schema_prefix) @@ -61,10 +63,10 @@ def __call__(self): field.validate(value) # Set the value. setattr(proxy, name, value) - except ValueError as e: - errors.append({"message": str(e), "field": name, "error": e}) except ValidationError as e: errors.append({"message": e.doc(), "field": name, "error": e}) + except (ValueError, Invalid) as e: + errors.append({"message": str(e), "field": name, "error": e}) else: field_data[name] = value @@ -77,4 +79,10 @@ def __call__(self): errors.append({"error": error, "message": str(error)}) if errors: + for error in errors: + if mask_validation_errors: + # Drop Python specific error classes in order to be able to better handle + # errors on front-end + error["error"] = "ValidationError" + error["message"] = translate(error["message"], context=self.request) raise BadRequest(errors) diff --git a/src/plone/restapi/tests/test_services_controlpanels.py b/src/plone/restapi/tests/test_services_controlpanels.py index f23affcca5..54a63b13a1 100644 --- a/src/plone/restapi/tests/test_services_controlpanels.py +++ b/src/plone/restapi/tests/test_services_controlpanels.py @@ -115,6 +115,22 @@ def test_update_required(self): self.assertIn("message", response) self.assertIn("Required input is missing.", response["message"]) + def test_update_validation(self): + response = self.api_session.patch( + "/@controlpanels/socialmedia", json={"twitter_username": "@test"} + ) + response = response.json() + self.assertIn( + 'Twitter username should not include the "@" prefix character.', + response["message"], + ) + + def test_update_validation_status(self): + response = self.api_session.patch( + "/@controlpanels/socialmedia", json={"twitter_username": "@test"} + ) + self.assertEqual(response.status_code, 400) + def test_get_usergroup_control_panel(self): # This control panel does not exist in Plone 5 response = self.api_session.get("/@controlpanels/usergroup")