Skip to content

Commit

Permalink
Merge 9667a26 into 9c5bd45
Browse files Browse the repository at this point in the history
  • Loading branch information
sneridagh committed Feb 16, 2019
2 parents 9c5bd45 + 9667a26 commit 80c7de6
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 1 deletion.
7 changes: 7 additions & 0 deletions news/671.feature
@@ -0,0 +1,7 @@
Improved serialization/deserialization of the Plone Site root to make
possible an opt-in to support Tiles attributes on it. The @workflow endpoint
now answers an empty object instead of a 404 for the Plone Site Root object,
hiding the implementation details (the Plone Site Root has no workflow for
historical reasons) and equalizing it to the other content types, making the
API more consistent.
[sneridagh]
21 changes: 21 additions & 0 deletions src/plone/restapi/deserializer/site.py
Expand Up @@ -8,6 +8,8 @@
from plone.restapi.deserializer.mixins import OrderingMixin
from zope.publisher.interfaces import IRequest

import json


@implementer(IDeserializeFromJson)
@adapter(IPloneSiteRoot, IRequest)
Expand All @@ -33,4 +35,23 @@ def __call__(self, validate_all=False):
data['ordering']['subset_ids'] = self.context.contentIds()
self.handle_ordering(data)

# Volto Tiles on the Plone Site root faker
if 'tiles' in data:
if not getattr(self.context, 'tiles', False):
self.context.manage_addProperty('tiles', json.dumps(data['tiles']), 'string') # noqa
else:
self.context.manage_changeProperties(tiles=json.dumps(data['tiles'])) # noqa

if 'tiles_layout' in data:
if not getattr(self.context, 'tiles_layout', False):
self.context.manage_addProperty('tiles_layout', json.dumps(data['tiles_layout']), 'string') # noqa
else:
self.context.manage_changeProperties(tiles_layout=json.dumps(data['tiles_layout'])) # noqa

if 'title' in data:
self.context.setTitle(data['title'])

if 'description' in data:
self.context.manage_changeProperties(description=data['description']) # noqa

return self.context
7 changes: 6 additions & 1 deletion src/plone/restapi/serializer/site.py
Expand Up @@ -10,6 +10,8 @@
from zope.interface import Interface
from zope.interface import implementer

import json


@implementer(ISerializeToJson)
@adapter(IPloneSiteRoot, Interface)
Expand Down Expand Up @@ -44,7 +46,10 @@ def __call__(self, version=None):
'@type': 'Plone Site',
'title': self.context.Title(),
'parent': {},
'is_folderish': True
'is_folderish': True,
'description': self.context.description,
'tiles': json.loads(getattr(self.context, 'tiles', '{}')),
'tiles_layout': json.loads(getattr(self.context, 'tiles_layout', '{}')) # noqa
}

# Insert expandable elements
Expand Down
8 changes: 8 additions & 0 deletions src/plone/restapi/services/workflow/configure.zcml
Expand Up @@ -3,6 +3,14 @@
xmlns:plone="http://namespaces.plone.org/plone"
xmlns:zcml="http://namespaces.zope.org/zcml">

<plone:service
method="GET"
name="@workflow"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
factory=".info.WorkflowInfoService"
permission="zope2.View"
/>

<plone:service
method="GET"
name="@workflow"
Expand Down
10 changes: 10 additions & 0 deletions src/plone/restapi/services/workflow/info.py
Expand Up @@ -2,6 +2,7 @@
from Products.CMFCore.WorkflowCore import WorkflowException
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.interfaces._content import IWorkflowAware
from Products.CMFPlone.interfaces import IPloneSiteRoot
from plone.restapi.interfaces import IExpandableElement
from plone.restapi.serializer.converters import json_compatible
from plone.restapi.services import Service
Expand All @@ -28,6 +29,14 @@ def __call__(self, expand=False):
if not expand:
return result

# Prevent 404 on site root on workflow request
# Although 404 will be more semantic, for the sake of uniformity of the
# API we fake the response to the endpoint by providing an empty
# response instead of a 404.
if IPloneSiteRoot.providedBy(self.context):
result['workflow'].update({'history': [], 'transitions': []})
return result

wftool = getToolByName(self.context, 'portal_workflow')
try:
history = wftool.getInfoFor(self.context, "review_history")
Expand Down Expand Up @@ -69,6 +78,7 @@ def __call__(self, expand=False):
class WorkflowInfoService(Service):
"""Get workflow information
"""

def reply(self):
info = WorkflowInfo(self.context, self.request)
return info(expand=True)['workflow']
3 changes: 3 additions & 0 deletions src/plone/restapi/tests/http-examples/jwt_logged_in.resp
Expand Up @@ -15,6 +15,7 @@ Content-Type: application/json
},
"@id": "http://localhost:55001/plone/",
"@type": "Plone Site",
"description": "",
"id": "plone",
"is_folderish": true,
"items": [
Expand All @@ -28,5 +29,7 @@ Content-Type: application/json
],
"items_total": 1,
"parent": {},
"tiles": {},
"tiles_layout": {},
"title": "Plone site"
}
3 changes: 3 additions & 0 deletions src/plone/restapi/tests/http-examples/siteroot.resp
Expand Up @@ -15,6 +15,7 @@ Content-Type: application/json
},
"@id": "http://localhost:55001/plone",
"@type": "Plone Site",
"description": "",
"id": "plone",
"is_folderish": true,
"items": [
Expand All @@ -28,5 +29,7 @@ Content-Type: application/json
],
"items_total": 1,
"parent": {},
"tiles": {},
"tiles_layout": {},
"title": "Plone site"
}
46 changes: 46 additions & 0 deletions src/plone/restapi/tests/test_deserialize_siteroot.py
@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
from plone.restapi.interfaces import IDeserializeFromJson
from plone.restapi.testing import PLONE_RESTAPI_DX_INTEGRATION_TESTING
from zope.component import getMultiAdapter

import json
import unittest


class TestDXContentDeserializer(unittest.TestCase):

layer = PLONE_RESTAPI_DX_INTEGRATION_TESTING

def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']

def deserialize(self, body='{}', validate_all=False, context=None):
context = context or self.portal
self.request['BODY'] = body
deserializer = getMultiAdapter((context, self.request),
IDeserializeFromJson)
return deserializer(validate_all=validate_all)

def test_opt_in_tiles_deserializer(self):
tiles = {
"0358abe2-b4f1-463d-a279-a63ea80daf19": {
"@type": "description"
},
"07c273fc-8bfc-4e7d-a327-d513e5a945bb": {
"@type": "title"
}
}
tiles_layout = {
"items": [
"07c273fc-8bfc-4e7d-a327-d513e5a945bb",
"0358abe2-b4f1-463d-a279-a63ea80daf19"
]
}

self.deserialize(
body='{{"tiles": {}, "tiles_layout": {}}}'.format(
json.dumps(tiles), json.dumps(tiles_layout)))

self.assertEqual(tiles, json.loads(self.portal.tiles))
self.assertEqual(tiles_layout, json.loads(self.portal.tiles_layout))
50 changes: 50 additions & 0 deletions src/plone/restapi/tests/test_serializer.py
Expand Up @@ -12,6 +12,7 @@
from Products.CMFCore.utils import getToolByName
from zope.component import getMultiAdapter

import json
import os
import unittest

Expand Down Expand Up @@ -370,3 +371,52 @@ def test_serialize_to_json_collection(self):
],
self.serialize(self.portal.collection1).get('items')
)

def test_serialize_returns_site_root_common(self):
self.assertIn(
'title',
self.serialize(self.portal),
)
self.assertIn(
'description',
self.serialize(self.portal)
)

def test_serialize_returns_site_root_opt_in_tiles_not_present(self):
self.assertEqual(
self.serialize(self.portal)['tiles'],
{}
)
self.assertEqual(
self.serialize(self.portal)['tiles_layout'],
{}
)

def test_serialize_returns_site_root_opt_in_tiles_present(self):
tiles = {
"0358abe2-b4f1-463d-a279-a63ea80daf19": {
"@type": "description"
},
"07c273fc-8bfc-4e7d-a327-d513e5a945bb": {
"@type": "title"
}
}
tiles_layout = {
"items": [
"07c273fc-8bfc-4e7d-a327-d513e5a945bb",
"0358abe2-b4f1-463d-a279-a63ea80daf19"
]
}
self.portal.manage_addProperty(
'tiles', json.dumps(tiles), 'string')
self.portal.manage_addProperty(
'tiles_layout', json.dumps(tiles_layout), 'string')

self.assertEqual(
self.serialize(self.portal)['tiles'],
tiles
)
self.assertEqual(
self.serialize(self.portal)['tiles_layout'],
tiles_layout
)
8 changes: 8 additions & 0 deletions src/plone/restapi/tests/test_workflow.py
Expand Up @@ -79,6 +79,14 @@ def test_expanded_workflow_info_in_content_serialization(self):
self.assertIn('transitions', obj['@components']['workflow'])
self.assertIn('history', obj['@components']['workflow'])

def test_workflow_info_empty_on_siteroot(self):
wfinfo = getMultiAdapter((self.portal, self.request),
name=u'GET_application_json_@workflow')
obj = wfinfo.reply()

self.assertEquals(obj['transitions'], [])
self.assertEquals(obj['history'], [])


class TestWorkflowTransition(TestCase):

Expand Down

0 comments on commit 80c7de6

Please sign in to comment.