From 95a6a14ed5e45012b94261733f336a55a905329e Mon Sep 17 00:00:00 2001 From: Eddy ZHANG Date: Thu, 3 Sep 2026 01:50:22 +0000 Subject: [PATCH] refactor(console,web): dep-inject query params with @model_validate (#41646) --- api/controllers/console/extension.py | 6 ++---- api/controllers/web/app.py | 6 +++--- .../unit_tests/controllers/console/test_extension.py | 8 ++++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/api/controllers/console/extension.py b/api/controllers/console/extension.py index b7ba7bf8075..6c1904f111f 100644 --- a/api/controllers/console/extension.py +++ b/api/controllers/console/extension.py @@ -2,7 +2,6 @@ from datetime import datetime from typing import Any from uuid import UUID -from flask import request from flask_restx import Resource from pydantic import BaseModel, Field, RootModel, field_validator @@ -91,9 +90,8 @@ class CodeBasedExtensionAPI(Resource): @setup_required @login_required @account_initialization_required - def get(self): - query = CodeBasedExtensionQuery.model_validate(request.args.to_dict(flat=True)) - + @model_validate(CodeBasedExtensionQuery) + def get(self, query: CodeBasedExtensionQuery): return CodeBasedExtensionResponse( module=query.module, data=CodeBasedExtensionService.get_code_based_extension(query.module), diff --git a/api/controllers/web/app.py b/api/controllers/web/app.py index 1349de9d486..1e985b77cc1 100644 --- a/api/controllers/web/app.py +++ b/api/controllers/web/app.py @@ -10,6 +10,7 @@ from constants import HEADER_NAME_APP_CODE from controllers.common.errors import InvalidArgumentError from controllers.common.fields import AccessModeResponse, BooleanResultResponse, Parameters from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models +from controllers.console.wraps import model_validate from controllers.web import web_ns from controllers.web.error import ( AgentNotPublishedError, @@ -133,9 +134,8 @@ class AppAccessMode(Resource): } ) @web_ns.response(200, "Success", web_ns.models[AccessModeResponse.__name__]) - def get(self): - raw_args = request.args.to_dict() - args = AppAccessModeQuery.model_validate(raw_args) + @model_validate(AppAccessModeQuery) + def get(self, args: AppAccessModeQuery): try: access_mode = application_services().webapp_access.get_access_mode( app_id=args.app_id, diff --git a/api/tests/unit_tests/controllers/console/test_extension.py b/api/tests/unit_tests/controllers/console/test_extension.py index ea60db4a1a3..e097930ced8 100644 --- a/api/tests/unit_tests/controllers/console/test_extension.py +++ b/api/tests/unit_tests/controllers/console/test_extension.py @@ -8,6 +8,7 @@ from unittest.mock import ANY, MagicMock import pytest from flask import Flask from flask.views import MethodView as FlaskMethodView +from werkzeug.exceptions import UnprocessableEntity from tests.unit_tests.config_override import apply_config_overrides @@ -105,6 +106,13 @@ def test_code_based_extension_get_returns_service_data(app: Flask, monkeypatch: service_mock.assert_called_once_with("workflow.tools") +def test_code_based_extension_get_rejects_a_missing_module(app: Flask) -> None: + """`module` is required, so this is what covers the decorator's rejection path.""" + with app.test_request_context("/console/api/code-based-extension", method="GET"): + with pytest.raises(UnprocessableEntity): + CodeBasedExtensionAPI().get() + + def test_api_based_extension_get_returns_tenant_extensions(app: Flask, monkeypatch: pytest.MonkeyPatch): extension = _make_extension(name="Weather API", api_key="abcdefghi123") service_mock = MagicMock(return_value=[extension])