mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
Plugin setting fix (#7258)
* Fix CUI URLs * Fix for plugin setting API - Fix conflict between "key" for PluginConfig and "key" for setting - Needs to be "plugin" for plugin lookup, which accesses the "key" field in the PluginConfig model * Fix for editing setting in PUI * Add 'r' back in * Remove debug code * Update unit tests * Bump API version * Another unit test fix
This commit is contained in:
parent
2431fc6d58
commit
e4dedb63f4
@ -1,11 +1,14 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# 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."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
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
|
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
|
- Refactor the plugin API endpoints to use the plugin "key" for lookup, rather than the PK value
|
||||||
|
|
||||||
|
@ -620,7 +620,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
|
|
||||||
# get data
|
# get data
|
||||||
url = reverse(
|
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)
|
response = self.get(url, expected_code=200)
|
||||||
|
|
||||||
@ -637,7 +637,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
# Non-existent plugin
|
# Non-existent plugin
|
||||||
url = reverse(
|
url = reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail',
|
||||||
kwargs={'key': 'doesnotexist', 'setting': 'doesnotmatter'},
|
kwargs={'plugin': 'doesnotexist', 'key': 'doesnotmatter'},
|
||||||
)
|
)
|
||||||
response = self.get(url, expected_code=404)
|
response = self.get(url, expected_code=404)
|
||||||
self.assertIn("Plugin 'doesnotexist' not installed", str(response.data))
|
self.assertIn("Plugin 'doesnotexist' not installed", str(response.data))
|
||||||
@ -645,7 +645,7 @@ class PluginSettingsApiTest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
# Wrong key
|
# Wrong key
|
||||||
url = reverse(
|
url = reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail',
|
||||||
kwargs={'key': 'sample', 'setting': 'doesnotexist'},
|
kwargs={'plugin': 'sample', 'key': 'doesnotexist'},
|
||||||
)
|
)
|
||||||
response = self.get(url, expected_code=404)
|
response = self.get(url, expected_code=404)
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
|
@ -156,6 +156,7 @@ class PluginDetail(RetrieveUpdateDestroyAPI):
|
|||||||
queryset = PluginConfig.objects.all()
|
queryset = PluginConfig.objects.all()
|
||||||
serializer_class = PluginSerializers.PluginConfigSerializer
|
serializer_class = PluginSerializers.PluginConfigSerializer
|
||||||
lookup_field = 'key'
|
lookup_field = 'key'
|
||||||
|
lookup_url_kwarg = 'plugin'
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
"""Handle DELETE request for a PluginConfig instance.
|
"""Handle DELETE request for a PluginConfig instance.
|
||||||
@ -202,6 +203,7 @@ class PluginUninstall(UpdateAPI):
|
|||||||
serializer_class = PluginSerializers.PluginUninstallSerializer
|
serializer_class = PluginSerializers.PluginUninstallSerializer
|
||||||
permission_classes = [IsSuperuser]
|
permission_classes = [IsSuperuser]
|
||||||
lookup_field = 'key'
|
lookup_field = 'key'
|
||||||
|
lookup_url_kwarg = 'plugin'
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
"""Uninstall the plugin."""
|
"""Uninstall the plugin."""
|
||||||
@ -222,6 +224,7 @@ class PluginActivate(UpdateAPI):
|
|||||||
serializer_class = PluginSerializers.PluginActivateSerializer
|
serializer_class = PluginSerializers.PluginActivateSerializer
|
||||||
permission_classes = [IsSuperuser]
|
permission_classes = [IsSuperuser]
|
||||||
lookup_field = 'key'
|
lookup_field = 'key'
|
||||||
|
lookup_url_kwarg = 'plugin'
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
"""Returns the object for the view."""
|
"""Returns the object for the view."""
|
||||||
@ -323,10 +326,10 @@ class PluginAllSettingList(APIView):
|
|||||||
@extend_schema(
|
@extend_schema(
|
||||||
responses={200: PluginSerializers.PluginSettingSerializer(many=True)}
|
responses={200: PluginSerializers.PluginSettingSerializer(many=True)}
|
||||||
)
|
)
|
||||||
def get(self, request, key):
|
def get(self, request, plugin):
|
||||||
"""Get all settings for a plugin config."""
|
"""Get all settings for a plugin config."""
|
||||||
# look up the plugin
|
# look up the plugin
|
||||||
plugin = check_plugin(key, None)
|
plugin = check_plugin(plugin, None)
|
||||||
|
|
||||||
settings = getattr(plugin, 'settings', {})
|
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.
|
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
|
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
|
# 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', {})
|
settings = getattr(plugin, 'settings', {})
|
||||||
|
|
||||||
@ -433,13 +436,13 @@ plugin_api_urls = [
|
|||||||
),
|
),
|
||||||
# Lookup for individual plugins (based on 'key', not 'pk')
|
# Lookup for individual plugins (based on 'key', not 'pk')
|
||||||
path(
|
path(
|
||||||
'<str:key>/',
|
'<str:plugin>/',
|
||||||
include([
|
include([
|
||||||
path(
|
path(
|
||||||
'settings/',
|
'settings/',
|
||||||
include([
|
include([
|
||||||
re_path(
|
re_path(
|
||||||
r'^(?P<setting>\w+)/',
|
r'^(?P<key>\w+)/',
|
||||||
PluginSettingDetail.as_view(),
|
PluginSettingDetail.as_view(),
|
||||||
name='api-plugin-setting-detail',
|
name='api-plugin-setting-detail',
|
||||||
),
|
),
|
||||||
|
@ -97,7 +97,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
assert plgs is not None
|
assert plgs is not None
|
||||||
self.assertEqual(plgs.active, active)
|
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
|
# Should not work - not a superuser
|
||||||
response = self.client.post(url, {}, follow=True)
|
response = self.client.post(url, {}, follow=True)
|
||||||
@ -227,7 +227,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
cfg = PluginConfig.objects.filter(key='sample').first()
|
cfg = PluginConfig.objects.filter(key='sample').first()
|
||||||
assert cfg is not None
|
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)
|
self.client.patch(url, {}, expected_code=200)
|
||||||
|
|
||||||
# Valid plugin settings endpoints
|
# Valid plugin settings endpoints
|
||||||
@ -236,8 +236,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
for key in valid_settings:
|
for key in valid_settings:
|
||||||
response = self.get(
|
response = self.get(
|
||||||
reverse(
|
reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail', kwargs={'plugin': 'sample', 'key': key}
|
||||||
kwargs={'key': 'sample', 'setting': key},
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -247,7 +246,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
response = self.get(
|
response = self.get(
|
||||||
reverse(
|
reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail',
|
||||||
kwargs={'key': 'sample', 'setting': 'INVALID_SETTING'},
|
kwargs={'plugin': 'sample', 'key': 'INVALID_SETTING'},
|
||||||
),
|
),
|
||||||
expected_code=404,
|
expected_code=404,
|
||||||
)
|
)
|
||||||
@ -256,7 +255,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
response = self.get(
|
response = self.get(
|
||||||
reverse(
|
reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail',
|
||||||
kwargs={'key': 'sample', 'setting': 'PROTECTED_SETTING'},
|
kwargs={'plugin': 'sample', 'key': 'PROTECTED_SETTING'},
|
||||||
),
|
),
|
||||||
expected_code=200,
|
expected_code=200,
|
||||||
)
|
)
|
||||||
@ -267,7 +266,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
response = self.patch(
|
response = self.patch(
|
||||||
reverse(
|
reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail',
|
||||||
kwargs={'key': 'sample', 'setting': 'NUMERICAL_SETTING'},
|
kwargs={'plugin': 'sample', 'key': 'NUMERICAL_SETTING'},
|
||||||
),
|
),
|
||||||
{'value': 456},
|
{'value': 456},
|
||||||
expected_code=200,
|
expected_code=200,
|
||||||
@ -279,7 +278,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
|||||||
response = self.get(
|
response = self.get(
|
||||||
reverse(
|
reverse(
|
||||||
'api-plugin-setting-detail',
|
'api-plugin-setting-detail',
|
||||||
kwargs={'key': 'sample', 'setting': 'NUMERICAL_SETTING'},
|
kwargs={'plugin': 'sample', 'key': 'NUMERICAL_SETTING'},
|
||||||
),
|
),
|
||||||
expected_code=200,
|
expected_code=200,
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,7 @@ $('table').find('.boolean-setting').change(function() {
|
|||||||
if (notification) {
|
if (notification) {
|
||||||
url = `/api/settings/notification/${pk}/`;
|
url = `/api/settings/notification/${pk}/`;
|
||||||
} else if (plugin) {
|
} else if (plugin) {
|
||||||
url = `/api/plugins/settings/${plugin}/${setting}/`;
|
url = `/api/plugins/${plugin}/settings/${setting}/`;
|
||||||
} else if (user) {
|
} else if (user) {
|
||||||
url = `/api/settings/user/${setting}/`;
|
url = `/api/settings/user/${setting}/`;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ function editSetting(key, options={}) {
|
|||||||
var url = '';
|
var url = '';
|
||||||
|
|
||||||
if (plugin) {
|
if (plugin) {
|
||||||
url = `/api/plugins/settings/${plugin}/${key}/`;
|
url = `/api/plugins/${plugin}/settings/${key}/`;
|
||||||
} else if (notification) {
|
} else if (notification) {
|
||||||
url = `/api/settings/notification/${pk}/`;
|
url = `/api/settings/notification/${pk}/`;
|
||||||
} else if (global) {
|
} else if (global) {
|
||||||
|
@ -47,6 +47,19 @@ export function SettingList({
|
|||||||
|
|
||||||
const [setting, setSetting] = useState<Setting | undefined>(undefined);
|
const [setting, setSetting] = useState<Setting | undefined>(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({
|
const editSettingModal = useEditApiFormModal({
|
||||||
url: settingsState.endpoint,
|
url: settingsState.endpoint,
|
||||||
pk: setting?.key,
|
pk: setting?.key,
|
||||||
@ -54,11 +67,7 @@ export function SettingList({
|
|||||||
title: t`Edit Setting`,
|
title: t`Edit Setting`,
|
||||||
fields: {
|
fields: {
|
||||||
value: {
|
value: {
|
||||||
value: setting?.value ?? '',
|
field_type: fieldType,
|
||||||
field_type:
|
|
||||||
setting?.type ?? (setting?.choices?.length ?? 0) > 0
|
|
||||||
? 'choice'
|
|
||||||
: 'string',
|
|
||||||
label: setting?.name,
|
label: setting?.name,
|
||||||
description: setting?.description,
|
description: setting?.description,
|
||||||
api_url: setting?.api_url ?? '',
|
api_url: setting?.api_url ?? '',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user