mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Plugin loading improvements (#6056)
* Add API endpoint to reload plugin registry * Tweak debug * Add elements for CUI * Update API version * Reload plugins from PUI
This commit is contained in:
parent
256f44b44e
commit
8d46521cab
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 158
|
INVENTREE_API_VERSION = 159
|
||||||
"""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 = """
|
||||||
|
|
||||||
|
v159 -> 2023-12-08 : https://github.com/inventree/InvenTree/pull/6056
|
||||||
|
- Adds API endpoint for reloading plugin registry
|
||||||
|
|
||||||
v158 -> 2023-11-21 : https://github.com/inventree/InvenTree/pull/5953
|
v158 -> 2023-11-21 : https://github.com/inventree/InvenTree/pull/5953
|
||||||
- Adds API endpoint for listing all settings of a particular plugin
|
- Adds API endpoint for listing all settings of a particular plugin
|
||||||
- Adds API endpoint for registry status (errors)
|
- Adds API endpoint for registry status (errors)
|
||||||
|
@ -189,6 +189,18 @@ class PluginActivate(UpdateAPI):
|
|||||||
serializer.save()
|
serializer.save()
|
||||||
|
|
||||||
|
|
||||||
|
class PluginReload(CreateAPI):
|
||||||
|
"""Endpoint for reloading all plugins."""
|
||||||
|
|
||||||
|
queryset = PluginConfig.objects.none()
|
||||||
|
serializer_class = PluginSerializers.PluginReloadSerializer
|
||||||
|
permission_classes = [IsSuperuser,]
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
"""Saving the serializer instance performs plugin installation"""
|
||||||
|
return serializer.save()
|
||||||
|
|
||||||
|
|
||||||
class PluginSettingList(ListAPI):
|
class PluginSettingList(ListAPI):
|
||||||
"""List endpoint for all plugin related settings.
|
"""List endpoint for all plugin related settings.
|
||||||
|
|
||||||
@ -374,6 +386,7 @@ plugin_api_urls = [
|
|||||||
re_path('^metadata/', MetadataView.as_view(), {'model': PluginConfig}, name='api-plugin-metadata'),
|
re_path('^metadata/', MetadataView.as_view(), {'model': PluginConfig}, name='api-plugin-metadata'),
|
||||||
|
|
||||||
# Plugin management
|
# Plugin management
|
||||||
|
re_path(r'^reload/', PluginReload.as_view(), name='api-plugin-reload'),
|
||||||
re_path(r'^install/', PluginInstall.as_view(), name='api-plugin-install'),
|
re_path(r'^install/', PluginInstall.as_view(), name='api-plugin-install'),
|
||||||
re_path(r'^activate/', PluginActivate.as_view(), name='api-plugin-activate'),
|
re_path(r'^activate/', PluginActivate.as_view(), name='api-plugin-activate'),
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
if self.loading_lock.acquire(blocking=False):
|
if self.loading_lock.acquire(blocking=False):
|
||||||
|
|
||||||
logger.info('Plugin Registry: Reloading plugins')
|
logger.info('Plugin Registry: Reloading plugins - Force: %s, Full: %s, Collect: %s', force_reload, full_reload, collect)
|
||||||
|
|
||||||
with maintenance_mode_on():
|
with maintenance_mode_on():
|
||||||
if collect:
|
if collect:
|
||||||
|
@ -131,6 +131,37 @@ class PluginConfigEmptySerializer(serializers.Serializer):
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class PluginReloadSerializer(serializers.Serializer):
|
||||||
|
"""Serializer for remotely forcing plugin registry reload"""
|
||||||
|
|
||||||
|
full_reload = serializers.BooleanField(
|
||||||
|
required=False, default=False,
|
||||||
|
label=_("Full reload"),
|
||||||
|
help_text=_("Perform a full reload of the plugin registry")
|
||||||
|
)
|
||||||
|
|
||||||
|
force_reload = serializers.BooleanField(
|
||||||
|
required=False, default=False,
|
||||||
|
label=_("Force reload"),
|
||||||
|
help_text=_("Force a reload of the plugin registry, even if it is already loaded")
|
||||||
|
)
|
||||||
|
|
||||||
|
collect_plugins = serializers.BooleanField(
|
||||||
|
required=False, default=False,
|
||||||
|
label=_("Collect plugins"),
|
||||||
|
help_text=_("Collect plugins and add them to the registry")
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""Reload the plugin registry."""
|
||||||
|
from plugin.registry import registry
|
||||||
|
registry.reload_plugins(
|
||||||
|
full_reload=self.validated_data.get('full_reload', False),
|
||||||
|
force_reload=self.validated_data.get('force_reload', False),
|
||||||
|
collect=self.validated_data.get('collect_plugins', False),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PluginActivateSerializer(serializers.Serializer):
|
class PluginActivateSerializer(serializers.Serializer):
|
||||||
"""Serializer for activating or deactivating a plugin"""
|
"""Serializer for activating or deactivating a plugin"""
|
||||||
|
|
||||||
|
@ -38,7 +38,13 @@
|
|||||||
{% admin_url user "plugin.pluginconfig" None as url %}
|
{% admin_url user "plugin.pluginconfig" None as url %}
|
||||||
{% include "admin_button.html" with url=url %}
|
{% include "admin_button.html" with url=url %}
|
||||||
{% if plug %}
|
{% if plug %}
|
||||||
<button class="btn btn-success" id="install-plugin" title="{% trans 'Install Plugin' %}"><span class='fas fa-plus-circle'></span> {% trans "Install Plugin" %}</button>
|
<button class="btn btn-success" id="install-plugin" title="{% trans 'Install Plugin' %}">
|
||||||
|
<span class='fas fa-plus-circle'></span> {% trans "Install Plugin" %}
|
||||||
|
</button>
|
||||||
|
<button class='btn btn-success' id='reload-plugins' title='{% trans "Reload Plugins" %}'>
|
||||||
|
<span class='fas fa-redo-alt'></span> {% trans "Reload Plugins" %}
|
||||||
|
</button>
|
||||||
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -591,5 +591,10 @@ onPanelLoad('plugin', function() {
|
|||||||
installPlugin();
|
installPlugin();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Callback to reload plugins
|
||||||
|
$('#reload-plugins').click(function() {
|
||||||
|
reloadPlugins();
|
||||||
|
});
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
activatePlugin,
|
activatePlugin,
|
||||||
installPlugin,
|
installPlugin,
|
||||||
loadPluginTable,
|
loadPluginTable,
|
||||||
locateItemOrLocation
|
locateItemOrLocation,
|
||||||
|
reloadPlugins,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -213,6 +214,37 @@ function activatePlugin(plugin_id, active=true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reload the plugin registry
|
||||||
|
*/
|
||||||
|
function reloadPlugins() {
|
||||||
|
let url = '{% url "api-plugin-reload" %}';
|
||||||
|
|
||||||
|
constructForm(url, {
|
||||||
|
title: '{% trans "Reload Plugins" %}',
|
||||||
|
method: 'POST',
|
||||||
|
confirm: true,
|
||||||
|
fields: {
|
||||||
|
force_reload: {
|
||||||
|
// hidden: true,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
full_reload: {
|
||||||
|
// hidden: true,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
collect_plugins: {
|
||||||
|
// hidden: true,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onSuccess: function() {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function locateItemOrLocation(options={}) {
|
function locateItemOrLocation(options={}) {
|
||||||
|
|
||||||
if (!options.item && !options.location) {
|
if (!options.item && !options.location) {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications, showNotification } from '@mantine/notifications';
|
||||||
import {
|
import {
|
||||||
IconCircleCheck,
|
IconCircleCheck,
|
||||||
IconCircleX,
|
IconCircleX,
|
||||||
@ -30,6 +30,7 @@ import { useCreateApiFormModal } from '../../../hooks/UseForm';
|
|||||||
import { useInstance } from '../../../hooks/UseInstance';
|
import { useInstance } from '../../../hooks/UseInstance';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl, useServerApiState } from '../../../states/ApiState';
|
import { apiUrl, useServerApiState } from '../../../states/ApiState';
|
||||||
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { ActionButton } from '../../buttons/ActionButton';
|
import { ActionButton } from '../../buttons/ActionButton';
|
||||||
import { ActionDropdown, EditItemAction } from '../../items/ActionDropdown';
|
import { ActionDropdown, EditItemAction } from '../../items/ActionDropdown';
|
||||||
import { InfoItem } from '../../items/InfoItem';
|
import { InfoItem } from '../../items/InfoItem';
|
||||||
@ -423,11 +424,39 @@ export function PluginListTable({ props }: { props: InvenTreeTableProps }) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
const reloadPlugins = useCallback(() => {
|
||||||
|
api
|
||||||
|
.post(apiUrl(ApiPaths.plugin_reload), {
|
||||||
|
full_reload: true,
|
||||||
|
force_reload: true,
|
||||||
|
collect_plugins: true
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
showNotification({
|
||||||
|
title: t`Plugins reloaded`,
|
||||||
|
message: t`Plugins were reloaded successfully`,
|
||||||
|
color: 'green'
|
||||||
|
});
|
||||||
|
table.refreshTable();
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Custom table actions
|
// Custom table actions
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
let actions = [];
|
let actions = [];
|
||||||
|
|
||||||
if (pluginsEnabled) {
|
if (user.user?.is_superuser && pluginsEnabled) {
|
||||||
|
actions.push(
|
||||||
|
<ActionButton
|
||||||
|
color="green"
|
||||||
|
icon={<IconRefresh />}
|
||||||
|
tooltip={t`Reload Plugins`}
|
||||||
|
onClick={reloadPlugins}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
actions.push(
|
actions.push(
|
||||||
<ActionButton
|
<ActionButton
|
||||||
color="green"
|
color="green"
|
||||||
@ -439,7 +468,7 @@ export function PluginListTable({ props }: { props: InvenTreeTableProps }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}, []);
|
}, [user, pluginsEnabled]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -86,6 +86,7 @@ export enum ApiPaths {
|
|||||||
plugin_list = 'api-plugin-list',
|
plugin_list = 'api-plugin-list',
|
||||||
plugin_setting_list = 'api-plugin-settings',
|
plugin_setting_list = 'api-plugin-settings',
|
||||||
plugin_install = 'api-plugin-install',
|
plugin_install = 'api-plugin-install',
|
||||||
|
plugin_reload = 'api-plugin-reload',
|
||||||
plugin_registry_status = 'api-plugin-registry-status',
|
plugin_registry_status = 'api-plugin-registry-status',
|
||||||
|
|
||||||
project_code_list = 'api-project-code-list',
|
project_code_list = 'api-project-code-list',
|
||||||
|
@ -187,6 +187,8 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
return 'plugins/status/';
|
return 'plugins/status/';
|
||||||
case ApiPaths.plugin_install:
|
case ApiPaths.plugin_install:
|
||||||
return 'plugins/install/';
|
return 'plugins/install/';
|
||||||
|
case ApiPaths.plugin_reload:
|
||||||
|
return 'plugins/reload/';
|
||||||
case ApiPaths.project_code_list:
|
case ApiPaths.project_code_list:
|
||||||
return 'project-code/';
|
return 'project-code/';
|
||||||
case ApiPaths.custom_unit_list:
|
case ApiPaths.custom_unit_list:
|
||||||
|
Loading…
Reference in New Issue
Block a user