Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for nested schemas with resolveuid block deserializer #1595

Merged
merged 13 commits into from May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions news/1595.feature
@@ -0,0 +1,2 @@
Added support for nested schemas with resolveuid deserializer.
[JeffersonBledsoe]
47 changes: 24 additions & 23 deletions src/plone/restapi/deserializer/blocks.py
Expand Up @@ -107,27 +107,30 @@ def __init__(self, context, request):

def __call__(self, block):
# Convert absolute links to resolveuid
for field in self.fields:
link = block.get(field, "")
if link and isinstance(link, str):
block[field] = path2uid(context=self.context, link=link)
elif link and isinstance(link, list):
# Detect if it has an object inside with an "@id" key (object_widget)
if len(link) > 0 and isinstance(link[0], dict) and "@id" in link[0]:
result = []
for item in link:
item_clone = deepcopy(item)
item_clone["@id"] = path2uid(
context=self.context, link=item_clone["@id"]
)
result.append(item_clone)

block[field] = result
elif len(link) > 0 and isinstance(link[0], str):
block[field] = [
path2uid(context=self.context, link=item) for item in link
]
return block
return self._process_data(block)

def _process_data(self, data, field=None):
JeffersonBledsoe marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(data, str) and field in self.fields:
return path2uid(context=self.context, link=data)
if isinstance(data, list):
return [self._process_data(data=value, field=field) for value in data]
if isinstance(data, dict):
if data.get("@type", None) == "URL" and data.get("value", None):
data["value"] = path2uid(context=self.context, link=data["value"])
elif data.get("@id", None):
item_clone = deepcopy(data)
item_clone["@id"] = path2uid(
context=self.context, link=item_clone["@id"]
)
return {
field: self._process_data(data=value, field=field)
for field, value in item_clone.items()
}
return {
field: self._process_data(data=value, field=field)
for field, value in data.items()
}
return data


class TextBlockDeserializerBase:
Expand Down Expand Up @@ -162,7 +165,6 @@ def __init__(self, context, request):
self.request = request

def __call__(self, block):

portal_transforms = api.portal.get_tool(name="portal_transforms")
raw_html = block.get("html", "")
data = portal_transforms.convertTo(
Expand Down Expand Up @@ -301,7 +303,6 @@ class SlateBlockDeserializerRoot(SlateBlockDeserializerBase):

class SlateTableBlockTransformer(SlateBlockTransformer):
def __call__(self, block):

rows = block.get("table", {}).get("rows", [])
for row in rows:
cells = row.get("cells", [])
Expand Down
41 changes: 23 additions & 18 deletions src/plone/restapi/serializer/blocks.py
Expand Up @@ -97,24 +97,29 @@ def __init__(self, context, request):
self.context = context
self.request = request

def __call__(self, value):
for field in self.fields:
if field in value.keys():
link = value.get(field, "")
if isinstance(link, str):
value[field] = uid_to_url(link)
elif isinstance(link, list):
if len(link) > 0 and isinstance(link[0], dict) and "@id" in link[0]:
result = []
for item in link:
item_clone = deepcopy(item)
item_clone["@id"] = uid_to_url(item_clone["@id"])
result.append(item_clone)

value[field] = result
elif len(link) > 0 and isinstance(link[0], str):
value[field] = [uid_to_url(item) for item in link]
return value
def __call__(self, block):
return self._process_data(block)

def _process_data(self, data, field=None):
if isinstance(data, str) and field in self.fields:
return uid_to_url(data)
if isinstance(data, list):
return [self._process_data(data=value, field=field) for value in data]
if isinstance(data, dict):
if data.get("@type", None) == "URL" and data.get("value", None):
data["value"] = uid_to_url(data["value"])
elif data.get("@id", None):
item_clone = deepcopy(data)
item_clone["@id"] = uid_to_url(item_clone["@id"])
return {
field: self._process_data(data=value, field=field)
for field, value in item_clone.items()
}
return {
field: self._process_data(data=value, field=field)
for field, value in data.items()
}
return data


class TextBlockSerializerBase:
Expand Down
45 changes: 45 additions & 0 deletions src/plone/restapi/tests/test_blocks_deserializer.py
Expand Up @@ -269,6 +269,35 @@ def test_blocks_custom_block_doesnt_resolve_non_standard_fields(self):
self.portal.doc1.absolute_url(),
)

def test_blocks_custom_block_resolve_standard_fields_nested(self):
self.deserialize(
blocks={
"123": {
"@type": "foo",
"bar": [{"url": self.portal.doc1.absolute_url()}],
}
}
)
doc_uid = IUUID(self.portal.doc1)

self.assertEqual(
self.portal.doc1.blocks["123"]["bar"][0]["url"], f"../resolveuid/{doc_uid}"
)

self.deserialize(
blocks={
"123": {
"@type": "foo",
"bar": [{"href": self.portal.doc1.absolute_url()}],
}
}
)
doc_uid = IUUID(self.portal.doc1)

self.assertEqual(
self.portal.doc1.blocks["123"]["bar"][0]["href"], f"../resolveuid/{doc_uid}"
)

def test_deserialize_blocks_smart_href_array_volto_object_browser(self):
self.deserialize(
blocks={
Expand All @@ -285,6 +314,22 @@ def test_deserialize_blocks_smart_href_array_volto_object_browser(self):
f"../resolveuid/{doc_uid}",
)

def test_deserialize_blocks_smart_href_array_volto_object_browser_nested(self):
self.deserialize(
blocks={
"123": {
"@type": "foo",
"bar": [{"href": [{"@id": self.portal.doc1.absolute_url()}]}],
}
}
)
doc_uid = IUUID(self.portal.doc1)

self.assertEqual(
self.portal.doc1.blocks["123"]["bar"][0]["href"][0]["@id"],
f"../resolveuid/{doc_uid}",
)

def test_deserialize_blocks_smart_href_array(self):
self.deserialize(
blocks={
Expand Down