Skip to content

Commit

Permalink
feat!: Only accept metadata object in `send_event_with_custom_metadat…
Browse files Browse the repository at this point in the history
…a` (#179)

This changes `send_event_with_custom_metadata` to accept a single
`metadata` object rather than all the fields that go into one.

It turns out that in practice, the event-bus-kafka consumer already needs
to build an EventsMetadata object or something equivalent to it as it
reconstructs the metadata from Kafka message headers, since that's where
the parsing and validation logic already is. It then has to splat the
object back out when calling this method. This leads to a possible
collision risk in the method's kwargs, but it's also just awkward. It is
likely that the same issue would arise in any other consumer.

Addresses #178
  • Loading branch information
timmc-edx authored Feb 3, 2023
1 parent bab0b39 commit 2a9eebd
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 45 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ Change Log
Unreleased
----------

[5.0.0] - 2023-02-03
--------------------
Changed
~~~~~~~
* **Breaking change**: ``send_event_with_custom_metadata`` changes to accept a single EventsMetadata object rather than all of the fields that go into one. This only directly affects event bus consumer libraries.

[4.2.0] - 2023-01-24
---------------------
--------------------
Added
~~~~~~
~~~~~
* Added ``send_event_with_custom_metadata``. This will enable event bus consumers to send the event signal with the same metadata fields that were used when the event was produced.

Fixed
Expand All @@ -38,7 +44,7 @@ Changed
[4.1.0] - 2023-01-03
---------------------
Added
~~~~~~~
~~~~~
* Added new XBLOCK_PUBLISHED, XBLOCK_DUPLICATED and XBLOCK_DELETED signals in content_authoring.
* Added XBlockData and DuplicatedXBlockData classes
* Added custom UsageKeyAvroSerializer for opaque_keys UsageKey.
Expand Down
2 changes: 1 addition & 1 deletion openedx_events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
more information about the project.
"""

__version__ = "4.2.0"
__version__ = "5.0.0"
30 changes: 14 additions & 16 deletions openedx_events/tests/test_tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import pytest
from django.test import TestCase, override_settings

from openedx_events.data import EventsMetadata
from openedx_events.exceptions import SenderValidationError
from openedx_events.tests.utils import FreezeSignalCacheMixin
from openedx_events.tooling import OpenEdxPublicSignal
Expand Down Expand Up @@ -235,27 +236,24 @@ def test_send_event_with_custom_metadata(self, mock_send_event_with_metadata):
Note:
The _send_event_with_metadata is fully tested with the various send_event tests.
"""
expected_metadata = {
"id": uuid1(),
"event_type": self.event_type,
"minorversion": 99,
"source": "mock-source",
"sourcehost": "mock-sourcehost",
"time": datetime.datetime.now(datetime.timezone.utc),
"sourcelib": [6, 1, 7],
}
metadata = EventsMetadata(
id=uuid1(),
event_type=self.event_type,
minorversion=99,
source="mock-source",
sourcehost="mock-sourcehost",
time=datetime.datetime.now(datetime.timezone.utc),
sourcelib=(6, 1, 7),
)
expected_response = "mock-response"
mock_send_event_with_metadata.return_value = expected_response

response = self.public_signal.send_event_with_custom_metadata(
user=self.user_mock, id=expected_metadata['id'], minorversion=expected_metadata['minorversion'],
source=expected_metadata['source'], sourcehost=expected_metadata['sourcehost'],
time=expected_metadata['time'], sourcelib=tuple(expected_metadata['sourcelib']),
)
response = self.public_signal.send_event_with_custom_metadata(metadata, foo="bar")

assert response == expected_response
metadata = mock_send_event_with_metadata.call_args.kwargs['metadata']
self.assertDictContainsSubset(expected_metadata, attr.asdict(metadata))
mock_send_event_with_metadata.assert_called_once_with(
metadata=metadata, send_robust=True, foo="bar",
)

@ddt.data(
(
Expand Down
30 changes: 6 additions & 24 deletions openedx_events/tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def send_event(self, send_robust=True, time=None, **kwargs):
current time in UTC. This argument is optional for backward
compatability, but ideally would be explicitly set. See OEP-41
for details.
kwargs: Data to be sent to the signal's receivers.
Used to send events just like Django signals are sent. In addition,
some validations are executed on the arguments, and then generates relevant
Expand All @@ -184,10 +185,6 @@ def send_event(self, send_robust=True, time=None, **kwargs):
)
[(<function callback at 0x7f2ce638ef70>, 'callback response')]
Keyword arguments:
send_robust (bool): determines whether the Django signal will be
sent using the method `send` or `send_robust`.
Returns:
list: response of each receiver following the format
[(receiver, response), ... ]. Empty list if the event is disabled.
Expand All @@ -201,40 +198,25 @@ def send_event(self, send_robust=True, time=None, **kwargs):
return self._send_event_with_metadata(metadata=metadata, send_robust=send_robust, **kwargs)

def send_event_with_custom_metadata(
self, *, id, minorversion, source, sourcehost, time, sourcelib, # pylint: disable=redefined-builtin
send_robust=True, **kwargs
self, metadata, /, *, send_robust=True, **kwargs
):
"""
Send events to all connected receivers using the provided metadata.
This method works exactly like ``send_event``, except it takes most of
the event metadata fields as arguments. This could be used for an
This method works exactly like ``send_event``, except it uses the given
event metadata rather than generating it. This is used by the
event bus consumer, where we want to recreate the metadata used
in the producer when resending the same signal on the consuming
side.
Arguments:
id (UUID): from event production metadata.
event_type (str): from event production metadata.
minorversion (int): from event production metadata.
source (str): from event production metadata.
sourcehost (str): from event production metadata.
time (datetime): from event production metadata.
sourcelib (tuple of ints): from event production metadata.
metadata (EventsMetadata): The metadata to be sent with the signal.
send_robust (bool): Defaults to True. See Django signal docs.
kwargs: Data to be sent to the signal's receivers.
See ``send_event`` docstring for more details.
"""
metadata = EventsMetadata(
id=id,
event_type=self.event_type,
minorversion=minorversion,
source=source,
sourcehost=sourcehost,
time=time,
sourcelib=sourcelib,
)
return self._send_event_with_metadata(metadata=metadata, send_robust=send_robust, **kwargs)

def send(self, sender, **kwargs): # pylint: disable=unused-argument
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 4.1.1
current_version = 5.0.0
commit = True
tag = True

Expand Down

0 comments on commit 2a9eebd

Please sign in to comment.