From 0bab40fe888f8ce2408cc54597a57028951ce5dc Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Mon, 17 Oct 2022 13:08:28 +0200 Subject: [PATCH] Plugin API refactor (#3637) * out-of-scope: refactor plugin lookup * lookup by settings (runtime not predefined) * Add registry function to set state of plugin quickly * Ensure plugin is active before assertations --- InvenTree/common/tests.py | 4 +++ InvenTree/plugin/api.py | 47 +++++++++++++++++++++++++++--------- InvenTree/plugin/registry.py | 15 ++++++++++++ 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index b299a44791..9720508d10 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -11,6 +11,7 @@ from django.urls import reverse from InvenTree.api_tester import InvenTreeAPITestCase, PluginMixin from InvenTree.helpers import InvenTreeTestCase, str2bool +from plugin import registry from plugin.models import NotificationUserSetting from .api import WebhookView @@ -560,6 +561,9 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase): def test_valid_plugin_slug(self): """Test that an valid plugin slug runs through.""" + # Activate plugin + registry.set_plugin_state('sample', True) + # get data url = reverse('api-plugin-setting-detail', kwargs={'plugin': 'sample', 'key': 'API_KEY'}) response = self.get(url, expected_code=200) diff --git a/InvenTree/plugin/api.py b/InvenTree/plugin/api.py index ea171e2770..d507a2f3a2 100644 --- a/InvenTree/plugin/api.py +++ b/InvenTree/plugin/api.py @@ -16,6 +16,7 @@ from plugin.base.action.api import ActionPluginView from plugin.base.barcodes.api import barcode_api_urls from plugin.base.locate.api import LocatePluginView from plugin.models import PluginConfig, PluginSetting +from plugin.plugin import InvenTreePlugin from plugin.registry import registry @@ -146,6 +147,38 @@ class PluginSettingList(ListAPI): ] +def check_plugin(plugin_slug: str) -> InvenTreePlugin: + """Check that a plugin for the provided slug exsists and get the config. + + Args: + plugin_slug (str): Slug for plugin. + + Raises: + NotFound: If plugin is not installed + NotFound: If plugin is not correctly registered + NotFound: If plugin is not active + + Returns: + InvenTreePlugin: The config object for the provided plugin. + """ + # Check that the 'plugin' specified is valid! + if not PluginConfig.objects.filter(key=plugin_slug).exists(): + raise NotFound(detail=f"Plugin '{plugin_slug}' not installed") + + # Get the list of settings available for the specified plugin + plugin = registry.get_plugin(plugin_slug) + + if plugin is None: + # This only occurs if the plugin mechanism broke + raise NotFound(detail=f"Plugin '{plugin_slug}' not found") # pragma: no cover + + # Check that the plugin is activated + if not plugin.is_active(): + raise NotFound(detail=f"Plugin '{plugin_slug}' is not active") + + return plugin + + class PluginSettingDetail(RetrieveUpdateAPI): """Detail endpoint for a plugin-specific setting. @@ -164,18 +197,10 @@ class PluginSettingDetail(RetrieveUpdateAPI): plugin_slug = self.kwargs['plugin'] key = self.kwargs['key'] - # Check that the 'plugin' specified is valid! - if not PluginConfig.objects.filter(key=plugin_slug).exists(): - raise NotFound(detail=f"Plugin '{plugin_slug}' not installed") + # Look up plugin + plugin = check_plugin(plugin_slug) - # Get the list of settings available for the specified plugin - plugin = registry.get_plugin(plugin_slug) - - if plugin is None: - # This only occurs if the plugin mechanism broke - raise NotFound(detail=f"Plugin '{plugin_slug}' not found") # pragma: no cover - - settings = getattr(plugin, 'SETTINGS', {}) + settings = getattr(plugin, 'settings', {}) if key not in settings: raise NotFound(detail=f"Plugin '{plugin_slug}' has no setting matching '{key}'") diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 9b0e8e29c0..09a0516c4f 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -67,6 +67,21 @@ class PluginsRegistry: return self.plugins[slug] + def set_plugin_state(self, slug, state): + """Set the state(active/inactive) of a plugin. + + Args: + slug (str): Plugin slug + state (bool): Plugin state - true = active, false = inactive + """ + if slug not in self.plugins_full: + logger.warning(f"Plugin registry has no record of plugin '{slug}'") + return + + plugin = self.plugins_full[slug].db + plugin.active = state + plugin.save() + def call_plugin_function(self, slug, func, *args, **kwargs): """Call a member function (named by 'func') of the plugin named by 'slug'.