Refactor: move diaspora XML generators to entities themselves

This change is backwards incompatible. Any implementations wishing to create a valid Post for example for diaspora shouls now use DiasporaPost.
merge-requests/130/head
Jason Robinson 2016-04-03 22:54:32 +03:00
rodzic 62380a5d77
commit a2920444ca
9 zmienionych plików z 85 dodań i 104 usunięć

Wyświetl plik

@ -1,10 +1,36 @@
# -*- coding: utf-8 -*-
from ..base import Comment
from lxml import etree
from federation.entities.base import Comment, Post
from federation.entities.diaspora.utils import format_dt, struct_to_xml
class DiasporaComment(Comment):
"""Diaspora comments additionally have an author_signature."""
@property
def author_signature(self):
#TODO: implement at later stage when outbound payloads are to be used
return ""
author_signature = ""
def to_xml(self):
element = etree.Element("comment")
struct_to_xml(element, [
{'guid': self.guid},
{'parent_guid': self.target_guid},
{'author_signature': self.author_signature},
{'text': self.raw_content},
{'diaspora_handle': self.handle},
])
return element
class DiasporaPost(Post):
"""Diaspora post, ie status message."""
def to_xml(self):
"""Convert to XML message."""
element = etree.Element("status_message")
struct_to_xml(element, [
{'raw_message': self.raw_content},
{'guid': self.guid},
{'diaspora_handle': self.handle},
{'public': 'true' if self.public else 'false'},
{'created_at': format_dt(self.created_at)}
])
return element

Wyświetl plik

@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
from dateutil.tz import tzlocal, tzutc
from lxml import etree
def ensure_timezone(dt, tz=None):
"""
Make sure the datetime <dt> has a timezone set, using timezone <tz> if it
doesn't. <tz> defaults to the local timezone.
"""
if dt.tzinfo is None:
return dt.replace(tzinfo=tz or tzlocal())
else:
return dt
class EntityConverter(object):
def __init__(self, entity):
self.entity = entity
self.entity_type = entity.__class__.__name__.lower()
def struct_to_xml(self, node, struct):
"""
Turn a list of dicts into XML nodes with tag names taken from the dict
keys and element text taken from dict values. This is a list of dicts
so that the XML nodes can be ordered in the XML output.
"""
for obj in struct:
for k, v in obj.items():
etree.SubElement(node, k).text = v
def convert_to_xml(self):
#TODO: move these to entities themselves as `to_xml` methods
if hasattr(self, "%s_to_xml" % self.entity_type):
method_name = "%s_to_xml" % self.entity_type
return getattr(self, method_name)()
def format_dt(cls, dt):
"""
Format a datetime in the way that D* nodes expect.
"""
return ensure_timezone(dt).astimezone(tzutc()).strftime(
'%Y-%m-%d %H:%M:%S %Z'
)
def post_to_xml(self):
req = etree.Element("status_message")
self.struct_to_xml(req, [
{'raw_message': self.entity.raw_content},
{'guid': self.entity.guid},
{'diaspora_handle': self.entity.handle},
{'public': 'true' if self.entity.public else 'false'},
{'created_at': self.format_dt(self.entity.created_at)}
])
return req
def diasporacomment_to_xml(self):
req = etree.Element("comment")
self.struct_to_xml(req, [
{'guid': self.entity.guid},
{'parent_guid': self.entity.target_guid},
{'author_signature': self.entity.author_signature},
{'text': self.entity.raw_content},
{'diaspora_handle': self.entity.handle},
])
return req

Wyświetl plik

@ -3,13 +3,14 @@ from datetime import datetime
from lxml import etree
from federation.entities.base import Post, Image, Comment
from federation.entities.base import Image
from federation.entities.diaspora.entities import DiasporaPost, DiasporaComment
MAPPINGS = {
"status_message": Post,
"status_message": DiasporaPost,
"photo": Image,
"comment": Comment,
"comment": DiasporaComment,
}
BOOLEAN_KEYS = [

Wyświetl plik

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from dateutil.tz import tzlocal, tzutc
from lxml import etree
def ensure_timezone(dt, tz=None):
"""
Make sure the datetime <dt> has a timezone set, using timezone <tz> if it
doesn't. <tz> defaults to the local timezone.
"""
if dt.tzinfo is None:
return dt.replace(tzinfo=tz or tzlocal())
else:
return dt
def format_dt(dt):
"""
Format a datetime in the way that D* nodes expect.
"""
return ensure_timezone(dt).astimezone(tzutc()).strftime(
'%Y-%m-%d %H:%M:%S %Z'
)
def struct_to_xml(node, struct):
"""
Turn a list of dicts into XML nodes with tag names taken from the dict
keys and element text taken from dict values. This is a list of dicts
so that the XML nodes can be ordered in the XML output.
"""
for obj in struct:
for k, v in obj.items():
etree.SubElement(node, k).text = v

Wyświetl plik

@ -10,7 +10,6 @@ from Crypto.Random import get_random_bytes
from Crypto.Signature import PKCS1_v1_5 as PKCSSign
from lxml import etree
from federation.entities.diaspora.generators import EntityConverter
from federation.exceptions import EncryptedMessageError, NoHeaderInMessageError, NoSenderKeyFoundError
from federation.protocols.base import BaseProtocol
@ -164,8 +163,7 @@ class Protocol(BaseProtocol):
def build_send(self, from_user, to_user, entity, *args, **kwargs):
"""Build POST data for sending out to remotes."""
converter = EntityConverter(entity)
xml = converter.convert_to_xml()
xml = entity.to_xml()
self.init_message(xml, from_user.handle, from_user.private_key)
xml = quote_plus(
self.create_salmon_envelope(to_user.key))

Wyświetl plik

@ -1,28 +1,13 @@
# -*- coding: utf-8 -*-
from unittest.mock import patch
from lxml import etree
from federation.entities.base import Post
from federation.entities.diaspora.entities import DiasporaComment
from federation.entities.diaspora.generators import EntityConverter
from federation.entities.diaspora.entities import DiasporaComment, DiasporaPost
class TestEntityConverterCallsToXML(object):
def test_entity_converter_call_to_xml(self):
entity = Post()
with patch.object(EntityConverter, "post_to_xml", return_value="foo") as mock_to_xml:
entity_converter = EntityConverter(entity=entity)
result = entity_converter.convert_to_xml()
assert result == "foo"
assert mock_to_xml.called
def test_entity_converter_converts_a_post(self):
entity = Post(raw_content="raw_content", guid="guid", handle="handle", public=True)
entity_converter = EntityConverter(entity)
result = entity_converter.convert_to_xml()
class TestEntitiesConvertToXML(object):
def test_post_to_xml(self):
entity = DiasporaPost(raw_content="raw_content", guid="guid", handle="handle", public=True)
result = entity.to_xml()
assert result.tag == "status_message"
assert len(result.find("created_at").text) > 0
result.find("created_at").text = "" # timestamp makes testing painful
@ -31,10 +16,9 @@ class TestEntityConverterCallsToXML(object):
b"</created_at></status_message>"
assert etree.tostring(result) == converted
def test_entity_converter_converts_a_comment(self):
def test_comment_to_xml(self):
entity = DiasporaComment(raw_content="raw_content", guid="guid", target_guid="target_guid", handle="handle")
entity_converter = EntityConverter(entity)
result = entity_converter.convert_to_xml()
result = entity.to_xml()
assert result.tag == "comment"
converted = b"<comment><guid>guid</guid><parent_guid>target_guid</parent_guid>" \
b"<author_signature></author_signature><text>raw_content</text>" \

Wyświetl plik

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from federation.entities.base import Post, Comment
from federation.entities.base import Comment, Post
from federation.entities.diaspora.entities import DiasporaPost, DiasporaComment
from federation.entities.diaspora.mappers import message_to_objects
from federation.tests.fixtures.payloads import DIASPORA_POST_SIMPLE, DIASPORA_POST_COMMENT
@ -12,6 +13,7 @@ class TestDiasporaEntityMappersReceive(object):
entities = message_to_objects(DIASPORA_POST_SIMPLE)
assert len(entities) == 1
post = entities[0]
assert isinstance(post, DiasporaPost)
assert isinstance(post, Post)
assert post.raw_content == "((status message))"
assert post.guid == "((guid))"
@ -23,6 +25,7 @@ class TestDiasporaEntityMappersReceive(object):
entities = message_to_objects(DIASPORA_POST_COMMENT)
assert len(entities) == 1
comment = entities[0]
assert isinstance(comment, DiasporaComment)
assert isinstance(comment, Comment)
assert comment.target_guid == "((parent_guid))"
assert comment.guid == "((guid))"

Wyświetl plik

@ -4,7 +4,7 @@ from Crypto.PublicKey import RSA
import pytest
from federation.controllers import handle_receive, handle_create_payload
from federation.entities.base import Post
from federation.entities.diaspora.entities import DiasporaPost
from federation.exceptions import NoSuitableProtocolFoundError
from federation.protocols.diaspora.protocol import Protocol
from federation.tests.fixtures.payloads import UNENCRYPTED_DIASPORA_PAYLOAD
@ -35,7 +35,7 @@ class TestHandleCreatePayloadBuildsAPayload(object):
def test_handle_create_payload_builds_an_xml(self):
from_user = Mock(private_key=RSA.generate(2048), handle="foobar@domain.tld")
to_user = Mock(key=RSA.generate(2048).publickey())
entity = Post()
entity = DiasporaPost()
data = handle_create_payload(from_user, to_user, entity)
assert len(data) > 0
parts = data.split("=")

2
pytest.ini 100644
Wyświetl plik

@ -0,0 +1,2 @@
[pytest]
testpaths = federation