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

WCM-285: revert IConnector <-> DAVPropertyConverter behaviour #857

Merged
merged 9 commits into from
Sep 25, 2024
1 change: 1 addition & 0 deletions core/docs/changelog/WCM-285.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WCM-285: bring back the old behavior for the connector and write data as read if enabled
16 changes: 14 additions & 2 deletions core/src/zeit/cms/content/dav.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,15 +327,20 @@ def toProperty(self, value):
class BoolProperty:
def __init__(self, context, properties, propertykey):
self.context = context
self.has_sql_type = ConnectorModel.column_by_name(*propertykey) is not None

def fromProperty(self, value):
if self.has_sql_type and FEATURE_TOGGLES.find('read_metadata_columns'):
return value
return self._fromProperty(value)

@staticmethod
def _fromProperty(value):
return value.lower() in ('yes', 'true')

def toProperty(self, value):
if self.has_sql_type and FEATURE_TOGGLES.find('write_metadata_columns'):
return value
return self._toProperty(value)

@staticmethod
Expand All @@ -355,18 +360,20 @@ def __init__(self, context, properties, propertykey):
def fromProperty(self, value):
if not value:
return None
if self.has_sql_type and FEATURE_TOGGLES.find('read_metadata_columns'):
return value
return self._fromProperty(value)

@staticmethod
def _fromProperty(value):
# We have _mostly_ iso8601, but some old content has the format
# "Thu, 13 Mar 2008 13:48:37 GMT", so we use a lenient parser.
if not value:
return None
date = pendulum.parse(value, strict=False)
return date.in_tz('UTC')

def toProperty(self, value):
if self.has_sql_type and FEATURE_TOGGLES.find('write_metadata_columns'):
return value
return self._toProperty(value)

@staticmethod
Expand Down Expand Up @@ -400,6 +407,7 @@ class CollectionTextLineProperty:
SPLIT_PATTERN = re.compile(r'(?!\\);')

def __init__(self, context, value_type, properties, propertykey):
self.has_sql_type = ConnectorModel.column_by_name(*propertykey) is not None
self.context = context
self.value_type = value_type
self.properties = properties
Expand All @@ -410,6 +418,8 @@ def __init__(self, context, value_type, properties, propertykey):
self._type = self._type[0]

def fromProperty(self, value):
if self.has_sql_type and FEATURE_TOGGLES.find('read_metadata_columns'):
return value
typ = zope.component.getMultiAdapter(
(self.value_type, self.properties, self.propertykey),
zeit.cms.content.interfaces.IDAVPropertyConverter,
Expand All @@ -434,6 +444,8 @@ def fromProperty(self, value):
return self._type(result)

def toProperty(self, value):
if self.has_sql_type and FEATURE_TOGGLES.find('write_metadata_columns'):
return value
typ = zope.component.getMultiAdapter(
(self.value_type, self.properties, self.propertykey),
zeit.cms.content.interfaces.IDAVPropertyConverter,
Expand Down
85 changes: 0 additions & 85 deletions core/src/zeit/connector/converter.py

This file was deleted.

72 changes: 72 additions & 0 deletions core/src/zeit/connector/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
import os.path

import gocept.cache.property
import grokcore.component as grok
import lxml.etree
import sqlalchemy
import zope.app.file.image
import zope.interface

from zeit.cms.content.sources import FEATURE_TOGGLES
from zeit.connector.interfaces import ID_NAMESPACE, CannonicalId
from zeit.connector.models import Content
import zeit.cms.config
import zeit.cms.content.dav
import zeit.connector.interfaces
Expand Down Expand Up @@ -251,13 +255,24 @@ def _get_properties(self, id):
return properties

properties.update(parse_properties(xml))
self._convert_sql_types(properties)

if zeit.connector.interfaces.RESOURCE_TYPE_PROPERTY not in properties:
properties[zeit.connector.interfaces.RESOURCE_TYPE_PROPERTY] = self._guess_type(id)

self.property_cache[id] = properties
return properties

def _convert_sql_types(self, properties):
if not FEATURE_TOGGLES.find('read_metadata_columns'):
return
for key, value in properties.items():
column = Content.column_by_name(*key)
if column is None:
continue
converter = IConverter(column)
properties[key] = converter.deserialize(value)

def _guess_type(self, id):
path = self._path(id)
if os.path.isdir(path):
Expand Down Expand Up @@ -303,3 +318,60 @@ def parse_properties(xml):
value += '</tag:rankedTags>'
properties[('keywords', 'http://namespaces.zeit.de/CMS/tagging')] = value
return properties


class IConverter(zope.interface.Interface):
"""Converts webdav values to and from the postgresql database."""

def serialize(value):
pass

def deserialize(value):
pass


@grok.implementer(IConverter)
@grok.adapter(sqlalchemy.Column)
def converter_from_column_type(column):
return IConverter(column.type)


@grok.implementer(IConverter)
class DefaultConverter(grok.Adapter):
grok.context(zope.interface.Interface)

def serialize(self, value):
return value

def deserialize(self, value):
return value


class BoolConverter(DefaultConverter):
grok.context(sqlalchemy.Boolean)

def serialize(self, value):
return zeit.cms.content.dav.BoolProperty._toProperty(value)

def deserialize(self, value):
return zeit.cms.content.dav.BoolProperty._fromProperty(value)


class IntConverter(DefaultConverter):
grok.context(sqlalchemy.Integer)

def serialize(self, value):
return str(value)

def deserialize(self, value):
return int(value)


class DatetimeConverter(DefaultConverter):
grok.context(sqlalchemy.TIMESTAMP)

def serialize(self, value):
return zeit.cms.content.dav.DatetimeProperty._toProperty(value)

def deserialize(self, value):
return zeit.cms.content.dav.DatetimeProperty._fromProperty(value)
10 changes: 0 additions & 10 deletions core/src/zeit/connector/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,16 +354,6 @@ class IResourceInvalidatedEvent(zope.interface.Interface):
id = zope.interface.Attribute('Unique id of resource')


class IConverter(zope.interface.Interface):
"""Converts webdav values to and from the postgresql database."""

def serialize(value):
pass

def deserialize(value):
pass


@zope.interface.implementer(IResourceInvalidatedEvent)
class ResourceInvalidatedEvent:
def __init__(self, id):
Expand Down
13 changes: 12 additions & 1 deletion core/src/zeit/connector/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import sqlalchemy
import zope.event

from zeit.cms.content.sources import FEATURE_TOGGLES
from zeit.connector.filesystem import DefaultConverter
from zeit.connector.interfaces import (
ID_NAMESPACE,
UUID_PROPERTY,
Expand All @@ -24,7 +26,7 @@
MoveError,
)
from zeit.connector.lock import lock_is_foreign
from zeit.connector.models import DevelopmentContent as Content
from zeit.connector.models import Content
import zeit.cms.config
import zeit.cms.repository.interfaces
import zeit.connector.cache
Expand Down Expand Up @@ -368,6 +370,7 @@ def _get_properties(self, id):
properties = super()._get_properties(id)
else:
properties = properties.copy()
self._convert_sql_types(properties)
return properties

def _set_properties(self, id, properties):
Expand All @@ -383,6 +386,14 @@ def _set_properties(self, id, properties):
stored_properties.pop((name, namespace), None)
continue

if FEATURE_TOGGLES.find('write_metadata_columns'):
column = Content.column_by_name(name, namespace)
converter = zeit.connector.filesystem.IConverter(column)
value = converter.serialize(value)
else:
converter = DefaultConverter(None)
if isinstance(converter, DefaultConverter) and not isinstance(value, str):
raise ValueError('Expected str, got %s: %r' % (type(value), value))
stored_properties[(name, namespace)] = value
self._properties[id] = stored_properties

Expand Down
29 changes: 4 additions & 25 deletions core/src/zeit/connector/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from sqlalchemy.orm import declared_attr, mapped_column, relationship
import pytz
import sqlalchemy
import zope.component

from zeit.cms.content.sources import FEATURE_TOGGLES
from zeit.connector.interfaces import INTERNAL_PROPERTY, DeleteProperty, LockStatus
Expand All @@ -40,7 +39,7 @@ def table_args(tablename):
channels = mapped_column(
JSONB,
nullable=True,
info={'namespace': 'document', 'name': 'channels', 'converter': 'channels'},
info={'namespace': 'document', 'name': 'channels'},
)


Expand Down Expand Up @@ -91,12 +90,6 @@ class DevelopmentCommonMetadata:
access = mapped_column(Unicode, index=True, info={'namespace': 'document', 'name': 'access'})


class DevelopmentZeitWeb:
overscrolling_enabled = mapped_column(
Boolean, info={'namespace': 'document', 'name': 'overscrolling'}
)


class ContentBase:
__abstract__ = True
__tablename__ = 'properties'
Expand Down Expand Up @@ -177,17 +170,6 @@ def lock_status(self):

NS = 'http://namespaces.zeit.de/CMS/'

@staticmethod
def converter(column):
if 'converter' in column.info:
return zope.component.queryAdapter(
column.type,
zeit.connector.interfaces.IConverter,
column.info['converter'],
)
else:
return zeit.connector.interfaces.IConverter(column)

def to_webdav(self):
if self.unsorted is None:
return {}
Expand All @@ -205,9 +187,7 @@ def to_webdav(self):
if FEATURE_TOGGLES.find('read_metadata_columns'):
for column in self._columns_with_name():
namespace, name = column.info['namespace'], column.info['name']
value = getattr(self, column.name)
converter = self.converter(column)
props[(name, self.NS + namespace)] = converter.serialize(value)
props[(name, self.NS + namespace)] = getattr(self, column.name)

if self.lock:
props[('lock_principal', INTERNAL_PROPERTY)] = self.lock.principal
Expand Down Expand Up @@ -235,8 +215,8 @@ def from_webdav(self, props):
namespace, name = column.info['namespace'], column.info['name']
value = props.get((name, self.NS + namespace), self)
if value is not self:
converter = self.converter(column)
setattr(self, column.name, converter.deserialize(value))
setattr(self, column.name, value)
props.pop((name, self.NS + namespace), None)

unsorted = collections.defaultdict(dict)
for (k, ns), v in props.items():
Expand Down Expand Up @@ -314,7 +294,6 @@ class DevelopmentContent(
PublishInfo,
SemanticChange,
DevelopmentCommonMetadata,
DevelopmentZeitWeb,
):
lock_class = 'LockWithMetadataColumns'

Expand Down
Loading