From 21525b78e4c30759ecfe335b81a306ea0754d935 Mon Sep 17 00:00:00 2001 From: chariri Date: Fri, 26 Jun 2026 02:55:57 +0900 Subject: [PATCH 1/2] refactor(api): migrate workspace account endpoints to BaseModel --- api/controllers/console/workspace/account.py | 117 ++++---- api/controllers/console/workspace/endpoint.py | 237 +++++++-------- api/controllers/console/workspace/members.py | 91 +++--- .../console/workspace/workspace.py | 122 ++++---- api/fields/member_fields.py | 18 +- api/openapi/markdown/console-openapi.md | 284 +++++++++++------- api/openapi/markdown/service-openapi.md | 4 +- .../console/workspace/test_endpoint.py | 57 +++- .../console/workspace/test_workspace.py | 15 +- .../generated/api/console/account/orpc.gen.ts | 38 +-- .../api/console/account/types.gen.ts | 44 +-- .../generated/api/console/account/zod.gen.ts | 45 +-- .../api/console/all-workspaces/types.gen.ts | 4 +- .../api/console/all-workspaces/zod.gen.ts | 6 +- .../generated/api/console/apps/types.gen.ts | 57 +++- .../generated/api/console/apps/zod.gen.ts | 217 +++++++------ .../generated/api/console/rag/types.gen.ts | 21 +- .../generated/api/console/rag/zod.gen.ts | 24 +- .../api/console/snippets/types.gen.ts | 12 +- .../generated/api/console/snippets/zod.gen.ts | 14 +- .../api/console/workspaces/orpc.gen.ts | 2 + .../api/console/workspaces/types.gen.ts | 166 ++++++---- .../api/console/workspaces/zod.gen.ts | 239 ++++++++++----- .../generated/api/service/types.gen.ts | 4 +- .../generated/api/service/zod.gen.ts | 6 +- packages/contracts/openapi-ts.api.config.ts | 78 ++++- 26 files changed, 1126 insertions(+), 796 deletions(-) diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index c13c8aa162f..e2aae8eee5e 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -1,12 +1,12 @@ from __future__ import annotations from datetime import datetime -from typing import Any, Literal +from typing import Literal import pytz from flask import request from flask_restx import Resource -from pydantic import BaseModel, Field, RootModel, field_validator, model_validator +from pydantic import BaseModel, Field, field_validator, model_validator from sqlalchemy import select from werkzeug.exceptions import NotFound @@ -47,7 +47,7 @@ from controllers.console.wraps import ( ) from extensions.ext_database import db from fields.base import ResponseModel -from fields.member_fields import Account as AccountResponse +from fields.member_fields import AccountResponse from graphon.file import helpers as file_helpers from libs.datetime_utils import naive_utc_now from libs.helper import EmailStr, dump_response, extract_remote_ip, timezone, to_timestamp @@ -194,10 +194,6 @@ register_schema_models( ) -def _serialize_account(account) -> dict[str, Any]: - return AccountResponse.model_validate(account, from_attributes=True).model_dump(mode="json") - - class AccountIntegrateResponse(ResponseModel): provider: str created_at: int | None = None @@ -236,23 +232,15 @@ class EducationAutocompleteResponse(ResponseModel): has_next: bool | None = None -class EducationActivateResponse(RootModel[dict[str, Any]]): - root: dict[str, Any] - - -register_schema_models( - console_ns, - AccountIntegrateResponse, - AccountIntegrateListResponse, - EducationVerifyResponse, - EducationStatusResponse, - EducationAutocompleteResponse, -) register_response_schema_models( console_ns, AccountResponse, + AccountIntegrateResponse, + AccountIntegrateListResponse, AvatarUrlResponse, - EducationActivateResponse, + EducationVerifyResponse, + EducationStatusResponse, + EducationAutocompleteResponse, SimpleResultDataResponse, SimpleResultResponse, VerificationTokenResponse, @@ -302,7 +290,7 @@ class AccountInitApi(Resource): account.initialized_at = naive_utc_now() db.session.commit() - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/account/profile") @@ -314,7 +302,7 @@ class AccountProfileApi(Resource): @enterprise_license_required @with_current_user def get(self, current_user: Account): - return _serialize_account(current_user) + return dump_response(AccountResponse, current_user) @console_ns.route("/account/name") @@ -330,7 +318,7 @@ class AccountNameApi(Resource): args = AccountNamePayload.model_validate(payload) updated_account = AccountService.update_account(current_user, session=db.session, name=args.name) - return _serialize_account(updated_account) + return dump_response(AccountResponse, updated_account) @console_ns.route("/account/avatar") @@ -349,7 +337,7 @@ class AccountAvatarApi(Resource): avatar = args.avatar if avatar.startswith(("http://", "https://")): - return dump_response(AvatarUrlResponse, {"avatar_url": avatar}) + return AvatarUrlResponse(avatar_url=avatar).model_dump(mode="json") upload_file = db.session.scalar(select(UploadFile).where(UploadFile.id == avatar).limit(1)) if upload_file is None: @@ -362,7 +350,7 @@ class AccountAvatarApi(Resource): raise NotFound("Avatar file not found") avatar_url = file_helpers.get_signed_file_url(upload_file_id=upload_file.id) - return dump_response(AvatarUrlResponse, {"avatar_url": avatar_url}) + return AvatarUrlResponse(avatar_url=avatar_url).model_dump(mode="json") @console_ns.expect(console_ns.models[AccountAvatarPayload.__name__]) @setup_required @@ -376,7 +364,7 @@ class AccountAvatarApi(Resource): updated_account = AccountService.update_account(current_user, session=db.session, avatar=args.avatar) - return _serialize_account(updated_account) + return dump_response(AccountResponse, updated_account) @console_ns.route("/account/interface-language") @@ -395,7 +383,7 @@ class AccountInterfaceLanguageApi(Resource): current_user, session=db.session, interface_language=args.interface_language ) - return _serialize_account(updated_account) + return dump_response(AccountResponse, updated_account) @console_ns.route("/account/interface-theme") @@ -414,7 +402,7 @@ class AccountInterfaceThemeApi(Resource): current_user, session=db.session, interface_theme=args.interface_theme ) - return _serialize_account(updated_account) + return dump_response(AccountResponse, updated_account) @console_ns.route("/account/timezone") @@ -431,7 +419,7 @@ class AccountTimezoneApi(Resource): updated_account = AccountService.update_account(current_user, session=db.session, timezone=args.timezone) - return _serialize_account(updated_account) + return dump_response(AccountResponse, updated_account) @console_ns.route("/account/password") @@ -452,7 +440,7 @@ class AccountPasswordApi(Resource): except ServiceCurrentPasswordIncorrectError: raise CurrentPasswordIncorrectError() - return _serialize_account(current_user) + return dump_response(AccountResponse, current_user) @console_ns.route("/account/integrates") @@ -471,33 +459,29 @@ class AccountIntegrateApi(Resource): oauth_base_path = "/console/api/oauth/login" providers = ["github", "google"] - integrate_data = [] + integrate_data: list[AccountIntegrateResponse] = [] for provider in providers: existing_integrate = next((ai for ai in account_integrates if ai.provider == provider), None) if existing_integrate: integrate_data.append( - { - "id": existing_integrate.id, - "provider": provider, - "created_at": existing_integrate.created_at, - "is_bound": True, - "link": None, - } + AccountIntegrateResponse( + provider=provider, + created_at=to_timestamp(existing_integrate.created_at), + is_bound=True, + link=None, + ) ) else: integrate_data.append( - { - "id": None, - "provider": provider, - "created_at": None, - "is_bound": False, - "link": f"{base_url}{oauth_base_path}/{provider}", - } + AccountIntegrateResponse( + provider=provider, + created_at=None, + is_bound=False, + link=f"{base_url}{oauth_base_path}/{provider}", + ) ) - return AccountIntegrateListResponse( - data=[AccountIntegrateResponse.model_validate(item) for item in integrate_data] - ).model_dump(mode="json") + return AccountIntegrateListResponse(data=integrate_data).model_dump(mode="json") @console_ns.route("/account/delete/verify") @@ -511,7 +495,7 @@ class AccountDeleteVerifyApi(Resource): token, code = AccountService.generate_account_deletion_verification_code(account) AccountService.send_account_deletion_verification_email(account, code) - return {"result": "success", "data": token} + return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json") @console_ns.route("/account/delete") @@ -531,7 +515,7 @@ class AccountDeleteApi(Resource): AccountService.delete_account(account) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/account/delete/feedback") @@ -545,7 +529,7 @@ class AccountDeleteUpdateFeedbackApi(Resource): BillingService.update_account_deletion_feedback(args.email, args.feedback) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/account/education/verify") @@ -558,15 +542,16 @@ class EducationVerifyApi(Resource): @console_ns.response(200, "Success", console_ns.models[EducationVerifyResponse.__name__]) @with_current_user def get(self, account: Account): - return EducationVerifyResponse.model_validate( - BillingService.EducationIdentity.verify(account.id, account.email) or {} - ).model_dump(mode="json") + return dump_response( + EducationVerifyResponse, BillingService.EducationIdentity.verify(account.id, account.email) or {} + ) @console_ns.route("/account/education") class EducationApi(Resource): @console_ns.expect(console_ns.models[EducationActivatePayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[EducationActivateResponse.__name__]) + # response-contract:ignore billing-service activation payload; TODO: model education activation result. + @console_ns.response(200, "Success") @setup_required @login_required @account_initialization_required @@ -577,7 +562,8 @@ class EducationApi(Resource): payload = console_ns.payload or {} args = EducationActivatePayload.model_validate(payload) - return BillingService.EducationIdentity.activate(account, args.token, args.institution, args.role) + result = BillingService.EducationIdentity.activate(account, args.token, args.institution, args.role) + return result @setup_required @login_required @@ -591,7 +577,7 @@ class EducationApi(Resource): # convert expire_at to UTC timestamp from isoformat if res and "expire_at" in res: res["expire_at"] = datetime.fromisoformat(res["expire_at"]).astimezone(pytz.utc) - return EducationStatusResponse.model_validate(res).model_dump(mode="json") + return dump_response(EducationStatusResponse, res) @console_ns.route("/account/education/autocomplete") @@ -607,9 +593,10 @@ class EducationAutoCompleteApi(Resource): payload = request.args.to_dict(flat=True) args = EducationAutocompleteQuery.model_validate(payload) - return EducationAutocompleteResponse.model_validate( - BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {} - ).model_dump(mode="json") + return dump_response( + EducationAutocompleteResponse, + BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit) or {}, + ) @console_ns.route("/account/change-email") @@ -669,7 +656,7 @@ class ChangeEmailSendEmailApi(Resource): language=language, phase=send_phase, ) - return {"result": "success", "data": token} + return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json") @console_ns.route("/account/change-email/validity") @@ -716,7 +703,9 @@ class ChangeEmailCheckApi(Resource): new_token = AccountService.generate_change_email_token(refreshed_token_data, current_user) AccountService.reset_change_email_error_rate_limit(user_email) - return {"is_valid": True, "email": normalized_token_email, "token": new_token} + return VerificationTokenResponse(is_valid=True, email=normalized_token_email, token=new_token).model_dump( + mode="json" + ) @console_ns.route("/account/change-email/reset") @@ -768,7 +757,7 @@ class ChangeEmailResetApi(Resource): email=normalized_new_email, ) - return _serialize_account(updated_account) + return dump_response(AccountResponse, updated_account) @console_ns.route("/account/change-email/check-email-unique") @@ -784,4 +773,4 @@ class CheckEmailUnique(Resource): raise AccountInFreezeError() if not AccountService.check_email_unique(normalized_email, session=db.session): raise EmailAlreadyInUseError() - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") diff --git a/api/controllers/console/workspace/endpoint.py b/api/controllers/console/workspace/endpoint.py index ddb0f7045d9..c55186ab984 100644 --- a/api/controllers/console/workspace/endpoint.py +++ b/api/controllers/console/workspace/endpoint.py @@ -12,7 +12,8 @@ from flask import request from flask_restx import Resource from pydantic import BaseModel, Field -from controllers.common.schema import query_params_from_model, register_schema_models +from controllers.common.fields import SuccessResponse +from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models from controllers.console import console_ns from controllers.console.wraps import ( RBACPermission, @@ -24,8 +25,9 @@ from controllers.console.wraps import ( with_current_tenant_id, with_current_user_id, ) +from core.plugin.entities.endpoint import EndpointEntityWithInstance from core.plugin.impl.exc import PluginPermissionDeniedError -from graphon.model_runtime.utils.encoders import jsonable_encoder +from fields.base import ResponseModel from libs.login import login_required from services.plugin.endpoint_service import EndpointService @@ -40,14 +42,17 @@ class EndpointIdPayload(BaseModel): endpoint_id: str -class EndpointUpdatePayload(BaseModel): +class EndpointSettingsPayload(BaseModel): settings: dict[str, Any] name: str = Field(min_length=1) -class LegacyEndpointUpdatePayload(EndpointIdPayload): - settings: dict[str, Any] - name: str = Field(min_length=1) +class EndpointUpdatePayload(EndpointSettingsPayload): + pass + + +class LegacyEndpointUpdatePayload(EndpointIdPayload, EndpointSettingsPayload): + pass class EndpointListQuery(BaseModel): @@ -59,98 +64,85 @@ class EndpointListForPluginQuery(EndpointListQuery): plugin_id: str -class EndpointCreateResponse(BaseModel): - success: bool = Field(description="Operation success") - - -class EndpointListResponse(BaseModel): - endpoints: list[dict[str, Any]] = Field( - description="Endpoint information", - ) - - -class PluginEndpointListResponse(BaseModel): - endpoints: list[dict[str, Any]] = Field( - description="Endpoint information", - ) - - -class EndpointDeleteResponse(BaseModel): - success: bool = Field(description="Operation success") - - -class EndpointUpdateResponse(BaseModel): - success: bool = Field(description="Operation success") - - -class EndpointEnableResponse(BaseModel): - success: bool = Field(description="Operation success") - - -class EndpointDisableResponse(BaseModel): - success: bool = Field(description="Operation success") +class EndpointListResponse(ResponseModel): + endpoints: list[EndpointEntityWithInstance] = Field(description="Endpoint information") register_schema_models( console_ns, EndpointCreatePayload, EndpointIdPayload, + EndpointSettingsPayload, EndpointUpdatePayload, LegacyEndpointUpdatePayload, EndpointListQuery, EndpointListForPluginQuery, - EndpointCreateResponse, +) +register_response_schema_models( + console_ns, + SuccessResponse, EndpointListResponse, - PluginEndpointListResponse, - EndpointDeleteResponse, - EndpointUpdateResponse, - EndpointEnableResponse, - EndpointDisableResponse, ) -def _create_endpoint(tenant_id: str, user_id: str) -> dict[str, bool]: +def _create_endpoint(tenant_id: str, user_id: str) -> bool: """Create a plugin endpoint for the injected workspace and user.""" args = EndpointCreatePayload.model_validate(console_ns.payload) try: - return { - "success": EndpointService.create_endpoint( - tenant_id=tenant_id, - user_id=user_id, - plugin_unique_identifier=args.plugin_unique_identifier, - name=args.name, - settings=args.settings, - ) - } + return EndpointService.create_endpoint( + tenant_id=tenant_id, + user_id=user_id, + plugin_unique_identifier=args.plugin_unique_identifier, + name=args.name, + settings=args.settings, + ) except PluginPermissionDeniedError as e: raise ValueError(e.description) from e -def _update_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> dict[str, bool]: +def _update_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> bool: """Update a plugin endpoint identified by the canonical path parameter.""" args = EndpointUpdatePayload.model_validate(console_ns.payload) - return { - "success": EndpointService.update_endpoint( - tenant_id=tenant_id, - user_id=user_id, - endpoint_id=endpoint_id, - name=args.name, - settings=args.settings, - ) - } + return EndpointService.update_endpoint( + tenant_id=tenant_id, + user_id=user_id, + endpoint_id=endpoint_id, + name=args.name, + settings=args.settings, + ) -def _delete_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> dict[str, bool]: +def _legacy_update_endpoint(tenant_id: str, user_id: str) -> bool: + args = LegacyEndpointUpdatePayload.model_validate(console_ns.payload) + return EndpointService.update_endpoint( + tenant_id=tenant_id, + user_id=user_id, + endpoint_id=args.endpoint_id, + name=args.name, + settings=args.settings, + ) + + +def _delete_endpoint(tenant_id: str, user_id: str, endpoint_id: str) -> bool: """Delete a plugin endpoint identified by the canonical path parameter.""" - return { - "success": EndpointService.delete_endpoint( - tenant_id=tenant_id, - user_id=user_id, - endpoint_id=endpoint_id, - ) - } + return EndpointService.delete_endpoint( + tenant_id=tenant_id, + user_id=user_id, + endpoint_id=endpoint_id, + ) + + +def _delete_endpoint_from_payload(tenant_id: str, user_id: str) -> bool: + args = EndpointIdPayload.model_validate(console_ns.payload) + return _delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id) + + +def _set_endpoint_enabled(tenant_id: str, user_id: str, *, enabled: bool) -> bool: + args = EndpointIdPayload.model_validate(console_ns.payload) + action = EndpointService.enable_endpoint if enabled else EndpointService.disable_endpoint + return action(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id) @console_ns.route("/workspaces/current/endpoints") @@ -163,7 +155,7 @@ class EndpointCollectionApi(Resource): @console_ns.response( 200, "Endpoint created successfully", - console_ns.models[EndpointCreateResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -174,7 +166,7 @@ class EndpointCollectionApi(Resource): @with_current_user_id @with_current_tenant_id def post(self, tenant_id: str, user_id: str): - return _create_endpoint(tenant_id=tenant_id, user_id=user_id) + return SuccessResponse(success=_create_endpoint(tenant_id=tenant_id, user_id=user_id)).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/create") @@ -192,7 +184,7 @@ class DeprecatedEndpointCreateApi(Resource): @console_ns.response( 200, "Endpoint created successfully", - console_ns.models[EndpointCreateResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -203,7 +195,7 @@ class DeprecatedEndpointCreateApi(Resource): @with_current_user_id @with_current_tenant_id def post(self, tenant_id: str, user_id: str): - return _create_endpoint(tenant_id=tenant_id, user_id=user_id) + return SuccessResponse(success=_create_endpoint(tenant_id=tenant_id, user_id=user_id)).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/list") @@ -224,19 +216,14 @@ class EndpointListApi(Resource): def get(self, tenant_id: str, user_id: str): args = EndpointListQuery.model_validate(request.args.to_dict(flat=True)) - page = args.page - page_size = args.page_size - - return jsonable_encoder( - { - "endpoints": EndpointService.list_endpoints( - tenant_id=tenant_id, - user_id=user_id, - page=page, - page_size=page_size, - ) - } - ) + return EndpointListResponse( + endpoints=EndpointService.list_endpoints( + tenant_id=tenant_id, + user_id=user_id, + page=args.page, + page_size=args.page_size, + ) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/list/plugin") @@ -247,7 +234,7 @@ class EndpointListForSinglePluginApi(Resource): @console_ns.response( 200, "Success", - console_ns.models[PluginEndpointListResponse.__name__], + console_ns.models[EndpointListResponse.__name__], ) @setup_required @login_required @@ -257,21 +244,15 @@ class EndpointListForSinglePluginApi(Resource): def get(self, tenant_id: str, user_id: str): args = EndpointListForPluginQuery.model_validate(request.args.to_dict(flat=True)) - page = args.page - page_size = args.page_size - plugin_id = args.plugin_id - - return jsonable_encoder( - { - "endpoints": EndpointService.list_endpoints_for_single_plugin( - tenant_id=tenant_id, - user_id=user_id, - plugin_id=plugin_id, - page=page, - page_size=page_size, - ) - } - ) + return EndpointListResponse( + endpoints=EndpointService.list_endpoints_for_single_plugin( + tenant_id=tenant_id, + user_id=user_id, + plugin_id=args.plugin_id, + page=args.page, + page_size=args.page_size, + ) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/") @@ -284,7 +265,7 @@ class EndpointItemApi(Resource): @console_ns.response( 200, "Endpoint deleted successfully", - console_ns.models[EndpointDeleteResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -295,7 +276,9 @@ class EndpointItemApi(Resource): @with_current_user_id @with_current_tenant_id def delete(self, tenant_id: str, user_id: str, id: str): - return _delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id) + return SuccessResponse( + success=_delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id) + ).model_dump(mode="json") @console_ns.doc("update_endpoint") @console_ns.doc(description="Update a plugin endpoint") @@ -304,7 +287,7 @@ class EndpointItemApi(Resource): @console_ns.response( 200, "Endpoint updated successfully", - console_ns.models[EndpointUpdateResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -315,7 +298,9 @@ class EndpointItemApi(Resource): @with_current_user_id @with_current_tenant_id def patch(self, tenant_id: str, user_id: str, id: str): - return _update_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id) + return SuccessResponse( + success=_update_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=id) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/delete") @@ -334,7 +319,7 @@ class DeprecatedEndpointDeleteApi(Resource): @console_ns.response( 200, "Endpoint deleted successfully", - console_ns.models[EndpointDeleteResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -345,8 +330,9 @@ class DeprecatedEndpointDeleteApi(Resource): @with_current_user_id @with_current_tenant_id def post(self, tenant_id: str, user_id: str): - args = EndpointIdPayload.model_validate(console_ns.payload) - return _delete_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id) + return SuccessResponse( # + success=_delete_endpoint_from_payload(tenant_id=tenant_id, user_id=user_id) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/update") @@ -365,7 +351,7 @@ class DeprecatedEndpointUpdateApi(Resource): @console_ns.response( 200, "Endpoint updated successfully", - console_ns.models[EndpointUpdateResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -376,8 +362,9 @@ class DeprecatedEndpointUpdateApi(Resource): @with_current_user_id @with_current_tenant_id def post(self, tenant_id: str, user_id: str): - args = LegacyEndpointUpdatePayload.model_validate(console_ns.payload) - return _update_endpoint(tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id) + return SuccessResponse( # + success=_legacy_update_endpoint(tenant_id=tenant_id, user_id=user_id) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/enable") @@ -388,7 +375,7 @@ class EndpointEnableApi(Resource): @console_ns.response( 200, "Endpoint enabled successfully", - console_ns.models[EndpointEnableResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -399,13 +386,9 @@ class EndpointEnableApi(Resource): @with_current_user_id @with_current_tenant_id def post(self, tenant_id: str, user_id: str): - args = EndpointIdPayload.model_validate(console_ns.payload) - - return { - "success": EndpointService.enable_endpoint( - tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id - ) - } + return SuccessResponse( + success=_set_endpoint_enabled(tenant_id=tenant_id, user_id=user_id, enabled=True) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/endpoints/disable") @@ -416,7 +399,7 @@ class EndpointDisableApi(Resource): @console_ns.response( 200, "Endpoint disabled successfully", - console_ns.models[EndpointDisableResponse.__name__], + console_ns.models[SuccessResponse.__name__], ) @console_ns.response(403, "Admin privileges required") @setup_required @@ -427,10 +410,6 @@ class EndpointDisableApi(Resource): @with_current_user_id @with_current_tenant_id def post(self, tenant_id: str, user_id: str): - args = EndpointIdPayload.model_validate(console_ns.payload) - - return { - "success": EndpointService.disable_endpoint( - tenant_id=tenant_id, user_id=user_id, endpoint_id=args.endpoint_id - ) - } + return SuccessResponse( + success=_set_endpoint_enabled(tenant_id=tenant_id, user_id=user_id, enabled=False) + ).model_dump(mode="json") diff --git a/api/controllers/console/workspace/members.py b/api/controllers/console/workspace/members.py index 3a2e3c92359..cb25180fb03 100644 --- a/api/controllers/console/workspace/members.py +++ b/api/controllers/console/workspace/members.py @@ -3,7 +3,7 @@ from uuid import UUID from flask import abort, request from flask_restx import Resource -from pydantic import BaseModel, Field, TypeAdapter +from pydantic import BaseModel, Field from sqlalchemy import func, select import services @@ -30,8 +30,8 @@ from controllers.console.wraps import ( from extensions.ext_database import db from extensions.ext_redis import redis_client from fields.base import ResponseModel -from fields.member_fields import AccountWithRole, AccountWithRoleList -from libs.helper import extract_remote_ip +from fields.member_fields import AccountWithRoleListResponse, AccountWithRoleResponse +from libs.helper import dump_response, extract_remote_ip from libs.login import current_account_with_tenant, login_required from models.account import Account, TenantAccountJoin, TenantAccountRole from services.account_service import AccountService, RegisterService, TenantService @@ -70,22 +70,20 @@ class MemberInviteResultResponse(ResponseModel): message: str | None = None +class MemberActionResponse(ResponseModel): + result: str + tenant_id: str = "" + + class MemberInviteResponse(ResponseModel): result: str invitation_results: list[MemberInviteResultResponse] tenant_id: str -class MemberActionTenantResponse(ResponseModel): - result: str - tenant_id: str - - register_enum_models(console_ns, TenantAccountRole) register_schema_models( console_ns, - AccountWithRole, - AccountWithRoleList, MemberInvitePayload, MemberRoleUpdatePayload, OwnerTransferEmailPayload, @@ -94,11 +92,14 @@ register_schema_models( ) register_response_schema_models( console_ns, + AccountWithRoleResponse, + AccountWithRoleListResponse, + MemberActionResponse, + MemberInviteResponse, + MemberInviteResultResponse, SimpleResultDataResponse, SimpleResultResponse, VerificationTokenResponse, - MemberInviteResponse, - MemberActionTenantResponse, ) @@ -179,7 +180,7 @@ class MemberListApi(Resource): @setup_required @login_required @account_initialization_required - @console_ns.response(200, "Success", console_ns.models[AccountWithRoleList.__name__]) + @console_ns.response(200, "Success", console_ns.models[AccountWithRoleListResponse.__name__]) @with_current_user def get(self, current_user: Account | None = None): if current_user is None: @@ -216,9 +217,7 @@ class MemberListApi(Resource): } ) - member_models = TypeAdapter(list[AccountWithRole]).validate_python(serialized_members) - response = AccountWithRoleList(accounts=member_models) - return response.model_dump(mode="json"), 200 + return dump_response(AccountWithRoleListResponse, {"accounts": serialized_members}), 200 @console_ns.route("/workspaces/current/members/invite-email") @@ -254,7 +253,7 @@ class MemberInviteEmailApi(Resource): check_workspace_member_invite_permission(inviter.current_tenant.id) - invitation_results = [] + invitation_results: list[MemberInviteResultResponse] = [] console_web_url = dify_config.CONSOLE_WEB_URL tenant_id = inviter.current_tenant.id @@ -277,38 +276,40 @@ class MemberInviteEmailApi(Resource): ) encoded_invitee_email = parse.quote(invitee_email) invitation_results.append( - { - "status": "success", - "email": invitee_email, - "url": f"{console_web_url}/activate?email={encoded_invitee_email}&token={token}", - } + MemberInviteResultResponse( + status="success", + email=invitee_email, + url=f"{console_web_url}/activate?email={encoded_invitee_email}&token={token}", + ) ) except AccountAlreadyInTenantError: invitation_results.append( - { - "status": "already_member", - "email": invitee_email, - "message": "Account already in workspace.", - } + MemberInviteResultResponse( + status="already_member", + email=invitee_email, + message="Account already in workspace.", + ) ) except Exception as e: - invitation_results.append({"status": "failed", "email": invitee_email, "message": str(e)}) + invitation_results.append( + MemberInviteResultResponse(status="failed", email=invitee_email, message=str(e)) + ) - return { - "result": "success", - "invitation_results": invitation_results, - "tenant_id": str(inviter.current_tenant.id) if inviter.current_tenant else "", - }, 201 + return MemberInviteResponse( + result="success", + invitation_results=invitation_results, + tenant_id=inviter.current_tenant.id if inviter.current_tenant else "", + ).model_dump(mode="json"), 201 @console_ns.route("/workspaces/current/members/") class MemberCancelInviteApi(Resource): """Cancel an invitation by member id.""" - @console_ns.response(200, "Success", console_ns.models[MemberActionTenantResponse.__name__]) @setup_required @login_required @account_initialization_required + @console_ns.response(200, "Success", console_ns.models[MemberActionResponse.__name__]) @with_current_user def delete(self, current_user: Account, member_id: UUID): if not current_user.current_tenant: @@ -330,10 +331,10 @@ class MemberCancelInviteApi(Resource): except Exception as e: raise ValueError(str(e)) - return { - "result": "success", - "tenant_id": str(current_user.current_tenant.id) if current_user.current_tenant else "", - }, 200 + return MemberActionResponse( + result="success", + tenant_id=current_user.current_tenant.id if current_user.current_tenant else "", + ).model_dump(mode="json"), 200 @console_ns.route("/workspaces/current/members//update-role") @@ -377,7 +378,7 @@ class MemberUpdateRoleApi(Resource): except Exception as e: raise ValueError(str(e)) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") @console_ns.route("/workspaces/current/dataset-operators") @@ -387,15 +388,13 @@ class DatasetOperatorMemberListApi(Resource): @setup_required @login_required @account_initialization_required - @console_ns.response(200, "Success", console_ns.models[AccountWithRoleList.__name__]) + @console_ns.response(200, "Success", console_ns.models[AccountWithRoleListResponse.__name__]) @with_current_user def get(self, current_user: Account): if not current_user.current_tenant: raise ValueError("No current tenant") members = TenantService.get_dataset_operator_members(current_user.current_tenant, session=db.session) - member_models = TypeAdapter(list[AccountWithRole]).validate_python(members, from_attributes=True) - response = AccountWithRoleList(accounts=member_models) - return response.model_dump(mode="json"), 200 + return dump_response(AccountWithRoleListResponse, {"accounts": members}), 200 @console_ns.route("/workspaces/current/members/send-owner-transfer-confirm-email") @@ -435,7 +434,7 @@ class SendOwnerTransferEmailApi(Resource): workspace_name=current_user.current_tenant.name if current_user.current_tenant else "", ) - return {"result": "success", "data": token} + return SimpleResultDataResponse(result="success", data=token).model_dump(mode="json") @console_ns.route("/workspaces/current/members/owner-transfer-check") @@ -480,7 +479,7 @@ class OwnerTransferCheckApi(Resource): _, new_token = AccountService.generate_owner_transfer_token(user_email, code=args.code, additional_data={}) AccountService.reset_owner_transfer_error_rate_limit(user_email) - return {"is_valid": True, "email": token_data.get("email"), "token": new_token} + return VerificationTokenResponse(is_valid=True, email=user_email, token=new_token).model_dump(mode="json") @console_ns.route("/workspaces/current/members//owner-transfer") @@ -546,4 +545,4 @@ class OwnerTransfer(Resource): except Exception as e: raise ValueError(str(e)) - return {"result": "success"} + return SimpleResultResponse(result="success").model_dump(mode="json") diff --git a/api/controllers/console/workspace/workspace.py b/api/controllers/console/workspace/workspace.py index 0afd7e06bf7..2644229d3e6 100644 --- a/api/controllers/console/workspace/workspace.py +++ b/api/controllers/console/workspace/workspace.py @@ -2,7 +2,7 @@ import logging from datetime import datetime from flask import request -from flask_restx import Resource, fields, marshal +from flask_restx import Resource from pydantic import BaseModel, Field, field_validator from sqlalchemy import select from werkzeug.exceptions import Unauthorized @@ -16,7 +16,12 @@ from controllers.common.errors import ( TooManyFilesError, UnsupportedFileTypeError, ) -from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models +from controllers.common.schema import ( + query_params_from_model, + query_params_from_request, + register_response_schema_models, + register_schema_models, +) from controllers.console import console_ns from controllers.console.admin import admin_required from controllers.console.error import AccountNotLinkTenantError @@ -31,7 +36,7 @@ from controllers.console.wraps import ( from enums.cloud_plan import CloudPlan from extensions.ext_database import db from fields.base import ResponseModel -from libs.helper import OptionalTimestampField, TimestampField, dump_response, to_timestamp +from libs.helper import dump_response, to_timestamp from libs.login import login_required from models.account import Account, Tenant, TenantAccountJoin, TenantCustomConfigDict, TenantStatus from services.account_service import TenantService @@ -102,6 +107,7 @@ class TenantListItemResponse(ResponseModel): plan: str | None = None status: str | None = None created_at: int | None = None + last_opened_at: int | None = None current: bool @field_validator("plan", "status", mode="before") @@ -113,9 +119,9 @@ class TenantListItemResponse(ResponseModel): return value return str(getattr(value, "value", value)) - @field_validator("created_at", mode="before") + @field_validator("created_at", "last_opened_at", mode="before") @classmethod - def _normalize_created_at(cls, value: datetime | int | None): + def _normalize_timestamp(cls, value: datetime | int | None): return to_timestamp(value) @@ -131,7 +137,7 @@ class WorkspaceListItemResponse(ResponseModel): @field_validator("status", mode="before") @classmethod - def _normalize_status(cls, value): + def _normalize_enum_like(cls, value): if value is None: return None if isinstance(value, str): @@ -144,7 +150,7 @@ class WorkspaceListItemResponse(ResponseModel): return to_timestamp(value) -class WorkspaceListResponse(ResponseModel): +class WorkspacePaginationResponse(ResponseModel): data: list[WorkspaceListItemResponse] has_more: bool limit: int @@ -157,7 +163,7 @@ class SwitchWorkspaceResponse(ResponseModel): new_tenant: TenantInfoResponse -class WorkspaceMutationResponse(ResponseModel): +class WorkspaceTenantResultResponse(ResponseModel): result: str tenant: TenantInfoResponse @@ -172,6 +178,16 @@ class WorkspacePermissionResponse(ResponseModel): allow_owner_transfer: bool +WORKSPACE_LOGO_UPLOAD_PARAMS = { + "file": { + "in": "formData", + "type": "file", + "required": True, + "description": "Workspace web app logo file. Only SVG and PNG files are supported.", + } +} + + register_schema_models( console_ns, WorkspaceListQuery, @@ -182,49 +198,17 @@ register_schema_models( register_response_schema_models( console_ns, TenantInfoResponse, + TenantListItemResponse, TenantListResponse, - WorkspaceListResponse, - SwitchWorkspaceResponse, - WorkspaceMutationResponse, - WorkspaceLogoUploadResponse, WorkspaceCustomConfigResponse, + WorkspaceListItemResponse, + WorkspacePaginationResponse, + SwitchWorkspaceResponse, + WorkspaceTenantResultResponse, + WorkspaceLogoUploadResponse, WorkspacePermissionResponse, ) -provider_fields = { - "provider_name": fields.String, - "provider_type": fields.String, - "is_valid": fields.Boolean, - "token_is_set": fields.Boolean, -} - -tenant_fields = { - "id": fields.String, - "name": fields.String, - "plan": fields.String, - "status": fields.String, - "created_at": TimestampField, - "role": fields.String, - "in_trial": fields.Boolean, - "trial_end_reason": fields.String, - "custom_config": fields.Raw(attribute="custom_config"), - "trial_credits": fields.Integer, - "trial_credits_used": fields.Integer, - "next_credit_reset_date": fields.Integer, -} - -tenants_fields = { - "id": fields.String, - "name": fields.String, - "plan": fields.String, - "status": fields.String, - "created_at": TimestampField, - "last_opened_at": OptionalTimestampField, - "current": fields.Boolean, -} - -workspace_fields = {"id": fields.String, "name": fields.String, "status": fields.String, "created_at": TimestampField} - @console_ns.route("/workspaces") class TenantListApi(Resource): @@ -279,18 +263,17 @@ class TenantListApi(Resource): tenant_dicts.append(tenant_dict) - return {"workspaces": marshal(tenant_dicts, tenants_fields)}, 200 + return dump_response(TenantListResponse, {"workspaces": tenant_dicts}), 200 @console_ns.route("/all-workspaces") class WorkspaceListApi(Resource): @console_ns.doc(params=query_params_from_model(WorkspaceListQuery)) - @console_ns.response(200, "Success", console_ns.models[WorkspaceListResponse.__name__]) + @console_ns.response(200, "Success", console_ns.models[WorkspacePaginationResponse.__name__]) @setup_required @admin_required def get(self): - payload = request.args.to_dict(flat=True) - args = WorkspaceListQuery.model_validate(payload) + args = query_params_from_request(WorkspaceListQuery) stmt = select(Tenant).order_by(Tenant.created_at.desc()) tenants = db.paginate(select=stmt, page=args.page, per_page=args.limit, error_out=False) @@ -299,13 +282,9 @@ class WorkspaceListApi(Resource): if tenants.has_next: has_more = True - return { - "data": marshal(tenants.items, workspace_fields), - "has_more": has_more, - "limit": args.limit, - "page": args.page, - "total": tenants.total, - }, 200 + return WorkspacePaginationResponse( + data=tenants.items, has_more=has_more, limit=args.limit, page=args.page, total=tenants.total or 0 + ).model_dump(mode="json"), 200 @console_ns.route("/workspaces/current", endpoint="workspaces_current") @@ -359,13 +338,15 @@ class SwitchWorkspaceApi(Resource): if new_tenant is None: raise ValueError("Tenant not found") - return {"result": "success", "new_tenant": marshal(WorkspaceService.get_tenant_info(new_tenant), tenant_fields)} + return SwitchWorkspaceResponse( + result="success", new_tenant=WorkspaceService.get_tenant_info(new_tenant) + ).model_dump(mode="json") @console_ns.route("/workspaces/custom-config") class CustomConfigWorkspaceApi(Resource): @console_ns.expect(console_ns.models[WorkspaceCustomConfigPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[WorkspaceMutationResponse.__name__]) + @console_ns.response(200, "Success", console_ns.models[WorkspaceTenantResultResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -388,11 +369,14 @@ class CustomConfigWorkspaceApi(Resource): tenant.custom_config_dict = custom_config_dict db.session.commit() - return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)} + return WorkspaceTenantResultResponse( + result="success", tenant=WorkspaceService.get_tenant_info(tenant) + ).model_dump(mode="json") @console_ns.route("/workspaces/custom-config/webapp-logo/upload") class WebappLogoWorkspaceApi(Resource): + @console_ns.doc(consumes=["multipart/form-data"], params=WORKSPACE_LOGO_UPLOAD_PARAMS) @console_ns.response(201, "Logo uploaded", console_ns.models[WorkspaceLogoUploadResponse.__name__]) @setup_required @login_required @@ -429,13 +413,13 @@ class WebappLogoWorkspaceApi(Resource): except services.errors.file.UnsupportedFileTypeError: raise UnsupportedFileTypeError() - return {"id": upload_file.id}, 201 + return WorkspaceLogoUploadResponse(id=upload_file.id).model_dump(mode="json"), 201 @console_ns.route("/workspaces/info") class WorkspaceInfoApi(Resource): @console_ns.expect(console_ns.models[WorkspaceInfoPayload.__name__]) - @console_ns.response(200, "Success", console_ns.models[WorkspaceMutationResponse.__name__]) + @console_ns.response(200, "Success", console_ns.models[WorkspaceTenantResultResponse.__name__]) @setup_required @login_required @account_initialization_required @@ -451,7 +435,9 @@ class WorkspaceInfoApi(Resource): tenant.name = args.name db.session.commit() - return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)} + return WorkspaceTenantResultResponse( + result="success", tenant=WorkspaceService.get_tenant_info(tenant) + ).model_dump(mode="json") @console_ns.route("/workspaces/current/permission") @@ -475,8 +461,8 @@ class WorkspacePermissionApi(Resource): # Get workspace permissions from enterprise service permission = EnterpriseService.WorkspacePermissionService.get_permission(current_tenant_id) - return { - "workspace_id": permission.workspace_id, - "allow_member_invite": permission.allow_member_invite, - "allow_owner_transfer": permission.allow_owner_transfer, - }, 200 + return WorkspacePermissionResponse( + workspace_id=permission.workspace_id, + allow_member_invite=permission.allow_member_invite, + allow_owner_transfer=permission.allow_owner_transfer, + ).model_dump(mode="json"), 200 diff --git a/api/fields/member_fields.py b/api/fields/member_fields.py index 9bbcbef8429..80b93f0a24b 100644 --- a/api/fields/member_fields.py +++ b/api/fields/member_fields.py @@ -15,13 +15,13 @@ simple_account_fields = { } -class SimpleAccount(ResponseModel): +class SimpleAccountResponse(ResponseModel): id: str name: str email: str -class _AccountAvatar(ResponseModel): +class _AccountAvatarResponseMixin(ResponseModel): avatar: str | None = None @computed_field(return_type=str | None) # type: ignore[prop-decorator] @@ -30,7 +30,7 @@ class _AccountAvatar(ResponseModel): return build_avatar_url(self.avatar) -class Account(_AccountAvatar): +class AccountResponse(_AccountAvatarResponseMixin): id: str name: str email: str @@ -48,7 +48,7 @@ class Account(_AccountAvatar): return to_timestamp(value) -class AccountWithRole(_AccountAvatar): +class AccountWithRoleResponse(_AccountAvatarResponseMixin): id: str name: str email: str @@ -65,5 +65,11 @@ class AccountWithRole(_AccountAvatar): return to_timestamp(value) -class AccountWithRoleList(ResponseModel): - accounts: list[AccountWithRole] +class AccountWithRoleListResponse(ResponseModel): + accounts: list[AccountWithRoleResponse] + + +SimpleAccount = SimpleAccountResponse +Account = AccountResponse +AccountWithRole = AccountWithRoleResponse +AccountWithRoleList = AccountWithRoleListResponse diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index b3a0b8a6a71..4d3ad85574e 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -38,7 +38,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/change-email #### Request Body @@ -77,7 +77,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/change-email/validity #### Request Body @@ -141,9 +141,9 @@ Get account avatar url #### Responses -| Code | Description | Schema | -| ---- | ----------- | ------ | -| 200 | Success | **application/json**: [EducationActivateResponse](#educationactivateresponse)
| +| Code | Description | +| ---- | ----------- | +| 200 | Success | ### [GET] /account/education/autocomplete #### Parameters @@ -198,7 +198,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/interface-theme #### Request Body @@ -211,7 +211,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/name #### Request Body @@ -224,7 +224,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/password #### Request Body @@ -237,14 +237,14 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [GET] /account/profile #### Responses | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /account/timezone #### Request Body @@ -257,7 +257,7 @@ Get account avatar url | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [Account](#account)
| +| 200 | Success | **application/json**: [AccountResponse](#accountresponse)
| ### [POST] /activate Activate account with invitation token @@ -1050,7 +1050,7 @@ Infer CLI tool + ENV suggestions from a standardized Agent App skill | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [WorkspaceListResponse](#workspacelistresponse)
| +| 200 | Success | **application/json**: [WorkspacePaginationResponse](#workspacepaginationresponse)
| ### [GET] /api-based-extension Get all API-based extensions for current tenant @@ -9268,7 +9268,7 @@ Increment snippet use count by 1 | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [AccountWithRoleList](#accountwithrolelist)
| +| 200 | Success | **application/json**: [AccountWithRoleListResponse](#accountwithrolelistresponse)
| ### [GET] /workspaces/current/default-model #### Parameters @@ -9309,7 +9309,7 @@ Create a new plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint created successfully | **application/json**: [EndpointCreateResponse](#endpointcreateresponse)
| +| 200 | Endpoint created successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### ~~[POST] /workspaces/current/endpoints/create~~ @@ -9328,7 +9328,7 @@ Deprecated legacy alias for creating a plugin endpoint. Use POST /workspaces/cur | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint created successfully | **application/json**: [EndpointCreateResponse](#endpointcreateresponse)
| +| 200 | Endpoint created successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### ~~[POST] /workspaces/current/endpoints/delete~~ @@ -9347,7 +9347,7 @@ Deprecated legacy alias for deleting a plugin endpoint. Use DELETE /workspaces/c | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint deleted successfully | **application/json**: [EndpointDeleteResponse](#endpointdeleteresponse)
| +| 200 | Endpoint deleted successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### [POST] /workspaces/current/endpoints/disable @@ -9363,7 +9363,7 @@ Disable a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint disabled successfully | **application/json**: [EndpointDisableResponse](#endpointdisableresponse)
| +| 200 | Endpoint disabled successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### [POST] /workspaces/current/endpoints/enable @@ -9379,7 +9379,7 @@ Enable a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint enabled successfully | **application/json**: [EndpointEnableResponse](#endpointenableresponse)
| +| 200 | Endpoint enabled successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### [GET] /workspaces/current/endpoints/list @@ -9413,7 +9413,7 @@ List endpoints for a specific plugin | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [PluginEndpointListResponse](#pluginendpointlistresponse)
| +| 200 | Success | **application/json**: [EndpointListResponse](#endpointlistresponse)
| ### ~~[POST] /workspaces/current/endpoints/update~~ @@ -9431,7 +9431,7 @@ Deprecated legacy alias for updating a plugin endpoint. Use PATCH /workspaces/cu | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint updated successfully | **application/json**: [EndpointUpdateResponse](#endpointupdateresponse)
| +| 200 | Endpoint updated successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### [DELETE] /workspaces/current/endpoints/{id} @@ -9447,7 +9447,7 @@ Delete a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint deleted successfully | **application/json**: [EndpointDeleteResponse](#endpointdeleteresponse)
| +| 200 | Endpoint deleted successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### [PATCH] /workspaces/current/endpoints/{id} @@ -9469,7 +9469,7 @@ Update a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Endpoint updated successfully | **application/json**: [EndpointUpdateResponse](#endpointupdateresponse)
| +| 200 | Endpoint updated successfully | **application/json**: [SuccessResponse](#successresponse)
| | 403 | Admin privileges required | | ### [GET] /workspaces/current/members @@ -9477,7 +9477,7 @@ Update a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [AccountWithRoleList](#accountwithrolelist)
| +| 200 | Success | **application/json**: [AccountWithRoleListResponse](#accountwithrolelistresponse)
| ### [POST] /workspaces/current/members/invite-email #### Request Body @@ -9529,7 +9529,7 @@ Update a plugin endpoint | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [MemberActionTenantResponse](#memberactiontenantresponse)
| +| 200 | Success | **application/json**: [MemberActionResponse](#memberactionresponse)
| ### [POST] /workspaces/current/members/{member_id}/owner-transfer #### Parameters @@ -11739,9 +11739,15 @@ Returns permission flags that control workspace features like member invitations | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [WorkspaceMutationResponse](#workspacemutationresponse)
| +| 200 | Success | **application/json**: [WorkspaceTenantResultResponse](#workspacetenantresultresponse)
| ### [POST] /workspaces/custom-config/webapp-logo/upload +#### Request Body + +| Required | Schema | +| -------- | ------ | +| Yes | **multipart/form-data**: { **"file"**: binary }
| + #### Responses | Code | Description | Schema | @@ -11759,7 +11765,7 @@ Returns permission flags that control workspace features like member invitations | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | Success | **application/json**: [WorkspaceMutationResponse](#workspacemutationresponse)
| +| 200 | Success | **application/json**: [WorkspaceTenantResultResponse](#workspacetenantresultresponse)
| ### [POST] /workspaces/switch #### Request Body @@ -11928,23 +11934,6 @@ Default namespace | role_name | string | | No | | tenant_id | string | | No | -#### Account - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| avatar | string | | No | -| avatar_url | string | | Yes | -| created_at | integer | | No | -| email | string | | Yes | -| id | string | | Yes | -| interface_language | string | | No | -| interface_theme | string | | No | -| is_password_set | boolean | | Yes | -| last_login_at | integer | | No | -| last_login_ip | string | | No | -| name | string | | Yes | -| timezone | string | | No | - #### AccountAvatarPayload | Name | Type | Description | Required | @@ -12020,17 +12009,41 @@ Default namespace | password | string | | No | | repeat_new_password | string | | Yes | +#### AccountResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| avatar | string | | No | +| avatar_url | string | | Yes | +| created_at | integer | | No | +| email | string | | Yes | +| id | string | | Yes | +| interface_language | string | | No | +| interface_theme | string | | No | +| is_password_set | boolean | | Yes | +| last_login_at | integer | | No | +| last_login_ip | string | | No | +| name | string | | Yes | +| timezone | string | | No | + #### AccountTimezonePayload | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | timezone | string | | Yes | -#### AccountWithRole +#### AccountWithRoleListResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| accounts | [ [AccountWithRoleResponse](#accountwithroleresponse) ] | | Yes | + +#### AccountWithRoleResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | avatar | string | | No | +| avatar_url | string | | Yes | | created_at | integer | | No | | email | string | | Yes | | id | string | | Yes | @@ -12041,12 +12054,6 @@ Default namespace | roles | [ object ] | | No | | status | string | | Yes | -#### AccountWithRoleList - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| accounts | [ [AccountWithRole](#accountwithrole) ] | | Yes | - #### ActivateCheckQuery | Name | Type | Description | Required | @@ -12095,7 +12102,7 @@ Default namespace | ---- | ---- | ----------- | -------- | | conversation_id | string | | No | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | elapsed_time | number | | No | | exceptions_count | integer | | No | | finished_at | integer | | No | @@ -13909,6 +13916,12 @@ AppMCPServer Status Enum | use_icon_as_answer_icon | boolean | | No | | workflow | [WorkflowPartial](#workflowpartial) | | No | +#### AppSelectorScope + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| AppSelectorScope | string | | | + #### AppSiteResponse | Name | Type | Description | Required | @@ -15782,12 +15795,6 @@ Request payload for bulk downloading documents as a zip archive. | role | string | | Yes | | token | string | | Yes | -#### EducationActivateResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| EducationActivateResponse | object | | | - #### EducationAutocompleteQuery | Name | Type | Description | Required | @@ -15897,29 +15904,32 @@ Request payload for bulk downloading documents as a zip archive. | plugin_unique_identifier | string | | Yes | | settings | object | | Yes | -#### EndpointCreateResponse +#### EndpointDeclaration + +declaration of an endpoint | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| success | boolean | Operation success | Yes | +| hidden | boolean | | No | +| method | string | | Yes | +| path | string | | Yes | -#### EndpointDeleteResponse +#### EndpointEntityWithInstance | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| success | boolean | Operation success | Yes | - -#### EndpointDisableResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| success | boolean | Operation success | Yes | - -#### EndpointEnableResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| success | boolean | Operation success | Yes | +| created_at | dateTime | | Yes | +| declaration | [EndpointProviderDeclaration](#endpointproviderdeclaration) | | No | +| enabled | boolean | | Yes | +| expired_at | dateTime | | Yes | +| hook_id | string | | Yes | +| id | string | | Yes | +| name | string | | Yes | +| plugin_id | string | | Yes | +| settings | object | | Yes | +| tenant_id | string | | Yes | +| updated_at | dateTime | | Yes | +| url | string | | Yes | #### EndpointIdPayload @@ -15946,7 +15956,23 @@ Request payload for bulk downloading documents as a zip archive. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| endpoints | [ object ] | Endpoint information | Yes | +| endpoints | [ [EndpointEntityWithInstance](#endpointentitywithinstance) ] | Endpoint information | Yes | + +#### EndpointProviderDeclaration + +declaration of an endpoint group + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| endpoints | [ [EndpointDeclaration](#endpointdeclaration) ] | | No | +| settings | [ [ProviderConfig](#providerconfig) ] | | No | + +#### EndpointSettingsPayload + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| name | string | | Yes | +| settings | object | | Yes | #### EndpointUpdatePayload @@ -15955,12 +15981,6 @@ Request payload for bulk downloading documents as a zip archive. | name | string | | Yes | | settings | object | | Yes | -#### EndpointUpdateResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| success | boolean | Operation success | Yes | - #### EnvSuggestion | Name | Type | Description | Required | @@ -16986,12 +17006,12 @@ Enum class for large language model mode. | marketplace_plugin_unique_identifier | string | | Yes | | version | string | | No | -#### MemberActionTenantResponse +#### MemberActionResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | result | string | | Yes | -| tenant_id | string | | Yes | +| tenant_id | string | | No | #### MemberBindingsResponse @@ -17281,6 +17301,12 @@ Enum class for model property key. | ---- | ---- | ----------- | -------- | | payment_link | string | | Yes | +#### ModelSelectorScope + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| ModelSelectorScope | string | | | + #### ModelStatus Enum class for model status. @@ -17588,6 +17614,13 @@ Coarse node-level status used by Inspector to pick a banner. | ---- | ---- | ----------- | -------- | | OpaqueObjectResponse | object | | | +#### Option + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| label | [I18nObject](#i18nobject) | The label of the option | Yes | +| value | string | The value of the option | Yes | + #### OutputErrorStrategy Per-output failure handling strategy. @@ -18287,12 +18320,6 @@ Shared permission levels for resources (datasets, credentials, etc.) | ---- | ---- | ----------- | -------- | | options | | | Yes | -#### PluginEndpointListResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| endpoints | [ object ] | Endpoint information | Yes | - #### PluginInstallationPermissionModel | Name | Type | Description | Required | @@ -18429,6 +18456,24 @@ Dataset Process Rule Mode | ---- | ---- | ----------- | -------- | | ProcessRuleMode | string | Dataset Process Rule Mode | | +#### ProviderConfig + +Model class for common provider settings like credentials + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| default | integer
string
number
boolean | | No | +| help | [I18nObject](#i18nobject) | | No | +| label | [I18nObject](#i18nobject) | | No | +| multiple | boolean | | No | +| name | string | The name of the credentials | Yes | +| options | [ [Option](#option) ] | | No | +| placeholder | [I18nObject](#i18nobject) | | No | +| required | boolean | | No | +| scope | [AppSelectorScope](#appselectorscope)
[ModelSelectorScope](#modelselectorscope)
[ToolSelectorScope](#toolselectorscope) | | No | +| type | [Type](#type) | The type of the credentials | Yes | +| url | string | | No | + #### ProviderCredentialResponse | Name | Type | Description | Required | @@ -19151,6 +19196,14 @@ Model class for provider quota configuration. | id | string | | Yes | | name | string | | Yes | +#### SimpleAccountResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| email | string | | Yes | +| id | string | | Yes | +| name | string | | Yes | + #### SimpleConversation | Name | Type | Description | Required | @@ -19458,7 +19511,7 @@ Query parameters for listing snippet published workflows. | ---- | ---- | ----------- | -------- | | conversation_variables | [ [WorkflowConversationVariableResponse](#workflowconversationvariableresponse) ] | | Yes | | created_at | integer | | Yes | -| created_by | [SimpleAccount](#simpleaccount) | | No | +| created_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | environment_variables | [ [WorkflowEnvironmentVariableResponse](#workflowenvironmentvariableresponse) ] | | Yes | | features | object | | Yes | | graph | object | | Yes | @@ -19470,7 +19523,7 @@ Query parameters for listing snippet published workflows. | rag_pipeline_variables | [ [PipelineVariableResponse](#pipelinevariableresponse) ] | | Yes | | tool_published | boolean | | Yes | | updated_at | integer | | Yes | -| updated_by | [SimpleAccount](#simpleaccount) | | No | +| updated_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | version | string | | Yes | #### StarredAppListQuery @@ -19722,6 +19775,7 @@ Tag type | created_at | integer | | No | | current | boolean | | Yes | | id | string | | Yes | +| last_opened_at | integer | | No | | name | string | | No | | plan | string | | No | | status | string | | No | @@ -19841,6 +19895,12 @@ Enum class for tool provider | ---- | ---- | ----------- | -------- | | ToolProviderType | string | Enum class for tool provider | | +#### ToolSelectorScope + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| ToolSelectorScope | string | | | + #### TraceAppConfigResponse | Name | Type | Description | Required | @@ -20390,7 +20450,7 @@ How a workflow node is bound to an Agent. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | created_from | string | | No | @@ -20427,7 +20487,7 @@ How a workflow node is bound to an Agent. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | id | string | | Yes | | trigger_metadata | | | No | @@ -20528,7 +20588,7 @@ How a workflow node is bound to an Agent. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| users | [ [AccountWithRole](#accountwithrole) ] | | Yes | +| users | [ [AccountWithRoleResponse](#accountwithroleresponse) ] | | Yes | #### WorkflowCommentReply @@ -20877,7 +20937,7 @@ can reuse its existing handler. | ---- | ---- | ----------- | -------- | | conversation_variables | [ [WorkflowConversationVariableResponse](#workflowconversationvariableresponse) ] | | Yes | | created_at | integer | | Yes | -| created_by | [SimpleAccount](#simpleaccount) | | No | +| created_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | environment_variables | [ [WorkflowEnvironmentVariableResponse](#workflowenvironmentvariableresponse) ] | | Yes | | features | object | | Yes | | graph | object | | Yes | @@ -20888,7 +20948,7 @@ can reuse its existing handler. | rag_pipeline_variables | [ [PipelineVariableResponse](#pipelinevariableresponse) ] | | Yes | | tool_published | boolean | | Yes | | updated_at | integer | | Yes | -| updated_by | [SimpleAccount](#simpleaccount) | | No | +| updated_by | [SimpleAccountResponse](#simpleaccountresponse) | | No | | version | string | | Yes | #### WorkflowRestoreResponse @@ -20923,7 +20983,7 @@ can reuse its existing handler. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | elapsed_time | number | | No | @@ -20962,7 +21022,7 @@ can reuse its existing handler. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | elapsed_time | number | | No | | exceptions_count | integer | | No | | finished_at | integer | | No | @@ -21009,7 +21069,7 @@ can reuse its existing handler. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | elapsed_time | number | | No | @@ -21202,7 +21262,13 @@ Workflow tool configuration | limit | integer,
**Default:** 20 | | No | | page | integer,
**Default:** 1 | | No | -#### WorkspaceListResponse +#### WorkspaceLogoUploadResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| id | string | | Yes | + +#### WorkspacePaginationResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | @@ -21212,19 +21278,6 @@ Workflow tool configuration | page | integer | | Yes | | total | integer | | Yes | -#### WorkspaceLogoUploadResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| id | string | | Yes | - -#### WorkspaceMutationResponse - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| result | string | | Yes | -| tenant | [TenantInfoResponse](#tenantinforesponse) | | Yes | - #### WorkspacePermissionResponse | Name | Type | Description | Required | @@ -21239,6 +21292,13 @@ Workflow tool configuration | ---- | ---- | ----------- | -------- | | permission_keys | [ string ] | | No | +#### WorkspaceTenantResultResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| result | string | | Yes | +| tenant | [TenantInfoResponse](#tenantinforesponse) | | Yes | + #### _AccessPolicyList | Name | Type | Description | Required | diff --git a/api/openapi/markdown/service-openapi.md b/api/openapi/markdown/service-openapi.md index 8fc5e75e3cf..5c30aefead1 100644 --- a/api/openapi/markdown/service-openapi.md +++ b/api/openapi/markdown/service-openapi.md @@ -3937,7 +3937,7 @@ Model class for provider with models response. | output_variable_name | string | | Yes | | type | string | | No | -#### SimpleAccount +#### SimpleAccountResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | @@ -4147,7 +4147,7 @@ in form definiton, or a variable while the workflow is running. | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | created_at | integer | | No | -| created_by_account | [SimpleAccount](#simpleaccount) | | No | +| created_by_account | [SimpleAccountResponse](#simpleaccountresponse) | | No | | created_by_end_user | [SimpleEndUser](#simpleenduser) | | No | | created_by_role | string | | No | | created_from | string | | No | diff --git a/api/tests/unit_tests/controllers/console/workspace/test_endpoint.py b/api/tests/unit_tests/controllers/console/workspace/test_endpoint.py index abd9b4facb9..8db3a295376 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_endpoint.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_endpoint.py @@ -1,4 +1,5 @@ import inspect +from datetime import UTC, datetime from unittest.mock import patch import pytest @@ -16,9 +17,32 @@ from controllers.console.workspace.endpoint import ( EndpointListApi, EndpointListForSinglePluginApi, ) +from core.plugin.entities.endpoint import EndpointEntityWithInstance from core.plugin.impl.exc import PluginPermissionDeniedError +def _endpoint_entity() -> EndpointEntityWithInstance: + now = datetime(2026, 1, 1, tzinfo=UTC) + return EndpointEntityWithInstance( + id="e1", + created_at=now, + updated_at=now, + tenant_id="t1", + plugin_id="p1", + settings={ + "secret": "value", + "enabled": True, + "ids": ["a", "b"], + "nested": {"limit": 3}, + }, + expired_at=now, + name="endpoint", + enabled=True, + url="https://example.test/hook-1", + hook_id="hook-1", + ) + + class TestEndpointCollectionApi: def test_create_success(self, app: Flask): api = EndpointCollectionApi() @@ -102,12 +126,34 @@ class TestEndpointListApi: with ( app.test_request_context("/?page=1&page_size=10"), - patch("controllers.console.workspace.endpoint.EndpointService.list_endpoints", return_value=[{"id": "e1"}]), + patch( + "controllers.console.workspace.endpoint.EndpointService.list_endpoints", + return_value=[_endpoint_entity()], + ), ): result = method(api, "t1", "u1") - assert "endpoints" in result - assert len(result["endpoints"]) == 1 + assert result["endpoints"] == [ + { + "id": "e1", + "created_at": "2026-01-01T00:00:00Z", + "updated_at": "2026-01-01T00:00:00Z", + "settings": { + "secret": "value", + "enabled": True, + "ids": ["a", "b"], + "nested": {"limit": 3}, + }, + "tenant_id": "t1", + "plugin_id": "p1", + "expired_at": "2026-01-01T00:00:00Z", + "declaration": {"settings": [], "endpoints": []}, + "name": "endpoint", + "enabled": True, + "url": "https://example.test/hook-1", + "hook_id": "hook-1", + } + ] def test_list_invalid_query(self, app: Flask): api = EndpointListApi() @@ -129,12 +175,13 @@ class TestEndpointListForSinglePluginApi: app.test_request_context("/?page=1&page_size=10&plugin_id=p1"), patch( "controllers.console.workspace.endpoint.EndpointService.list_endpoints_for_single_plugin", - return_value=[{"id": "e1"}], + return_value=[_endpoint_entity()], ), ): result = method(api, "t1", "u1") - assert "endpoints" in result + assert result["endpoints"][0]["id"] == "e1" + assert result["endpoints"][0]["settings"]["nested"] == {"limit": 3} def test_list_for_plugin_missing_param(self, app: Flask): api = EndpointListForSinglePluginApi() diff --git a/api/tests/unit_tests/controllers/console/workspace/test_workspace.py b/api/tests/unit_tests/controllers/console/workspace/test_workspace.py index 47e9f51fb27..e9cd3410cb9 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_workspace.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_workspace.py @@ -26,7 +26,9 @@ from controllers.console.workspace.workspace import ( WebappLogoWorkspaceApi, WorkspaceInfoApi, WorkspaceListApi, + WorkspaceLogoUploadResponse, WorkspacePermissionApi, + WorkspacePermissionResponse, ) from enums.cloud_plan import CloudPlan from libs.datetime_utils import naive_utc_now @@ -587,7 +589,8 @@ class TestWebappLogoWorkspaceApi: result, status = method(api, user) assert status == 201 - assert result["id"] == "file1" + assert result == {"id": "file1"} + assert WorkspaceLogoUploadResponse.model_validate(result).model_dump(mode="json") == {"id": "file1"} def test_filename_missing(self, app: Flask): api = WebappLogoWorkspaceApi() @@ -676,7 +679,7 @@ class TestWorkspaceInfoApi: patch("controllers.console.workspace.workspace.db.session.commit"), patch( "controllers.console.workspace.workspace.WorkspaceService.get_tenant_info", - return_value={"name": "New Name"}, + return_value={"id": "t1", "name": "New Name"}, ), ): result = method(api, "t1") @@ -717,7 +720,13 @@ class TestWorkspacePermissionApi: result, status = method(api, "t1") assert status == 200 - assert result["workspace_id"] == "t1" + expected = { + "workspace_id": "t1", + "allow_member_invite": True, + "allow_owner_transfer": False, + } + assert result == expected + assert WorkspacePermissionResponse.model_validate(result).model_dump(mode="json") == expected def test_no_current_tenant(self, app: Flask): api = WorkspacePermissionApi() diff --git a/packages/contracts/generated/api/console/account/orpc.gen.ts b/packages/contracts/generated/api/console/account/orpc.gen.ts index a9261036675..e719bbfabd0 100644 --- a/packages/contracts/generated/api/console/account/orpc.gen.ts +++ b/packages/contracts/generated/api/console/account/orpc.gen.ts @@ -27,8 +27,6 @@ import { zPostAccountDeleteFeedbackBody, zPostAccountDeleteFeedbackResponse, zPostAccountDeleteResponse, - zPostAccountEducationBody, - zPostAccountEducationResponse, zPostAccountInitBody, zPostAccountInitResponse, zPostAccountInterfaceLanguageBody, @@ -222,25 +220,13 @@ export const get5 = oc }) .output(zGetAccountEducationResponse) -export const post8 = oc - .route({ - inputStructure: 'detailed', - method: 'POST', - operationId: 'postAccountEducation', - path: '/account/education', - tags: ['console'], - }) - .input(z.object({ body: zPostAccountEducationBody })) - .output(zPostAccountEducationResponse) - export const education = { get: get5, - post: post8, autocomplete, verify: verify2, } -export const post9 = oc +export const post8 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -252,7 +238,7 @@ export const post9 = oc .output(zPostAccountInitResponse) export const init = { - post: post9, + post: post8, } export const get6 = oc @@ -269,7 +255,7 @@ export const integrates = { get: get6, } -export const post10 = oc +export const post9 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -281,10 +267,10 @@ export const post10 = oc .output(zPostAccountInterfaceLanguageResponse) export const interfaceLanguage = { - post: post10, + post: post9, } -export const post11 = oc +export const post10 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -296,10 +282,10 @@ export const post11 = oc .output(zPostAccountInterfaceThemeResponse) export const interfaceTheme = { - post: post11, + post: post10, } -export const post12 = oc +export const post11 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -311,10 +297,10 @@ export const post12 = oc .output(zPostAccountNameResponse) export const name = { - post: post12, + post: post11, } -export const post13 = oc +export const post12 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -326,7 +312,7 @@ export const post13 = oc .output(zPostAccountPasswordResponse) export const password = { - post: post13, + post: post12, } export const get7 = oc @@ -343,7 +329,7 @@ export const profile = { get: get7, } -export const post14 = oc +export const post13 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -355,7 +341,7 @@ export const post14 = oc .output(zPostAccountTimezoneResponse) export const timezone = { - post: post14, + post: post13, } export const account = { diff --git a/packages/contracts/generated/api/console/account/types.gen.ts b/packages/contracts/generated/api/console/account/types.gen.ts index cdd45925fb2..390e7caeffa 100644 --- a/packages/contracts/generated/api/console/account/types.gen.ts +++ b/packages/contracts/generated/api/console/account/types.gen.ts @@ -12,7 +12,7 @@ export type AccountAvatarPayload = { avatar: string } -export type Account = { +export type AccountResponse = { avatar?: string | null readonly avatar_url: string | null created_at?: number | null @@ -81,16 +81,6 @@ export type EducationStatusResponse = { result?: boolean | null } -export type EducationActivatePayload = { - institution: string - role: string - token: string -} - -export type EducationActivateResponse = { - [key: string]: unknown -} - export type EducationAutocompleteResponse = { curr_page?: number | null data?: Array @@ -140,7 +130,7 @@ export type AccountIntegrateResponse = { provider: string } -export type AccountWritable = { +export type AccountResponseWritable = { avatar?: string | null created_at?: number | null email: string @@ -177,7 +167,7 @@ export type PostAccountAvatarData = { } export type PostAccountAvatarResponses = { - 200: Account + 200: AccountResponse } export type PostAccountAvatarResponse = PostAccountAvatarResponses[keyof PostAccountAvatarResponses] @@ -218,7 +208,7 @@ export type PostAccountChangeEmailResetData = { } export type PostAccountChangeEmailResetResponses = { - 200: Account + 200: AccountResponse } export type PostAccountChangeEmailResetResponse @@ -293,20 +283,6 @@ export type GetAccountEducationResponses = { export type GetAccountEducationResponse = GetAccountEducationResponses[keyof GetAccountEducationResponses] -export type PostAccountEducationData = { - body: EducationActivatePayload - path?: never - query?: never - url: '/account/education' -} - -export type PostAccountEducationResponses = { - 200: EducationActivateResponse -} - -export type PostAccountEducationResponse - = PostAccountEducationResponses[keyof PostAccountEducationResponses] - export type GetAccountEducationAutocompleteData = { body?: never path?: never @@ -374,7 +350,7 @@ export type PostAccountInterfaceLanguageData = { } export type PostAccountInterfaceLanguageResponses = { - 200: Account + 200: AccountResponse } export type PostAccountInterfaceLanguageResponse @@ -388,7 +364,7 @@ export type PostAccountInterfaceThemeData = { } export type PostAccountInterfaceThemeResponses = { - 200: Account + 200: AccountResponse } export type PostAccountInterfaceThemeResponse @@ -402,7 +378,7 @@ export type PostAccountNameData = { } export type PostAccountNameResponses = { - 200: Account + 200: AccountResponse } export type PostAccountNameResponse = PostAccountNameResponses[keyof PostAccountNameResponses] @@ -415,7 +391,7 @@ export type PostAccountPasswordData = { } export type PostAccountPasswordResponses = { - 200: Account + 200: AccountResponse } export type PostAccountPasswordResponse @@ -429,7 +405,7 @@ export type GetAccountProfileData = { } export type GetAccountProfileResponses = { - 200: Account + 200: AccountResponse } export type GetAccountProfileResponse = GetAccountProfileResponses[keyof GetAccountProfileResponses] @@ -442,7 +418,7 @@ export type PostAccountTimezoneData = { } export type PostAccountTimezoneResponses = { - 200: Account + 200: AccountResponse } export type PostAccountTimezoneResponse diff --git a/packages/contracts/generated/api/console/account/zod.gen.ts b/packages/contracts/generated/api/console/account/zod.gen.ts index 9951efc8d9f..feb3a45d78b 100644 --- a/packages/contracts/generated/api/console/account/zod.gen.ts +++ b/packages/contracts/generated/api/console/account/zod.gen.ts @@ -17,9 +17,9 @@ export const zAccountAvatarPayload = z.object({ }) /** - * Account + * AccountResponse */ -export const zAccount = z.object({ +export const zAccountResponse = z.object({ avatar: z.string().nullish(), avatar_url: z.string().nullable(), created_at: z.int().nullish(), @@ -118,20 +118,6 @@ export const zEducationStatusResponse = z.object({ result: z.boolean().nullish(), }) -/** - * EducationActivatePayload - */ -export const zEducationActivatePayload = z.object({ - institution: z.string(), - role: z.string(), - token: z.string(), -}) - -/** - * EducationActivateResponse - */ -export const zEducationActivateResponse = z.record(z.string(), z.unknown()) - /** * EducationAutocompleteResponse */ @@ -212,9 +198,9 @@ export const zAccountIntegrateListResponse = z.object({ }) /** - * Account + * AccountResponse */ -export const zAccountWritable = z.object({ +export const zAccountResponseWritable = z.object({ avatar: z.string().nullish(), created_at: z.int().nullish(), email: z.string(), @@ -242,7 +228,7 @@ export const zPostAccountAvatarBody = zAccountAvatarPayload /** * Success */ -export const zPostAccountAvatarResponse = zAccount +export const zPostAccountAvatarResponse = zAccountResponse export const zPostAccountChangeEmailBody = zChangeEmailSendPayload @@ -263,7 +249,7 @@ export const zPostAccountChangeEmailResetBody = zChangeEmailResetPayload /** * Success */ -export const zPostAccountChangeEmailResetResponse = zAccount +export const zPostAccountChangeEmailResetResponse = zAccountResponse export const zPostAccountChangeEmailValidityBody = zChangeEmailValidityPayload @@ -296,13 +282,6 @@ export const zGetAccountDeleteVerifyResponse = zSimpleResultDataResponse */ export const zGetAccountEducationResponse = zEducationStatusResponse -export const zPostAccountEducationBody = zEducationActivatePayload - -/** - * Success - */ -export const zPostAccountEducationResponse = zEducationActivateResponse - export const zGetAccountEducationAutocompleteQuery = z.object({ keywords: z.string(), limit: z.int().optional().default(20), @@ -336,37 +315,37 @@ export const zPostAccountInterfaceLanguageBody = zAccountInterfaceLanguagePayloa /** * Success */ -export const zPostAccountInterfaceLanguageResponse = zAccount +export const zPostAccountInterfaceLanguageResponse = zAccountResponse export const zPostAccountInterfaceThemeBody = zAccountInterfaceThemePayload /** * Success */ -export const zPostAccountInterfaceThemeResponse = zAccount +export const zPostAccountInterfaceThemeResponse = zAccountResponse export const zPostAccountNameBody = zAccountNamePayload /** * Success */ -export const zPostAccountNameResponse = zAccount +export const zPostAccountNameResponse = zAccountResponse export const zPostAccountPasswordBody = zAccountPasswordPayload /** * Success */ -export const zPostAccountPasswordResponse = zAccount +export const zPostAccountPasswordResponse = zAccountResponse /** * Success */ -export const zGetAccountProfileResponse = zAccount +export const zGetAccountProfileResponse = zAccountResponse export const zPostAccountTimezoneBody = zAccountTimezonePayload /** * Success */ -export const zPostAccountTimezoneResponse = zAccount +export const zPostAccountTimezoneResponse = zAccountResponse diff --git a/packages/contracts/generated/api/console/all-workspaces/types.gen.ts b/packages/contracts/generated/api/console/all-workspaces/types.gen.ts index 4683b2d9921..b4c9fa0349a 100644 --- a/packages/contracts/generated/api/console/all-workspaces/types.gen.ts +++ b/packages/contracts/generated/api/console/all-workspaces/types.gen.ts @@ -4,7 +4,7 @@ export type ClientOptions = { baseUrl: `${string}://${string}/console/api` | (string & {}) } -export type WorkspaceListResponse = { +export type WorkspacePaginationResponse = { data: Array has_more: boolean limit: number @@ -30,7 +30,7 @@ export type GetAllWorkspacesData = { } export type GetAllWorkspacesResponses = { - 200: WorkspaceListResponse + 200: WorkspacePaginationResponse } export type GetAllWorkspacesResponse = GetAllWorkspacesResponses[keyof GetAllWorkspacesResponses] diff --git a/packages/contracts/generated/api/console/all-workspaces/zod.gen.ts b/packages/contracts/generated/api/console/all-workspaces/zod.gen.ts index f63bd0e396f..c9cdda11681 100644 --- a/packages/contracts/generated/api/console/all-workspaces/zod.gen.ts +++ b/packages/contracts/generated/api/console/all-workspaces/zod.gen.ts @@ -13,9 +13,9 @@ export const zWorkspaceListItemResponse = z.object({ }) /** - * WorkspaceListResponse + * WorkspacePaginationResponse */ -export const zWorkspaceListResponse = z.object({ +export const zWorkspacePaginationResponse = z.object({ data: z.array(zWorkspaceListItemResponse), has_more: z.boolean(), limit: z.int(), @@ -31,4 +31,4 @@ export const zGetAllWorkspacesQuery = z.object({ /** * Success */ -export const zGetAllWorkspacesResponse = zWorkspaceListResponse +export const zGetAllWorkspacesResponse = zWorkspacePaginationResponse diff --git a/packages/contracts/generated/api/console/apps/types.gen.ts b/packages/contracts/generated/api/console/apps/types.gen.ts index 9e79518f3cd..e2d2c577f54 100644 --- a/packages/contracts/generated/api/console/apps/types.gen.ts +++ b/packages/contracts/generated/api/console/apps/types.gen.ts @@ -731,7 +731,7 @@ export type WorkflowRunPaginationResponse = { export type WorkflowRunDetailResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -799,7 +799,7 @@ export type WorkflowCommentCreate = { } export type WorkflowCommentMentionUsersPayload = { - users: Array + users: Array } export type WorkflowCommentDetail = { @@ -887,7 +887,7 @@ export type DefaultBlockConfigResponse = { export type WorkflowResponse = { conversation_variables: Array created_at: number - created_by?: SimpleAccount | null + created_by?: SimpleAccountResponse | null environment_variables: Array features: { [key: string]: unknown @@ -902,7 +902,7 @@ export type WorkflowResponse = { rag_pipeline_variables: Array tool_published: boolean updated_at: number - updated_by?: SimpleAccount | null + updated_by?: SimpleAccountResponse | null version: string } @@ -1029,7 +1029,7 @@ export type AgentComposerValidateResponse = { export type WorkflowRunNodeExecutionResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -1289,7 +1289,7 @@ export type WorkflowOnlineUsersByApp = { export type AdvancedChatWorkflowRunForListResponse = { conversation_id?: string | null created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -1596,7 +1596,7 @@ export type UserSatisfactionRateStatisticItem = { export type WorkflowAppLogPartialResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null created_from?: string | null @@ -1607,7 +1607,7 @@ export type WorkflowAppLogPartialResponse = { export type WorkflowArchivedLogPartialResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null id: string trigger_metadata?: unknown @@ -1616,7 +1616,7 @@ export type WorkflowArchivedLogPartialResponse = { export type WorkflowRunForListResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -1628,7 +1628,7 @@ export type WorkflowRunForListResponse = { version?: string | null } -export type SimpleAccount = { +export type SimpleAccountResponse = { email: string id: string name: string @@ -1671,8 +1671,9 @@ export type WorkflowCommentBasic = { updated_at?: number | null } -export type AccountWithRole = { +export type AccountWithRoleResponse = { avatar?: string | null + readonly avatar_url: string | null created_at?: number | null email: string id: string @@ -1984,7 +1985,14 @@ export type ModelConfigPartial = { export type LlmMode = 'chat' | 'completion' -export type Type = 'github' | 'marketplace' | 'package' +export type Type + = | 'app-selector' + | 'array[tools]' + | 'boolean' + | 'model-selector' + | 'secret-input' + | 'select' + | 'text-input' export type Github = { github_plugin_unique_identifier: string @@ -2054,6 +2062,12 @@ export type SimpleMessageDetail = { query: string } +export type SimpleAccount = { + email: string + id: string + name: string +} + export type HumanInputFormDefinition = { actions?: Array display_in_ui?: boolean @@ -2637,6 +2651,10 @@ export type WorkflowCommentBasicListWritable = { data: Array } +export type WorkflowCommentMentionUsersPayloadWritable = { + users: Array +} + export type WorkflowCommentDetailWritable = { content: string created_at?: number | null @@ -2716,6 +2734,21 @@ export type WorkflowCommentBasicWritable = { updated_at?: number | null } +export type AccountWithRoleResponseWritable = { + avatar?: string | null + created_at?: number | null + email: string + id: string + last_active_at?: number | null + last_login_at?: number | null + name: string + role: string + roles?: Array<{ + [key: string]: string + }> + status: string +} + export type WorkflowCommentAccountWritable = { email: string id: string diff --git a/packages/contracts/generated/api/console/apps/zod.gen.ts b/packages/contracts/generated/api/console/apps/zod.gen.ts index 9b86fda0a62..b7f5189dd60 100644 --- a/packages/contracts/generated/api/console/apps/zod.gen.ts +++ b/packages/contracts/generated/api/console/apps/zod.gen.ts @@ -1319,9 +1319,9 @@ export const zUserSatisfactionRateStatisticResponse = z.object({ }) /** - * SimpleAccount + * SimpleAccountResponse */ -export const zSimpleAccount = z.object({ +export const zSimpleAccountResponse = z.object({ email: z.string(), id: z.string(), name: z.string(), @@ -1333,7 +1333,7 @@ export const zSimpleAccount = z.object({ export const zAdvancedChatWorkflowRunForListResponse = z.object({ conversation_id: z.string().nullish(), created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), elapsed_time: z.number().nullish(), exceptions_count: z.int().nullish(), finished_at: z.int().nullish(), @@ -1355,72 +1355,12 @@ export const zAdvancedChatWorkflowRunPaginationResponse = z.object({ limit: z.int(), }) -/** - * ConversationAnnotation - */ -export const zConversationAnnotation = z.object({ - account: zSimpleAccount.nullish(), - content: z.string(), - created_at: z.int().nullish(), - id: z.string(), - question: z.string().nullish(), -}) - -/** - * ConversationAnnotationHitHistory - */ -export const zConversationAnnotationHitHistory = z.object({ - annotation_create_account: zSimpleAccount.nullish(), - created_at: z.int().nullish(), - id: z.string(), -}) - -/** - * Feedback - */ -export const zFeedback = z.object({ - content: z.string().nullish(), - from_account: zSimpleAccount.nullish(), - from_end_user_id: z.string().nullish(), - from_source: z.string(), - rating: z.string(), -}) - -/** - * MessageDetail - */ -export const zMessageDetail = z.object({ - agent_thoughts: z.array(zAgentThought), - annotation: zConversationAnnotation.nullish(), - annotation_hit_history: zConversationAnnotationHitHistory.nullish(), - answer_tokens: z.int(), - conversation_id: z.string(), - created_at: z.int().nullish(), - error: z.string().nullish(), - feedbacks: z.array(zFeedback), - from_account_id: z.string().nullish(), - from_end_user_id: z.string().nullish(), - from_source: z.string(), - id: z.string(), - inputs: z.record(z.string(), zJsonValue), - message: zJsonValue, - message_files: z.array(zMessageFile), - message_metadata_dict: zJsonValue, - message_tokens: z.int(), - parent_message_id: z.string().nullish(), - provider_response_latency: z.number(), - query: z.string(), - re_sign_file_url_answer: z.string(), - status: z.string(), - workflow_run_id: z.string().nullish(), -}) - /** * WorkflowRunForListResponse */ export const zWorkflowRunForListResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), elapsed_time: z.number().nullish(), exceptions_count: z.int().nullish(), finished_at: z.int().nullish(), @@ -1456,7 +1396,7 @@ export const zSimpleEndUser = z.object({ */ export const zWorkflowRunDetailResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -1478,7 +1418,7 @@ export const zWorkflowRunDetailResponse = z.object({ */ export const zWorkflowRunNodeExecutionResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -1544,10 +1484,11 @@ export const zSandboxUploadResponse = z.object({ }) /** - * AccountWithRole + * AccountWithRoleResponse */ -export const zAccountWithRole = z.object({ +export const zAccountWithRoleResponse = z.object({ avatar: z.string().nullish(), + avatar_url: z.string().nullable(), created_at: z.int().nullish(), email: z.string(), id: z.string(), @@ -1563,7 +1504,7 @@ export const zAccountWithRole = z.object({ * WorkflowCommentMentionUsersPayload */ export const zWorkflowCommentMentionUsersPayload = z.object({ - users: z.array(zAccountWithRole), + users: z.array(zAccountWithRoleResponse), }) /** @@ -1752,7 +1693,7 @@ export const zPipelineVariableResponse = z.object({ export const zWorkflowResponse = z.object({ conversation_variables: z.array(zWorkflowConversationVariableResponse), created_at: z.int(), - created_by: zSimpleAccount.nullish(), + created_by: zSimpleAccountResponse.nullish(), environment_variables: z.array(zWorkflowEnvironmentVariableResponse), features: z.record(z.string(), z.unknown()), graph: z.record(z.string(), z.unknown()), @@ -1763,7 +1704,7 @@ export const zWorkflowResponse = z.object({ rag_pipeline_variables: z.array(zPipelineVariableResponse), tool_published: z.boolean(), updated_at: z.int(), - updated_by: zSimpleAccount.nullish(), + updated_by: zSimpleAccountResponse.nullish(), version: z.string(), }) @@ -2152,24 +2093,18 @@ export const zConversationDetail = z.object({ user_feedback_stats: zFeedbackStat.nullish(), }) -/** - * ConversationMessageDetail - */ -export const zConversationMessageDetail = z.object({ - created_at: z.int().nullish(), - first_message: zMessageDetail.nullish(), - from_account_id: z.string().nullish(), - from_end_user_id: z.string().nullish(), - from_source: z.string(), - id: z.string(), - model_config: zModelConfig.nullish(), - status: z.string(), -}) - /** * Type */ -export const zType = z.enum(['github', 'marketplace', 'package']) +export const zType = z.enum([ + 'app-selector', + 'array[tools]', + 'boolean', + 'model-selector', + 'secret-input', + 'select', + 'text-input', +]) /** * Github @@ -2366,6 +2301,26 @@ export const zSimpleMessageDetail = z.object({ query: z.string(), }) +/** + * SimpleAccount + */ +export const zSimpleAccount = z.object({ + email: z.string(), + id: z.string(), + name: z.string(), +}) + +/** + * ConversationAnnotation + */ +export const zConversationAnnotation = z.object({ + account: zSimpleAccount.nullish(), + content: z.string(), + created_at: z.int().nullish(), + id: z.string(), + question: z.string().nullish(), +}) + /** * Conversation */ @@ -2398,6 +2353,69 @@ export const zConversationPagination = z.object({ total: z.int(), }) +/** + * ConversationAnnotationHitHistory + */ +export const zConversationAnnotationHitHistory = z.object({ + annotation_create_account: zSimpleAccount.nullish(), + created_at: z.int().nullish(), + id: z.string(), +}) + +/** + * Feedback + */ +export const zFeedback = z.object({ + content: z.string().nullish(), + from_account: zSimpleAccount.nullish(), + from_end_user_id: z.string().nullish(), + from_source: z.string(), + rating: z.string(), +}) + +/** + * MessageDetail + */ +export const zMessageDetail = z.object({ + agent_thoughts: z.array(zAgentThought), + annotation: zConversationAnnotation.nullish(), + annotation_hit_history: zConversationAnnotationHitHistory.nullish(), + answer_tokens: z.int(), + conversation_id: z.string(), + created_at: z.int().nullish(), + error: z.string().nullish(), + feedbacks: z.array(zFeedback), + from_account_id: z.string().nullish(), + from_end_user_id: z.string().nullish(), + from_source: z.string(), + id: z.string(), + inputs: z.record(z.string(), zJsonValue), + message: zJsonValue, + message_files: z.array(zMessageFile), + message_metadata_dict: zJsonValue, + message_tokens: z.int(), + parent_message_id: z.string().nullish(), + provider_response_latency: z.number(), + query: z.string(), + re_sign_file_url_answer: z.string(), + status: z.string(), + workflow_run_id: z.string().nullish(), +}) + +/** + * ConversationMessageDetail + */ +export const zConversationMessageDetail = z.object({ + created_at: z.int().nullish(), + first_message: zMessageDetail.nullish(), + from_account_id: z.string().nullish(), + from_end_user_id: z.string().nullish(), + from_source: z.string(), + id: z.string(), + model_config: zModelConfig.nullish(), + status: z.string(), +}) + /** * ExecutionContentType */ @@ -2425,7 +2443,7 @@ export const zWorkflowRunForLogResponse = z.object({ */ export const zWorkflowAppLogPartialResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), created_from: z.string().nullish(), @@ -2461,7 +2479,7 @@ export const zWorkflowRunForArchivedLogResponse = z.object({ */ export const zWorkflowArchivedLogPartialResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), id: z.string(), trigger_metadata: z.unknown().optional(), @@ -3586,6 +3604,29 @@ export const zAppDetailWithSiteWritable = z.object({ workflow: zWorkflowPartial.nullish(), }) +/** + * AccountWithRoleResponse + */ +export const zAccountWithRoleResponseWritable = z.object({ + avatar: z.string().nullish(), + created_at: z.int().nullish(), + email: z.string(), + id: z.string(), + last_active_at: z.int().nullish(), + last_login_at: z.int().nullish(), + name: z.string(), + role: z.string(), + roles: z.array(z.record(z.string(), z.string())).optional(), + status: z.string(), +}) + +/** + * WorkflowCommentMentionUsersPayload + */ +export const zWorkflowCommentMentionUsersPayloadWritable = z.object({ + users: z.array(zAccountWithRoleResponseWritable), +}) + /** * WorkflowCommentAccount */ diff --git a/packages/contracts/generated/api/console/rag/types.gen.ts b/packages/contracts/generated/api/console/rag/types.gen.ts index b9862a8d1e8..b49f5d522d4 100644 --- a/packages/contracts/generated/api/console/rag/types.gen.ts +++ b/packages/contracts/generated/api/console/rag/types.gen.ts @@ -119,7 +119,7 @@ export type SimpleResultResponse = { export type WorkflowRunDetailResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -158,7 +158,7 @@ export type DefaultBlockConfigResponse = { export type WorkflowResponse = { conversation_variables: Array created_at: number - created_by?: SimpleAccount | null + created_by?: SimpleAccountResponse | null environment_variables: Array features: { [key: string]: unknown @@ -173,7 +173,7 @@ export type WorkflowResponse = { rag_pipeline_variables: Array tool_published: boolean updated_at: number - updated_by?: SimpleAccount | null + updated_by?: SimpleAccountResponse | null version: string } @@ -221,7 +221,7 @@ export type DatasourceVariablesPayload = { export type WorkflowRunNodeExecutionResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -421,7 +421,7 @@ export type PluginDependency = { export type WorkflowRunForListResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -433,7 +433,7 @@ export type WorkflowRunForListResponse = { version?: string | null } -export type SimpleAccount = { +export type SimpleAccountResponse = { email: string id: string name: string @@ -515,7 +515,14 @@ export type DatasetWeightedScoreResponse = { weight_type?: string | null } -export type Type = 'github' | 'marketplace' | 'package' +export type Type + = | 'app-selector' + | 'array[tools]' + | 'boolean' + | 'model-selector' + | 'secret-input' + | 'select' + | 'text-input' export type Github = { github_plugin_unique_identifier: string diff --git a/packages/contracts/generated/api/console/rag/zod.gen.ts b/packages/contracts/generated/api/console/rag/zod.gen.ts index 717db30baa7..60d70850ef4 100644 --- a/packages/contracts/generated/api/console/rag/zod.gen.ts +++ b/packages/contracts/generated/api/console/rag/zod.gen.ts @@ -322,9 +322,9 @@ export const zPipelineTemplateListResponse = z.object({ }) /** - * SimpleAccount + * SimpleAccountResponse */ -export const zSimpleAccount = z.object({ +export const zSimpleAccountResponse = z.object({ email: z.string(), id: z.string(), name: z.string(), @@ -335,7 +335,7 @@ export const zSimpleAccount = z.object({ */ export const zWorkflowRunForListResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), elapsed_time: z.number().nullish(), exceptions_count: z.int().nullish(), finished_at: z.int().nullish(), @@ -371,7 +371,7 @@ export const zSimpleEndUser = z.object({ */ export const zWorkflowRunDetailResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -393,7 +393,7 @@ export const zWorkflowRunDetailResponse = z.object({ */ export const zWorkflowRunNodeExecutionResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -471,7 +471,7 @@ export const zPipelineVariableResponse = z.object({ export const zWorkflowResponse = z.object({ conversation_variables: z.array(zWorkflowConversationVariableResponse), created_at: z.int(), - created_by: zSimpleAccount.nullish(), + created_by: zSimpleAccountResponse.nullish(), environment_variables: z.array(zWorkflowEnvironmentVariableResponse), features: z.record(z.string(), z.unknown()), graph: z.record(z.string(), z.unknown()), @@ -482,7 +482,7 @@ export const zWorkflowResponse = z.object({ rag_pipeline_variables: z.array(zPipelineVariableResponse), tool_published: z.boolean(), updated_at: z.int(), - updated_by: zSimpleAccount.nullish(), + updated_by: zSimpleAccountResponse.nullish(), version: z.string(), }) @@ -547,7 +547,15 @@ export const zDatasetRerankingModelResponse = z.object({ /** * Type */ -export const zType = z.enum(['github', 'marketplace', 'package']) +export const zType = z.enum([ + 'app-selector', + 'array[tools]', + 'boolean', + 'model-selector', + 'secret-input', + 'select', + 'text-input', +]) /** * Github diff --git a/packages/contracts/generated/api/console/snippets/types.gen.ts b/packages/contracts/generated/api/console/snippets/types.gen.ts index 631da7bace8..2fc256bcbb4 100644 --- a/packages/contracts/generated/api/console/snippets/types.gen.ts +++ b/packages/contracts/generated/api/console/snippets/types.gen.ts @@ -16,7 +16,7 @@ export type SimpleResultResponse = { export type WorkflowRunDetailResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -51,7 +51,7 @@ export type DefaultBlockConfigsResponse = Array<{ export type SnippetWorkflowResponse = { conversation_variables: Array created_at: number - created_by?: SimpleAccount | null + created_by?: SimpleAccountResponse | null environment_variables: Array features: { [key: string]: unknown @@ -69,7 +69,7 @@ export type SnippetWorkflowResponse = { rag_pipeline_variables: Array tool_published: boolean updated_at: number - updated_by?: SimpleAccount | null + updated_by?: SimpleAccountResponse | null version: string } @@ -120,7 +120,7 @@ export type SnippetLoopNodeRunPayload = { export type WorkflowRunNodeExecutionResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null created_by_end_user?: SimpleEndUser | null created_by_role?: string | null elapsed_time?: number | null @@ -210,7 +210,7 @@ export type WorkflowPublishResponse = { export type WorkflowRunForListResponse = { created_at?: number | null - created_by_account?: SimpleAccount | null + created_by_account?: SimpleAccountResponse | null elapsed_time?: number | null exceptions_count?: number | null finished_at?: number | null @@ -222,7 +222,7 @@ export type WorkflowRunForListResponse = { version?: string | null } -export type SimpleAccount = { +export type SimpleAccountResponse = { email: string id: string name: string diff --git a/packages/contracts/generated/api/console/snippets/zod.gen.ts b/packages/contracts/generated/api/console/snippets/zod.gen.ts index 85e5b961547..303fb07c66a 100644 --- a/packages/contracts/generated/api/console/snippets/zod.gen.ts +++ b/packages/contracts/generated/api/console/snippets/zod.gen.ts @@ -134,9 +134,9 @@ export const zWorkflowPublishResponse = z.object({ }) /** - * SimpleAccount + * SimpleAccountResponse */ -export const zSimpleAccount = z.object({ +export const zSimpleAccountResponse = z.object({ email: z.string(), id: z.string(), name: z.string(), @@ -147,7 +147,7 @@ export const zSimpleAccount = z.object({ */ export const zWorkflowRunForListResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), elapsed_time: z.number().nullish(), exceptions_count: z.int().nullish(), finished_at: z.int().nullish(), @@ -183,7 +183,7 @@ export const zSimpleEndUser = z.object({ */ export const zWorkflowRunDetailResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -205,7 +205,7 @@ export const zWorkflowRunDetailResponse = z.object({ */ export const zWorkflowRunNodeExecutionResponse = z.object({ created_at: z.int().nullish(), - created_by_account: zSimpleAccount.nullish(), + created_by_account: zSimpleAccountResponse.nullish(), created_by_end_user: zSimpleEndUser.nullish(), created_by_role: z.string().nullish(), elapsed_time: z.number().nullish(), @@ -283,7 +283,7 @@ export const zPipelineVariableResponse = z.object({ export const zSnippetWorkflowResponse = z.object({ conversation_variables: z.array(zWorkflowConversationVariableResponse), created_at: z.int(), - created_by: zSimpleAccount.nullish(), + created_by: zSimpleAccountResponse.nullish(), environment_variables: z.array(zWorkflowEnvironmentVariableResponse), features: z.record(z.string(), z.unknown()), graph: z.record(z.string(), z.unknown()), @@ -295,7 +295,7 @@ export const zSnippetWorkflowResponse = z.object({ rag_pipeline_variables: z.array(zPipelineVariableResponse), tool_published: z.boolean(), updated_at: z.int(), - updated_by: zSimpleAccount.nullish(), + updated_by: zSimpleAccountResponse.nullish(), version: z.string(), }) diff --git a/packages/contracts/generated/api/console/workspaces/orpc.gen.ts b/packages/contracts/generated/api/console/workspaces/orpc.gen.ts index 7e676564999..4f7c1179949 100644 --- a/packages/contracts/generated/api/console/workspaces/orpc.gen.ts +++ b/packages/contracts/generated/api/console/workspaces/orpc.gen.ts @@ -370,6 +370,7 @@ import { zPostWorkspacesCurrentTriggerProviderBySubscriptionIdSubscriptionsUpdateResponse, zPostWorkspacesCustomConfigBody, zPostWorkspacesCustomConfigResponse, + zPostWorkspacesCustomConfigWebappLogoUploadBody, zPostWorkspacesCustomConfigWebappLogoUploadResponse, zPostWorkspacesInfoBody, zPostWorkspacesInfoResponse, @@ -4121,6 +4122,7 @@ export const post72 = oc successStatus: 201, tags: ['console'], }) + .input(z.object({ body: zPostWorkspacesCustomConfigWebappLogoUploadBody })) .output(zPostWorkspacesCustomConfigWebappLogoUploadResponse) export const upload2 = { diff --git a/packages/contracts/generated/api/console/workspaces/types.gen.ts b/packages/contracts/generated/api/console/workspaces/types.gen.ts index 29f23567e95..9fd704c2a5c 100644 --- a/packages/contracts/generated/api/console/workspaces/types.gen.ts +++ b/packages/contracts/generated/api/console/workspaces/types.gen.ts @@ -104,8 +104,8 @@ export type SnippetUseCountResponse = { use_count: number } -export type AccountWithRoleList = { - accounts: Array +export type AccountWithRoleListResponse = { + accounts: Array } export type DefaultModelDataResponse = { @@ -128,7 +128,7 @@ export type EndpointCreatePayload = { } } -export type EndpointCreateResponse = { +export type SuccessResponse = { success: boolean } @@ -136,28 +136,8 @@ export type EndpointIdPayload = { endpoint_id: string } -export type EndpointDeleteResponse = { - success: boolean -} - -export type EndpointDisableResponse = { - success: boolean -} - -export type EndpointEnableResponse = { - success: boolean -} - export type EndpointListResponse = { - endpoints: Array<{ - [key: string]: unknown - }> -} - -export type PluginEndpointListResponse = { - endpoints: Array<{ - [key: string]: unknown - }> + endpoints: Array } export type LegacyEndpointUpdatePayload = { @@ -168,10 +148,6 @@ export type LegacyEndpointUpdatePayload = { } } -export type EndpointUpdateResponse = { - success: boolean -} - export type EndpointUpdatePayload = { name: string settings: { @@ -211,9 +187,9 @@ export type SimpleResultDataResponse = { result: string } -export type MemberActionTenantResponse = { +export type MemberActionResponse = { result: string - tenant_id: string + tenant_id?: string } export type OwnerTransferPayload = { @@ -391,10 +367,6 @@ export type ParserExcludePlugin = { plugin_id: string } -export type SuccessResponse = { - success: boolean -} - export type PluginAutoUpgradeFetchResponse = { auto_upgrade: PluginAutoUpgradeSettingsResponseModel category: PluginCategory @@ -836,7 +808,7 @@ export type WorkspaceCustomConfigPayload = { replace_webapp_logo?: string | null } -export type WorkspaceMutationResponse = { +export type WorkspaceTenantResultResponse = { result: string tenant: TenantInfoResponse } @@ -862,6 +834,7 @@ export type TenantListItemResponse = { created_at?: number | null current: boolean id: string + last_opened_at?: number | null name?: string | null plan?: string | null status?: string | null @@ -921,8 +894,9 @@ export type AnonymousInlineModel7B8B49Ca164e = { type?: string } -export type AccountWithRole = { +export type AccountWithRoleResponse = { avatar?: string | null + readonly avatar_url: string | null created_at?: number | null email: string id: string @@ -948,6 +922,23 @@ export type Inner = { provider?: string | null } +export type EndpointEntityWithInstance = { + created_at: string + declaration?: EndpointProviderDeclaration + enabled: boolean + expired_at: string + hook_id: string + id: string + name: string + plugin_id: string + settings: { + [key: string]: unknown + } + tenant_id: string + updated_at: string + url: string +} + export type MemberInviteResultResponse = { email: string message?: string | null @@ -1209,6 +1200,11 @@ export type SimpleProviderEntityResponse = { tenant_id: string } +export type EndpointProviderDeclaration = { + endpoints?: Array | null + settings?: Array +} + export type ConfigurateMethod = 'customizable-model' | 'predefined-model' export type CustomConfigurationResponse = { @@ -1419,6 +1415,26 @@ export type AiModelEntityResponse = { pricing?: PriceConfigResponse | null } +export type EndpointDeclaration = { + hidden?: boolean + method: string + path: string +} + +export type ProviderConfig = { + default?: number | string | number | boolean | null + help?: I18nObject | null + label?: I18nObject | null + multiple?: boolean + name: string + options?: Array