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

Implement IJSONSummarySerializerMetadata #1261

Merged
merged 4 commits into from
Nov 4, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions news/1250.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Implement IJSONSummarySerializerMetadata allowing addons to extend the metadata returned by Summary serializer.
[ericof]
16 changes: 16 additions & 0 deletions src/plone/restapi/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,19 @@ def __init__(field, context, request):

def __call__(value):
"""Extract text from the block value. Returns text"""


class IJSONSummarySerializerMetadata(Interface):
"""Configure JSONSummary serializer."""

def default_metadata():
"""Returns a set with default metadata to be serialized."""

def field_accessors():
"""Returns a dictionary with field accessors to be used during serialization."""

def non_metadata_attributes():
"""Returns a set with non metadata attributes."""

def blocklisted_attributes():
"""Returns a set with attributes blocked during serialization."""
6 changes: 6 additions & 0 deletions src/plone/restapi/serializer/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,10 @@

<include package=".controlpanels" />

<!-- Summary Serializer Metadata -->
<utility
factory=".summary.JSONSummarySerializerMetadata"
name="plone.restapi.summary_serializer_metadata"
/>

</configure>
100 changes: 68 additions & 32 deletions src/plone/restapi/serializer/summary.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,66 @@
from plone.app.contentlisting.interfaces import IContentListingObject
from plone.restapi.interfaces import ISerializeToJsonSummary
from plone.restapi.interfaces import IJSONSummarySerializerMetadata
from plone.restapi.serializer.converters import json_compatible
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import WorkflowException
from Products.CMFPlone.interfaces import IPloneSiteRoot
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface

# fmt: off
DEFAULT_METADATA_FIELDS = {
'@id',
'@type',
'description',
'review_state',
'title',
}

FIELD_ACCESSORS = {
"@id": "getURL",
"@type": "PortalType",
"description": "Description",
"title": "Title",
}

NON_METADATA_ATTRIBUTES = {
"getPath",
"getURL",
}

BLACKLISTED_ATTRIBUTES = {
'getDataOrigin',
'getObject',
'getUserData',
}
# fmt: on
from zope.component import getAllUtilitiesRegisteredFor


@implementer(IJSONSummarySerializerMetadata)
class JSONSummarySerializerMetadata:
def default_metadata_fields(self):
return {
"@id",
"@type",
"description",
"review_state",
"title",
}

def field_accessors(self):
return {
"@id": "getURL",
"@type": "PortalType",
"description": "Description",
"title": "Title",
}

def non_metadata_attributes(self):
return {
"getPath",
"getURL",
}

def blocklisted_attributes(self):
return {
"getDataOrigin",
"getObject",
"getUserData",
}


def merge_serializer_metadata_utilities_data():
"""Merge data returned by utilities registered for IJSONSummarySerializerMetadata."""
serializer_metadata = {
"default_metadata_fields": set(),
"field_accessors": {},
"non_metadata_attributes": set(),
"blocklisted_attributes": set(),
}
utils = getAllUtilitiesRegisteredFor(IJSONSummarySerializerMetadata)
for name in serializer_metadata.keys():
for util in utils:
method = getattr(util, name, None)
if not method:
continue
value = method()
serializer_metadata[name].update(value)
return serializer_metadata


@implementer(ISerializeToJsonSummary)
Expand All @@ -49,15 +75,25 @@ class DefaultJSONSummarySerializer:
def __init__(self, context, request):
self.context = context
self.request = request
# Cache summary_serializer_metadata on request
metadata = self.request.form.get("summary_serializer_metadata", None)
if not metadata:
metadata = merge_serializer_metadata_utilities_data()
self.request.set("summary_serializer_metadata", metadata)

self.default_metadata_fields = metadata["default_metadata_fields"]
self.field_accessors = metadata["field_accessors"]
self.non_metadata_attributes = metadata["non_metadata_attributes"]
self.blocklisted_attributes = metadata["blocklisted_attributes"]

def __call__(self):
obj = IContentListingObject(self.context)

summary = {}
for field in self.metadata_fields():
if field.startswith("_") or field in BLACKLISTED_ATTRIBUTES:
if field.startswith("_") or field in self.blocklisted_attributes:
continue
accessor = FIELD_ACCESSORS.get(field, field)
accessor = self.field_accessors.get(field, field)
value = getattr(obj, accessor, None)
try:
if callable(value):
Expand All @@ -78,11 +114,11 @@ def metadata_fields(self):
fields_cache = self.request.get("_summary_fields_cache", None)
if fields_cache is None:
catalog = getToolByName(self.context, "portal_catalog")
fields_cache = set(catalog.schema()) | NON_METADATA_ATTRIBUTES
fields_cache = set(catalog.schema()) | self.non_metadata_attributes
self.request.set("_summary_fields_cache", fields_cache)
additional_metadata_fields = fields_cache

return DEFAULT_METADATA_FIELDS | additional_metadata_fields
return self.default_metadata_fields | additional_metadata_fields


@implementer(ISerializeToJsonSummary)
Expand Down