diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 9ccac7c365..b3a584aed7 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 197 +INVENTREE_API_VERSION = 198 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v198 - 2024-05-19 : https://github.com/inventree/InvenTree/pull/7258 + - Fixed lookup field conflicts in the plugins API + v197 - 2024-05-14 : https://github.com/inventree/InvenTree/pull/7224 - Refactor the plugin API endpoints to use the plugin "key" for lookup, rather than the PK value diff --git a/src/backend/InvenTree/common/tests.py b/src/backend/InvenTree/common/tests.py index a98ca5cb7b..cc5ac61270 100644 --- a/src/backend/InvenTree/common/tests.py +++ b/src/backend/InvenTree/common/tests.py @@ -620,7 +620,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase): # get data url = reverse( - 'api-plugin-setting-detail', kwargs={'key': 'sample', 'setting': 'API_KEY'} + 'api-plugin-setting-detail', kwargs={'plugin': 'sample', 'key': 'API_KEY'} ) response = self.get(url, expected_code=200) @@ -637,7 +637,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase): # Non-existent plugin url = reverse( 'api-plugin-setting-detail', - kwargs={'key': 'doesnotexist', 'setting': 'doesnotmatter'}, + kwargs={'plugin': 'doesnotexist', 'key': 'doesnotmatter'}, ) response = self.get(url, expected_code=404) self.assertIn("Plugin 'doesnotexist' not installed", str(response.data)) @@ -645,7 +645,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase): # Wrong key url = reverse( 'api-plugin-setting-detail', - kwargs={'key': 'sample', 'setting': 'doesnotexist'}, + kwargs={'plugin': 'sample', 'key': 'doesnotexist'}, ) response = self.get(url, expected_code=404) self.assertIn( diff --git a/src/backend/InvenTree/plugin/api.py b/src/backend/InvenTree/plugin/api.py index 0df8638809..df1877bf56 100644 --- a/src/backend/InvenTree/plugin/api.py +++ b/src/backend/InvenTree/plugin/api.py @@ -156,6 +156,7 @@ class PluginDetail(RetrieveUpdateDestroyAPI): queryset = PluginConfig.objects.all() serializer_class = PluginSerializers.PluginConfigSerializer lookup_field = 'key' + lookup_url_kwarg = 'plugin' def delete(self, request, *args, **kwargs): """Handle DELETE request for a PluginConfig instance. @@ -202,6 +203,7 @@ class PluginUninstall(UpdateAPI): serializer_class = PluginSerializers.PluginUninstallSerializer permission_classes = [IsSuperuser] lookup_field = 'key' + lookup_url_kwarg = 'plugin' def perform_update(self, serializer): """Uninstall the plugin.""" @@ -222,6 +224,7 @@ class PluginActivate(UpdateAPI): serializer_class = PluginSerializers.PluginActivateSerializer permission_classes = [IsSuperuser] lookup_field = 'key' + lookup_url_kwarg = 'plugin' def get_object(self): """Returns the object for the view.""" @@ -323,10 +326,10 @@ class PluginAllSettingList(APIView): @extend_schema( responses={200: PluginSerializers.PluginSettingSerializer(many=True)} ) - def get(self, request, key): + def get(self, request, plugin): """Get all settings for a plugin config.""" # look up the plugin - plugin = check_plugin(key, None) + plugin = check_plugin(plugin, None) settings = getattr(plugin, 'settings', {}) @@ -355,10 +358,10 @@ class PluginSettingDetail(RetrieveUpdateAPI): 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 """ - setting_key = self.kwargs['setting'] + setting_key = self.kwargs['key'] # Look up plugin - plugin = check_plugin(self.kwargs.pop('key', None), None) + plugin = check_plugin(self.kwargs.get('plugin', None), None) settings = getattr(plugin, 'settings', {}) @@ -433,13 +436,13 @@ plugin_api_urls = [ ), # Lookup for individual plugins (based on 'key', not 'pk') path( - '/', + '/', include([ path( 'settings/', include([ re_path( - r'^(?P\w+)/', + r'^(?P\w+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail', ), diff --git a/src/backend/InvenTree/plugin/test_api.py b/src/backend/InvenTree/plugin/test_api.py index 4622e93f3a..b6746bec99 100644 --- a/src/backend/InvenTree/plugin/test_api.py +++ b/src/backend/InvenTree/plugin/test_api.py @@ -97,7 +97,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): assert plgs is not None self.assertEqual(plgs.active, active) - url = reverse('api-plugin-detail-activate', kwargs={'key': test_plg.key}) + url = reverse('api-plugin-detail-activate', kwargs={'plugin': test_plg.key}) # Should not work - not a superuser response = self.client.post(url, {}, follow=True) @@ -227,7 +227,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): cfg = PluginConfig.objects.filter(key='sample').first() assert cfg is not None - url = reverse('api-plugin-detail-activate', kwargs={'key': cfg.key}) + url = reverse('api-plugin-detail-activate', kwargs={'plugin': cfg.key}) self.client.patch(url, {}, expected_code=200) # Valid plugin settings endpoints @@ -236,8 +236,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): for key in valid_settings: response = self.get( reverse( - 'api-plugin-setting-detail', - kwargs={'key': 'sample', 'setting': key}, + 'api-plugin-setting-detail', kwargs={'plugin': 'sample', 'key': key} ) ) @@ -247,7 +246,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): response = self.get( reverse( 'api-plugin-setting-detail', - kwargs={'key': 'sample', 'setting': 'INVALID_SETTING'}, + kwargs={'plugin': 'sample', 'key': 'INVALID_SETTING'}, ), expected_code=404, ) @@ -256,7 +255,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): response = self.get( reverse( 'api-plugin-setting-detail', - kwargs={'key': 'sample', 'setting': 'PROTECTED_SETTING'}, + kwargs={'plugin': 'sample', 'key': 'PROTECTED_SETTING'}, ), expected_code=200, ) @@ -267,7 +266,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): response = self.patch( reverse( 'api-plugin-setting-detail', - kwargs={'key': 'sample', 'setting': 'NUMERICAL_SETTING'}, + kwargs={'plugin': 'sample', 'key': 'NUMERICAL_SETTING'}, ), {'value': 456}, expected_code=200, @@ -279,7 +278,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): response = self.get( reverse( 'api-plugin-setting-detail', - kwargs={'key': 'sample', 'setting': 'NUMERICAL_SETTING'}, + kwargs={'plugin': 'sample', 'key': 'NUMERICAL_SETTING'}, ), expected_code=200, ) diff --git a/src/backend/InvenTree/templates/InvenTree/settings/settings_js.html b/src/backend/InvenTree/templates/InvenTree/settings/settings_js.html index cbc0b56e05..8e857739a7 100644 --- a/src/backend/InvenTree/templates/InvenTree/settings/settings_js.html +++ b/src/backend/InvenTree/templates/InvenTree/settings/settings_js.html @@ -19,7 +19,7 @@ $('table').find('.boolean-setting').change(function() { if (notification) { url = `/api/settings/notification/${pk}/`; } else if (plugin) { - url = `/api/plugins/settings/${plugin}/${setting}/`; + url = `/api/plugins/${plugin}/settings/${setting}/`; } else if (user) { url = `/api/settings/user/${setting}/`; } diff --git a/src/backend/InvenTree/templates/js/dynamic/settings.js b/src/backend/InvenTree/templates/js/dynamic/settings.js index 56984189fe..61787290ec 100644 --- a/src/backend/InvenTree/templates/js/dynamic/settings.js +++ b/src/backend/InvenTree/templates/js/dynamic/settings.js @@ -44,7 +44,7 @@ function editSetting(key, options={}) { var url = ''; if (plugin) { - url = `/api/plugins/settings/${plugin}/${key}/`; + url = `/api/plugins/${plugin}/settings/${key}/`; } else if (notification) { url = `/api/settings/notification/${pk}/`; } else if (global) { diff --git a/src/frontend/src/components/settings/SettingList.tsx b/src/frontend/src/components/settings/SettingList.tsx index 4cd0b4e3ad..c94fcae01d 100644 --- a/src/frontend/src/components/settings/SettingList.tsx +++ b/src/frontend/src/components/settings/SettingList.tsx @@ -47,6 +47,19 @@ export function SettingList({ const [setting, setSetting] = useState(undefined); + // Determine the field type of the setting + const fieldType = useMemo(() => { + if (setting?.type != undefined) { + return setting.type; + } + + if (setting?.choices != undefined && setting.choices.length > 0) { + return 'choice'; + } + + return 'string'; + }, [setting]); + const editSettingModal = useEditApiFormModal({ url: settingsState.endpoint, pk: setting?.key, @@ -54,11 +67,7 @@ export function SettingList({ title: t`Edit Setting`, fields: { value: { - value: setting?.value ?? '', - field_type: - setting?.type ?? (setting?.choices?.length ?? 0) > 0 - ? 'choice' - : 'string', + field_type: fieldType, label: setting?.name, description: setting?.description, api_url: setting?.api_url ?? '',