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

messaging: Provide metadata for messages #504

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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: 1 addition & 1 deletion leapp/messaging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,4 @@ def consume(self, actor, *types):
if types:
filtered = set(requested.__name__ for requested in types)
messages = [message for message in messages if message['type'] in filtered]
return (lookup[message['type']].create(json.loads(message['message']['data'])) for message in messages)
return (lookup[msg['type']].create(msg, json.loads(msg['message']['data'])) for msg in messages)
51 changes: 48 additions & 3 deletions leapp/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,50 @@ def __init__(cls, name, bases, attrs):
super(ModelMeta, cls).__init__(name, bases, attrs)


class MetaData(object):
def __init__(self, data):
self._data = data

@property
def row_id(self):
""" Returns the row id of the message in the message table. """
return self._data.get('id')

@property
def actor(self):
""" Returns the name of the actor that has sent this message. """
return self._data.get('actor')

@property
def phase(self):
""" Returns the name of the phase in which this message has been sent. """
return self._data.get('phase')

@property
def timestamp(self):
""" Returns the timestamp string in ISO formst from when this message has been sent. """
return self._data.get('stamp')

@property
def topic(self):
""" Returns the name of the topic of this message. """
return self._data.get('topic')

@property
def hostname(self):
""" Returns the name of the host (FQDN) from where this message originates. """
return self._data.get('hostname')


class Model(with_metaclass(ModelMeta)):
"""
Model is a base class for all models.

Models are defining the data structure of the payload of messages and the
metadata required, such as a name and topic.
"""
def __init__(self, init_method='from_initialization', **kwargs):
def __init__(self, metadata=None, init_method='from_initialization', **kwargs):
self._metadata = MetaData(metadata or {})
super(Model, self).__init__()
defined_fields = type(self).fields
for key in kwargs.keys():
Expand All @@ -101,16 +137,25 @@ def __init__(self, init_method='from_initialization', **kwargs):
Note: Dynamically added fields are ignored by the framework.
"""

def message_metadata(self):
"""
Provides the message meta data of a message.

:returns: An instance of :py:class:`leapp.models.MetaData` or None
:rtype: :py:class:`leapp.models.MetaData` or `NoneType`
"""
return self._metadata

@classmethod
def create(cls, data):
def create(cls, metadata, data):
"""
Create an instance of this class and use the data to initialize the fields within.

:param data: Data to initialize the Model from deserialized data
:type data: dict
:return: Instance of this class
"""
return cls(init_method='to_model', **data)
return cls(metadata=metadata, init_method='to_model', **data)

def dump(self):
"""
Expand Down
14 changes: 7 additions & 7 deletions tests/scripts/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ def test_base_usage():

def test_basic_model():
m = BasicModel(message='Some message')
m2 = BasicModel.create(m.dump())
m2 = BasicModel.create(None, m.dump())
assert m.message == m2.message


def test_string_list_model():
m = WithStringListModel(messages=['Some message'])
m2 = WithStringListModel.create(m.dump())
m2 = WithStringListModel.create(None, m.dump())
assert m.messages == m2.messages
m2.messages = 'str'

Expand All @@ -98,7 +98,7 @@ def test_string_fields_violations():

def test_nested_model():
m = WithNestedModel(basic=BasicModel(message='Some message'))
m2 = WithNestedModel.create(m.dump())
m2 = WithNestedModel.create(None, m.dump())
assert m.basic == m2.basic

with pytest.raises(fields.ModelMisuseError):
Expand All @@ -120,24 +120,24 @@ def test_nested_model():
x.dump()

with pytest.raises(fields.ModelViolationError):
WithNestedModel.create(dict(basic=None))
WithNestedModel.create(None, dict(basic=None))

with pytest.raises(fields.ModelViolationError):
WithNestedModel(basic=None)

assert WithNestedModel.create({'basic': {'message': 'test-message'}}).basic.message == 'test-message'
assert WithNestedModel.create(None, {'basic': {'message': 'test-message'}}).basic.message == 'test-message'
assert WithNestedModel(basic=BasicModel(message='test-message')).basic.message == 'test-message'


def test_nested_list_model():
m = WithNestedListModel(items=[BasicModel(message='Some message')])
m2 = WithNestedListModel.create(m.dump())
m2 = WithNestedListModel.create(None, m.dump())
assert m.items == m2.items


def test_field_types():
m = AllFieldTypesModel()
m2 = AllFieldTypesModel.create(m.dump())
m2 = AllFieldTypesModel.create(None, m.dump())
assert m == m2


Expand Down