Implement new approach for plugin settings

- URL specifies plugin slug and setting key
This commit is contained in:
Oliver Walters 2022-05-08 20:08:22 +10:00
parent f733e23b65
commit 3a9bacb27c
7 changed files with 91 additions and 24 deletions

View File

@ -465,12 +465,35 @@ class NotificationUserSettingsApiTest(InvenTreeAPITestCase):
class PluginSettingsApiTest(InvenTreeAPITestCase):
"""Tests for the plugin settings API"""
def test_plugin_list(self):
"""List installed plugins via API"""
url = reverse('api-plugin-list')
response = self.get(url, expected_code=200)
def test_api_list(self):
"""Test list URL"""
url = reverse('api-plugin-setting-list')
self.get(url, expected_code=200)
def test_invalid_plugin_slug(self):
"""Test that an invalid plugin slug returns a 404"""
url = reverse('api-plugin-setting-detail', kwargs={'plugin': 'doesnotexist', 'key': 'doesnotmatter'})
response = self.get(url, expected_code=404)
self.assertIn("Plugin 'doesnotexist' not installed", str(response.data))
def test_invalid_setting_key(self):
"""Test that an invalid setting key returns a 404"""
...
def test_uninitialized_setting(self):
"""Test that requesting an uninitialized setting creates the setting"""
...
class WebhookMessageTests(TestCase):
def setUp(self):

View File

@ -71,10 +71,9 @@ class LabelPrintMixin:
plugin_key = request.query_params.get('plugin', None)
for slug, plugin in registry.plugins.items():
if slug == plugin_key and plugin.mixin_enabled('labels'):
plugin = registry.get_plugin(plugin_key)
if plugin:
config = plugin.plugin_config()
if config and config.active:

View File

@ -10,6 +10,7 @@ from django.urls import include, re_path
from rest_framework import generics
from rest_framework import status
from rest_framework import permissions
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
@ -17,6 +18,7 @@ from django_filters.rest_framework import DjangoFilterBackend
from common.api import GlobalSettingsPermissions
from plugin.models import PluginConfig, PluginSetting
import plugin.serializers as PluginSerializers
from plugin.registry import registry
class PluginList(generics.ListAPIView):
@ -120,6 +122,34 @@ class PluginSettingDetail(generics.RetrieveUpdateAPIView):
queryset = PluginSetting.objects.all()
serializer_class = PluginSerializers.PluginSettingSerializer
def get_object(self):
"""
Lookup the plugin setting object, based on the URL.
The URL provides the 'slug' of the plugin, and the 'key' of the setting.
Both the 'slug' and 'key' must be valid, else a 404 error is raised
"""
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")
# Get the list of settings available for the specified plugin
plugin = registry.get_plugin(plugin_slug)
if plugin is None:
raise NotFound(detail=f"Plugin '{plugin_slug}' not found")
settings = getattr(plugin, 'SETTINGS', {})
if key not in settings:
raise NotFound(detail=f"Plugin '{plugin_slug}' has no setting matching '{key}'")
return PluginSetting.get_setting_object(key, plugin=plugin)
# Staff permission required
permission_classes = [
GlobalSettingsPermissions,
@ -130,7 +160,7 @@ plugin_api_urls = [
# Plugin settings URLs
re_path(r'^settings/', include([
re_path(r'^(?P<pk>\d+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'),
re_path(r'^(?P<plugin>\w+)/(?P<key>\w+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'),
re_path(r'^.*$', PluginSettingList.as_view(), name='api-plugin-setting-list'),
])),

View File

@ -63,6 +63,17 @@ class PluginsRegistry:
# mixins
self.mixins_settings = {}
def get_plugin(self, slug):
"""
Lookup plugin by slug (unique key).
"""
if slug not in self.plugins:
logger.warning(f"Plugin registry has no record of plugin '{slug}'")
return None
return self.plugins[slug]
def call_plugin_function(self, slug, func, *args, **kwargs):
"""
Call a member function (named by 'func') of the plugin named by 'slug'.
@ -73,7 +84,15 @@ class PluginsRegistry:
Instead, any error messages are returned to the worker.
"""
plugin = self.plugins[slug]
plugin = self.get_plugin(slug)
if not plugin:
return
# Check that the plugin is enabled
config = plugin.plugin_config()
if config and config.active:
plugin_func = getattr(plugin, func)

View File

@ -24,7 +24,7 @@
<td>
{% if setting.is_bool %}
<div class='form-check form-switch'>
<input class='form-check-input boolean-setting' fieldname='{{ setting.key.upper }}' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' {% if setting.as_bool %}checked=''{% endif %} {% if plugin %}plugin='{{ plugin.pk }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}{% if notification_setting %}notification='{{request.user.id}}'{% endif %}>
<input class='form-check-input boolean-setting' fieldname='{{ setting.key.upper }}' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' {% if setting.as_bool %}checked=''{% endif %} {% if plugin %}plugin='{{ plugin.slug }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}{% if notification_setting %}notification='{{request.user.id}}'{% endif %}>
</div>
{% else %}
<div id='setting-{{ setting.pk }}'>
@ -41,7 +41,7 @@
</span>
{{ setting.units }}
<div class='btn-group float-right'>
<button class='btn btn-outline-secondary btn-small btn-edit-setting' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' title='{% trans "Edit setting" %}' {% if plugin %}plugin='{{ plugin.pk }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}>
<button class='btn btn-outline-secondary btn-small btn-edit-setting' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' title='{% trans "Edit setting" %}' {% if plugin %}plugin='{{ plugin.slug }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}>
<span class='fas fa-edit icon-green'></span>
</button>
</div>

View File

@ -67,7 +67,6 @@
$('table').find('.boolean-setting').change(function() {
var setting = $(this).attr('setting');
var pk = $(this).attr('pk');
var plugin = $(this).attr('plugin');
var user = $(this).attr('user');
var notification = $(this).attr('notification');
@ -78,7 +77,7 @@ $('table').find('.boolean-setting').change(function() {
var url = `/api/settings/global/${setting}/`;
if (plugin) {
url = `/api/plugin/settings/${pk}/`;
url = `/api/plugin/settings/${plugin}/${setting}/`;
} else if (user) {
url = `/api/settings/user/${setting}/`;
} else if (notification) {
@ -105,7 +104,6 @@ $('table').find('.boolean-setting').change(function() {
// Callback for when non-boolean settings are edited
$('table').find('.btn-edit-setting').click(function() {
var setting = $(this).attr('setting');
var pk = $(this).attr('pk');
var plugin = $(this).attr('plugin');
var is_global = true;
var notification = $(this).attr('notification');
@ -122,13 +120,11 @@ $('table').find('.btn-edit-setting').click(function() {
title = '{% trans "Edit Notification Setting" %}';
} else if (is_global) {
title = '{% trans "Edit Global Setting" %}';
pk = setting;
} else {
title = '{% trans "Edit User Setting" %}';
pk = setting;
}
editSetting(pk, {
editSetting(setting, {
plugin: plugin,
global: is_global,
notification: notification,

View File

@ -32,7 +32,7 @@ const plugins_enabled = false;
* Interactively edit a setting value.
* Launches a modal dialog form to adjut the value of the setting.
*/
function editSetting(pk, options={}) {
function editSetting(key, options={}) {
// Is this a global setting or a user setting?
var global = options.global || false;
@ -44,13 +44,13 @@ function editSetting(pk, options={}) {
var url = '';
if (plugin) {
url = `/api/plugin/settings/${pk}/`;
url = `/api/plugin/settings/${plugin}/${key}/`;
} else if (notification) {
url = `/api/settings/notification/${pk}/`;
} else if (global) {
url = `/api/settings/global/${pk}/`;
url = `/api/settings/global/${key}/`;
} else {
url = `/api/settings/user/${pk}/`;
url = `/api/settings/user/${key}/`;
}
var reload_required = false;