Common: Prevent topic collision between types and tags

If an event tag happened to be the same as an event type, subscribers
could receive event types that they were not prepared to handle,
resulting in difficult-to-diagnose bugs. Prevent tags and types from
being sent to the wrong subscribers by appending unique strings to the
topics.
This commit is contained in:
Mike Salvatore 2022-08-10 11:28:56 -04:00
parent 3384c049a4
commit a6c24af622
2 changed files with 26 additions and 4 deletions

View File

@ -22,10 +22,12 @@ class PyPubSubEventQueue(IEventQueue):
self, event_type: Type[AbstractEvent], subscriber: Callable[[AbstractEvent], None]
):
# pypubsub.pub.subscribe needs a string as the topic/event name
self._pypubsub_publisher.subscribe(listener=subscriber, topicName=event_type.__name__)
event_type_topic = PyPubSubEventQueue._get_type_topic(event_type)
self._pypubsub_publisher.subscribe(listener=subscriber, topicName=event_type_topic)
def subscribe_tag(self, tag: str, subscriber: Callable[[AbstractEvent], None]):
self._pypubsub_publisher.subscribe(listener=subscriber, topicName=tag)
tag_topic = PyPubSubEventQueue._get_tag_topic(tag)
self._pypubsub_publisher.subscribe(listener=subscriber, topicName=tag_topic)
def publish(self, event: AbstractEvent):
self._publish_to_all_events_topic(event)
@ -36,8 +38,20 @@ class PyPubSubEventQueue(IEventQueue):
self._pypubsub_publisher.sendMessage(_INTERNAL_ALL_EVENT_TYPES_TOPIC, event=event)
def _publish_to_type_topic(self, event: AbstractEvent):
self._pypubsub_publisher.sendMessage(event.__class__.__name__, event=event)
event_type_topic = PyPubSubEventQueue._get_type_topic(event.__class__)
self._pypubsub_publisher.sendMessage(event_type_topic, event=event)
def _publish_to_tags_topics(self, event: AbstractEvent):
for tag in event.tags:
self._pypubsub_publisher.sendMessage(tag, event=event)
tag_topic = PyPubSubEventQueue._get_tag_topic(tag)
self._pypubsub_publisher.sendMessage(tag_topic, event=event)
# Appending a unique string to the topics for type and tags prevents bugs caused by collisions
# between type names and tag names.
@staticmethod
def _get_type_topic(event_type: Type[AbstractEvent]) -> str:
return f"{event_type.__name__}-type"
@staticmethod
def _get_tag_topic(tag: str) -> str:
return f"{tag}-tag"

View File

@ -99,3 +99,11 @@ def test_subscribe_tags_multiple_types(event_queue, subscriber):
assert TestEvent1 in subscriber.call_types
assert TestEvent2 in subscriber.call_types
assert {EVENT_TAG_1, EVENT_TAG_2}.issubset(subscriber.call_tags)
def test_type_tag_collision(event_queue, subscriber):
event_queue.subscribe_type(TestEvent1, subscriber)
event_queue.publish(TestEvent2(tags={TestEvent1.__name__}))
assert subscriber.call_count == 0