refactor: replace manual model_validate with @model_validate in workspace plugin/tool controllers (#40238)

This commit is contained in:
Likalikali
2026-08-09 06:13:24 +00:00
committed by GitHub
parent 98378cfe03
commit acda0341ed
4 changed files with 332 additions and 273 deletions
+102 -91
View File
@@ -24,6 +24,7 @@ from controllers.console.wraps import (
RBACResourceScope,
account_initialization_required,
is_admin_or_owner_required,
model_validate,
rbac_permission_required,
setup_required,
with_current_tenant_id,
@@ -592,10 +593,10 @@ class PluginListApi(Resource):
@account_initialization_required
@with_current_user_id
@with_current_tenant_id
def get(self, tenant_id: str, user_id: str):
args = ParserList.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserList)
def get(self, req_data: ParserList, tenant_id: str, user_id: str):
try:
plugins_with_total = PluginService.list_with_total(tenant_id, user_id, args.page, args.page_size)
plugins_with_total = PluginService.list_with_total(tenant_id, user_id, req_data.page, req_data.page_size)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -660,10 +661,10 @@ class PluginInstalledIdsApi(Resource):
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, tenant_id: str):
args = PluginInstalledIdsQuery.model_validate(request.args.to_dict(flat=True))
@model_validate(PluginInstalledIdsQuery)
def get(self, req_data: PluginInstalledIdsQuery, tenant_id: str):
try:
plugin_ids = PluginService.list_installed_plugin_ids(tenant_id, args.category)
plugin_ids = PluginService.list_installed_plugin_ids(tenant_id, req_data.category)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -677,11 +678,11 @@ class PluginListLatestVersionsApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
args = ParserLatest.model_validate(console_ns.payload)
@model_validate(ParserLatest)
def post(self, req_data: ParserLatest):
try:
versions = PluginService.list_latest_versions(args.plugin_ids)
versions = PluginService.list_latest_versions(req_data.plugin_ids)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -696,11 +697,11 @@ class PluginListInstallationsFromIdsApi(Resource):
@login_required
@account_initialization_required
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserLatest.model_validate(console_ns.payload)
@model_validate(ParserLatest)
def post(self, req_data: ParserLatest, tenant_id: str):
try:
plugins = PluginService.list_installations_from_ids(tenant_id, args.plugin_ids)
plugins = PluginService.list_installations_from_ids(tenant_id, req_data.plugin_ids)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -712,11 +713,11 @@ class PluginIconApi(Resource):
@console_ns.doc(params=query_params_from_model(ParserIcon))
@console_ns.response(200, "Success", console_ns.models[BinaryFileResponse.__name__])
@setup_required
def get(self):
args = ParserIcon.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserIcon)
def get(self, req_data: ParserIcon):
try:
icon_bytes, mimetype = PluginService.get_asset(args.tenant_id, args.filename)
icon_bytes, mimetype = PluginService.get_asset(req_data.tenant_id, req_data.filename)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -732,11 +733,11 @@ class PluginAssetApi(Resource):
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, tenant_id: str):
args = ParserAsset.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserAsset)
def get(self, req_data: ParserAsset, tenant_id: str):
try:
binary = PluginService.extract_asset(tenant_id, args.plugin_unique_identifier, args.file_name)
binary = PluginService.extract_asset(tenant_id, req_data.plugin_unique_identifier, req_data.file_name)
return send_file(io.BytesIO(binary), mimetype="application/octet-stream")
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -773,11 +774,13 @@ class PluginUploadFromGithubApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserGithubUpload.model_validate(console_ns.payload)
@model_validate(ParserGithubUpload)
def post(self, req_data: ParserGithubUpload, tenant_id: str):
try:
response = PluginService.upload_pkg_from_github(tenant_id, args.repo, args.version, args.package)
response = PluginService.upload_pkg_from_github(
tenant_id, req_data.repo, req_data.version, req_data.package
)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -814,11 +817,11 @@ class PluginInstallFromPkgApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserPluginIdentifiers.model_validate(console_ns.payload)
@model_validate(ParserPluginIdentifiers)
def post(self, req_data: ParserPluginIdentifiers, tenant_id: str):
try:
response = PluginService.install_from_local_pkg(tenant_id, args.plugin_unique_identifiers)
response = PluginService.install_from_local_pkg(tenant_id, req_data.plugin_unique_identifiers)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -835,16 +838,16 @@ class PluginInstallFromGithubApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserGithubInstall.model_validate(console_ns.payload)
@model_validate(ParserGithubInstall)
def post(self, req_data: ParserGithubInstall, tenant_id: str):
try:
response = PluginService.install_from_github(
tenant_id,
args.plugin_unique_identifier,
args.repo,
args.version,
args.package,
req_data.plugin_unique_identifier,
req_data.repo,
req_data.version,
req_data.package,
)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -862,11 +865,11 @@ class PluginInstallFromMarketplaceApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserPluginIdentifiers.model_validate(console_ns.payload)
@model_validate(ParserPluginIdentifiers)
def post(self, req_data: ParserPluginIdentifiers, tenant_id: str):
try:
response = PluginService.install_from_marketplace_pkg(tenant_id, args.plugin_unique_identifiers)
response = PluginService.install_from_marketplace_pkg(tenant_id, req_data.plugin_unique_identifiers)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -883,15 +886,15 @@ class PluginFetchMarketplacePkgApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def get(self, tenant_id: str):
args = ParserPluginIdentifierQuery.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserPluginIdentifierQuery)
def get(self, req_data: ParserPluginIdentifierQuery, tenant_id: str):
try:
return jsonable_encoder(
{
"manifest": PluginService.fetch_marketplace_pkg(
tenant_id,
args.plugin_unique_identifier,
req_data.plugin_unique_identifier,
)
}
)
@@ -909,12 +912,16 @@ class PluginFetchManifestApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_INSTALL, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def get(self, tenant_id: str):
args = ParserPluginIdentifierQuery.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserPluginIdentifierQuery)
def get(self, req_data: ParserPluginIdentifierQuery, tenant_id: str):
try:
return jsonable_encoder(
{"manifest": PluginService.fetch_plugin_manifest(tenant_id, args.plugin_unique_identifier).model_dump()}
{
"manifest": PluginService.fetch_plugin_manifest(
tenant_id, req_data.plugin_unique_identifier
).model_dump()
},
)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -929,11 +936,13 @@ class PluginFetchInstallTasksApi(Resource):
@account_initialization_required
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def get(self, tenant_id: str):
args = ParserTasks.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserTasks)
def get(self, req_data: ParserTasks, tenant_id: str):
try:
return jsonable_encoder({"tasks": PluginService.fetch_install_tasks(tenant_id, args.page, args.page_size)})
return jsonable_encoder(
{"tasks": PluginService.fetch_install_tasks(tenant_id, req_data.page, req_data.page_size)}
)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -1008,13 +1017,13 @@ class PluginUpgradeFromMarketplaceApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_MODEL_CONFIG, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserMarketplaceUpgrade.model_validate(console_ns.payload)
@model_validate(ParserMarketplaceUpgrade)
def post(self, req_data: ParserMarketplaceUpgrade, tenant_id: str):
try:
return jsonable_encoder(
PluginService.upgrade_plugin_with_marketplace(
tenant_id, args.original_plugin_unique_identifier, args.new_plugin_unique_identifier
tenant_id, req_data.original_plugin_unique_identifier, req_data.new_plugin_unique_identifier
)
)
except PluginDaemonClientSideError as e:
@@ -1031,18 +1040,18 @@ class PluginUpgradeFromGithubApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_MODEL_CONFIG, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserGithubUpgrade.model_validate(console_ns.payload)
@model_validate(ParserGithubUpgrade)
def post(self, req_data: ParserGithubUpgrade, tenant_id: str):
try:
return jsonable_encoder(
PluginService.upgrade_plugin_with_github(
tenant_id,
args.original_plugin_unique_identifier,
args.new_plugin_unique_identifier,
args.repo,
args.version,
args.package,
req_data.original_plugin_unique_identifier,
req_data.new_plugin_unique_identifier,
req_data.repo,
req_data.version,
req_data.package,
)
)
except PluginDaemonClientSideError as e:
@@ -1059,15 +1068,15 @@ class PluginUninstallApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_DELETE, resource_required=False)
@plugin_permission_required(install_required=True)
@with_current_tenant_id
def post(self, tenant_id: str):
args = ParserUninstall.model_validate(console_ns.payload)
@model_validate(ParserUninstall)
def post(self, req_data: ParserUninstall, tenant_id: str):
try:
return {
"success": PluginService.uninstall(
tenant_id,
args.plugin_installation_id,
preserve_credentials=args.preserve_credentials,
req_data.plugin_installation_id,
preserve_credentials=req_data.preserve_credentials,
)
}
except PluginDaemonClientSideError as e:
@@ -1083,14 +1092,13 @@ class PluginChangePermissionApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
@model_validate(ParserPermissionChange)
def post(self, req_data: ParserPermissionChange, tenant_id: str, user: Account):
if not user.is_admin_or_owner:
raise Forbidden()
args = ParserPermissionChange.model_validate(console_ns.payload)
set_permission_result = PluginPermissionService.change_permission(
tenant_id, args.install_permission, args.debug_permission, session=db.session()
tenant_id, req_data.install_permission, req_data.debug_permission, session=db.session()
)
if not set_permission_result:
return jsonable_encoder({"success": False, "message": "Failed to set permission"})
@@ -1134,19 +1142,19 @@ class PluginFetchDynamicSelectOptionsApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def get(self, tenant_id: str, current_user: Account):
args = ParserDynamicOptions.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserDynamicOptions)
def get(self, req_data: ParserDynamicOptions, tenant_id: str, current_user: Account):
try:
options = PluginParameterService.get_dynamic_select_options(
tenant_id=tenant_id,
user_id=current_user.id,
plugin_id=args.plugin_id,
provider=args.provider,
action=args.action,
parameter=args.parameter,
credential_id=args.credential_id,
provider_type=args.provider_type,
plugin_id=req_data.plugin_id,
provider=req_data.provider,
action=req_data.action,
parameter=req_data.parameter,
credential_id=req_data.credential_id,
provider_type=req_data.provider_type,
)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -1165,20 +1173,20 @@ class PluginFetchDynamicSelectOptionsWithCredentialsApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, current_user: Account):
@model_validate(ParserDynamicOptionsWithCredentials)
def post(self, req_data: ParserDynamicOptionsWithCredentials, tenant_id: str, current_user: Account):
"""Fetch dynamic options using credentials directly (for edit mode)."""
args = ParserDynamicOptionsWithCredentials.model_validate(console_ns.payload)
try:
options = PluginParameterService.get_dynamic_select_options_with_credentials(
tenant_id=tenant_id,
user_id=current_user.id,
plugin_id=args.plugin_id,
provider=args.provider,
action=args.action,
parameter=args.parameter,
credential_id=args.credential_id,
credentials=args.credentials,
plugin_id=req_data.plugin_id,
provider=req_data.provider,
action=req_data.action,
parameter=req_data.parameter,
credential_id=req_data.credential_id,
credentials=req_data.credentials,
)
except PluginDaemonClientSideError as e:
return {"code": "plugin_error", "message": e.description}, 400
@@ -1196,13 +1204,12 @@ class PluginChangeAutoUpgradeApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_PREFERENCES, resource_required=False)
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
@model_validate(ParserAutoUpgradeChange)
def post(self, req_data: ParserAutoUpgradeChange, tenant_id: str, user: Account):
if not dify_config.RBAC_ENABLED and not user.is_admin_or_owner:
raise Forbidden()
args = ParserAutoUpgradeChange.model_validate(console_ns.payload)
auto_upgrade = args.auto_upgrade
auto_upgrade = req_data.auto_upgrade
set_auto_upgrade_strategy_result = PluginAutoUpgradeService.change_strategy(
tenant_id,
auto_upgrade.strategy_setting,
@@ -1210,7 +1217,7 @@ class PluginChangeAutoUpgradeApi(Resource):
auto_upgrade.upgrade_mode,
auto_upgrade.exclude_plugins,
auto_upgrade.include_plugins,
category=args.category,
category=req_data.category,
session=db.session(),
)
if not set_auto_upgrade_strategy_result:
@@ -1227,16 +1234,16 @@ class PluginFetchAutoUpgradeApi(Resource):
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, tenant_id: str):
args = ParserAutoUpgradeFetch.model_validate(request.args.to_dict(flat=True))
auto_upgrade = PluginAutoUpgradeService.get_strategy(tenant_id, args.category, session=db.session())
@model_validate(ParserAutoUpgradeFetch)
def get(self, req_data: ParserAutoUpgradeFetch, tenant_id: str):
auto_upgrade = PluginAutoUpgradeService.get_strategy(tenant_id, req_data.category, session=db.session())
auto_upgrade_dict = (
_auto_upgrade_settings_to_dict(auto_upgrade) if auto_upgrade else _missing_auto_upgrade_settings(tenant_id)
)
return jsonable_encoder(
{
"category": args.category,
"category": req_data.category,
"auto_upgrade": auto_upgrade_dict,
}
)
@@ -1251,14 +1258,14 @@ class PluginAutoUpgradeExcludePluginApi(Resource):
@account_initialization_required
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.PLUGIN_PREFERENCES, resource_required=False)
@with_current_tenant_id
def post(self, tenant_id: str):
@model_validate(ParserExcludePlugin)
def post(self, req_data: ParserExcludePlugin, tenant_id: str):
# exclude one single plugin
args = ParserExcludePlugin.model_validate(console_ns.payload)
return jsonable_encoder(
{
"success": PluginAutoUpgradeService.exclude_plugin(
tenant_id, args.plugin_id, args.category, session=db.session()
tenant_id, req_data.plugin_id, req_data.category, session=db.session()
)
}
)
@@ -1272,8 +1279,12 @@ class PluginReadmeApi(Resource):
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, tenant_id: str):
args = ParserReadme.model_validate(request.args.to_dict(flat=True))
@model_validate(ParserReadme)
def get(self, req_data: ParserReadme, tenant_id: str):
return jsonable_encoder(
{"readme": PluginService.fetch_plugin_readme(tenant_id, args.plugin_unique_identifier, args.language)}
{
"readme": PluginService.fetch_plugin_readme(
tenant_id, req_data.plugin_unique_identifier, req_data.language
)
},
)
@@ -33,6 +33,7 @@ from controllers.console.wraps import (
account_initialization_required,
enterprise_license_required,
is_admin_or_owner_required,
model_validate,
rbac_permission_required,
setup_required,
with_current_tenant_id,
@@ -555,16 +556,15 @@ class ToolBuiltinProviderDeleteApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False)
@account_initialization_required
@with_current_tenant_id
def post(self, tenant_id: str, provider: str):
payload = BuiltinToolCredentialDeletePayload.model_validate(console_ns.payload or {})
@model_validate(BuiltinToolCredentialDeletePayload)
def post(self, req_data: BuiltinToolCredentialDeletePayload, tenant_id: str, provider: str):
return dump_response(
SimpleResultResponse,
BuiltinToolManageService.delete_builtin_tool_provider(
tenant_id,
provider,
payload.credential_id,
req_data.credential_id,
),
)
@@ -582,19 +582,18 @@ class ToolBuiltinProviderAddApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account, provider: str):
payload = BuiltinToolAddPayload.model_validate(console_ns.payload or {})
@model_validate(BuiltinToolAddPayload)
def post(self, req_data: BuiltinToolAddPayload, tenant_id: str, user: Account, provider: str):
return dump_response(
SimpleResultResponse,
BuiltinToolManageService.add_builtin_tool_provider(
user_id=user.id,
tenant_id=tenant_id,
provider=provider,
credentials=payload.credentials,
name=payload.name,
api_type=CredentialType.of(payload.type),
visibility=payload.visibility,
credentials=req_data.credentials,
name=req_data.name,
api_type=CredentialType.of(req_data.type),
visibility=req_data.visibility,
),
)
@@ -614,16 +613,15 @@ class ToolBuiltinProviderUpdateApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account, provider: str):
payload = BuiltinToolUpdatePayload.model_validate(console_ns.payload or {})
@model_validate(BuiltinToolUpdatePayload)
def post(self, req_data: BuiltinToolUpdatePayload, tenant_id: str, user: Account, provider: str):
result = BuiltinToolManageService.update_builtin_tool_provider(
user_id=user.id,
tenant_id=tenant_id,
provider=provider,
credential_id=payload.credential_id,
credentials=payload.credentials,
name=payload.name or "",
credential_id=req_data.credential_id,
credentials=req_data.credentials,
name=req_data.name or "",
)
return dump_response(SimpleResultResponse, result)
@@ -683,22 +681,21 @@ class ToolApiProviderAddApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = ApiToolProviderAddPayload.model_validate(console_ns.payload or {})
@model_validate(ApiToolProviderAddPayload)
def post(self, req_data: ApiToolProviderAddPayload, tenant_id: str, user: Account):
return dump_response(
SimpleResultResponse,
ApiToolManageService.create_api_tool_provider(
user.id,
tenant_id,
payload.provider,
payload.icon.model_dump(mode="json"),
payload.credentials,
payload.schema_type,
payload.schema_,
payload.privacy_policy or "",
payload.custom_disclaimer or "",
payload.labels or [],
req_data.provider,
req_data.icon.model_dump(mode="json"),
req_data.credentials,
req_data.schema_type,
req_data.schema_,
req_data.privacy_policy or "",
req_data.custom_disclaimer or "",
req_data.labels or [],
),
)
@@ -764,23 +761,22 @@ class ToolApiProviderUpdateApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = ApiToolProviderUpdatePayload.model_validate(console_ns.payload or {})
@model_validate(ApiToolProviderUpdatePayload)
def post(self, req_data: ApiToolProviderUpdatePayload, tenant_id: str, user: Account):
return dump_response(
SimpleResultResponse,
ApiToolManageService.update_api_tool_provider(
user.id,
tenant_id,
payload.provider,
payload.original_provider,
payload.icon.model_dump(mode="json"),
payload.credentials,
payload.schema_type,
payload.schema_,
payload.privacy_policy,
payload.custom_disclaimer,
payload.labels or [],
req_data.provider,
req_data.original_provider,
req_data.icon.model_dump(mode="json"),
req_data.credentials,
req_data.schema_type,
req_data.schema_,
req_data.privacy_policy,
req_data.custom_disclaimer,
req_data.labels or [],
),
)
@@ -796,15 +792,14 @@ class ToolApiProviderDeleteApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = ApiToolProviderDeletePayload.model_validate(console_ns.payload or {})
@model_validate(ApiToolProviderDeletePayload)
def post(self, req_data: ApiToolProviderDeletePayload, tenant_id: str, user: Account):
return dump_response(
SimpleResultResponse,
ApiToolManageService.delete_api_tool_provider(
user.id,
tenant_id,
payload.provider,
req_data.provider,
),
)
@@ -861,10 +856,9 @@ class ToolApiProviderSchemaApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
payload = ApiToolSchemaPayload.model_validate(console_ns.payload or {})
return dump_response(ApiSchemaParseResponse, ApiToolManageService.parser_api_schema(schema=payload.schema_))
@model_validate(ApiToolSchemaPayload)
def post(self, req_data: ApiToolSchemaPayload):
return dump_response(ApiSchemaParseResponse, ApiToolManageService.parser_api_schema(schema=req_data.schema_))
@console_ns.route("/workspaces/current/tool-provider/api/test/pre")
@@ -879,18 +873,18 @@ class ToolApiProviderPreviousTestApi(Resource):
@login_required
@account_initialization_required
@with_current_tenant_id
def post(self, current_tenant_id: str):
payload = ApiToolTestPayload.model_validate(console_ns.payload or {})
@model_validate(ApiToolTestPayload)
def post(self, req_data: ApiToolTestPayload, current_tenant_id: str):
return dump_response(
ApiToolPreviewResponse,
ApiToolManageService.test_api_tool_preview(
current_tenant_id,
payload.provider_name or "",
payload.tool_name,
payload.credentials,
payload.parameters,
payload.schema_type,
payload.schema_,
req_data.provider_name or "",
req_data.tool_name,
req_data.credentials,
req_data.parameters,
req_data.schema_type,
req_data.schema_,
),
)
@@ -906,22 +900,21 @@ class ToolWorkflowProviderCreateApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = WorkflowToolCreatePayload.model_validate(console_ns.payload or {})
@model_validate(WorkflowToolCreatePayload)
def post(self, req_data: WorkflowToolCreatePayload, tenant_id: str, user: Account):
return dump_response(
SimpleResultResponse,
WorkflowToolManageService.create_workflow_tool(
user_id=user.id,
tenant_id=tenant_id,
workflow_app_id=payload.workflow_app_id,
name=payload.name,
label=payload.label,
icon=payload.icon.model_dump(mode="json"),
description=payload.description,
parameters=payload.parameters,
privacy_policy=payload.privacy_policy or "",
labels=payload.labels or [],
workflow_app_id=req_data.workflow_app_id,
name=req_data.name,
label=req_data.label,
icon=req_data.icon.model_dump(mode="json"),
description=req_data.description,
parameters=req_data.parameters,
privacy_policy=req_data.privacy_policy or "",
labels=req_data.labels or [],
),
)
@@ -937,22 +930,21 @@ class ToolWorkflowProviderUpdateApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = WorkflowToolUpdatePayload.model_validate(console_ns.payload or {})
@model_validate(WorkflowToolUpdatePayload)
def post(self, req_data: WorkflowToolUpdatePayload, tenant_id: str, user: Account):
return dump_response(
SimpleResultResponse,
WorkflowToolManageService.update_workflow_tool(
user.id,
tenant_id,
payload.workflow_tool_id,
payload.name,
payload.label,
payload.icon.model_dump(mode="json"),
payload.description,
payload.parameters,
payload.privacy_policy or "",
payload.labels or [],
req_data.workflow_tool_id,
req_data.name,
req_data.label,
req_data.icon.model_dump(mode="json"),
req_data.description,
req_data.parameters,
req_data.privacy_policy or "",
req_data.labels or [],
),
)
@@ -968,15 +960,14 @@ class ToolWorkflowProviderDeleteApi(Resource):
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = WorkflowToolDeletePayload.model_validate(console_ns.payload or {})
@model_validate(WorkflowToolDeletePayload)
def post(self, req_data: WorkflowToolDeletePayload, tenant_id: str, user: Account):
return dump_response(
SimpleResultResponse,
WorkflowToolManageService.delete_workflow_tool(
user.id,
tenant_id,
payload.workflow_tool_id,
req_data.workflow_tool_id,
),
)
@@ -1224,12 +1215,12 @@ class ToolBuiltinProviderSetDefaultApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False)
@account_initialization_required
@with_current_tenant_id
def post(self, current_tenant_id: str, provider: str):
payload = BuiltinProviderDefaultCredentialPayload.model_validate(console_ns.payload or {})
@model_validate(BuiltinProviderDefaultCredentialPayload)
def post(self, req_data: BuiltinProviderDefaultCredentialPayload, current_tenant_id: str, provider: str):
return dump_response(
SimpleResultResponse,
BuiltinToolManageService.set_default_provider(
tenant_id=current_tenant_id, provider=provider, id=payload.id
tenant_id=current_tenant_id, provider=provider, id=req_data.id
),
)
@@ -1246,17 +1237,16 @@ class ToolOAuthCustomClient(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False)
@account_initialization_required
@with_current_tenant_id
def post(self, tenant_id: str, provider: str):
payload = ToolOAuthCustomClientPayload.model_validate(console_ns.payload or {})
@model_validate(ToolOAuthCustomClientPayload)
def post(self, req_data: ToolOAuthCustomClientPayload, tenant_id: str, provider: str):
return dump_response(
SimpleResultResponse,
BuiltinToolManageService.save_custom_oauth_client_params(
tenant_id=tenant_id,
provider=provider,
client_params=payload.client_params or {},
enable_oauth_custom_client=payload.enable_oauth_custom_client
if payload.enable_oauth_custom_client is not None
client_params=req_data.client_params or {},
enable_oauth_custom_client=req_data.enable_oauth_custom_client
if req_data.enable_oauth_custom_client is not None
else True,
),
)
@@ -1349,11 +1339,10 @@ class ToolProviderMCPApi(Resource):
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False)
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, user: Account):
payload = MCPProviderCreatePayload.model_validate(console_ns.payload or {})
configuration = payload.configuration or MCPConfiguration()
authentication = payload.authentication
@model_validate(MCPProviderCreatePayload)
def post(self, req_data: MCPProviderCreatePayload, tenant_id: str, user: Account):
configuration = req_data.configuration or MCPConfiguration()
authentication = req_data.authentication
# 1) Create provider in a short transaction (no network I/O inside)
with session_factory.create_session() as session, session.begin():
@@ -1361,24 +1350,24 @@ class ToolProviderMCPApi(Resource):
result = service.create_provider(
tenant_id=tenant_id,
user_id=user.id,
server_url=payload.server_url,
name=payload.name,
icon=payload.icon,
icon_type=payload.icon_type,
icon_background=payload.icon_background,
server_identifier=payload.server_identifier,
headers=payload.headers or {},
server_url=req_data.server_url,
name=req_data.name,
icon=req_data.icon,
icon_type=req_data.icon_type,
icon_background=req_data.icon_background,
server_identifier=req_data.server_identifier,
headers=req_data.headers or {},
configuration=configuration,
authentication=authentication,
identity_mode=_resolve_identity_mode(payload.identity_mode, current=IdentityMode.OFF),
identity_mode=_resolve_identity_mode(req_data.identity_mode, current=IdentityMode.OFF),
)
# 2) Try to fetch tools immediately after creation so they appear without a second save.
# Perform network I/O outside any DB session to avoid holding locks.
try:
reconnect = MCPToolManageService.reconnect_with_url(
server_url=payload.server_url,
headers=payload.headers or {},
server_url=req_data.server_url,
headers=req_data.headers or {},
timeout=configuration.timeout,
sse_read_timeout=configuration.sse_read_timeout,
)
@@ -1403,24 +1392,24 @@ class ToolProviderMCPApi(Resource):
@account_initialization_required
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False)
@with_current_tenant_id
def put(self, current_tenant_id: str):
payload = MCPProviderUpdatePayload.model_validate(console_ns.payload or {})
configuration = payload.configuration or MCPConfiguration()
authentication = payload.authentication
@model_validate(MCPProviderUpdatePayload)
def put(self, req_data: MCPProviderUpdatePayload, current_tenant_id: str):
configuration = req_data.configuration or MCPConfiguration()
authentication = req_data.authentication
# Step 1: Get provider data for URL validation (short-lived session, no network I/O)
validation_data = None
with sessionmaker(db.engine).begin() as session:
service = MCPToolManageService(session=session)
validation_data = service.get_provider_for_url_validation(
tenant_id=current_tenant_id, provider_id=payload.provider_id
tenant_id=current_tenant_id, provider_id=req_data.provider_id
)
# Step 2: Perform URL validation with network I/O OUTSIDE of any database session
# This prevents holding database locks during potentially slow network operations
validation_result = MCPToolManageService.validate_server_url_standalone(
tenant_id=current_tenant_id,
new_server_url=payload.server_url,
new_server_url=req_data.server_url,
validation_data=validation_data,
)
@@ -1430,18 +1419,18 @@ class ToolProviderMCPApi(Resource):
# Resolve "leave unchanged" (None) against the stored value, and gate
# the result on ENTERPRISE_ENABLED — both are API-layer concerns, so
# the service receives a concrete IdentityMode.
existing = service.get_provider(provider_id=payload.provider_id, tenant_id=current_tenant_id)
identity_mode = _resolve_identity_mode(payload.identity_mode, current=IdentityMode(existing.identity_mode))
existing = service.get_provider(provider_id=req_data.provider_id, tenant_id=current_tenant_id)
identity_mode = _resolve_identity_mode(req_data.identity_mode, current=IdentityMode(existing.identity_mode))
service.update_provider(
tenant_id=current_tenant_id,
provider_id=payload.provider_id,
server_url=payload.server_url,
name=payload.name,
icon=payload.icon,
icon_type=payload.icon_type,
icon_background=payload.icon_background,
server_identifier=payload.server_identifier,
headers=payload.headers or {},
provider_id=req_data.provider_id,
server_url=req_data.server_url,
name=req_data.name,
icon=req_data.icon,
icon_type=req_data.icon_type,
icon_background=req_data.icon_background,
server_identifier=req_data.server_identifier,
headers=req_data.headers or {},
configuration=configuration,
authentication=authentication,
validation_result=validation_result,
@@ -1457,12 +1446,11 @@ class ToolProviderMCPApi(Resource):
@account_initialization_required
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False)
@with_current_tenant_id
def delete(self, current_tenant_id: str):
payload = MCPProviderDeletePayload.model_validate(console_ns.payload or {})
@model_validate(MCPProviderDeletePayload)
def delete(self, req_data: MCPProviderDeletePayload, current_tenant_id: str):
with sessionmaker(db.engine).begin() as session:
service = MCPToolManageService(session=session)
service.delete_provider(tenant_id=current_tenant_id, provider_id=payload.provider_id)
service.delete_provider(tenant_id=current_tenant_id, provider_id=req_data.provider_id)
return SimpleResultResponse(result="success").model_dump(mode="json")
@@ -1476,9 +1464,9 @@ class ToolMCPAuthApi(Resource):
@account_initialization_required
@rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.MCP_MANAGE, resource_required=False)
@with_current_tenant_id
def post(self, tenant_id: str):
payload = MCPAuthPayload.model_validate(console_ns.payload or {})
provider_id = payload.provider_id
@model_validate(MCPAuthPayload)
def post(self, req_data: MCPAuthPayload, tenant_id: str):
provider_id = req_data.provider_id
with sessionmaker(db.engine).begin() as session:
service = MCPToolManageService(session=session)
@@ -1515,7 +1503,7 @@ class ToolMCPAuthApi(Resource):
# Pass the extracted OAuth metadata hints to auth()
auth_result = auth(
provider_entity,
payload.authorization_code,
req_data.authorization_code,
resource_metadata_url=e.resource_metadata_url,
scope_hint=e.scope_hint,
)