mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 15:38:19 +08:00
fix(plugin): reset stopped plugin config and data (#6031)
This commit is contained in:
@@ -615,9 +615,9 @@ def reset_plugin(
|
|||||||
# 事件处理器需要运行中插件完成补偿;补偿后先停止插件,避免删除数据时仍有任务读写旧状态。
|
# 事件处理器需要运行中插件完成补偿;补偿后先停止插件,避免删除数据时仍有任务读写旧状态。
|
||||||
plugin_manager.stop(plugin_id)
|
plugin_manager.stop(plugin_id)
|
||||||
# 删除配置
|
# 删除配置
|
||||||
plugin_manager.delete_plugin_config(plugin_id)
|
plugin_manager.delete_plugin_config(plugin_id, force=True)
|
||||||
# 删除插件所有数据
|
# 删除插件所有数据
|
||||||
plugin_manager.delete_plugin_data(plugin_id)
|
plugin_manager.delete_plugin_data(plugin_id, force=True)
|
||||||
# 重新加载插件
|
# 重新加载插件
|
||||||
reload_plugin(plugin_id)
|
reload_plugin(plugin_id)
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
|
|||||||
+6
-4
@@ -723,21 +723,23 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
await SystemConfigOper().async_set(self._config_key % pid, conf)
|
await SystemConfigOper().async_set(self._config_key % pid, conf)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def delete_plugin_config(self, pid: str) -> bool:
|
def delete_plugin_config(self, pid: str, force: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
删除插件配置
|
删除插件配置
|
||||||
:param pid: 插件ID
|
:param pid: 插件ID
|
||||||
|
:param force: 插件停止后仍允许按插件 ID 删除持久化配置
|
||||||
"""
|
"""
|
||||||
if not self._plugins.get(pid):
|
if not force and not self._plugins.get(pid):
|
||||||
return False
|
return False
|
||||||
return SystemConfigOper().delete(self._config_key % pid)
|
return SystemConfigOper().delete(self._config_key % pid)
|
||||||
|
|
||||||
def delete_plugin_data(self, pid: str) -> bool:
|
def delete_plugin_data(self, pid: str, force: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
删除插件数据
|
删除插件数据
|
||||||
:param pid: 插件ID
|
:param pid: 插件ID
|
||||||
|
:param force: 插件停止后仍允许按插件 ID 删除持久化数据
|
||||||
"""
|
"""
|
||||||
if not self._plugins.get(pid):
|
if not force and not self._plugins.get(pid):
|
||||||
return False
|
return False
|
||||||
PluginDataOper().del_data(pid)
|
PluginDataOper().del_data(pid)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ from app.api.endpoints.plugin import plugin_releases
|
|||||||
from app.api.endpoints.plugin import reset_plugin
|
from app.api.endpoints.plugin import reset_plugin
|
||||||
from app.api.endpoints.system import sync_plugin_market_from_wiki
|
from app.api.endpoints.system import sync_plugin_market_from_wiki
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
from app.core.plugin import PluginManager
|
||||||
from app.schemas.event import PluginDataResetEventData
|
from app.schemas.event import PluginDataResetEventData
|
||||||
from app.schemas.types import ChainEventType
|
from app.schemas.types import ChainEventType
|
||||||
|
from app.utils.singleton import Singleton
|
||||||
|
|
||||||
|
|
||||||
def test_plugin_history_merges_remote_metadata():
|
def test_plugin_history_merges_remote_metadata():
|
||||||
@@ -279,12 +281,12 @@ def test_reset_plugin_sends_pre_reset_chain_event_before_deleting_data():
|
|||||||
plugin_manager = MagicMock()
|
plugin_manager = MagicMock()
|
||||||
calls = []
|
calls = []
|
||||||
|
|
||||||
def delete_config(plugin_id):
|
def delete_config(plugin_id, force=False):
|
||||||
calls.append(("delete_config", plugin_id))
|
calls.append(("delete_config", plugin_id, force))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def delete_data(plugin_id):
|
def delete_data(plugin_id, force=False):
|
||||||
calls.append(("delete_data", plugin_id))
|
calls.append(("delete_data", plugin_id, force))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def stop_plugin(plugin_id):
|
def stop_plugin(plugin_id):
|
||||||
@@ -314,7 +316,38 @@ def test_reset_plugin_sends_pre_reset_chain_event_before_deleting_data():
|
|||||||
assert event_call[2].reset_data is True
|
assert event_call[2].reset_data is True
|
||||||
assert calls[1:] == [
|
assert calls[1:] == [
|
||||||
("stop", "SubscribeAssistantEnhanced"),
|
("stop", "SubscribeAssistantEnhanced"),
|
||||||
("delete_config", "SubscribeAssistantEnhanced"),
|
("delete_config", "SubscribeAssistantEnhanced", True),
|
||||||
("delete_data", "SubscribeAssistantEnhanced"),
|
("delete_data", "SubscribeAssistantEnhanced", True),
|
||||||
]
|
]
|
||||||
reload_plugin_mock.assert_called_once_with("SubscribeAssistantEnhanced")
|
reload_plugin_mock.assert_called_once_with("SubscribeAssistantEnhanced")
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_plugin_config_can_force_delete_after_plugin_is_stopped():
|
||||||
|
"""
|
||||||
|
重置入口会先停止插件;配置删除需要能处理运行态注册已清理的插件 ID。
|
||||||
|
"""
|
||||||
|
Singleton._instances.pop((PluginManager, (), frozenset()), None)
|
||||||
|
manager = PluginManager()
|
||||||
|
|
||||||
|
with patch("app.core.plugin.SystemConfigOper") as system_config_oper:
|
||||||
|
system_config_oper.return_value.delete.return_value = True
|
||||||
|
assert manager.delete_plugin_config("DemoPlugin", force=True) is True
|
||||||
|
|
||||||
|
system_config_oper.return_value.delete.assert_called_once_with("plugin.DemoPlugin")
|
||||||
|
Singleton._instances.pop((PluginManager, (), frozenset()), None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_plugin_data_can_force_delete_after_plugin_is_stopped():
|
||||||
|
"""
|
||||||
|
重置入口会先停止插件;插件数据删除不能依赖运行态注册仍存在。
|
||||||
|
"""
|
||||||
|
Singleton._instances.pop((PluginManager, (), frozenset()), None)
|
||||||
|
manager = PluginManager()
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
with patch("app.core.plugin.PluginDataOper") as plugin_data_oper:
|
||||||
|
plugin_data_oper.return_value.del_data.side_effect = lambda pid: calls.append(pid)
|
||||||
|
assert manager.delete_plugin_data("DemoPlugin", force=True) is True
|
||||||
|
|
||||||
|
assert calls == ["DemoPlugin"]
|
||||||
|
Singleton._instances.pop((PluginManager, (), frozenset()), None)
|
||||||
|
|||||||
Reference in New Issue
Block a user