Skip to content

Commit 880d516

Browse files
committed
messaging: Provide metadata for messages
This patch introduces the necessities to provide message metadata. The meta data can be useful to identify the time, phase, actor, topic or hostname. Example usage from an actor: ---------------------------- ``` from leapp.libraries.stdlib import api from leapp.models import MyModel def needs_metadata(): for model in api.consume(MyModel): md = model.message_metadata() if md and md.phase == 'facts': api.current_logger().info('Facts phase run at %s', md.timestamp) ``` Signed-off-by: Vinzenz Feenstra <[email protected]>
1 parent f766a84 commit 880d516

File tree

3 files changed

+56
-11
lines changed

3 files changed

+56
-11
lines changed

leapp/messaging/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,4 @@ def consume(self, actor, *types):
189189
if types:
190190
filtered = set(requested.__name__ for requested in types)
191191
messages = [message for message in messages if message['type'] in filtered]
192-
return (lookup[message['type']].create(json.loads(message['message']['data'])) for message in messages)
192+
return (lookup[msg['type']].create(msg, json.loads(msg['message']['data'])) for msg in messages)

leapp/models/__init__.py

+48-3
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,50 @@ def __init__(cls, name, bases, attrs):
7171
super(ModelMeta, cls).__init__(name, bases, attrs)
7272

7373

74+
class MetaData(object):
75+
def __init__(self, data):
76+
self._data = data
77+
78+
@property
79+
def row_id(self):
80+
""" Returns the row id of the message in the message table. """
81+
return self._data.get('id')
82+
83+
@property
84+
def actor(self):
85+
""" Returns the name of the actor that has sent this message. """
86+
return self._data.get('actor')
87+
88+
@property
89+
def phase(self):
90+
""" Returns the name of the phase in which this message has been sent. """
91+
return self._data.get('phase')
92+
93+
@property
94+
def timestamp(self):
95+
""" Returns the timestamp string in ISO formst from when this message has been sent. """
96+
return self._data.get('stamp')
97+
98+
@property
99+
def topic(self):
100+
""" Returns the name of the topic of this message. """
101+
return self._data.get('topic')
102+
103+
@property
104+
def hostname(self):
105+
""" Returns the name of the host (FQDN) from where this message originates. """
106+
return self._data.get('hostname')
107+
108+
74109
class Model(with_metaclass(ModelMeta)):
75110
"""
76111
Model is a base class for all models.
77112
78113
Models are defining the data structure of the payload of messages and the
79114
metadata required, such as a name and topic.
80115
"""
81-
def __init__(self, init_method='from_initialization', **kwargs):
116+
def __init__(self, metadata=None, init_method='from_initialization', **kwargs):
117+
self._metadata = MetaData(metadata or {})
82118
super(Model, self).__init__()
83119
defined_fields = type(self).fields
84120
for key in kwargs.keys():
@@ -101,16 +137,25 @@ def __init__(self, init_method='from_initialization', **kwargs):
101137
Note: Dynamically added fields are ignored by the framework.
102138
"""
103139

140+
def message_metadata(self):
141+
"""
142+
Provides the message meta data of a message.
143+
144+
:returns: An instance of :py:class:`leapp.models.MetaData` or None
145+
:rtype: :py:class:`leapp.models.MetaData` or `NoneType`
146+
"""
147+
return self._metadata
148+
104149
@classmethod
105-
def create(cls, data):
150+
def create(cls, metadata, data):
106151
"""
107152
Create an instance of this class and use the data to initialize the fields within.
108153
109154
:param data: Data to initialize the Model from deserialized data
110155
:type data: dict
111156
:return: Instance of this class
112157
"""
113-
return cls(init_method='to_model', **data)
158+
return cls(metadata=metadata, init_method='to_model', **data)
114159

115160
def dump(self):
116161
"""

tests/scripts/test_serialization.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ def test_base_usage():
7070

7171
def test_basic_model():
7272
m = BasicModel(message='Some message')
73-
m2 = BasicModel.create(m.dump())
73+
m2 = BasicModel.create(None, m.dump())
7474
assert m.message == m2.message
7575

7676

7777
def test_string_list_model():
7878
m = WithStringListModel(messages=['Some message'])
79-
m2 = WithStringListModel.create(m.dump())
79+
m2 = WithStringListModel.create(None, m.dump())
8080
assert m.messages == m2.messages
8181
m2.messages = 'str'
8282

@@ -98,7 +98,7 @@ def test_string_fields_violations():
9898

9999
def test_nested_model():
100100
m = WithNestedModel(basic=BasicModel(message='Some message'))
101-
m2 = WithNestedModel.create(m.dump())
101+
m2 = WithNestedModel.create(None, m.dump())
102102
assert m.basic == m2.basic
103103

104104
with pytest.raises(fields.ModelMisuseError):
@@ -120,24 +120,24 @@ def test_nested_model():
120120
x.dump()
121121

122122
with pytest.raises(fields.ModelViolationError):
123-
WithNestedModel.create(dict(basic=None))
123+
WithNestedModel.create(None, dict(basic=None))
124124

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

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

131131

132132
def test_nested_list_model():
133133
m = WithNestedListModel(items=[BasicModel(message='Some message')])
134-
m2 = WithNestedListModel.create(m.dump())
134+
m2 = WithNestedListModel.create(None, m.dump())
135135
assert m.items == m2.items
136136

137137

138138
def test_field_types():
139139
m = AllFieldTypesModel()
140-
m2 = AllFieldTypesModel.create(m.dump())
140+
m2 = AllFieldTypesModel.create(None, m.dump())
141141
assert m == m2
142142

143143

0 commit comments

Comments
 (0)