Skip to content

Commit

Permalink
Fix serializer/deserializer for footerTop blocks: use blocks handlers…
Browse files Browse the repository at this point in the history
… to fix data
  • Loading branch information
cekk committed Apr 2, 2024
1 parent a049dad commit 7bae14e
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 38 deletions.
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Changelog
1.3.3 (unreleased)
------------------

- Nothing changed yet.
- Fix serializer/deserializer for footerTop blocks: use blocks handlers to fix data.
[cekk]


1.3.2 (2024-03-14)
Expand Down
2 changes: 1 addition & 1 deletion base.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ recipe = plone.recipe.codeanalysis
directory = ${buildout:directory}/src
flake8-exclude=bootstrap.py,bootstrap-buildout.py,docs,bin,*.egg,setup.py,overrides,omelette
flake8-max-complexity = 25
flake8-ignore = E203, E266, E501, W503, E999
flake8-ignore = E203, E266, E501, W503, E999, C101
flake8-max-line-length = 200
# flake8-select = B,C,E,F,W,T4,B9
flake8-extensions =
Expand Down
12 changes: 4 additions & 8 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,8 @@ ignore =
.gitattributes

[isort]
# for details see
# http://docs.plone.org/develop/styleguide/python.html#grouping-and-sorting
force_alphabetical_sort = True
force_single_line = True
lines_after_imports = 2
line_length = 200
not_skip = __init__.py
# black compatible isort rules:
profile = plone

[flake8]
# black compatible flake8 rules:
Expand All @@ -22,10 +17,11 @@ ignore =
E501
T001
C813
C101
# E203, E266
exclude = bootstrap.py,docs,*.egg.,omelette
max-line-length = 88
max-complexity = 18
select = B,C,E,F,W,T4,B9
builtins = unicode,basestring

builtins = unicode,basestring
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
from plone.app.registry.browser import controlpanel
from redturtle.voltoplugin.editablefooter.interfaces import (
IEditableFooterSettings,
)
from redturtle.voltoplugin.editablefooter import _
from redturtle.voltoplugin.editablefooter.interfaces import IEditableFooterSettings


class EditableFooterForm(controlpanel.RegistryEditForm):
Expand Down
14 changes: 14 additions & 0 deletions src/redturtle/voltoplugin/editablefooter/restapi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from plone.restapi.blocks import iter_block_transform_handlers
from plone.restapi.blocks import visit_blocks


def fix_footer_top_blocks(context, blocks, transformer):
if not blocks:
return blocks
for block in visit_blocks(context, blocks):
new_block = block.copy()
for handler in iter_block_transform_handlers(context, block, transformer):
new_block = handler(new_block)
block.clear()
block.update(new_block)
return blocks
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from plone.restapi.controlpanels import RegistryConfigletPanel
from redturtle.voltoplugin.editablefooter.interfaces import IEditableFooterSettings
from redturtle.voltoplugin.editablefooter.interfaces import (
IRedturtleVoltoEditablefooterLayer,
IEditableFooterSettings,
)
from zope.component import adapter
from zope.interface import implementer
Expand Down
45 changes: 37 additions & 8 deletions src/redturtle/voltoplugin/editablefooter/restapi/deserializer.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
from plone import api
from plone.restapi.deserializer import json_body
from plone.restapi.deserializer.controlpanels import (
ControlpanelDeserializeFromJson,
)
from plone.restapi.deserializer.controlpanels import ControlpanelDeserializeFromJson
from plone.restapi.interfaces import IBlockFieldDeserializationTransformer
from plone.restapi.interfaces import IDeserializeFromJson
from redturtle.voltoplugin.editablefooter.interfaces import (
IEditableFooterSettings,
)
from redturtle.voltoplugin.editablefooter import _
from redturtle.voltoplugin.editablefooter.interfaces import IEditableFooterSettings
from redturtle.voltoplugin.editablefooter.restapi import fix_footer_top_blocks
from zExceptions import BadRequest
from zope.component import adapter
from zope.interface import implementer
Expand All @@ -21,10 +21,39 @@ def __call__(self):
req = json_body(self.controlpanel.request)
proxy = self.registry.forInterface(self.schema, prefix=self.schema_prefix)
errors = []
data = req.get("footer_columns", {})
data = req.get("footer_columns", [])
if not data:
errors.append({"message": "Missing data", "field": "footer_columns"})
errors.append(
{
"message": api.portal.translate(
_("missing_data_label", default="Missing data")
),
"field": "footer_columns",
}
)
raise BadRequest(errors)
if not isinstance(data, list):
errors.append(
{
"message": api.portal.translate(
_(
"wrong_type_data_label",
default="Wrong type: need to be a list of values",
)
),
"field": "footer_columns",
}
)
raise BadRequest(errors)
for path_setting in data:
footer_top = path_setting.get("footerTop", {}).get("blocks", {})
if footer_top:
path_setting["footerTop"]["blocks"] = fix_footer_top_blocks(
context=self.context,
blocks=footer_top,
transformer=IBlockFieldDeserializationTransformer,
)

try:
# later we need to do some validations
setattr(proxy, "footer_columns", json.dumps(data))
Expand Down
16 changes: 13 additions & 3 deletions src/redturtle/voltoplugin/editablefooter/restapi/get.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
from redturtle.voltoplugin.editablefooter.interfaces import (
IEditableFooterSettings,
)
from plone import api
from plone.registry.interfaces import IRegistry
from plone.restapi.interfaces import IBlockFieldSerializationTransformer
from plone.restapi.services import Service
from redturtle.voltoplugin.editablefooter.interfaces import IEditableFooterSettings
from redturtle.voltoplugin.editablefooter.restapi import fix_footer_top_blocks
from zope.component import getUtility
from zope.interface import implementer
from zope.publisher.interfaces import IPublishTraverse


try:
from plone.volto.interfaces import IVoltoSettings

Expand All @@ -34,6 +35,15 @@ def reply(self):
portal_url = self.get_portal_url()
for el in data or []:
if isinstance(el, dict):

footer_top = el.get("footerTop", {}).get("blocks", {})
if footer_top:
el["footerTop"]["blocks"] = fix_footer_top_blocks(
context=self.context,
blocks=footer_top,
transformer=IBlockFieldSerializationTransformer,
)

for item in el.get("items") or []:
if (
isinstance(item, dict)
Expand Down
25 changes: 19 additions & 6 deletions src/redturtle/voltoplugin/editablefooter/restapi/serializer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from redturtle.voltoplugin.editablefooter.interfaces import (
IEditableFooterSettings,
)
from plone import api
from plone.restapi.interfaces import IBlockFieldSerializationTransformer
from plone.restapi.interfaces import ISerializeToJson
from plone.restapi.serializer.controlpanels import ControlpanelSerializeToJson
from redturtle.voltoplugin.editablefooter.interfaces import IEditableFooterSettings
from redturtle.voltoplugin.editablefooter.restapi import fix_footer_top_blocks
from zope.component import adapter
from zope.interface import implementer

Expand All @@ -14,8 +15,20 @@
@adapter(IEditableFooterSettings)
class EditableFooterControlpanelSerializeToJson(ControlpanelSerializeToJson):
def __call__(self):
json_data = super(EditableFooterControlpanelSerializeToJson, self).__call__()
json_data = super().__call__()
conf = json_data["data"].get("footer_columns", "")
if conf:
json_data["data"]["footer_columns"] = json.loads(conf)
if not conf:
return json_data
footer_columns = json.loads(conf)

for path_setting in footer_columns:
footer_top = path_setting.get("footerTop", {}).get("blocks", {})
if footer_top:
path_setting["footerTop"]["blocks"] = fix_footer_top_blocks(
context=api.portal.get(),
blocks=footer_top,
transformer=IBlockFieldSerializationTransformer,
)

json_data["data"]["footer_columns"] = footer_columns
return json_data
2 changes: 1 addition & 1 deletion src/redturtle/voltoplugin/editablefooter/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
from plone.restapi.testing import PloneRestApiDXLayer
from plone.testing import z2

import redturtle.voltoplugin.editablefooter
import plone.restapi
import plone.volto
import redturtle.voltoplugin.editablefooter


class VoltoEditableFooterLayer(PloneSandboxLayer):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
from plone.restapi.testing import RelativeSession
from redturtle.voltoplugin.editablefooter.interfaces import IEditableFooterSettings
from redturtle.voltoplugin.editablefooter.testing import (
VOLTO_EDITABLEFOOTER_API_FUNCTIONAL_TESTING,
)
from redturtle.voltoplugin.editablefooter.interfaces import (
IEditableFooterSettings,
)
from transaction import commit

import json
Expand Down Expand Up @@ -63,6 +61,11 @@ def setUp(self):
self.api_session.headers.update({"Accept": "application/json"})
self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

self.document = api.content.create(
container=self.portal, type="Document", title="document"
)
commit()

def tearDown(self):
self.api_session.close()

Expand All @@ -82,16 +85,114 @@ def test_set_wrong_data(self):
self.assertEqual(response.status_code, 400)

def test_deserializer_convert_dict_into_json_string(self):
data = {"foo": "", "bar": 2}
data = [{"foo": "", "bar": 2}]
self.api_session.patch(self.controlpanel_url, json={"footer_columns": data})
commit()
self.assertEqual(
self.get_record_value(field="footer_columns"), json.dumps(data)
)

def test_deserializer_raise_error_if_footer_columns_not_a_list(self):
data = {"foo": "", "bar": 2}
res = self.api_session.patch(
self.controlpanel_url, json={"footer_columns": data}
)
self.assertEqual(res.status_code, 400)

def test_deserializer_save_resolveuid_in_footerTop_internal_links(self):
data = [
{
"footerTop": {
"blocks": {
"2955de0f-ea5e-475f-8efd-34c7060b99b9": {
"@type": "slate",
"plaintext": " link ",
"value": [
{
"children": [
{
"type": "link",
"data": {
"url": self.document.absolute_url(),
"dataElement": "",
},
"children": [{"text": "link"}],
},
],
"type": "p",
}
],
}
},
"blocks_layout": {
"items": ["2955de0f-ea5e-475f-8efd-34c7060b99b9"]
},
},
"rootPath": "/",
}
]
res = self.api_session.patch(
self.controlpanel_url, json={"footer_columns": data}
)
commit()

stored_value = json.loads(self.get_record_value(field="footer_columns"))

self.assertEqual(res.status_code, 204)
self.assertEqual(
stored_value[0]["footerTop"]["blocks"][
"2955de0f-ea5e-475f-8efd-34c7060b99b9"
]["value"][0]["children"][0]["data"]["url"],
f"resolveuid/{self.document.UID()}",
)

def test_serializer_return_expanded_resolveuid_in_footerTop_internal_links(self):
data = [
{
"footerTop": {
"blocks": {
"2955de0f-ea5e-475f-8efd-34c7060b99b9": {
"@type": "slate",
"plaintext": " link ",
"value": [
{
"children": [
{
"type": "link",
"data": {
"url": self.document.absolute_url(),
"dataElement": "",
},
"children": [{"text": "link"}],
},
],
"type": "p",
}
],
}
},
"blocks_layout": {
"items": ["2955de0f-ea5e-475f-8efd-34c7060b99b9"]
},
},
"rootPath": "/",
}
]
self.api_session.patch(self.controlpanel_url, json={"footer_columns": data})
commit()

res = self.api_session.get(self.controlpanel_url).json()

self.assertEqual(
res["data"]["footer_columns"][0]["footerTop"]["blocks"][
"2955de0f-ea5e-475f-8efd-34c7060b99b9"
]["value"][0]["children"][0]["data"]["url"],
self.document.absolute_url(),
)

def test_serializer_convert_string_into_json_object(self):
self.assertEqual(self.get_record_value(field="footer_columns"), "")
value = {"foo": "bar"}
value = [{"foo": "bar"}]
self.set_record_value(field="footer_columns", value=json.dumps(value))

response = self.api_session.get(self.controlpanel_url)
Expand Down
Loading

0 comments on commit 7bae14e

Please sign in to comment.