mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Refactor behaviour of "event" mixin:
- Trigger a new background task for each plugin - Call plugin.process_event - Plugin class can then decide what to do with the particular event
This commit is contained in:
parent
af1bfb2f87
commit
3731d688c9
@ -185,63 +185,19 @@ class EventMixin:
|
|||||||
"""
|
"""
|
||||||
Mixin that provides support for responding to triggered events.
|
Mixin that provides support for responding to triggered events.
|
||||||
|
|
||||||
Implementing classes must provide a list of tuples,
|
Implementing classes must provide a "process_event" function:
|
||||||
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'),
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Override this in subclass model
|
def process_event(self, event, *args, **kwargs):
|
||||||
EVENTS = []
|
# Default implementation does not do anything
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
class MixinMeta:
|
class MixinMeta:
|
||||||
MIXIN_NAME = 'Events'
|
MIXIN_NAME = 'Events'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.add_mixin('events', 'has_events', __class__)
|
self.add_mixin('events', True, __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))
|
|
||||||
|
|
||||||
|
|
||||||
class UrlsMixin:
|
class UrlsMixin:
|
||||||
|
@ -30,15 +30,30 @@ def trigger_event(event, *args, **kwargs):
|
|||||||
|
|
||||||
logger.debug(f"Event triggered: '{event}'")
|
logger.debug(f"Event triggered: '{event}'")
|
||||||
|
|
||||||
offload_task(
|
# Offload a separate task for each plugin
|
||||||
'plugin.events.process_event',
|
# Determine if there are any plugins which are interested in responding
|
||||||
event,
|
if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'):
|
||||||
*args,
|
|
||||||
**kwargs,
|
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.
|
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.
|
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
|
plugin = plugin_registry.plugins[plugin_slug]
|
||||||
if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'):
|
|
||||||
|
|
||||||
# Run atomically, to ensure that either *all* tasks are registered, or *none*
|
plugin.process_event(event, *args, **kwargs)
|
||||||
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
|
|
||||||
)
|
|
||||||
|
@ -176,6 +176,11 @@ class IntegrationPluginBase(MixinBase, plugin.InvenTreePlugin):
|
|||||||
"""check if mixin is enabled and ready"""
|
"""check if mixin is enabled and ready"""
|
||||||
if self.mixin(key):
|
if self.mixin(key):
|
||||||
fnc_name = self._mixins.get(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 getattr(self, fnc_name, True)
|
||||||
return False
|
return False
|
||||||
# endregion
|
# endregion
|
||||||
|
@ -59,7 +59,6 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
# mixins
|
# mixins
|
||||||
self.mixins_settings = {}
|
self.mixins_settings = {}
|
||||||
self.mixins_events = {}
|
|
||||||
|
|
||||||
# region public plugin functions
|
# region public plugin functions
|
||||||
def load_plugins(self):
|
def load_plugins(self):
|
||||||
@ -267,7 +266,6 @@ class PluginsRegistry:
|
|||||||
logger.info(f'Found {len(plugins)} active plugins')
|
logger.info(f'Found {len(plugins)} active plugins')
|
||||||
|
|
||||||
self.activate_integration_settings(plugins)
|
self.activate_integration_settings(plugins)
|
||||||
self.activate_integration_events(plugins)
|
|
||||||
self.activate_integration_schedule(plugins)
|
self.activate_integration_schedule(plugins)
|
||||||
self.activate_integration_app(plugins, force_reload=force_reload)
|
self.activate_integration_app(plugins, force_reload=force_reload)
|
||||||
|
|
||||||
@ -278,7 +276,6 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
self.deactivate_integration_app()
|
self.deactivate_integration_app()
|
||||||
self.deactivate_integration_schedule()
|
self.deactivate_integration_schedule()
|
||||||
self.deactivate_integration_events()
|
|
||||||
self.deactivate_integration_settings()
|
self.deactivate_integration_settings()
|
||||||
|
|
||||||
def activate_integration_settings(self, plugins):
|
def activate_integration_settings(self, plugins):
|
||||||
@ -303,41 +300,6 @@ class PluginsRegistry:
|
|||||||
# clear cache
|
# clear cache
|
||||||
self.mixins_settings = {}
|
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):
|
def activate_integration_schedule(self, plugins):
|
||||||
|
|
||||||
logger.info('Activating plugin tasks')
|
logger.info('Activating plugin tasks')
|
||||||
|
@ -6,16 +6,7 @@ from plugin import IntegrationPluginBase
|
|||||||
from plugin.mixins import EventMixin
|
from plugin.mixins import EventMixin
|
||||||
|
|
||||||
|
|
||||||
def on_part_saved(*args, **kwargs):
|
class EventPluginSample(EventMixin, IntegrationPluginBase):
|
||||||
"""
|
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
A sample plugin which provides supports for triggered events
|
A sample plugin which provides supports for triggered events
|
||||||
"""
|
"""
|
||||||
@ -24,6 +15,9 @@ class EventPlugin(EventMixin, IntegrationPluginBase):
|
|||||||
PLUGIN_SLUG = "event"
|
PLUGIN_SLUG = "event"
|
||||||
PLUGIN_TITLE = "Triggered Events"
|
PLUGIN_TITLE = "Triggered Events"
|
||||||
|
|
||||||
EVENTS = [
|
def process_event(self, event, *args, **kwargs):
|
||||||
('part.saved', 'plugin.samples.integration.event_sample.on_part_saved'),
|
""" Custom event processing """
|
||||||
]
|
|
||||||
|
print(f"Processing triggered event: '{event}'")
|
||||||
|
print("args:", str(args))
|
||||||
|
print("kwargs:", str(kwargs))
|
||||||
|
@ -15,10 +15,6 @@ def print_world():
|
|||||||
print("World")
|
print("World")
|
||||||
|
|
||||||
|
|
||||||
def fail_task():
|
|
||||||
raise ValueError("This task should fail!")
|
|
||||||
|
|
||||||
|
|
||||||
class ScheduledTaskPlugin(ScheduleMixin, IntegrationPluginBase):
|
class ScheduledTaskPlugin(ScheduleMixin, IntegrationPluginBase):
|
||||||
"""
|
"""
|
||||||
A sample plugin which provides support for scheduled tasks
|
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',
|
'func': 'plugin.samples.integration.scheduled_task.print_hello',
|
||||||
'schedule': 'H',
|
'schedule': 'H',
|
||||||
},
|
},
|
||||||
'failure': {
|
|
||||||
'func': 'plugin.samples.integration.scheduled_task.fail_task',
|
|
||||||
'schedule': 'D',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user