diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index dff749af53..6354ca3973 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -185,63 +185,19 @@ class EventMixin: """ Mixin that provides support for responding to triggered events. - Implementing classes must provide a list of tuples, - which provide pairs of 'event':'function' - - Notes: - - Events are called by name, and based on the django signal nomenclature, - e.g. 'part.pre_save' - - Receiving functions must be prototyped to match the 'event' they receive. - - Example: - - EVENTS = [ - ('part.pre_save', 'myplugin.functions.do_stuff'), - ('build.complete', 'myplugin.functions.process_completed_build'), - ] + Implementing classes must provide a "process_event" function: """ - # Override this in subclass model - EVENTS = [] - + def process_event(self, event, *args, **kwargs): + # Default implementation does not do anything + raise NotImplementedError + class MixinMeta: MIXIN_NAME = 'Events' def __init__(self): super().__init__() - self.add_mixin('events', 'has_events', __class__) - self.events = getattr(self, 'EVENTS', []) - - self.validate_events() - - @property - def has_events(self): - return bool(self.events) and len(self.events) > 0 - - def validate_events(self): - """ - Check that the provided event callbacks are valid - """ - - if not self.has_events: - raise ValueError('EVENTS not defined') - - for pair in self.events: - valid = True - - if len(pair) == 2: - event = pair[0].strip() - func = pair[1].strip() - - if len(event) == 0 or len(func) == 0: - valid = False - else: - valid = False - - if not valid: - raise ValueError("Invalid event callback: " + str(pair)) + self.add_mixin('events', True, __class__) class UrlsMixin: diff --git a/InvenTree/plugin/events.py b/InvenTree/plugin/events.py index bd3bf7c06c..49f5a87c6f 100644 --- a/InvenTree/plugin/events.py +++ b/InvenTree/plugin/events.py @@ -30,15 +30,30 @@ def trigger_event(event, *args, **kwargs): logger.debug(f"Event triggered: '{event}'") - offload_task( - 'plugin.events.process_event', - event, - *args, - **kwargs, - ) + # Offload a separate task for each plugin + # Determine if there are any plugins which are interested in responding + if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'): + + for slug, plugin in plugin_registry.plugins.items(): + + if plugin.mixin_enabled('events'): + + config = plugin.plugin_config() + + if config and config.active: + + logger.debug(f"Registering callback for plugin '{slug}'") + + offload_task( + 'plugin.events.process_event', + slug, + event, + *args, + **kwargs + ) -def process_event(event, *args, **kwargs): +def process_event(plugin_slug, event, *args, **kwargs): """ Respond to a triggered event. @@ -47,24 +62,8 @@ def process_event(event, *args, **kwargs): This function may queue multiple functions to be handled by the background worker. """ - logger.info(f"Processing event '{event}'") + logger.info(f"Plugin '{plugin_slug}' is processing triggered event '{event}'") - # Determine if there are any plugins which are interested in responding - if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'): + plugin = plugin_registry.plugins[plugin_slug] - # Run atomically, to ensure that either *all* tasks are registered, or *none* - with transaction.atomic(): - for slug, callbacks in plugin_registry.mixins_events.items(): - # slug = plugin slug - # callbacks = list of (event, function) tuples - - for _event, _func in callbacks: - if _event == event: - - logger.info(f"Running task '{_func}' for plugin '{slug}'") - - offload_task( - _func, - *args, - **kwargs - ) + plugin.process_event(event, *args, **kwargs) diff --git a/InvenTree/plugin/integration.py b/InvenTree/plugin/integration.py index b7ae7d1fc4..c012336a7a 100644 --- a/InvenTree/plugin/integration.py +++ b/InvenTree/plugin/integration.py @@ -176,6 +176,11 @@ class IntegrationPluginBase(MixinBase, plugin.InvenTreePlugin): """check if mixin is enabled and ready""" if self.mixin(key): fnc_name = self._mixins.get(key) + + # Allow for simple case where the mixin is "always" ready + if fnc_name == True: + return True + return getattr(self, fnc_name, True) return False # endregion diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 5e6016fc86..8d04620120 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -59,7 +59,6 @@ class PluginsRegistry: # mixins self.mixins_settings = {} - self.mixins_events = {} # region public plugin functions def load_plugins(self): @@ -267,7 +266,6 @@ class PluginsRegistry: logger.info(f'Found {len(plugins)} active plugins') self.activate_integration_settings(plugins) - self.activate_integration_events(plugins) self.activate_integration_schedule(plugins) self.activate_integration_app(plugins, force_reload=force_reload) @@ -278,7 +276,6 @@ class PluginsRegistry: self.deactivate_integration_app() self.deactivate_integration_schedule() - self.deactivate_integration_events() self.deactivate_integration_settings() def activate_integration_settings(self, plugins): @@ -303,41 +300,6 @@ class PluginsRegistry: # clear cache self.mixins_settings = {} - def activate_integration_events(self, plugins): - """ - Activate triggered events mixin for applicable plugins - """ - - logger.info('Activating plugin events') - - from common.models import InvenTreeSetting - - self.mixins_events = {} - - event_count = 0 - - if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'): - - for slug, plugin in plugins: - - if plugin.mixin_enabled('events'): - config = plugin.plugin_config() - - # Only activate events for plugins which are enabled - if config and config.active: - self.mixins_events[slug] = plugin.events - - event_count += len(plugin.events) - - if event_count > 0: - logger.info(f"Registered callbacks for {event_count} events") - - def deactivate_integration_events(self): - """ - Deactivate events for all plugins - """ - self.mixins_events = {} - def activate_integration_schedule(self, plugins): logger.info('Activating plugin tasks') diff --git a/InvenTree/plugin/samples/integration/event_sample.py b/InvenTree/plugin/samples/integration/event_sample.py index 48516cfa3c..dddb97da1d 100644 --- a/InvenTree/plugin/samples/integration/event_sample.py +++ b/InvenTree/plugin/samples/integration/event_sample.py @@ -6,16 +6,7 @@ from plugin import IntegrationPluginBase from plugin.mixins import EventMixin -def on_part_saved(*args, **kwargs): - """ - Simple function which responds to a triggered event - """ - - part_id = kwargs['part_id'] - print(f"func on_part_saved() - part: {part_id}") - - -class EventPlugin(EventMixin, IntegrationPluginBase): +class EventPluginSample(EventMixin, IntegrationPluginBase): """ A sample plugin which provides supports for triggered events """ @@ -24,6 +15,9 @@ class EventPlugin(EventMixin, IntegrationPluginBase): PLUGIN_SLUG = "event" PLUGIN_TITLE = "Triggered Events" - EVENTS = [ - ('part.saved', 'plugin.samples.integration.event_sample.on_part_saved'), - ] + def process_event(self, event, *args, **kwargs): + """ Custom event processing """ + + print(f"Processing triggered event: '{event}'") + print("args:", str(args)) + print("kwargs:", str(kwargs)) diff --git a/InvenTree/plugin/samples/integration/scheduled_task.py b/InvenTree/plugin/samples/integration/scheduled_task.py index 7a08a21287..825dab134f 100644 --- a/InvenTree/plugin/samples/integration/scheduled_task.py +++ b/InvenTree/plugin/samples/integration/scheduled_task.py @@ -15,10 +15,6 @@ def print_world(): print("World") -def fail_task(): - raise ValueError("This task should fail!") - - class ScheduledTaskPlugin(ScheduleMixin, IntegrationPluginBase): """ A sample plugin which provides support for scheduled tasks @@ -38,8 +34,4 @@ class ScheduledTaskPlugin(ScheduleMixin, IntegrationPluginBase): 'func': 'plugin.samples.integration.scheduled_task.print_hello', 'schedule': 'H', }, - 'failure': { - 'func': 'plugin.samples.integration.scheduled_task.fail_task', - 'schedule': 'D', - }, }