mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[bug] Logic fix for plugins (#9934)
* Logic fix for plugins - Prevent tasks being run for disabled plugins * Adjust default value for "get_plugin" method * Fix return type * Update typing * Tweak unit test * Update unit tests * More test updates
This commit is contained in:
		| @@ -30,7 +30,7 @@ class InvenTreeExchange(SimpleExchangeBackend): | ||||
|         # Find the selected exchange rate plugin | ||||
|         slug = get_global_setting('CURRENCY_UPDATE_PLUGIN', create=False) | ||||
|  | ||||
|         plugin = registry.get_plugin(slug) if slug else None | ||||
|         plugin = registry.get_plugin(slug, active=True) if slug else None | ||||
|  | ||||
|         if not plugin: | ||||
|             # Find the first active currency exchange plugin | ||||
|   | ||||
| @@ -35,7 +35,7 @@ def export_data( | ||||
|     """ | ||||
|     from plugin import registry | ||||
|  | ||||
|     if (plugin := registry.get_plugin(plugin_key)) is None: | ||||
|     if (plugin := registry.get_plugin(plugin_key, active=True)) is None: | ||||
|         logger.warning("export_data: Plugin '%s' not found", plugin_key) | ||||
|         return | ||||
|  | ||||
|   | ||||
| @@ -105,10 +105,10 @@ def process_event(plugin_slug, event, *args, **kwargs): | ||||
|     This function is run by the background worker process. | ||||
|     This function may queue multiple functions to be handled by the background worker. | ||||
|     """ | ||||
|     plugin = registry.get_plugin(plugin_slug) | ||||
|     plugin = registry.get_plugin(plugin_slug, active=True) | ||||
|  | ||||
|     if plugin is None:  # pragma: no cover | ||||
|         logger.error("Could not find matching plugin for '%s'", plugin_slug) | ||||
|         logger.error("Could not find matching active plugin for '%s'", plugin_slug) | ||||
|         return | ||||
|  | ||||
|     logger.debug("Plugin '%s' is processing triggered event '%s'", plugin_slug, event) | ||||
|   | ||||
| @@ -26,10 +26,10 @@ def print_label(plugin_slug: str, **kwargs): | ||||
|     """ | ||||
|     logger.info("Plugin '%s' is printing a label", plugin_slug) | ||||
|  | ||||
|     plugin = registry.get_plugin(plugin_slug) | ||||
|     plugin = registry.get_plugin(plugin_slug, active=True) | ||||
|  | ||||
|     if plugin is None:  # pragma: no cover | ||||
|         logger.error("Could not find matching plugin for '%s'", plugin_slug) | ||||
|         logger.error("Could not find matching active plugin for '%s'", plugin_slug) | ||||
|         return | ||||
|  | ||||
|     try: | ||||
|   | ||||
| @@ -124,7 +124,7 @@ class LabelMixinTests(PrintTestMixins, InvenTreeAPITestCase): | ||||
|         plugins = registry.with_mixin(PluginMixinEnum.LABELS) | ||||
|         self.assertGreater(len(plugins), 0) | ||||
|  | ||||
|         plugin = registry.get_plugin('samplelabelprinter') | ||||
|         plugin = registry.get_plugin('samplelabelprinter', active=None) | ||||
|         self.assertIsNotNone(plugin) | ||||
|         config = plugin.plugin_config() | ||||
|  | ||||
|   | ||||
| @@ -16,9 +16,7 @@ class LocatePluginTests(InvenTreeAPITestCase): | ||||
|         super().setUp() | ||||
|  | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('samplelocate').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|         registry.set_plugin_state('samplelocate', True) | ||||
|  | ||||
|     fixtures = ['category', 'part', 'location', 'stock'] | ||||
|  | ||||
|   | ||||
| @@ -101,16 +101,18 @@ class PluginsRegistry: | ||||
|         self.installed_apps = []  # Holds all added plugin_paths | ||||
|  | ||||
|     @property | ||||
|     def is_loading(self): | ||||
|     def is_loading(self) -> bool: | ||||
|         """Return True if the plugin registry is currently loading.""" | ||||
|         return self.loading_lock.locked() | ||||
|  | ||||
|     def get_plugin(self, slug, active=None, with_mixin=None): | ||||
|     def get_plugin( | ||||
|         self, slug: str, active: bool = True, with_mixin: Optional[str] = None | ||||
|     ) -> InvenTreePlugin: | ||||
|         """Lookup plugin by slug (unique key). | ||||
|  | ||||
|         Args: | ||||
|             slug (str): The slug of the plugin to look up. | ||||
|             active (bool, optional): Filter by 'active' status of the plugin. If None, no filtering is applied. Defaults to None. | ||||
|             active (bool, optional): Filter by 'active' status of the plugin. If None, no filtering is applied. Defaults to True. | ||||
|             with_mixin (str, optional): Filter by mixin name. If None, no filtering is applied. Defaults to None. | ||||
|  | ||||
|         Returns: | ||||
| @@ -136,7 +138,7 @@ class PluginsRegistry: | ||||
|     def get_plugin_config(self, slug: str, name: Union[str, None] = None): | ||||
|         """Return the matching PluginConfig instance for a given plugin. | ||||
|  | ||||
|         Args: | ||||
|         Arguments: | ||||
|             slug: The plugin slug | ||||
|             name: The plugin name (optional) | ||||
|         """ | ||||
| @@ -167,10 +169,10 @@ class PluginsRegistry: | ||||
|  | ||||
|         return cfg | ||||
|  | ||||
|     def set_plugin_state(self, slug, state): | ||||
|     def set_plugin_state(self, slug: str, state: bool): | ||||
|         """Set the state(active/inactive) of a plugin. | ||||
|  | ||||
|         Args: | ||||
|         Arguments: | ||||
|             slug (str): Plugin slug | ||||
|             state (bool): Plugin state - true = active, false = inactive | ||||
|         """ | ||||
| @@ -374,7 +376,7 @@ class PluginsRegistry: | ||||
|             # Ensure the lock is released always | ||||
|             self.loading_lock.release() | ||||
|  | ||||
|     def plugin_dirs(self): | ||||
|     def plugin_dirs(self) -> list[str]: | ||||
|         """Construct a list of directories from where plugins can be loaded.""" | ||||
|         # Builtin plugins are *always* loaded | ||||
|         dirs = ['plugin.builtin'] | ||||
|   | ||||
| @@ -15,9 +15,7 @@ class EventPluginSampleTests(TestCase): | ||||
|     def test_run_event(self): | ||||
|         """Check if the event is issued.""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('sampleevent').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|         registry.set_plugin_state('sampleevent', True) | ||||
|  | ||||
|         InvenTreeSetting.set_setting('ENABLE_PLUGINS_EVENTS', True, change_user=None) | ||||
|  | ||||
|   | ||||
| @@ -13,9 +13,7 @@ class FilteredEventPluginSampleTests(TestCase): | ||||
|     def test_run_event(self): | ||||
|         """Check if the event is issued.""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('filteredsampleevent').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|         registry.set_plugin_state('filteredsampleevent', True) | ||||
|  | ||||
|         InvenTreeSetting.set_setting('ENABLE_PLUGINS_EVENTS', True, change_user=None) | ||||
|  | ||||
| @@ -29,9 +27,7 @@ class FilteredEventPluginSampleTests(TestCase): | ||||
|     def test_ignore_event(self): | ||||
|         """Check if the event is issued.""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('filteredsampleevent').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|         registry.set_plugin_state('filteredsampleevent', True) | ||||
|  | ||||
|         InvenTreeSetting.set_setting('ENABLE_PLUGINS_EVENTS', True, change_user=None) | ||||
|  | ||||
|   | ||||
| @@ -14,9 +14,7 @@ class SampleIconPackPluginTests(InvenTreeAPITestCase): | ||||
|     def test_get_icons_api(self): | ||||
|         """Check get icons api.""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('sampleicons').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|         registry.set_plugin_state('sampleicons', True) | ||||
|  | ||||
|         response = self.get(reverse('api-icon-list'), expected_code=200) | ||||
|         self.assertEqual(len(response.data), 2) | ||||
|   | ||||
| @@ -45,6 +45,7 @@ class SampleIntegrationPluginTests(InvenTreeTestCase): | ||||
|  | ||||
|     def test_settings(self): | ||||
|         """Check the SettingsMixin.check_settings function.""" | ||||
|         registry.set_plugin_state('sample', True) | ||||
|         plugin = registry.get_plugin('sample') | ||||
|         self.assertIsNotNone(plugin) | ||||
|  | ||||
| @@ -57,13 +58,19 @@ class SampleIntegrationPluginTests(InvenTreeTestCase): | ||||
|  | ||||
|     def test_settings_validator(self): | ||||
|         """Test settings validator for plugins.""" | ||||
|         registry.set_plugin_state('sample', False) | ||||
|         self.assertIsNone(registry.get_plugin('sample')) | ||||
|  | ||||
|         registry.set_plugin_state('sample', True) | ||||
|         plugin = registry.get_plugin('sample') | ||||
|         self.assertIsNotNone(plugin) | ||||
|  | ||||
|         valid_json = '{"ts": 13}' | ||||
|         not_valid_json = '{"ts""13"}' | ||||
|  | ||||
|         # no error, should pass validator | ||||
|         plugin.set_setting('VALIDATOR_SETTING', valid_json) | ||||
|  | ||||
|         # should throw an error | ||||
|         # This should throw an error | ||||
|         with self.assertRaises(ValidationError): | ||||
|             plugin.set_setting('VALIDATOR_SETTING', not_valid_json) | ||||
|   | ||||
| @@ -76,7 +76,15 @@ class ExampleScheduledTaskPluginTests(TestCase): | ||||
|  | ||||
|     def test_calling(self): | ||||
|         """Test calling of plugin functions by name.""" | ||||
|         # Check with right parameters | ||||
|         # First, plugin is *not* enabled | ||||
|         registry.set_plugin_state('schedule', False) | ||||
|  | ||||
|         with self.assertRaises(AttributeError): | ||||
|             self.assertEqual(call_plugin_function('schedule', 'member_func'), False) | ||||
|  | ||||
|         registry.set_plugin_state('schedule', True) | ||||
|  | ||||
|         # Should work now | ||||
|         self.assertEqual(call_plugin_function('schedule', 'member_func'), False) | ||||
|  | ||||
|         # Check with wrong key | ||||
|   | ||||
| @@ -8,7 +8,7 @@ from plugin.helpers import MixinNotImplementedError | ||||
| from plugin.mixins import LocateMixin | ||||
|  | ||||
|  | ||||
| class SampleLocatePlugintests(InvenTreeAPITestCase): | ||||
| class SampleLocatePluginTests(InvenTreeAPITestCase): | ||||
|     """Tests for SampleLocatePlugin.""" | ||||
|  | ||||
|     fixtures = ['location', 'category', 'part', 'stock'] | ||||
| @@ -16,7 +16,7 @@ class SampleLocatePlugintests(InvenTreeAPITestCase): | ||||
|     def test_run_locator(self): | ||||
|         """Check if the event is issued.""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('samplelocate').plugin_config() | ||||
|         config = registry.get_plugin('samplelocate', active=None).plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ class MailPluginSampleTests(TestCase): | ||||
|  | ||||
|     def activate_plugin(self): | ||||
|         """Activate the sample mail plugin.""" | ||||
|         config = registry.get_plugin('samplemail').plugin_config() | ||||
|         config = registry.get_plugin('samplemail', active=None).plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|  | ||||
|   | ||||
| @@ -217,6 +217,7 @@ class RegistryTests(TestCase): | ||||
|         with mock.patch.dict(os.environ, envs): | ||||
|             # Reload to rediscover plugins | ||||
|             registry.reload_plugins(full_reload=True, collect=True) | ||||
|             registry.set_plugin_state('simple', True) | ||||
|  | ||||
|             # Depends on the meta set in InvenTree/plugin/mock/simple:SimplePlugin | ||||
|             plg = registry.get_plugin('simple') | ||||
| @@ -264,7 +265,7 @@ class RegistryTests(TestCase): | ||||
|         registry.reload_plugins(full_reload=True, collect=True) | ||||
|  | ||||
|         # Test that plugin was installed | ||||
|         plg = registry.get_plugin('zapier') | ||||
|         plg = registry.get_plugin('zapier', active=None) | ||||
|         self.assertEqual(plg.slug, 'zapier') | ||||
|         self.assertEqual(plg.name, 'inventree_zapier') | ||||
|  | ||||
|   | ||||
| @@ -68,7 +68,7 @@ def print_labels( | ||||
|     model = template.get_model() | ||||
|     items = model.objects.filter(pk__in=item_ids) | ||||
|  | ||||
|     plugin = registry.get_plugin(plugin_slug) | ||||
|     plugin = registry.get_plugin(plugin_slug, active=True) | ||||
|  | ||||
|     if not plugin: | ||||
|         logger.warning("Label printing plugin '%s' not found", plugin_slug) | ||||
|   | ||||
| @@ -357,12 +357,9 @@ class PrintTestMixins: | ||||
|  | ||||
|     def do_activate_plugin(self): | ||||
|         """Activate the 'samplelabel' plugin.""" | ||||
|         registry.set_plugin_state(self.plugin_ref, True) | ||||
|         plugin = registry.get_plugin(self.plugin_ref) | ||||
|         self.assertIsNotNone(plugin) | ||||
|         config = plugin.plugin_config() | ||||
|         self.assertIsNotNone(config) | ||||
|         config.active = True | ||||
|         config.save() | ||||
|  | ||||
|     def run_print_test(self, qs, model_type, label: bool = True): | ||||
|         """Run tests on single and multiple page printing. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user