From 445022d47d8db3360a1df25e458ebd64c9a0f51a Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Jan 10 2019 07:26:05 +0000 Subject: Allow setting order of handlers. New "order" attribute defines the order of this handler when evaluating multiple handlers. The handlers with lower order are called as first. If two handlers have the same order value, they can be called in any random order. --- diff --git a/freshmaker/consumer.py b/freshmaker/consumer.py index 471c9d1..26f2fb5 100644 --- a/freshmaker/consumer.py +++ b/freshmaker/consumer.py @@ -146,7 +146,9 @@ class FreshmakerConsumer(fedmsg.consumers.FedmsgConsumer): log.debug('Received a message with an ID of "{0}" and of type "{1}"' .format(getattr(msg, 'msg_id', None), type(msg).__name__)) - for handler_class in load_classes(conf.handlers): + handlers = load_classes(conf.handlers) + handlers = sorted(handlers, key=lambda handler: getattr(handler, "order", 50)) + for handler_class in handlers: handler = handler_class() if not handler.can_handle(msg): diff --git a/freshmaker/handlers/__init__.py b/freshmaker/handlers/__init__.py index 44a1f6b..0c5a86a 100644 --- a/freshmaker/handlers/__init__.py +++ b/freshmaker/handlers/__init__.py @@ -119,6 +119,11 @@ class BaseHandler(object): """ __metaclass__ = abc.ABCMeta + # Defines the order of this handler when evaluating multiple handlers. + # The handlers with lower order are called as first. If two handlers + # have the same order value, they can be called in any random order. + order = 50 + def __init__(self): self._db_event_id = None self._db_artifact_build_id = None diff --git a/freshmaker/handlers/internal/update_db_on_advisory_change.py b/freshmaker/handlers/internal/update_db_on_advisory_change.py index 26000c5..41c7268 100644 --- a/freshmaker/handlers/internal/update_db_on_advisory_change.py +++ b/freshmaker/handlers/internal/update_db_on_advisory_change.py @@ -40,6 +40,7 @@ class UpdateDBOnAdvisoryChange(BaseHandler): """ name = 'UpdateDBOnAdvisoryChange' + order = 0 def can_handle(self, event): if not isinstance(event, ErrataAdvisoryStateChangedEvent): diff --git a/freshmaker/handlers/internal/update_db_on_module_build.py b/freshmaker/handlers/internal/update_db_on_module_build.py index 50fa0d0..aa0589d 100644 --- a/freshmaker/handlers/internal/update_db_on_module_build.py +++ b/freshmaker/handlers/internal/update_db_on_module_build.py @@ -31,6 +31,7 @@ from freshmaker.events import MBSModuleStateChangeEvent class UpdateDBOnModuleBuild(BaseHandler): name = "UpdateDBOnModuleBuild" + order = 0 def can_handle(self, event): if isinstance(event, MBSModuleStateChangeEvent): diff --git a/tests/test_consumer.py b/tests/test_consumer.py index 8f937de..7087ebd 100644 --- a/tests/test_consumer.py +++ b/tests/test_consumer.py @@ -68,6 +68,59 @@ class ConsumerTest(ConsumerBaseTest): event = consumer.incoming.get() self.assertEqual(event.msg_id, "ModuleBuilt handled") + @mock.patch("freshmaker.handlers.koji.RebuildImagesOnRPMBodhiUpdate.can_handle") + @mock.patch("freshmaker.handlers.internal.UpdateDBOnModuleBuild.order", + new_callable=mock.PropertyMock) + @mock.patch("freshmaker.handlers.internal.UpdateDBOnModuleBuild.can_handle") + @mock.patch("freshmaker.consumer.get_global_consumer") + def test_consumer_handlers_order(self, global_consumer, handler1, + handler1_order, handler2): + """ + Tests that consumer parses the message, forwards the event + to proper handler and is able to get the further work from + the handler. + """ + consumer = self.create_consumer() + global_consumer.return_value = consumer + + for reverse in [False, True]: + order_lst = [] + + def mocked_handler1(*args, **kwargs): + order_lst.append(1) + return False + + def mocked_handler2(*args, **kwargs): + order_lst.append(2) + return False + + handler1.side_effect = mocked_handler1 + handler2.side_effect = mocked_handler2 + handler1_order.return_value = 100 if reverse else 0 + + msg = self._module_state_change_msg() + consumer.consume(msg) + self.assertEqual(order_lst, [2, 1] if reverse else [1, 2]) + + @mock.patch("freshmaker.handlers.koji.RebuildImagesOnRPMBodhiUpdate.handle") + @mock.patch("freshmaker.handlers.koji.RebuildImagesOnRPMBodhiUpdate.can_handle") + @mock.patch("freshmaker.handlers.internal.UpdateDBOnModuleBuild.handle") + @mock.patch("freshmaker.handlers.internal.UpdateDBOnModuleBuild.can_handle") + @mock.patch("freshmaker.consumer.get_global_consumer") + def test_consumer_multiple_handlers_called( + self, global_consumer, handler1_can_handle, handler1, handler2_can_handle, + handler2): + consumer = self.create_consumer() + global_consumer.return_value = consumer + + handler1_can_handle.return_value = True + handler2_can_handle.return_value = True + msg = self._module_state_change_msg() + consumer.consume(msg) + + handler1.assert_called_once() + handler2.assert_called_once() + @mock.patch("freshmaker.consumer.get_global_consumer") def test_consumer_subscribe_to_specified_topics(self, global_consumer): """