fix: GET query parameter OpenAPI contracts (#37378)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Stephen Zhou 2026-06-12 17:01:22 +08:00 committed by GitHub
parent 800bfc988e
commit e0c6ca9930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 678 additions and 702 deletions

View File

@ -27,12 +27,18 @@ QueryParamDoc = TypedDict(
"description": NotRequired[str],
"enum": NotRequired[list[object]],
"default": NotRequired[object],
"format": NotRequired[str],
"minimum": NotRequired[int | float],
"maximum": NotRequired[int | float],
"exclusiveMinimum": NotRequired[int | float],
"exclusiveMaximum": NotRequired[int | float],
"minLength": NotRequired[int],
"maxLength": NotRequired[int],
"pattern": NotRequired[str],
"minItems": NotRequired[int],
"maxItems": NotRequired[int],
"uniqueItems": NotRequired[bool],
"multipleOf": NotRequired[int | float],
},
)
@ -129,6 +135,7 @@ def query_params_from_model(model: type[BaseModel]) -> dict[str, QueryParamDoc]:
"""
schema = model.model_json_schema(ref_template=DEFAULT_REF_TEMPLATE_OPENAPI_3_0)
definitions = _schema_definitions(schema)
properties = schema.get("properties", {})
if not isinstance(properties, Mapping):
return {}
@ -141,7 +148,11 @@ def query_params_from_model(model: type[BaseModel]) -> dict[str, QueryParamDoc]:
if not isinstance(name, str) or not isinstance(property_schema, Mapping):
continue
params[name] = _query_param_from_property(property_schema, required=name in required_names)
params[name] = _query_param_from_property(
property_schema,
required=name in required_names,
definitions=definitions,
)
return params
@ -198,8 +209,18 @@ def _drop_malformed_defaulted_integer_params(model: type[BaseModel], params: dic
params.pop(name)
def _query_param_from_property(property_schema: Mapping[str, Any], *, required: bool) -> QueryParamDoc:
param_schema = _nullable_property_schema(property_schema)
def _schema_definitions(schema: Mapping[str, Any]) -> Mapping[str, Any]:
definitions = schema.get("$defs")
return definitions if isinstance(definitions, Mapping) else {}
def _query_param_from_property(
property_schema: Mapping[str, Any],
*,
required: bool,
definitions: Mapping[str, Any],
) -> QueryParamDoc:
param_schema = _resolve_schema_ref(_nullable_property_schema(property_schema), definitions)
param_doc: QueryParamDoc = {"in": "query", "required": required}
description = param_schema.get("description")
@ -212,9 +233,16 @@ def _query_param_from_property(property_schema: Mapping[str, Any], *, required:
if schema_type == "array":
items = param_schema.get("items")
if isinstance(items, Mapping):
item_type = items.get("type")
item_schema = _resolve_schema_ref(items, definitions)
item_type = item_schema.get("type")
if isinstance(item_type, str):
param_doc["items"] = {"type": item_type}
item_enum = item_schema.get("enum")
if isinstance(item_enum, list):
param_doc.setdefault("items", {})["enum"] = item_enum
item_format = item_schema.get("format")
if isinstance(item_format, str):
param_doc.setdefault("items", {})["format"] = item_format
enum = param_schema.get("enum")
if isinstance(enum, list):
@ -224,6 +252,10 @@ def _query_param_from_property(property_schema: Mapping[str, Any], *, required:
if default is not None:
param_doc["default"] = default
schema_format = param_schema.get("format")
if isinstance(schema_format, str):
param_doc["format"] = schema_format
minimum = param_schema.get("minimum")
if isinstance(minimum, int | float):
param_doc["minimum"] = minimum
@ -232,6 +264,14 @@ def _query_param_from_property(property_schema: Mapping[str, Any], *, required:
if isinstance(maximum, int | float):
param_doc["maximum"] = maximum
exclusive_minimum = param_schema.get("exclusiveMinimum")
if isinstance(exclusive_minimum, int | float):
param_doc["exclusiveMinimum"] = exclusive_minimum
exclusive_maximum = param_schema.get("exclusiveMaximum")
if isinstance(exclusive_maximum, int | float):
param_doc["exclusiveMaximum"] = exclusive_maximum
min_length = param_schema.get("minLength")
if isinstance(min_length, int):
param_doc["minLength"] = min_length
@ -240,6 +280,10 @@ def _query_param_from_property(property_schema: Mapping[str, Any], *, required:
if isinstance(max_length, int):
param_doc["maxLength"] = max_length
pattern = param_schema.get("pattern")
if isinstance(pattern, str):
param_doc["pattern"] = pattern
min_items = param_schema.get("minItems")
if isinstance(min_items, int):
param_doc["minItems"] = min_items
@ -248,9 +292,31 @@ def _query_param_from_property(property_schema: Mapping[str, Any], *, required:
if isinstance(max_items, int):
param_doc["maxItems"] = max_items
unique_items = param_schema.get("uniqueItems")
if isinstance(unique_items, bool):
param_doc["uniqueItems"] = unique_items
multiple_of = param_schema.get("multipleOf")
if isinstance(multiple_of, int | float):
param_doc["multipleOf"] = multiple_of
return param_doc
def _resolve_schema_ref(property_schema: Mapping[str, Any], definitions: Mapping[str, Any]) -> Mapping[str, Any]:
ref = property_schema.get("$ref")
if not isinstance(ref, str):
return property_schema
ref_name = ref.rsplit("/", 1)[-1]
resolved = definitions.get(ref_name)
if not isinstance(resolved, Mapping):
return property_schema
property_without_ref = {key: value for key, value in property_schema.items() if key != "$ref"}
return {**resolved, **property_without_ref}
def _nullable_property_schema(property_schema: Mapping[str, Any]) -> Mapping[str, Any]:
any_of = property_schema.get("anyOf")
if not isinstance(any_of, list):

View File

@ -2,7 +2,7 @@ from flask import request
from flask_restx import Resource, fields
from pydantic import BaseModel, Field
from controllers.common.schema import DEFAULT_REF_TEMPLATE_OPENAPI_3_0
from controllers.common.schema import DEFAULT_REF_TEMPLATE_OPENAPI_3_0, query_params_from_model
from controllers.console import console_ns
from controllers.console.wraps import account_initialization_required, setup_required
from libs.login import login_required
@ -26,7 +26,7 @@ console_ns.schema_model(
class AdvancedPromptTemplateList(Resource):
@console_ns.doc("get_advanced_prompt_templates")
@console_ns.doc(description="Get advanced prompt templates based on app mode and model configuration")
@console_ns.expect(console_ns.models[AdvancedPromptTemplateQuery.__name__])
@console_ns.doc(params=query_params_from_model(AdvancedPromptTemplateQuery))
@console_ns.response(
200, "Prompt templates retrieved successfully", fields.List(fields.Raw(description="Prompt template data"))
)

View File

@ -2,7 +2,7 @@ from flask import request
from flask_restx import Resource, fields
from pydantic import BaseModel, Field, field_validator
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required, with_current_user
@ -36,7 +36,7 @@ class AgentLogApi(Resource):
@console_ns.doc("get_agent_logs")
@console_ns.doc(description="Get agent execution logs for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[AgentLogQuery.__name__])
@console_ns.doc(params=query_params_from_model(AgentLogQuery))
@console_ns.response(
200, "Agent logs retrieved successfully", fields.List(fields.Raw(description="Agent log entries"))
)

View File

@ -6,7 +6,7 @@ from flask_restx import Resource
from pydantic import BaseModel, Field, TypeAdapter, field_validator
from controllers.common.errors import NoFileUploadedError, TooManyFilesError
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.wraps import (
account_initialization_required,
@ -77,6 +77,11 @@ class AnnotationReplyStatusQuery(BaseModel):
action: Literal["enable", "disable"]
class AnnotationHitHistoryListQuery(BaseModel):
page: int = Field(default=1, ge=1, description="Page number")
limit: int = Field(default=20, ge=1, description="Page size")
class AnnotationFilePayload(BaseModel):
message_id: str = Field(..., description="Message ID")
@ -99,6 +104,7 @@ register_schema_models(
CreateAnnotationPayload,
UpdateAnnotationPayload,
AnnotationReplyStatusQuery,
AnnotationHitHistoryListQuery,
AnnotationFilePayload,
)
@ -204,7 +210,7 @@ class AnnotationApi(Resource):
@console_ns.doc("list_annotations")
@console_ns.doc(description="Get annotations for an app with pagination")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[AnnotationListQuery.__name__])
@console_ns.doc(params=query_params_from_model(AnnotationListQuery))
@console_ns.response(200, "Annotations retrieved successfully")
@console_ns.response(403, "Insufficient permissions")
@setup_required
@ -424,11 +430,7 @@ class AnnotationHitHistoryListApi(Resource):
@console_ns.doc("list_annotation_hit_histories")
@console_ns.doc(description="Get hit histories for an annotation")
@console_ns.doc(params={"app_id": "Application ID", "annotation_id": "Annotation ID"})
@console_ns.expect(
console_ns.parser()
.add_argument("page", type=int, location="args", default=1, help="Page number")
.add_argument("limit", type=int, location="args", default=20, help="Page size")
)
@console_ns.doc(params=query_params_from_model(AnnotationHitHistoryListQuery))
@console_ns.response(
200,
"Hit histories retrieved successfully",

View File

@ -14,7 +14,12 @@ from werkzeug.exceptions import BadRequest
from controllers.common.fields import RedirectUrlResponse, SimpleResultResponse
from controllers.common.helpers import FileInfo
from controllers.common.schema import register_enum_models, register_response_schema_models, register_schema_models
from controllers.common.schema import (
query_params_from_model,
register_enum_models,
register_response_schema_models,
register_schema_models,
)
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model, with_session
from controllers.console.workspace.models import LoadBalancingPayload
@ -495,7 +500,7 @@ register_schema_models(
class AppListApi(Resource):
@console_ns.doc("list_apps")
@console_ns.doc(description="Get list of applications with pagination and filtering")
@console_ns.expect(console_ns.models[AppListQuery.__name__])
@console_ns.doc(params=query_params_from_model(AppListQuery))
@console_ns.response(200, "Success", console_ns.models[AppPagination.__name__])
@setup_required
@login_required
@ -737,7 +742,7 @@ class AppExportApi(Resource):
@console_ns.doc("export_app")
@console_ns.doc(description="Export application configuration as DSL")
@console_ns.doc(params={"app_id": "Application ID to export"})
@console_ns.expect(console_ns.models[AppExportQuery.__name__])
@console_ns.doc(params=query_params_from_model(AppExportQuery))
@console_ns.response(200, "App exported successfully", console_ns.models[AppExportResponse.__name__])
@console_ns.response(403, "Insufficient permissions")
@get_app_model

View File

@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
from werkzeug.exceptions import InternalServerError
import services
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.error import (
AppUnavailableError,
@ -162,7 +162,7 @@ class TextModesApi(Resource):
@console_ns.doc("get_text_to_speech_voices")
@console_ns.doc(description="Get available TTS voices for a specific language")
@console_ns.doc(params={"app_id": "App ID"})
@console_ns.expect(console_ns.models[TextToSpeechVoiceQuery.__name__])
@console_ns.doc(params=query_params_from_model(TextToSpeechVoiceQuery))
@console_ns.response(
200, "TTS voices retrieved successfully", fields.List(fields.Raw(description="Available voices"))
)

View File

@ -9,7 +9,7 @@ from sqlalchemy import func, or_
from sqlalchemy.orm import selectinload
from werkzeug.exceptions import NotFound
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import (
@ -91,7 +91,7 @@ class CompletionConversationApi(Resource):
@console_ns.doc("list_completion_conversations")
@console_ns.doc(description="Get completion conversations with pagination and filtering")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[CompletionConversationQuery.__name__])
@console_ns.doc(params=query_params_from_model(CompletionConversationQuery))
@console_ns.response(200, "Success", console_ns.models[ConversationPaginationResponse.__name__])
@console_ns.response(403, "Insufficient permissions")
@setup_required
@ -206,7 +206,7 @@ class ChatConversationApi(Resource):
@console_ns.doc("list_chat_conversations")
@console_ns.doc(description="Get chat conversations with pagination, filtering and summary")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[ChatConversationQuery.__name__])
@console_ns.doc(params=query_params_from_model(ChatConversationQuery))
@console_ns.response(200, "Success", console_ns.models[ConversationWithSummaryPaginationResponse.__name__])
@console_ns.response(403, "Insufficient permissions")
@setup_required

View File

@ -9,7 +9,7 @@ from pydantic import BaseModel, Field, field_validator
from sqlalchemy import select
from sqlalchemy.orm import sessionmaker
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required
@ -84,7 +84,7 @@ class ConversationVariablesApi(Resource):
@console_ns.doc("get_conversation_variables")
@console_ns.doc(description="Get conversation variables for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[ConversationVariablesQuery.__name__])
@console_ns.doc(params=query_params_from_model(ConversationVariablesQuery))
@console_ns.response(
200,
"Conversation variables retrieved successfully",

View File

@ -11,7 +11,7 @@ from werkzeug.exceptions import InternalServerError, NotFound
from controllers.common.controller_schemas import MessageFeedbackPayload as _MessageFeedbackPayloadBase
from controllers.common.fields import SimpleResultResponse
from controllers.common.schema import register_response_schema_models, register_schema_models
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.app.error import (
CompletionRequestError,
@ -174,7 +174,7 @@ class ChatMessageListApi(Resource):
@console_ns.doc("list_chat_messages")
@console_ns.doc(description="Get chat messages for a conversation with pagination")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[ChatMessagesQuery.__name__])
@console_ns.doc(params=query_params_from_model(ChatMessagesQuery))
@console_ns.response(200, "Success", console_ns.models[MessageInfiniteScrollPaginationResponse.__name__])
@console_ns.response(404, "Conversation not found")
@login_required
@ -372,7 +372,7 @@ class MessageFeedbackExportApi(Resource):
@console_ns.doc("export_feedbacks")
@console_ns.doc(description="Export user feedback data for Google Sheets")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[FeedbackExportQuery.__name__])
@console_ns.doc(params=query_params_from_model(FeedbackExportQuery))
@console_ns.response(200, "Feedback data exported successfully")
@console_ns.response(400, "Invalid parameters")
@console_ns.response(500, "Internal server error")

View File

@ -5,7 +5,7 @@ from flask_restx import Resource, fields
from pydantic import BaseModel, Field
from werkzeug.exceptions import BadRequest
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.error import TracingConfigCheckError, TracingConfigIsExist, TracingConfigNotExist
from controllers.console.app.wraps import get_app_model
@ -36,7 +36,7 @@ class TraceAppConfigApi(Resource):
@console_ns.doc("get_trace_app_config")
@console_ns.doc(description="Get tracing configuration for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[TraceProviderQuery.__name__])
@console_ns.doc(params=query_params_from_model(TraceProviderQuery))
@console_ns.response(
200, "Tracing configuration retrieved successfully", fields.Raw(description="Tracing configuration data")
)

View File

@ -5,7 +5,7 @@ from flask import abort, jsonify, request
from flask_restx import Resource, fields
from pydantic import BaseModel, Field, field_validator
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required, with_current_user
@ -39,7 +39,7 @@ class DailyMessageStatistic(Resource):
@console_ns.doc("get_daily_message_statistics")
@console_ns.doc(description="Get daily message statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Daily message statistics retrieved successfully",
@ -99,7 +99,7 @@ class DailyConversationStatistic(Resource):
@console_ns.doc("get_daily_conversation_statistics")
@console_ns.doc(description="Get daily conversation statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Daily conversation statistics retrieved successfully",
@ -158,7 +158,7 @@ class DailyTerminalsStatistic(Resource):
@console_ns.doc("get_daily_terminals_statistics")
@console_ns.doc(description="Get daily terminal/end-user statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Daily terminal statistics retrieved successfully",
@ -218,7 +218,7 @@ class DailyTokenCostStatistic(Resource):
@console_ns.doc("get_daily_token_cost_statistics")
@console_ns.doc(description="Get daily token cost statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Daily token cost statistics retrieved successfully",
@ -281,7 +281,7 @@ class AverageSessionInteractionStatistic(Resource):
@console_ns.doc("get_average_session_interaction_statistics")
@console_ns.doc(description="Get average session interaction statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Average session interaction statistics retrieved successfully",
@ -360,7 +360,7 @@ class UserSatisfactionRateStatistic(Resource):
@console_ns.doc("get_user_satisfaction_rate_statistics")
@console_ns.doc(description="Get user satisfaction rate statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"User satisfaction rate statistics retrieved successfully",
@ -429,7 +429,7 @@ class AverageResponseTimeStatistic(Resource):
@console_ns.doc("get_average_response_time_statistics")
@console_ns.doc(description="Get average response time statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Average response time statistics retrieved successfully",
@ -489,7 +489,7 @@ class TokensPerSecondStatistic(Resource):
@console_ns.doc("get_tokens_per_second_statistics")
@console_ns.doc(description="Get tokens per second statistics for an application")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[StatisticTimeRangeQuery.__name__])
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
@console_ns.response(
200,
"Tokens per second statistics retrieved successfully",

View File

@ -15,6 +15,7 @@ from controllers.common.controller_schemas import DefaultBlockConfigQuery, Workf
from controllers.common.errors import InvalidArgumentError
from controllers.common.fields import NewAppResponse, SimpleResultResponse
from controllers.common.schema import (
query_params_from_model,
register_response_schema_model,
register_response_schema_models,
register_schema_models,
@ -1054,7 +1055,7 @@ class DefaultBlockConfigApi(Resource):
@console_ns.doc(params={"app_id": "Application ID", "block_type": "Block type"})
@console_ns.response(200, "Default block configuration retrieved successfully")
@console_ns.response(404, "Block type not found")
@console_ns.expect(console_ns.models[DefaultBlockConfigQuery.__name__])
@console_ns.doc(params=query_params_from_model(DefaultBlockConfigQuery))
@setup_required
@login_required
@account_initialization_required
@ -1149,7 +1150,7 @@ class WorkflowFeaturesApi(Resource):
@console_ns.route("/apps/<uuid:app_id>/workflows")
class PublishedAllWorkflowApi(Resource):
@console_ns.expect(console_ns.models[WorkflowListQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowListQuery))
@console_ns.doc("get_all_published_workflows")
@console_ns.doc(description="Get all published workflows for an application")
@console_ns.doc(params={"app_id": "Application ID"})

View File

@ -7,7 +7,7 @@ from flask_restx import Resource
from pydantic import BaseModel, Field, field_validator
from sqlalchemy.orm import sessionmaker
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required
@ -166,7 +166,7 @@ class WorkflowAppLogApi(Resource):
@console_ns.doc("get_workflow_app_logs")
@console_ns.doc(description="Get workflow application execution logs")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[WorkflowAppLogQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowAppLogQuery))
@console_ns.response(
200,
"Workflow app logs retrieved successfully",
@ -209,7 +209,7 @@ class WorkflowArchivedLogApi(Resource):
@console_ns.doc("get_workflow_archived_logs")
@console_ns.doc(description="Get workflow archived execution logs")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[WorkflowAppLogQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowAppLogQuery))
@console_ns.response(
200,
"Workflow archived logs retrieved successfully",

View File

@ -10,7 +10,7 @@ from pydantic import BaseModel, Field
from sqlalchemy.orm import sessionmaker
from controllers.common.errors import InvalidArgumentError, NotFoundError
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.error import (
DraftWorkflowNotExist,
@ -248,7 +248,7 @@ def _api_prerequisite[T, **P, R](
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/variables")
class WorkflowVariableCollectionApi(Resource):
@console_ns.expect(console_ns.models[WorkflowDraftVariableListQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowDraftVariableListQuery))
@console_ns.doc("get_workflow_variables")
@console_ns.doc(description="Get draft workflow variables")
@console_ns.doc(params={"app_id": "Application ID"})

View File

@ -3,7 +3,7 @@ from flask_restx import Resource
from pydantic import BaseModel, Field, field_validator
from sqlalchemy.orm import sessionmaker
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required, with_current_user
@ -41,7 +41,7 @@ class WorkflowDailyRunsStatistic(Resource):
@console_ns.doc("get_workflow_daily_runs_statistic")
@console_ns.doc(description="Get workflow daily runs statistics")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[WorkflowStatisticQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowStatisticQuery))
@console_ns.response(200, "Daily runs statistics retrieved successfully")
@get_app_model
@setup_required
@ -80,7 +80,7 @@ class WorkflowDailyTerminalsStatistic(Resource):
@console_ns.doc("get_workflow_daily_terminals_statistic")
@console_ns.doc(description="Get workflow daily terminals statistics")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[WorkflowStatisticQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowStatisticQuery))
@console_ns.response(200, "Daily terminals statistics retrieved successfully")
@get_app_model
@setup_required
@ -119,7 +119,7 @@ class WorkflowDailyTokenCostStatistic(Resource):
@console_ns.doc("get_workflow_daily_token_cost_statistic")
@console_ns.doc(description="Get workflow daily token cost statistics")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[WorkflowStatisticQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowStatisticQuery))
@console_ns.response(200, "Daily token cost statistics retrieved successfully")
@get_app_model
@setup_required
@ -158,7 +158,7 @@ class WorkflowAverageAppInteractionStatistic(Resource):
@console_ns.doc("get_workflow_average_app_interaction_statistic")
@console_ns.doc(description="Get workflow average app interaction statistics")
@console_ns.doc(params={"app_id": "Application ID"})
@console_ns.expect(console_ns.models[WorkflowStatisticQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowStatisticQuery))
@console_ns.response(200, "Average app interaction statistics retrieved successfully")
@setup_required
@login_required

View File

@ -9,7 +9,7 @@ from sqlalchemy.orm import sessionmaker
from werkzeug.exceptions import NotFound
from configs import dify_config
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from extensions.ext_database import db
from fields.base import ResponseModel
from libs.login import login_required
@ -86,7 +86,7 @@ register_schema_models(
class WebhookTriggerApi(Resource):
"""Webhook Trigger API"""
@console_ns.expect(console_ns.models[Parser.__name__])
@console_ns.doc(params=query_params_from_model(Parser))
@setup_required
@login_required
@account_initialization_required

View File

@ -4,7 +4,7 @@ from pydantic import BaseModel, Field, field_validator
from configs import dify_config
from constants.languages import supported_language
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.error import AccountInFreezeError, AlreadyActivateError
from extensions.ext_database import db
@ -69,7 +69,7 @@ register_schema_models(
class ActivateCheckApi(Resource):
@console_ns.doc("check_activation_token")
@console_ns.doc(description="Check if activation token is valid")
@console_ns.expect(console_ns.models[ActivateCheckQuery.__name__])
@console_ns.doc(params=query_params_from_model(ActivateCheckQuery))
@console_ns.response(
200,
"Success",

View File

@ -2,6 +2,7 @@ from flask import request
from flask_restx import Resource
from pydantic import BaseModel, Field
from controllers.common.schema import query_params_from_model
from libs.helper import extract_remote_ip
from libs.login import login_required
from models import Account
@ -30,7 +31,7 @@ console_ns.schema_model(
@console_ns.route("/compliance/download")
class ComplianceApi(Resource):
@console_ns.expect(console_ns.models[ComplianceDownloadQuery.__name__])
@console_ns.doc(params=query_params_from_model(ComplianceDownloadQuery))
@console_ns.doc("download_compliance_document")
@console_ns.doc(description="Get compliance document download link")
@setup_required

View File

@ -4,7 +4,7 @@ from flask import request
from flask_restx import Resource
from pydantic import BaseModel
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.datasets.error import WebsiteCrawlError
from controllers.console.wraps import account_initialization_required, setup_required
@ -57,7 +57,7 @@ class WebsiteCrawlStatusApi(Resource):
@console_ns.doc("get_crawl_status")
@console_ns.doc(description="Get website crawl status")
@console_ns.doc(params={"job_id": "Crawl job ID", "provider": "Crawl provider (firecrawl/watercrawl/jinareader)"})
@console_ns.expect(console_ns.models[WebsiteCrawlStatusQuery.__name__])
@console_ns.doc(params=query_params_from_model(WebsiteCrawlStatusQuery))
@console_ns.response(200, "Crawl status retrieved successfully")
@console_ns.response(404, "Crawl job not found")
@console_ns.response(400, "Invalid provider")

View File

@ -7,7 +7,7 @@ from sqlalchemy.orm import sessionmaker
from werkzeug.exceptions import NotFound
from controllers.common.controller_schemas import ConversationRenamePayload
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.console.app.error import AppUnavailableError
from controllers.console.explore.error import NotChatAppError
from controllers.console.explore.wraps import InstalledAppResource
@ -44,7 +44,7 @@ register_response_schema_models(console_ns, ResultResponse)
endpoint="installed_app_conversations",
)
class ConversationListApi(InstalledAppResource):
@console_ns.expect(console_ns.models[ConversationListQuery.__name__])
@console_ns.doc(params=query_params_from_model(ConversationListQuery))
@with_current_user
def get(self, current_user: Account, installed_app: InstalledApp):
app_model = installed_app.app

View File

@ -7,7 +7,7 @@ from pydantic import BaseModel, TypeAdapter
from werkzeug.exceptions import InternalServerError, NotFound
from controllers.common.controller_schemas import MessageFeedbackPayload, MessageListQuery
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.console.app.error import (
AppMoreLikeThisDisabledError,
AppUnavailableError,
@ -60,7 +60,7 @@ register_response_schema_models(console_ns, ResultResponse, SuggestedQuestionsRe
endpoint="installed_app_messages",
)
class MessageListApi(InstalledAppResource):
@console_ns.expect(console_ns.models[MessageListQuery.__name__])
@console_ns.doc(params=query_params_from_model(MessageListQuery))
@with_current_user
def get(self, current_user: Account, installed_app: InstalledApp):
app_model = installed_app.app
@ -129,7 +129,7 @@ class MessageFeedbackApi(InstalledAppResource):
endpoint="installed_app_more_like_this",
)
class MessageMoreLikeThisApi(InstalledAppResource):
@console_ns.expect(console_ns.models[MoreLikeThisQuery.__name__])
@console_ns.doc(params=query_params_from_model(MoreLikeThisQuery))
@with_current_user
def get(self, current_user: Account, installed_app: InstalledApp, message_id: UUID):
app_model = installed_app.app

View File

@ -5,7 +5,7 @@ from pydantic import TypeAdapter
from werkzeug.exceptions import NotFound
from controllers.common.controller_schemas import SavedMessageCreatePayload, SavedMessageListQuery
from controllers.common.schema import register_response_schema_models, register_schema_models
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.app.error import AppUnavailableError
from controllers.console.explore.error import NotCompletionAppError
@ -24,7 +24,7 @@ register_response_schema_models(console_ns, ResultResponse)
@console_ns.route("/installed-apps/<uuid:installed_app_id>/saved-messages", endpoint="installed_app_saved_messages")
class SavedMessageListApi(InstalledAppResource):
@console_ns.expect(console_ns.models[SavedMessageListQuery.__name__])
@console_ns.doc(params=query_params_from_model(SavedMessageListQuery))
@with_current_user
def get(self, current_user: Account, installed_app: InstalledApp):
app_model = installed_app.app

View File

@ -8,7 +8,7 @@ from pydantic import Field
from sqlalchemy.orm import Session, sessionmaker
from werkzeug.exceptions import BadRequest, InternalServerError, NotFound
from controllers.common.schema import register_response_schema_models, register_schema_models
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.app.error import DraftWorkflowNotExist, DraftWorkflowNotSync
from controllers.console.app.workflow import (
@ -283,7 +283,7 @@ class SnippetDefaultBlockConfigsApi(Resource):
@console_ns.route("/snippets/<uuid:snippet_id>/workflows")
class SnippetPublishedAllWorkflowApi(Resource):
@console_ns.expect(console_ns.models[SnippetWorkflowListQuery.__name__])
@console_ns.doc(params=query_params_from_model(SnippetWorkflowListQuery))
@console_ns.doc("get_all_snippet_published_workflows")
@console_ns.doc(description="Get all published workflows for a snippet")
@console_ns.doc(params={"snippet_id": "Snippet ID"})

View File

@ -19,6 +19,7 @@ from flask_restx import Resource, marshal, marshal_with
from sqlalchemy.orm import Session, sessionmaker
from controllers.common.errors import InvalidArgumentError, NotFoundError
from controllers.common.schema import query_params_from_model
from controllers.console import console_ns
from controllers.console.app.error import DraftWorkflowNotExist
from controllers.console.app.workflow_draft_variable import (
@ -90,7 +91,7 @@ def _snippet_draft_var_prerequisite[T, **P, R](
@console_ns.route("/snippets/<uuid:snippet_id>/workflows/draft/variables")
class SnippetWorkflowVariableCollectionApi(Resource):
@console_ns.expect(console_ns.models[WorkflowDraftVariableListQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkflowDraftVariableListQuery))
@console_ns.doc("get_snippet_workflow_variables")
@console_ns.doc(description="List draft workflow variables without values (paginated, snippet scope)")
@console_ns.response(

View File

@ -585,7 +585,7 @@ class EducationApi(Resource):
@console_ns.route("/account/education/autocomplete")
class EducationAutoCompleteApi(Resource):
@console_ns.expect(console_ns.models[EducationAutocompleteQuery.__name__])
@console_ns.doc(params=query_params_from_model(EducationAutocompleteQuery))
@setup_required
@login_required
@account_initialization_required

View File

@ -12,7 +12,7 @@ from flask import request
from flask_restx import Resource
from pydantic import BaseModel, Field
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.wraps import (
account_initialization_required,
@ -201,7 +201,7 @@ class DeprecatedEndpointCreateApi(Resource):
class EndpointListApi(Resource):
@console_ns.doc("list_endpoints")
@console_ns.doc(description="List plugin endpoints with pagination")
@console_ns.expect(console_ns.models[EndpointListQuery.__name__])
@console_ns.doc(params=query_params_from_model(EndpointListQuery))
@console_ns.response(
200,
"Success",
@ -234,7 +234,7 @@ class EndpointListApi(Resource):
class EndpointListForSinglePluginApi(Resource):
@console_ns.doc("list_plugin_endpoints")
@console_ns.doc(description="List endpoints for a specific plugin")
@console_ns.expect(console_ns.models[EndpointListForPluginQuery.__name__])
@console_ns.doc(params=query_params_from_model(EndpointListForPluginQuery))
@console_ns.response(
200,
"Success",

View File

@ -6,7 +6,7 @@ from flask_restx import Resource
from pydantic import BaseModel, Field, field_validator
from controllers.common.fields import SimpleResultResponse
from controllers.common.schema import register_response_schema_models, register_schema_models
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 (
account_initialization_required,
@ -98,7 +98,7 @@ register_response_schema_models(console_ns, SimpleResultResponse)
@console_ns.route("/workspaces/current/model-providers")
class ModelProviderListApi(Resource):
@console_ns.expect(console_ns.models[ParserModelList.__name__])
@console_ns.doc(params=query_params_from_model(ParserModelList))
@setup_required
@login_required
@account_initialization_required
@ -115,7 +115,7 @@ class ModelProviderListApi(Resource):
@console_ns.route("/workspaces/current/model-providers/<path:provider>/credentials")
class ModelProviderCredentialApi(Resource):
@console_ns.expect(console_ns.models[ParserCredentialId.__name__])
@console_ns.doc(params=query_params_from_model(ParserCredentialId))
@setup_required
@login_required
@account_initialization_required

View File

@ -6,7 +6,12 @@ from flask_restx import Resource
from pydantic import BaseModel, Field, field_validator
from controllers.common.fields import SimpleResultResponse
from controllers.common.schema import register_enum_models, register_response_schema_models, register_schema_models
from controllers.common.schema import (
query_params_from_model,
register_enum_models,
register_response_schema_models,
register_schema_models,
)
from controllers.console import console_ns
from controllers.console.wraps import (
account_initialization_required,
@ -141,7 +146,7 @@ register_enum_models(console_ns, ModelType)
@console_ns.route("/workspaces/current/default-model")
class DefaultModelApi(Resource):
@console_ns.expect(console_ns.models[ParserGetDefault.__name__])
@console_ns.doc(params=query_params_from_model(ParserGetDefault))
@setup_required
@login_required
@account_initialization_required
@ -267,7 +272,7 @@ class ModelProviderModelApi(Resource):
@console_ns.route("/workspaces/current/model-providers/<path:provider>/models/credentials")
class ModelProviderModelCredentialApi(Resource):
@console_ns.expect(console_ns.models[ParserGetCredentials.__name__])
@console_ns.doc(params=query_params_from_model(ParserGetCredentials))
@setup_required
@login_required
@account_initialization_required
@ -515,7 +520,7 @@ class ModelProviderModelValidateApi(Resource):
@console_ns.route("/workspaces/current/model-providers/<path:provider>/models/parameter-rules")
class ModelProviderModelParameterRuleApi(Resource):
@console_ns.expect(console_ns.models[ParserParameter.__name__])
@console_ns.doc(params=query_params_from_model(ParserParameter))
@setup_required
@login_required
@account_initialization_required

View File

@ -10,7 +10,12 @@ from werkzeug.exceptions import Forbidden
from configs import dify_config
from controllers.common.fields import SuccessResponse
from controllers.common.schema import register_enum_models, register_response_schema_models, register_schema_models
from controllers.common.schema import (
query_params_from_model,
register_enum_models,
register_response_schema_models,
register_schema_models,
)
from controllers.console import console_ns
from controllers.console.workspace import plugin_permission_required
from controllers.console.wraps import (
@ -221,7 +226,7 @@ class PluginDebuggingKeyApi(Resource):
@console_ns.route("/workspaces/current/plugin/list")
class PluginListApi(Resource):
@console_ns.expect(console_ns.models[ParserList.__name__])
@console_ns.doc(params=query_params_from_model(ParserList))
@setup_required
@login_required
@account_initialization_required
@ -274,7 +279,7 @@ class PluginListInstallationsFromIdsApi(Resource):
@console_ns.route("/workspaces/current/plugin/icon")
class PluginIconApi(Resource):
@console_ns.expect(console_ns.models[ParserIcon.__name__])
@console_ns.doc(params=query_params_from_model(ParserIcon))
@setup_required
def get(self):
args = ParserIcon.model_validate(request.args.to_dict(flat=True))
@ -290,7 +295,7 @@ class PluginIconApi(Resource):
@console_ns.route("/workspaces/current/plugin/asset")
class PluginAssetApi(Resource):
@console_ns.expect(console_ns.models[ParserAsset.__name__])
@console_ns.doc(params=query_params_from_model(ParserAsset))
@setup_required
@login_required
@account_initialization_required
@ -425,7 +430,7 @@ class PluginInstallFromMarketplaceApi(Resource):
@console_ns.route("/workspaces/current/plugin/marketplace/pkg")
class PluginFetchMarketplacePkgApi(Resource):
@console_ns.expect(console_ns.models[ParserPluginIdentifierQuery.__name__])
@console_ns.doc(params=query_params_from_model(ParserPluginIdentifierQuery))
@setup_required
@login_required
@account_initialization_required
@ -449,7 +454,7 @@ class PluginFetchMarketplacePkgApi(Resource):
@console_ns.route("/workspaces/current/plugin/fetch-manifest")
class PluginFetchManifestApi(Resource):
@console_ns.expect(console_ns.models[ParserPluginIdentifierQuery.__name__])
@console_ns.doc(params=query_params_from_model(ParserPluginIdentifierQuery))
@setup_required
@login_required
@account_initialization_required
@ -468,7 +473,7 @@ class PluginFetchManifestApi(Resource):
@console_ns.route("/workspaces/current/plugin/tasks")
class PluginFetchInstallTasksApi(Resource):
@console_ns.expect(console_ns.models[ParserTasks.__name__])
@console_ns.doc(params=query_params_from_model(ParserTasks))
@setup_required
@login_required
@account_initialization_required
@ -655,7 +660,7 @@ class PluginFetchPermissionApi(Resource):
@console_ns.route("/workspaces/current/plugin/parameters/dynamic-options")
class PluginFetchDynamicSelectOptionsApi(Resource):
@console_ns.expect(console_ns.models[ParserDynamicOptions.__name__])
@console_ns.doc(params=query_params_from_model(ParserDynamicOptions))
@setup_required
@login_required
@is_admin_or_owner_required
@ -817,7 +822,7 @@ class PluginAutoUpgradeExcludePluginApi(Resource):
@console_ns.route("/workspaces/current/plugin/readme")
class PluginReadmeApi(Resource):
@console_ns.expect(console_ns.models[ParserReadme.__name__])
@console_ns.doc(params=query_params_from_model(ParserReadme))
@setup_required
@login_required
@account_initialization_required

View File

@ -8,7 +8,7 @@ from sqlalchemy.orm import Session, sessionmaker
from werkzeug.datastructures import MultiDict
from werkzeug.exceptions import NotFound
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.console import console_ns
from controllers.console.snippets.payloads import (
CreateSnippetPayload,
@ -89,7 +89,7 @@ snippet_pagination_model = console_ns.model("SnippetPagination", snippet_paginat
@console_ns.route("/workspaces/current/customized-snippets")
class CustomizedSnippetsApi(Resource):
@console_ns.doc("list_customized_snippets")
@console_ns.expect(console_ns.models.get(SnippetListQuery.__name__))
@console_ns.doc(params=query_params_from_model(SnippetListQuery))
@console_ns.response(200, "Snippets retrieved successfully", snippet_pagination_model)
@setup_required
@login_required

View File

@ -16,7 +16,7 @@ from controllers.common.errors import (
TooManyFilesError,
UnsupportedFileTypeError,
)
from controllers.common.schema import register_response_schema_models, register_schema_models
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.admin import admin_required
from controllers.console.error import AccountNotLinkTenantError
@ -201,7 +201,7 @@ class TenantListApi(Resource):
@console_ns.route("/all-workspaces")
class WorkspaceListApi(Resource):
@console_ns.expect(console_ns.models[WorkspaceListQuery.__name__])
@console_ns.doc(params=query_params_from_model(WorkspaceListQuery))
@setup_required
@admin_required
def get(self):

View File

@ -10,7 +10,7 @@ from werkzeug.exceptions import BadRequest, NotFound
import services
from controllers.common.controller_schemas import ConversationRenamePayload
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_schema_models
from controllers.service_api import service_api_ns
from controllers.service_api.app.error import NotChatAppError
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
@ -138,7 +138,7 @@ register_schema_models(
@service_api_ns.route("/conversations")
class ConversationApi(Resource):
@service_api_ns.expect(service_api_ns.models[ConversationListQuery.__name__])
@service_api_ns.doc(params=query_params_from_model(ConversationListQuery))
@service_api_ns.doc("list_conversations")
@service_api_ns.doc(description="List all conversations for the current user")
@service_api_ns.doc(
@ -250,7 +250,7 @@ class ConversationRenameApi(Resource):
@service_api_ns.route("/conversations/<uuid:c_id>/variables")
class ConversationVariablesApi(Resource):
@service_api_ns.expect(service_api_ns.models[ConversationVariablesQuery.__name__])
@service_api_ns.doc(params=query_params_from_model(ConversationVariablesQuery))
@service_api_ns.doc("list_conversation_variables")
@service_api_ns.doc(description="List all variables for a conversation")
@service_api_ns.doc(params={"c_id": "Conversation ID"})

View File

@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from sqlalchemy import select
from controllers.common.file_response import enforce_download_for_html
from controllers.common.schema import register_schema_model
from controllers.common.schema import query_params_from_model, register_schema_model
from controllers.service_api import service_api_ns
from controllers.service_api.app.error import (
FileAccessDeniedError,
@ -38,7 +38,7 @@ class FilePreviewApi(Resource):
Files can only be accessed if they belong to messages within the requesting app's context.
"""
@service_api_ns.expect(service_api_ns.models[FilePreviewQuery.__name__])
@service_api_ns.doc(params=query_params_from_model(FilePreviewQuery))
@service_api_ns.doc("preview_file")
@service_api_ns.doc(description="Preview or download a file uploaded via Service API")
@service_api_ns.doc(params={"file_id": "UUID of the file to preview"})

View File

@ -9,7 +9,7 @@ from werkzeug.exceptions import BadRequest, InternalServerError, NotFound
import services
from controllers.common.controller_schemas import MessageFeedbackPayload, MessageListQuery
from controllers.common.fields import SimpleResultStringListResponse
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.service_api import service_api_ns
from controllers.service_api.app.error import NotChatAppError
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
@ -39,7 +39,7 @@ register_response_schema_models(service_api_ns, ResultResponse, SimpleResultStri
@service_api_ns.route("/messages")
class MessageListApi(Resource):
@service_api_ns.expect(service_api_ns.models[MessageListQuery.__name__])
@service_api_ns.doc(params=query_params_from_model(MessageListQuery))
@service_api_ns.doc("list_messages")
@service_api_ns.doc(description="List messages in a conversation")
@service_api_ns.doc(
@ -120,7 +120,7 @@ class MessageFeedbackApi(Resource):
@service_api_ns.route("/app/feedbacks")
class AppGetFeedbacksApi(Resource):
@service_api_ns.expect(service_api_ns.models[FeedbackListQuery.__name__])
@service_api_ns.doc(params=query_params_from_model(FeedbackListQuery))
@service_api_ns.doc("get_app_feedbacks")
@service_api_ns.doc(description="Get all feedbacks for the application")
@service_api_ns.doc(

View File

@ -12,7 +12,7 @@ from werkzeug.exceptions import BadRequest, InternalServerError, NotFound
from controllers.common.controller_schemas import WorkflowRunPayload as WorkflowRunPayloadBase
from controllers.common.fields import SimpleResultResponse
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.service_api import service_api_ns
from controllers.service_api.app.error import (
CompletionRequestError,
@ -407,7 +407,7 @@ class WorkflowTaskStopApi(Resource):
@service_api_ns.route("/workflows/logs")
class WorkflowAppLogApi(Resource):
@service_api_ns.expect(service_api_ns.models[WorkflowLogQuery.__name__])
@service_api_ns.doc(params=query_params_from_model(WorkflowLogQuery))
@service_api_ns.doc("get_workflow_logs")
@service_api_ns.doc(description="Get workflow execution logs")
@service_api_ns.doc(

View File

@ -7,7 +7,7 @@ from pydantic import BaseModel, Field, TypeAdapter
from werkzeug.exceptions import InternalServerError, NotFound
from controllers.common.controller_schemas import MessageFeedbackPayload, MessageListQuery
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.web import web_ns
from controllers.web.error import (
AppMoreLikeThisDisabledError,
@ -156,7 +156,7 @@ class MessageFeedbackApi(WebApiResource):
class MessageMoreLikeThisApi(WebApiResource):
@web_ns.doc("Generate More Like This")
@web_ns.doc(description="Generate a new completion similar to an existing message (completion apps only).")
@web_ns.expect(web_ns.models[MessageMoreLikeThisQuery.__name__])
@web_ns.doc(params=query_params_from_model(MessageMoreLikeThisQuery))
@web_ns.doc(
responses={
200: "Success",

View File

@ -330,99 +330,6 @@ def _replace_legacy_refs(value: object) -> object:
HTTP_METHODS = {"delete", "get", "head", "options", "patch", "post", "put", "trace"}
def _resolve_component_schema(payload: dict[str, object], schema: object) -> dict[str, object] | None:
if not isinstance(schema, dict):
return None
ref = schema.get("$ref")
if isinstance(ref, str) and ref.startswith("#/components/schemas/"):
name = ref.removeprefix("#/components/schemas/")
components = payload.get("components")
if not isinstance(components, dict):
return None
schemas = components.get("schemas")
if not isinstance(schemas, dict):
return None
resolved = schemas.get(name)
return resolved if isinstance(resolved, dict) else None
return schema
def _request_body_schema(request_body: object) -> object | None:
if not isinstance(request_body, dict):
return None
content = request_body.get("content")
if not isinstance(content, dict):
return None
media_type = content.get("application/json")
if not isinstance(media_type, dict):
return None
return media_type.get("schema")
def _query_parameters_from_schema(schema: dict[str, object]) -> list[dict[str, object]]:
properties = schema.get("properties")
if not isinstance(properties, dict):
return []
required = schema.get("required")
required_names = set(required) if isinstance(required, list) else set()
parameters: list[dict[str, object]] = []
for name, property_schema in sorted(properties.items()):
if not isinstance(name, str) or not isinstance(property_schema, dict):
continue
schema_copy = dict(property_schema)
description = schema_copy.get("description")
parameter: dict[str, object] = {
"name": name,
"in": "query",
"required": name in required_names,
"schema": schema_copy,
}
if isinstance(description, str):
parameter["description"] = description
parameters.append(parameter)
return parameters
def _move_get_request_bodies_to_query_parameters(payload: dict[str, object]) -> dict[str, object]:
"""Represent GET request bodies as query parameters in exported specs."""
paths = payload.get("paths")
if not isinstance(paths, dict):
return payload
for path_item in paths.values():
if not isinstance(path_item, dict):
continue
operation = path_item.get("get")
if not isinstance(operation, dict) or "requestBody" not in operation:
continue
schema = _resolve_component_schema(payload, _request_body_schema(operation.get("requestBody")))
existing_parameters = operation.get("parameters")
parameters = list(existing_parameters) if isinstance(existing_parameters, list) else []
existing_query_names = {
parameter.get("name")
for parameter in parameters
if isinstance(parameter, dict) and parameter.get("in") == "query"
}
if schema is not None:
for parameter in _query_parameters_from_schema(schema):
if parameter["name"] not in existing_query_names:
parameters.append(parameter)
if parameters:
operation["parameters"] = parameters
operation.pop("requestBody", None)
return payload
def _deduplicate_operation_ids(payload: dict[str, object]) -> dict[str, object]:
"""Make operationId values unique while preserving already-unique IDs."""
@ -497,7 +404,6 @@ def generate_specs(output_dir: Path) -> list[Path]:
if not isinstance(payload, dict):
raise RuntimeError(f"unexpected response payload for {target.route}")
payload = _merge_registered_schemas(payload, target.namespace)
payload = _move_get_request_bodies_to_query_parameters(payload)
payload = _deduplicate_operation_ids(payload)
payload = drop_null_values(payload)
payload = sort_openapi_arrays(payload)

View File

@ -283,9 +283,9 @@ Check if activation token is valid
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| email | query | | No | |
| email | query | | No | string |
| token | query | | Yes | string |
| workspace_id | query | | No | |
| workspace_id | query | | No | string |
#### Responses
@ -570,13 +570,13 @@ Get list of applications with pagination and filtering
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| creator_ids | query | Filter by creator account IDs | No | |
| is_created_by_me | query | Filter by creator | No | |
| creator_ids | query | Filter by creator account IDs | No | [ string ] |
| is_created_by_me | query | Filter by creator | No | boolean |
| limit | query | Page size (1-100) | No | integer, <br>**Default:** 20 |
| mode | query | App mode filter | No | string, <br>**Available values:** "advanced-chat", "agent", "agent-chat", "all", "channel", "chat", "completion", "workflow", <br>**Default:** all |
| name | query | Filter by app name | No | |
| name | query | Filter by app name | No | string |
| page | query | Page number (1-99999) | No | integer, <br>**Default:** 1 |
| tag_ids | query | Filter by tag IDs | No | |
| tag_ids | query | Filter by tag IDs | No | [ string ] |
#### Responses
@ -1406,12 +1406,12 @@ Get chat conversations with pagination, filtering and summary
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| annotation_status | query | Annotation status filter | No | string, <br>**Available values:** "all", "annotated", "not_annotated", <br>**Default:** all |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| keyword | query | Search keyword | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| keyword | query | Search keyword | No | string |
| limit | query | Page size (1-100) | No | integer, <br>**Default:** 20 |
| page | query | Page number | No | integer, <br>**Default:** 1 |
| sort_by | query | Sort field and direction | No | string, <br>**Available values:** "-created_at", "-updated_at", "created_at", "updated_at", <br>**Default:** -updated_at |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -1465,7 +1465,7 @@ Get chat messages for a conversation with pagination
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| conversation_id | query | Conversation ID | Yes | string |
| first_id | query | First message ID for pagination | No | |
| first_id | query | First message ID for pagination | No | string |
| limit | query | Number of messages to return (1-100) | No | integer, <br>**Default:** 20 |
#### Responses
@ -1517,11 +1517,11 @@ Get completion conversations with pagination and filtering
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| annotation_status | query | Annotation status filter | No | string, <br>**Available values:** "all", "annotated", "not_annotated", <br>**Default:** all |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| keyword | query | Search keyword | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| keyword | query | Search keyword | No | string |
| limit | query | Page size (1-100) | No | integer, <br>**Default:** 20 |
| page | query | Page number | No | integer, <br>**Default:** 1 |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -1683,7 +1683,7 @@ Export application configuration as DSL
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID to export | Yes | string |
| include_secret | query | Include secrets in export | No | boolean |
| workflow_id | query | Specific workflow ID to export | No | |
| workflow_id | query | Specific workflow ID to export | No | string |
#### Responses
@ -1723,12 +1723,12 @@ Export user feedback data for Google Sheets
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end_date | query | End date (YYYY-MM-DD) | No | |
| end_date | query | End date (YYYY-MM-DD) | No | string |
| format | query | Export format | No | string, <br>**Available values:** "csv", "json", <br>**Default:** csv |
| from_source | query | Filter by feedback source | No | |
| has_comment | query | Only include feedback with comments | No | |
| rating | query | Filter by rating | No | |
| start_date | query | Start date (YYYY-MM-DD) | No | |
| from_source | query | Filter by feedback source | No | string, <br>**Available values:** "admin", "user" |
| has_comment | query | Only include feedback with comments | No | boolean |
| rating | query | Filter by rating | No | string, <br>**Available values:** "dislike", "like" |
| start_date | query | Start date (YYYY-MM-DD) | No | string |
#### Responses
@ -1968,8 +1968,8 @@ Get average response time statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -1985,8 +1985,8 @@ Get average session interaction statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2002,8 +2002,8 @@ Get daily conversation statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2019,8 +2019,8 @@ Get daily terminal/end-user statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2036,8 +2036,8 @@ Get daily message statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2053,8 +2053,8 @@ Get daily token cost statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2070,8 +2070,8 @@ Get tokens per second statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2087,8 +2087,8 @@ Get user satisfaction rate statistics for an application
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | |
| end | query | End date (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2309,15 +2309,15 @@ Get workflow application execution logs
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| created_at__after | query | Filter logs created after this timestamp | No | |
| created_at__before | query | Filter logs created before this timestamp | No | |
| created_by_account | query | Filter by account | No | |
| created_by_end_user_session_id | query | Filter by end user session ID | No | |
| created_at__after | query | Filter logs created after this timestamp | No | dateTime |
| created_at__before | query | Filter logs created before this timestamp | No | dateTime |
| created_by_account | query | Filter by account | No | string |
| created_by_end_user_session_id | query | Filter by end user session ID | No | string |
| detail | query | Whether to return detailed logs | No | boolean |
| keyword | query | Search keyword for filtering logs | No | |
| keyword | query | Search keyword for filtering logs | No | string |
| limit | query | Number of items per page (1-100) | No | integer, <br>**Default:** 20 |
| page | query | Page number (1-99999) | No | integer, <br>**Default:** 1 |
| status | query | Execution status filter (succeeded, failed, stopped, partial-succeeded) | No | |
| status | query | Execution status filter (succeeded, failed, stopped, partial-succeeded) | No | string, <br>**Available values:** "failed", "partial-succeeded", "paused", "running", "scheduled", "stopped", "succeeded" |
#### Responses
@ -2335,15 +2335,15 @@ Get workflow archived execution logs
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| created_at__after | query | Filter logs created after this timestamp | No | |
| created_at__before | query | Filter logs created before this timestamp | No | |
| created_by_account | query | Filter by account | No | |
| created_by_end_user_session_id | query | Filter by end user session ID | No | |
| created_at__after | query | Filter logs created after this timestamp | No | dateTime |
| created_at__before | query | Filter logs created before this timestamp | No | dateTime |
| created_by_account | query | Filter by account | No | string |
| created_by_end_user_session_id | query | Filter by end user session ID | No | string |
| detail | query | Whether to return detailed logs | No | boolean |
| keyword | query | Search keyword for filtering logs | No | |
| keyword | query | Search keyword for filtering logs | No | string |
| limit | query | Number of items per page (1-100) | No | integer, <br>**Default:** 20 |
| page | query | Page number (1-99999) | No | integer, <br>**Default:** 1 |
| status | query | Execution status filter (succeeded, failed, stopped, partial-succeeded) | No | |
| status | query | Execution status filter (succeeded, failed, stopped, partial-succeeded) | No | string, <br>**Available values:** "failed", "partial-succeeded", "paused", "running", "scheduled", "stopped", "succeeded" |
#### Responses
@ -2710,8 +2710,8 @@ Get workflow average app interaction statistics
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2727,8 +2727,8 @@ Get workflow daily runs statistics
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2744,8 +2744,8 @@ Get workflow daily terminals statistics
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2761,8 +2761,8 @@ Get workflow daily token cost statistics
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | |
| end | query | End date and time (YYYY-MM-DD HH:MM) | No | string |
| start | query | Start date and time (YYYY-MM-DD HH:MM) | No | string |
#### Responses
@ -2783,7 +2783,7 @@ Get all published workflows for an application
| limit | query | | No | integer, <br>**Default:** 10 |
| named_only | query | | No | boolean |
| page | query | | No | integer, <br>**Default:** 1 |
| user_id | query | | No | |
| user_id | query | | No | string |
#### Responses
@ -2819,7 +2819,7 @@ Get default block configuration by type
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| block_type | path | Block type | Yes | string |
| q | query | | No | |
| q | query | | No | string |
#### Responses
@ -3467,8 +3467,8 @@ Get draft workflow variables
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | Application ID | Yes | string |
| limit | query | Number of items per page (1-100) | No | string |
| page | query | Page number (1-100000) | No | string |
| limit | query | Items per page | No | integer, <br>**Default:** 20 |
| page | query | Page number | No | integer, <br>**Default:** 1 |
#### Responses
@ -3667,9 +3667,7 @@ Full value for one declared output of a published run.
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| credential_id | query | | No | |
| datasource_type | query | | Yes | string |
| inputs | query | | Yes | object |
| node_id | query | | Yes | string |
| app_id | path | | Yes | string |
#### Responses
@ -5762,9 +5760,9 @@ Request body:
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| last_id | query | | No | |
| last_id | query | | No | string |
| limit | query | | No | integer, <br>**Default:** 20 |
| pinned | query | | No | |
| pinned | query | | No | boolean |
| installed_app_id | path | | Yes | string |
#### Responses
@ -5841,7 +5839,7 @@ Request body:
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| conversation_id | query | Conversation UUID | Yes | string |
| first_id | query | First message ID for pagination | No | |
| first_id | query | First message ID for pagination | No | string |
| limit | query | Number of messages to return (1-100) | No | integer, <br>**Default:** 20 |
| installed_app_id | path | | Yes | string |
@ -5935,7 +5933,7 @@ Request body:
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| last_id | query | | No | |
| last_id | query | | No | string |
| limit | query | | No | integer, <br>**Default:** 20 |
| installed_app_id | path | | Yes | string |
@ -8155,7 +8153,7 @@ Get website crawl status
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| job_id | path | Crawl job ID | Yes | string |
| provider | query | Crawl provider (firecrawl/watercrawl/jinareader) | No | string |
| provider | query | Crawl provider (firecrawl/watercrawl/jinareader) | Yes | string, <br>**Available values:** "firecrawl", "jinareader", "watercrawl" |
#### Responses
@ -8267,12 +8265,12 @@ Get list of available agent providers
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| creators | query | Filter by creator account IDs | No | |
| is_published | query | Filter by published status | No | |
| keyword | query | | No | |
| creators | query | Filter by creator account IDs | No | [ string ] |
| is_published | query | Filter by published status | No | boolean |
| keyword | query | | No | string |
| limit | query | | No | integer, <br>**Default:** 20 |
| page | query | | No | integer, <br>**Default:** 1 |
| tag_ids | query | Filter by tag IDs | No | |
| tag_ids | query | Filter by tag IDs | No | [ string ] |
#### Responses
@ -8448,7 +8446,7 @@ Increment snippet use count by 1
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| model_type | query | | Yes | [ModelType](#modeltype) |
| model_type | query | Enum class for model type. | Yes | string, <br>**Available values:** "llm", "moderation", "rerank", "speech2text", "text-embedding", "tts" |
#### Responses
@ -8747,7 +8745,7 @@ Update a plugin endpoint
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| model_type | query | | No | |
| model_type | query | Enum class for model type. | No | string, <br>**Available values:** "llm", "moderation", "rerank", "speech2text", "text-embedding", "tts" |
#### Responses
@ -8792,7 +8790,7 @@ Update a plugin endpoint
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| credential_id | query | | No | |
| credential_id | query | | No | string |
| provider | path | | Yes | string |
#### Responses
@ -8952,10 +8950,10 @@ Update a plugin endpoint
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| config_from | query | | No | |
| credential_id | query | | No | |
| config_from | query | | No | string |
| credential_id | query | | No | string |
| model | query | | Yes | string |
| model_type | query | | Yes | [ModelType](#modeltype) |
| model_type | query | Enum class for model type. | Yes | string, <br>**Available values:** "llm", "moderation", "rerank", "speech2text", "text-embedding", "tts" |
| provider | path | | Yes | string |
#### Responses
@ -9320,7 +9318,7 @@ Returns permission flags that control workspace features like member invitations
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| action | query | | Yes | string |
| credential_id | query | | No | |
| credential_id | query | | No | string |
| parameter | query | | Yes | string |
| plugin_id | query | | Yes | string |
| provider | query | | Yes | string |
@ -11466,6 +11464,13 @@ Soft lifecycle state for Agent records.
| page | integer | | Yes |
| total | integer | | Yes |
#### AnnotationHitHistoryListQuery
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| limit | integer, <br>**Default:** 20 | Page size | No |
| page | integer, <br>**Default:** 1 | Page number | No |
#### AnnotationList
| Name | Type | Description | Required |

View File

@ -81,7 +81,7 @@ User-scoped operations
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| limit | query | | No | integer, <br>**Default:** 20 |
| mode | query | | No | string |
| mode | query | | No | string, <br>**Available values:** "advanced-chat", "agent", "agent-chat", "channel", "chat", "completion", "rag-pipeline", "workflow" |
| name | query | | No | string |
| page | query | | No | integer, <br>**Default:** 1 |
| tag | query | | No | string |
@ -318,7 +318,7 @@ Upload a file to use as an input variable when running the app
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| limit | query | | No | integer, <br>**Default:** 20 |
| mode | query | | No | string |
| mode | query | | No | string, <br>**Available values:** "advanced-chat", "agent", "agent-chat", "channel", "chat", "completion", "rag-pipeline", "workflow" |
| name | query | | No | string |
| page | query | | No | integer, <br>**Default:** 1 |

View File

@ -264,7 +264,7 @@ Supports pagination using last_id and limit parameters.
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| last_id | query | Last conversation ID for pagination | No | |
| last_id | query | Last conversation ID for pagination | No | string |
| limit | query | Number of conversations to return | No | integer, <br>**Default:** 20 |
| sort_by | query | Sort order for conversations | No | string, <br>**Available values:** "-created_at", "-updated_at", "created_at", "updated_at", <br>**Default:** -updated_at |
@ -327,9 +327,9 @@ Conversational variables are only available for chat applications.
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| c_id | path | Conversation ID | Yes | string |
| last_id | query | Last variable ID for pagination | No | |
| last_id | query | Last variable ID for pagination | No | string |
| limit | query | Number of variables to return | No | integer, <br>**Default:** 20 |
| variable_name | query | Filter variables by name | No | |
| variable_name | query | Filter variables by name | No | string |
#### Responses
@ -1570,7 +1570,7 @@ Retrieves messages with pagination support using first_id.
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| conversation_id | query | Conversation UUID | Yes | string |
| first_id | query | First message ID for pagination | No | |
| first_id | query | First message ID for pagination | No | string |
| limit | query | Number of messages to return (1-100) | No | integer, <br>**Default:** 20 |
#### Responses
@ -1722,14 +1722,14 @@ Returns paginated workflow execution logs with filtering options.
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| created_at__after | query | | No | |
| created_at__before | query | | No | |
| created_by_account | query | | No | |
| created_by_end_user_session_id | query | | No | |
| keyword | query | | No | |
| created_at__after | query | | No | string |
| created_at__before | query | | No | string |
| created_by_account | query | | No | string |
| created_by_end_user_session_id | query | | No | string |
| keyword | query | | No | string |
| limit | query | | No | integer, <br>**Default:** 20 |
| page | query | | No | integer, <br>**Default:** 1 |
| status | query | | No | |
| status | query | | No | string, <br>**Available values:** "failed", "stopped", "succeeded" |
#### Responses

View File

@ -95,7 +95,7 @@ def test_generate_specs_writes_unique_operation_ids(tmp_path):
assert len(operation_ids) == len(set(operation_ids))
def test_generate_specs_moves_get_request_bodies_to_query_parameters(tmp_path):
def test_generate_specs_writes_get_operations_without_request_bodies(tmp_path):
module = _load_generate_swagger_specs_module()
written_paths = module.generate_specs(tmp_path)

View File

@ -1,4 +1,5 @@
import sys
from datetime import datetime
from enum import StrEnum
from typing import Literal
from unittest.mock import MagicMock, patch
@ -43,6 +44,8 @@ class QueryModel(BaseModel):
page: int = Field(default=1, ge=1, le=100, description="Page number")
keyword: str | None = Field(default=None, min_length=1, max_length=50, description="Search keyword")
status: Literal["active", "inactive"] | None = Field(default=None, description="Status filter")
enum_status: StatusEnum | None = Field(default=None, description="Enum status filter")
created_at: datetime | None = Field(default=None, description="Creation time")
app_id: str = Field(..., alias="appId", description="Application ID")
tag_ids: list[str] = Field(default_factory=list, min_length=1, max_length=3, description="Tag IDs")
ambiguous: int | str | None = Field(default=None, description="Ambiguous query parameter")
@ -303,6 +306,20 @@ def test_query_params_from_model_builds_flask_restx_doc_params():
"type": "string",
"enum": ["active", "inactive"],
}
assert params["enum_status"] == {
"in": "query",
"required": False,
"description": "Enum status filter",
"type": "string",
"enum": ["active", "inactive"],
}
assert params["created_at"] == {
"in": "query",
"required": False,
"description": "Creation time",
"type": "string",
"format": "date-time",
}
assert params["appId"] == {
"in": "query",
"required": True,

View File

@ -1,5 +1,7 @@
"""OpenAPI JSON rendering tests for Flask-RESTX API blueprints."""
from collections.abc import Iterator
import pytest
from flask import Flask
@ -31,6 +33,17 @@ def _parameters_by_name(operation: dict[str, object]) -> dict[str, dict[str, obj
return result
def _get_operations(payload: dict[str, object]) -> Iterator[tuple[str, dict[str, object]]]:
paths = payload["paths"]
assert isinstance(paths, dict)
for path, path_item in paths.items():
if not isinstance(path, str) or not isinstance(path_item, dict):
continue
operation = path_item.get("get")
if isinstance(operation, dict):
yield path, operation
def _multipart_form_schema(operation: dict[str, object]) -> dict[str, object]:
request_body = operation.get("requestBody")
assert isinstance(request_body, dict)
@ -93,6 +106,8 @@ def test_openapi_json_endpoints_render(monkeypatch: pytest.MonkeyPatch):
assert isinstance(payload["components"]["schemas"], dict)
missing_refs = _schema_refs(payload) - set(payload["components"]["schemas"])
assert not missing_refs
get_request_body_paths = [path for path, operation in _get_operations(payload) if "requestBody" in operation]
assert not get_request_body_paths
assert app.config["RESTX_INCLUDE_ALL_MODELS"] is True

View File

@ -1,4 +1,4 @@
import type { AppDescribeResponse, AppListResponse } from '@dify/contracts/api/openapi/types.gen'
import type { AppDescribeResponse, AppListResponse, AppMode } from '@dify/contracts/api/openapi/types.gen'
import type { OpenApiClient } from '@/http/orpc'
import type { HttpClient } from '@/http/types'
import { createOpenApiClient } from '@/http/orpc'
@ -7,7 +7,7 @@ export type ListQuery = {
readonly workspaceId: string
readonly page?: number
readonly limit?: number
readonly mode?: string
readonly mode?: AppMode | ''
readonly name?: string
readonly tag?: string
}

View File

@ -8,6 +8,7 @@ import { runGetApp } from './run'
const APP_MODE_VALUES: readonly AppMode[] = [
'advanced-chat',
'agent',
'agent-chat',
'channel',
'chat',
@ -56,7 +57,7 @@ export default class GetApp extends DifyCommand {
allWorkspaces: flags['all-workspaces'],
page: flags.page,
limitRaw: flags.limit,
mode: flags.mode,
mode: flags.mode as AppMode | undefined,
name: flags.name,
tag: flags.tag,
format,

View File

@ -17,7 +17,7 @@ export type GetAppOptions = {
readonly allWorkspaces?: boolean
readonly page?: number
readonly limitRaw?: string
readonly mode?: string
readonly mode?: AppMode
readonly name?: string
readonly tag?: string
readonly format?: string

View File

@ -53,9 +53,9 @@ export type GetActivateCheckData = {
body?: never
path?: never
query: {
email?: string | null
email?: string
token: string
workspace_id?: string | null
workspace_id?: string
}
url: '/activate/check'
}

View File

@ -46,9 +46,9 @@ export const zPostActivateBody = zActivatePayload
export const zPostActivateResponse = zActivationResponse
export const zGetActivateCheckQuery = z.object({
email: z.string().nullish(),
email: z.string().optional(),
token: z.string(),
workspace_id: z.string().nullish(),
workspace_id: z.string().optional(),
})
/**

View File

@ -602,15 +602,6 @@ export type WorkflowTriggerListResponse = {
data: Array<WorkflowTriggerResponse>
}
export type WorkflowExecutionStatus
= | 'failed'
| 'partial-succeeded'
| 'paused'
| 'running'
| 'scheduled'
| 'stopped'
| 'succeeded'
export type WorkflowAppLogPaginationResponse = {
data: Array<WorkflowAppLogPartialResponse>
has_more: boolean
@ -1567,6 +1558,15 @@ export type AgentComposerImpactBindingResponse = {
workflow_id: string
}
export type WorkflowExecutionStatus
= | 'failed'
| 'partial-succeeded'
| 'paused'
| 'running'
| 'scheduled'
| 'stopped'
| 'succeeded'
export type NodeStatus = 'failed' | 'idle' | 'ready' | 'running'
export type NodeOutputView = {
@ -2250,8 +2250,8 @@ export type GetAppsData = {
body?: never
path?: never
query?: {
creator_ids?: Array<string> | null
is_created_by_me?: boolean | null
creator_ids?: Array<string>
is_created_by_me?: boolean
limit?: number
mode?:
| 'advanced-chat'
@ -2262,9 +2262,9 @@ export type GetAppsData = {
| 'chat'
| 'completion'
| 'workflow'
name?: string | null
name?: string
page?: number
tag_ids?: Array<string> | null
tag_ids?: Array<string>
}
url: '/apps'
}
@ -3288,12 +3288,12 @@ export type GetAppsByAppIdChatConversationsData = {
}
query?: {
annotation_status?: 'all' | 'annotated' | 'not_annotated'
end?: string | null
keyword?: string | null
end?: string
keyword?: string
limit?: number
page?: number
sort_by?: '-created_at' | '-updated_at' | 'created_at' | 'updated_at'
start?: string | null
start?: string
}
url: '/apps/{app_id}/chat-conversations'
}
@ -3379,7 +3379,7 @@ export type GetAppsByAppIdChatMessagesData = {
}
query: {
conversation_id: string
first_id?: string | null
first_id?: string
limit?: number
}
url: '/apps/{app_id}/chat-messages'
@ -3451,11 +3451,11 @@ export type GetAppsByAppIdCompletionConversationsData = {
}
query?: {
annotation_status?: 'all' | 'annotated' | 'not_annotated'
end?: string | null
keyword?: string | null
end?: string
keyword?: string
limit?: number
page?: number
start?: string | null
start?: string
}
url: '/apps/{app_id}/completion-conversations'
}
@ -3658,7 +3658,7 @@ export type GetAppsByAppIdExportData = {
}
query?: {
include_secret?: boolean
workflow_id?: string | null
workflow_id?: string
}
url: '/apps/{app_id}/export'
}
@ -3712,12 +3712,12 @@ export type GetAppsByAppIdFeedbacksExportData = {
app_id: string
}
query?: {
end_date?: string | null
end_date?: string
format?: 'csv' | 'json'
from_source?: 'admin' | 'user' | null
has_comment?: boolean | null
rating?: 'dislike' | 'like' | null
start_date?: string | null
from_source?: 'admin' | 'user'
has_comment?: boolean
rating?: 'dislike' | 'like'
start_date?: string
}
url: '/apps/{app_id}/feedbacks/export'
}
@ -4011,8 +4011,8 @@ export type GetAppsByAppIdStatisticsAverageResponseTimeData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/average-response-time'
}
@ -4032,8 +4032,8 @@ export type GetAppsByAppIdStatisticsAverageSessionInteractionsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/average-session-interactions'
}
@ -4053,8 +4053,8 @@ export type GetAppsByAppIdStatisticsDailyConversationsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/daily-conversations'
}
@ -4074,8 +4074,8 @@ export type GetAppsByAppIdStatisticsDailyEndUsersData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/daily-end-users'
}
@ -4095,8 +4095,8 @@ export type GetAppsByAppIdStatisticsDailyMessagesData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/daily-messages'
}
@ -4116,8 +4116,8 @@ export type GetAppsByAppIdStatisticsTokenCostsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/token-costs'
}
@ -4137,8 +4137,8 @@ export type GetAppsByAppIdStatisticsTokensPerSecondData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/tokens-per-second'
}
@ -4158,8 +4158,8 @@ export type GetAppsByAppIdStatisticsUserSatisfactionRateData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/statistics/user-satisfaction-rate'
}
@ -4417,15 +4417,22 @@ export type GetAppsByAppIdWorkflowAppLogsData = {
app_id: string
}
query?: {
created_at__after?: string | null
created_at__before?: string | null
created_by_account?: string | null
created_by_end_user_session_id?: string | null
created_at__after?: string
created_at__before?: string
created_by_account?: string
created_by_end_user_session_id?: string
detail?: boolean
keyword?: string | null
keyword?: string
limit?: number
page?: number
status?: WorkflowExecutionStatus | null
status?:
| 'failed'
| 'partial-succeeded'
| 'paused'
| 'running'
| 'scheduled'
| 'stopped'
| 'succeeded'
}
url: '/apps/{app_id}/workflow-app-logs'
}
@ -4443,15 +4450,22 @@ export type GetAppsByAppIdWorkflowArchivedLogsData = {
app_id: string
}
query?: {
created_at__after?: string | null
created_at__before?: string | null
created_by_account?: string | null
created_by_end_user_session_id?: string | null
created_at__after?: string
created_at__before?: string
created_by_account?: string
created_by_end_user_session_id?: string
detail?: boolean
keyword?: string | null
keyword?: string
limit?: number
page?: number
status?: WorkflowExecutionStatus | null
status?:
| 'failed'
| 'partial-succeeded'
| 'paused'
| 'running'
| 'scheduled'
| 'stopped'
| 'succeeded'
}
url: '/apps/{app_id}/workflow-archived-logs'
}
@ -4838,8 +4852,8 @@ export type GetAppsByAppIdWorkflowStatisticsAverageAppInteractionsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/workflow/statistics/average-app-interactions'
}
@ -4859,8 +4873,8 @@ export type GetAppsByAppIdWorkflowStatisticsDailyConversationsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/workflow/statistics/daily-conversations'
}
@ -4880,8 +4894,8 @@ export type GetAppsByAppIdWorkflowStatisticsDailyTerminalsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/workflow/statistics/daily-terminals'
}
@ -4901,8 +4915,8 @@ export type GetAppsByAppIdWorkflowStatisticsTokenCostsData = {
app_id: string
}
query?: {
end?: string | null
start?: string | null
end?: string
start?: string
}
url: '/apps/{app_id}/workflow/statistics/token-costs'
}
@ -4925,7 +4939,7 @@ export type GetAppsByAppIdWorkflowsData = {
limit?: number
named_only?: boolean
page?: number
user_id?: string | null
user_id?: string
}
url: '/apps/{app_id}/workflows'
}
@ -4962,7 +4976,7 @@ export type GetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeData =
block_type: string
}
query?: {
q?: string | null
q?: string
}
url: '/apps/{app_id}/workflows/default-workflow-block-configs/{block_type}'
}
@ -5721,8 +5735,8 @@ export type GetAppsByAppIdWorkflowsDraftVariablesData = {
app_id: string
}
query?: {
limit?: string
page?: string
limit?: number
page?: number
}
url: '/apps/{app_id}/workflows/draft/variables'
}
@ -5991,11 +6005,7 @@ export type GetAppsByAppIdWorkflowsTriggersWebhookData = {
app_id: string
}
query: {
credential_id?: string | null
datasource_type: string
inputs: {
[key: string]: unknown
}
node_id: string
}
url: '/apps/{app_id}/workflows/triggers/webhook'
}

View File

@ -394,19 +394,6 @@ export const zWorkflowTriggerListResponse = z.object({
data: z.array(zWorkflowTriggerResponse),
})
/**
* WorkflowExecutionStatus
*/
export const zWorkflowExecutionStatus = z.enum([
'failed',
'partial-succeeded',
'paused',
'running',
'scheduled',
'stopped',
'succeeded',
])
/**
* WorkflowRunExportResponse
*/
@ -1430,6 +1417,19 @@ export const zAgentComposerImpactResponse = z.object({
workflow_node_count: z.int(),
})
/**
* WorkflowExecutionStatus
*/
export const zWorkflowExecutionStatus = z.enum([
'failed',
'partial-succeeded',
'paused',
'running',
'scheduled',
'stopped',
'succeeded',
])
/**
* NodeStatus
*
@ -3010,8 +3010,8 @@ export const zWorkflowCommentDetailWritable = z.object({
})
export const zGetAppsQuery = z.object({
creator_ids: z.array(z.string()).nullish(),
is_created_by_me: z.boolean().nullish(),
creator_ids: z.array(z.string()).optional(),
is_created_by_me: z.boolean().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
mode: z
.enum([
@ -3026,9 +3026,9 @@ export const zGetAppsQuery = z.object({
])
.optional()
.default('all'),
name: z.string().nullish(),
name: z.string().optional(),
page: z.int().gte(1).lte(99999).optional().default(1),
tag_ids: z.array(z.string()).nullish(),
tag_ids: z.array(z.string()).optional(),
})
/**
@ -3494,8 +3494,8 @@ export const zGetAppsByAppIdAnnotationsByAnnotationIdHitHistoriesPath = z.object
})
export const zGetAppsByAppIdAnnotationsByAnnotationIdHitHistoriesQuery = z.object({
limit: z.int().optional().default(20),
page: z.int().optional().default(1),
limit: z.int().gte(1).optional().default(20),
page: z.int().gte(1).optional().default(1),
})
/**
@ -3530,15 +3530,15 @@ export const zGetAppsByAppIdChatConversationsPath = z.object({
export const zGetAppsByAppIdChatConversationsQuery = z.object({
annotation_status: z.enum(['all', 'annotated', 'not_annotated']).optional().default('all'),
end: z.string().nullish(),
keyword: z.string().nullish(),
end: z.string().optional(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(99999).optional().default(1),
sort_by: z
.enum(['-created_at', '-updated_at', 'created_at', 'updated_at'])
.optional()
.default('-updated_at'),
start: z.string().nullish(),
start: z.string().optional(),
})
/**
@ -3572,7 +3572,7 @@ export const zGetAppsByAppIdChatMessagesPath = z.object({
export const zGetAppsByAppIdChatMessagesQuery = z.object({
conversation_id: z.string(),
first_id: z.string().nullish(),
first_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
})
@ -3608,11 +3608,11 @@ export const zGetAppsByAppIdCompletionConversationsPath = z.object({
export const zGetAppsByAppIdCompletionConversationsQuery = z.object({
annotation_status: z.enum(['all', 'annotated', 'not_annotated']).optional().default('all'),
end: z.string().nullish(),
keyword: z.string().nullish(),
end: z.string().optional(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(99999).optional().default(1),
start: z.string().nullish(),
start: z.string().optional(),
})
/**
@ -3703,7 +3703,7 @@ export const zGetAppsByAppIdExportPath = z.object({
export const zGetAppsByAppIdExportQuery = z.object({
include_secret: z.boolean().optional().default(false),
workflow_id: z.string().nullish(),
workflow_id: z.string().optional(),
})
/**
@ -3727,12 +3727,12 @@ export const zGetAppsByAppIdFeedbacksExportPath = z.object({
})
export const zGetAppsByAppIdFeedbacksExportQuery = z.object({
end_date: z.string().nullish(),
end_date: z.string().optional(),
format: z.enum(['csv', 'json']).optional().default('csv'),
from_source: z.enum(['admin', 'user']).nullish(),
has_comment: z.boolean().nullish(),
rating: z.enum(['dislike', 'like']).nullish(),
start_date: z.string().nullish(),
from_source: z.enum(['admin', 'user']).optional(),
has_comment: z.boolean().optional(),
rating: z.enum(['dislike', 'like']).optional(),
start_date: z.string().optional(),
})
/**
@ -3859,8 +3859,8 @@ export const zGetAppsByAppIdStatisticsAverageResponseTimePath = z.object({
})
export const zGetAppsByAppIdStatisticsAverageResponseTimeQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3875,8 +3875,8 @@ export const zGetAppsByAppIdStatisticsAverageSessionInteractionsPath = z.object(
})
export const zGetAppsByAppIdStatisticsAverageSessionInteractionsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3891,8 +3891,8 @@ export const zGetAppsByAppIdStatisticsDailyConversationsPath = z.object({
})
export const zGetAppsByAppIdStatisticsDailyConversationsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3907,8 +3907,8 @@ export const zGetAppsByAppIdStatisticsDailyEndUsersPath = z.object({
})
export const zGetAppsByAppIdStatisticsDailyEndUsersQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3923,8 +3923,8 @@ export const zGetAppsByAppIdStatisticsDailyMessagesPath = z.object({
})
export const zGetAppsByAppIdStatisticsDailyMessagesQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3939,8 +3939,8 @@ export const zGetAppsByAppIdStatisticsTokenCostsPath = z.object({
})
export const zGetAppsByAppIdStatisticsTokenCostsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3955,8 +3955,8 @@ export const zGetAppsByAppIdStatisticsTokensPerSecondPath = z.object({
})
export const zGetAppsByAppIdStatisticsTokensPerSecondQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -3971,8 +3971,8 @@ export const zGetAppsByAppIdStatisticsUserSatisfactionRatePath = z.object({
})
export const zGetAppsByAppIdStatisticsUserSatisfactionRateQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -4097,15 +4097,17 @@ export const zGetAppsByAppIdWorkflowAppLogsPath = z.object({
})
export const zGetAppsByAppIdWorkflowAppLogsQuery = z.object({
created_at__after: z.iso.datetime().nullish(),
created_at__before: z.iso.datetime().nullish(),
created_by_account: z.string().nullish(),
created_by_end_user_session_id: z.string().nullish(),
created_at__after: z.iso.datetime().optional(),
created_at__before: z.iso.datetime().optional(),
created_by_account: z.string().optional(),
created_by_end_user_session_id: z.string().optional(),
detail: z.boolean().optional().default(false),
keyword: z.string().nullish(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(99999).optional().default(1),
status: zWorkflowExecutionStatus.nullish(),
status: z
.enum(['failed', 'partial-succeeded', 'paused', 'running', 'scheduled', 'stopped', 'succeeded'])
.optional(),
})
/**
@ -4118,15 +4120,17 @@ export const zGetAppsByAppIdWorkflowArchivedLogsPath = z.object({
})
export const zGetAppsByAppIdWorkflowArchivedLogsQuery = z.object({
created_at__after: z.iso.datetime().nullish(),
created_at__before: z.iso.datetime().nullish(),
created_by_account: z.string().nullish(),
created_by_end_user_session_id: z.string().nullish(),
created_at__after: z.iso.datetime().optional(),
created_at__before: z.iso.datetime().optional(),
created_by_account: z.string().optional(),
created_by_end_user_session_id: z.string().optional(),
detail: z.boolean().optional().default(false),
keyword: z.string().nullish(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(99999).optional().default(1),
status: zWorkflowExecutionStatus.nullish(),
status: z
.enum(['failed', 'partial-succeeded', 'paused', 'running', 'scheduled', 'stopped', 'succeeded'])
.optional(),
})
/**
@ -4376,8 +4380,8 @@ export const zGetAppsByAppIdWorkflowStatisticsAverageAppInteractionsPath = z.obj
})
export const zGetAppsByAppIdWorkflowStatisticsAverageAppInteractionsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -4393,8 +4397,8 @@ export const zGetAppsByAppIdWorkflowStatisticsDailyConversationsPath = z.object(
})
export const zGetAppsByAppIdWorkflowStatisticsDailyConversationsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -4410,8 +4414,8 @@ export const zGetAppsByAppIdWorkflowStatisticsDailyTerminalsPath = z.object({
})
export const zGetAppsByAppIdWorkflowStatisticsDailyTerminalsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -4427,8 +4431,8 @@ export const zGetAppsByAppIdWorkflowStatisticsTokenCostsPath = z.object({
})
export const zGetAppsByAppIdWorkflowStatisticsTokenCostsQuery = z.object({
end: z.string().nullish(),
start: z.string().nullish(),
end: z.string().optional(),
start: z.string().optional(),
})
/**
@ -4444,7 +4448,7 @@ export const zGetAppsByAppIdWorkflowsQuery = z.object({
limit: z.int().gte(1).lte(100).optional().default(10),
named_only: z.boolean().optional().default(false),
page: z.int().gte(1).lte(99999).optional().default(1),
user_id: z.string().nullish(),
user_id: z.string().optional(),
})
/**
@ -4470,7 +4474,7 @@ export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypePath
})
export const zGetAppsByAppIdWorkflowsDefaultWorkflowBlockConfigsByBlockTypeQuery = z.object({
q: z.string().nullish(),
q: z.string().optional(),
})
/**
@ -4880,8 +4884,8 @@ export const zGetAppsByAppIdWorkflowsDraftVariablesPath = z.object({
})
export const zGetAppsByAppIdWorkflowsDraftVariablesQuery = z.object({
limit: z.string().optional(),
page: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(100000).optional().default(1),
})
/**
@ -5007,9 +5011,7 @@ export const zGetAppsByAppIdWorkflowsTriggersWebhookPath = z.object({
})
export const zGetAppsByAppIdWorkflowsTriggersWebhookQuery = z.object({
credential_id: z.string().nullish(),
datasource_type: z.string(),
inputs: z.record(z.string(), z.unknown()),
node_id: z.string(),
})
/**

View File

@ -259,9 +259,9 @@ export type GetInstalledAppsByInstalledAppIdConversationsData = {
installed_app_id: string
}
query?: {
last_id?: string | null
last_id?: string
limit?: number
pinned?: boolean | null
pinned?: boolean
}
url: '/installed-apps/{installed_app_id}/conversations'
}
@ -352,7 +352,7 @@ export type GetInstalledAppsByInstalledAppIdMessagesData = {
}
query: {
conversation_id: string
first_id?: string | null
first_id?: string
limit?: number
}
url: '/installed-apps/{installed_app_id}/messages'
@ -464,7 +464,7 @@ export type GetInstalledAppsByInstalledAppIdSavedMessagesData = {
installed_app_id: string
}
query?: {
last_id?: string | null
last_id?: string
limit?: number
}
url: '/installed-apps/{installed_app_id}/saved-messages'

View File

@ -234,9 +234,9 @@ export const zGetInstalledAppsByInstalledAppIdConversationsPath = z.object({
})
export const zGetInstalledAppsByInstalledAppIdConversationsQuery = z.object({
last_id: z.string().nullish(),
last_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
pinned: z.boolean().nullish(),
pinned: z.boolean().optional(),
})
/**
@ -299,7 +299,7 @@ export const zGetInstalledAppsByInstalledAppIdMessagesPath = z.object({
export const zGetInstalledAppsByInstalledAppIdMessagesQuery = z.object({
conversation_id: z.string(),
first_id: z.string().nullish(),
first_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
})
@ -373,7 +373,7 @@ export const zGetInstalledAppsByInstalledAppIdSavedMessagesPath = z.object({
})
export const zGetInstalledAppsByInstalledAppIdSavedMessagesQuery = z.object({
last_id: z.string().nullish(),
last_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
})

View File

@ -32,7 +32,7 @@ export const get = oc
.input(
z.object({
params: zGetWebsiteCrawlStatusByJobIdPath,
query: zGetWebsiteCrawlStatusByJobIdQuery.optional(),
query: zGetWebsiteCrawlStatusByJobIdQuery,
}),
)
.output(zGetWebsiteCrawlStatusByJobIdResponse)

View File

@ -40,8 +40,8 @@ export type GetWebsiteCrawlStatusByJobIdData = {
path: {
job_id: string
}
query?: {
provider?: string
query: {
provider: 'firecrawl' | 'jinareader' | 'watercrawl'
}
url: '/website/crawl/status/{job_id}'
}

View File

@ -23,7 +23,7 @@ export const zGetWebsiteCrawlStatusByJobIdPath = z.object({
})
export const zGetWebsiteCrawlStatusByJobIdQuery = z.object({
provider: z.string().optional(),
provider: z.enum(['firecrawl', 'jinareader', 'watercrawl']),
})
/**

View File

@ -85,8 +85,6 @@ export type AccountWithRoleList = {
accounts: Array<AccountWithRole>
}
export type ModelType = 'llm' | 'moderation' | 'rerank' | 'speech2text' | 'text-embedding' | 'tts'
export type ParserPostDefault = {
model_settings: Array<Inner>
}
@ -643,6 +641,8 @@ export type Inner = {
export type TenantAccountRole = 'admin' | 'dataset_operator' | 'editor' | 'normal' | 'owner'
export type ModelType = 'llm' | 'moderation' | 'rerank' | 'speech2text' | 'text-embedding' | 'tts'
export type LoadBalancingPayload = {
configs?: Array<{
[key: string]: unknown
@ -752,12 +752,12 @@ export type GetWorkspacesCurrentCustomizedSnippetsData = {
body?: never
path?: never
query?: {
creators?: Array<string> | null
is_published?: boolean | null
keyword?: string | null
creators?: Array<string>
is_published?: boolean
keyword?: string
limit?: number
page?: number
tag_ids?: Array<string> | null
tag_ids?: Array<string>
}
url: '/workspaces/current/customized-snippets'
}
@ -1024,7 +1024,7 @@ export type GetWorkspacesCurrentDefaultModelData = {
body?: never
path?: never
query: {
model_type: ModelType
model_type: 'llm' | 'moderation' | 'rerank' | 'speech2text' | 'text-embedding' | 'tts'
}
url: '/workspaces/current/default-model'
}
@ -1391,7 +1391,7 @@ export type GetWorkspacesCurrentModelProvidersData = {
body?: never
path?: never
query?: {
model_type?: ModelType | null
model_type?: 'llm' | 'moderation' | 'rerank' | 'speech2text' | 'text-embedding' | 'tts'
}
url: '/workspaces/current/model-providers'
}
@ -1445,7 +1445,7 @@ export type GetWorkspacesCurrentModelProvidersByProviderCredentialsData = {
provider: string
}
query?: {
credential_id?: string | null
credential_id?: string
}
url: '/workspaces/current/model-providers/{provider}/credentials'
}
@ -1603,10 +1603,10 @@ export type GetWorkspacesCurrentModelProvidersByProviderModelsCredentialsData =
provider: string
}
query: {
config_from?: string | null
credential_id?: string | null
config_from?: string
credential_id?: string
model: string
model_type: ModelType
model_type: 'llm' | 'moderation' | 'rerank' | 'speech2text' | 'text-embedding' | 'tts'
}
url: '/workspaces/current/model-providers/{provider}/models/credentials'
}
@ -2023,7 +2023,7 @@ export type GetWorkspacesCurrentPluginParametersDynamicOptionsData = {
path?: never
query: {
action: string
credential_id?: string | null
credential_id?: string
parameter: string
plugin_id: string
provider: string

View File

@ -16,20 +16,6 @@ export const zSnippetImportPayload = z.object({
yaml_url: z.string().nullish(),
})
/**
* ModelType
*
* Enum class for model type.
*/
export const zModelType = z.enum([
'llm',
'moderation',
'rerank',
'speech2text',
'text-embedding',
'tts',
])
/**
* SimpleResultResponse
*/
@ -203,71 +189,6 @@ export const zParserCredentialValidate = z.object({
credentials: z.record(z.string(), z.unknown()),
})
/**
* ParserDeleteModels
*/
export const zParserDeleteModels = z.object({
model: z.string(),
model_type: zModelType,
})
/**
* ParserDeleteCredential
*/
export const zParserDeleteCredential = z.object({
credential_id: z.string(),
model: z.string(),
model_type: zModelType,
})
/**
* ParserCreateCredential
*/
export const zParserCreateCredential = z.object({
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
name: z.string().max(30).nullish(),
})
/**
* ParserUpdateCredential
*/
export const zParserUpdateCredential = z.object({
credential_id: z.string(),
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
name: z.string().max(30).nullish(),
})
/**
* ParserSwitch
*/
export const zParserSwitch = z.object({
credential_id: z.string(),
model: z.string(),
model_type: zModelType,
})
/**
* ParserValidate
*/
export const zParserValidate = z.object({
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
})
/**
* LoadBalancingCredentialPayload
*/
export const zLoadBalancingCredentialPayload = z.object({
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
})
/**
* ParserPreferredProviderType
*/
@ -655,6 +576,99 @@ export const zAccountWithRoleList = z.object({
accounts: z.array(zAccountWithRole),
})
/**
* TenantAccountRole
*/
export const zTenantAccountRole = z.enum(['admin', 'dataset_operator', 'editor', 'normal', 'owner'])
/**
* MemberInvitePayload
*/
export const zMemberInvitePayload = z.object({
emails: z.array(z.string()).optional(),
language: z.string().nullish(),
role: zTenantAccountRole,
})
/**
* ModelType
*
* Enum class for model type.
*/
export const zModelType = z.enum([
'llm',
'moderation',
'rerank',
'speech2text',
'text-embedding',
'tts',
])
/**
* ParserDeleteModels
*/
export const zParserDeleteModels = z.object({
model: z.string(),
model_type: zModelType,
})
/**
* ParserDeleteCredential
*/
export const zParserDeleteCredential = z.object({
credential_id: z.string(),
model: z.string(),
model_type: zModelType,
})
/**
* ParserCreateCredential
*/
export const zParserCreateCredential = z.object({
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
name: z.string().max(30).nullish(),
})
/**
* ParserUpdateCredential
*/
export const zParserUpdateCredential = z.object({
credential_id: z.string(),
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
name: z.string().max(30).nullish(),
})
/**
* ParserSwitch
*/
export const zParserSwitch = z.object({
credential_id: z.string(),
model: z.string(),
model_type: zModelType,
})
/**
* ParserValidate
*/
export const zParserValidate = z.object({
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
})
/**
* LoadBalancingCredentialPayload
*/
export const zLoadBalancingCredentialPayload = z.object({
credentials: z.record(z.string(), z.unknown()),
model: z.string(),
model_type: zModelType,
})
/**
* Inner
*/
@ -671,20 +685,6 @@ export const zParserPostDefault = z.object({
model_settings: z.array(zInner),
})
/**
* TenantAccountRole
*/
export const zTenantAccountRole = z.enum(['admin', 'dataset_operator', 'editor', 'normal', 'owner'])
/**
* MemberInvitePayload
*/
export const zMemberInvitePayload = z.object({
emails: z.array(z.string()).optional(),
language: z.string().nullish(),
role: zTenantAccountRole,
})
/**
* LoadBalancingPayload
*/
@ -941,12 +941,12 @@ export const zGetWorkspacesCurrentAgentProvidersResponse = z.array(
)
export const zGetWorkspacesCurrentCustomizedSnippetsQuery = z.object({
creators: z.array(z.string()).nullish(),
is_published: z.boolean().nullish(),
keyword: z.string().nullish(),
creators: z.array(z.string()).optional(),
is_published: z.boolean().optional(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(99999).optional().default(1),
tag_ids: z.array(z.string()).nullish(),
tag_ids: z.array(z.string()).optional(),
})
/**
@ -1049,7 +1049,7 @@ export const zPostWorkspacesCurrentCustomizedSnippetsBySnippetIdUseCountIncremen
export const zGetWorkspacesCurrentDatasetOperatorsResponse = zAccountWithRoleList
export const zGetWorkspacesCurrentDefaultModelQuery = z.object({
model_type: zModelType,
model_type: z.enum(['llm', 'moderation', 'rerank', 'speech2text', 'text-embedding', 'tts']),
})
/**
@ -1213,7 +1213,9 @@ export const zPutWorkspacesCurrentMembersByMemberIdUpdateRoleResponse = z.record
)
export const zGetWorkspacesCurrentModelProvidersQuery = z.object({
model_type: zModelType.nullish(),
model_type: z
.enum(['llm', 'moderation', 'rerank', 'speech2text', 'text-embedding', 'tts'])
.optional(),
})
/**
@ -1250,7 +1252,7 @@ export const zGetWorkspacesCurrentModelProvidersByProviderCredentialsPath = z.ob
})
export const zGetWorkspacesCurrentModelProvidersByProviderCredentialsQuery = z.object({
credential_id: z.string().nullish(),
credential_id: z.string().optional(),
})
/**
@ -1371,10 +1373,10 @@ export const zGetWorkspacesCurrentModelProvidersByProviderModelsCredentialsPath
})
export const zGetWorkspacesCurrentModelProvidersByProviderModelsCredentialsQuery = z.object({
config_from: z.string().nullish(),
credential_id: z.string().nullish(),
config_from: z.string().optional(),
credential_id: z.string().optional(),
model: z.string(),
model_type: zModelType,
model_type: z.enum(['llm', 'moderation', 'rerank', 'speech2text', 'text-embedding', 'tts']),
})
/**
@ -1641,7 +1643,7 @@ export const zGetWorkspacesCurrentPluginMarketplacePkgResponse = z.record(z.stri
export const zGetWorkspacesCurrentPluginParametersDynamicOptionsQuery = z.object({
action: z.string(),
credential_id: z.string().nullish(),
credential_id: z.string().optional(),
parameter: z.string(),
plugin_id: z.string(),
provider: z.string(),

View File

@ -569,7 +569,15 @@ export type GetAppsData = {
path?: never
query: {
limit?: number
mode?: string
mode?:
| 'advanced-chat'
| 'agent'
| 'agent-chat'
| 'channel'
| 'chat'
| 'completion'
| 'rag-pipeline'
| 'workflow'
name?: string
page?: number
tag?: string
@ -891,7 +899,15 @@ export type GetPermittedExternalAppsData = {
path?: never
query?: {
limit?: number
mode?: string
mode?:
| 'advanced-chat'
| 'agent'
| 'agent-chat'
| 'channel'
| 'chat'
| 'completion'
| 'rag-pipeline'
| 'workflow'
name?: string
page?: number
}

View File

@ -678,7 +678,18 @@ export const zDeleteAccountSessionsBySessionIdResponse = zRevokeResponse
export const zGetAppsQuery = z.object({
limit: z.int().gte(1).lte(200).optional().default(20),
mode: z.string().optional(),
mode: z
.enum([
'advanced-chat',
'agent',
'agent-chat',
'channel',
'chat',
'completion',
'rag-pipeline',
'workflow',
])
.optional(),
name: z.string().max(200).optional(),
page: z.int().gte(1).optional().default(1),
tag: z.string().max(100).optional(),
@ -827,7 +838,18 @@ export const zPostOauthDeviceTokenResponse = z.record(z.string(), z.unknown())
export const zGetPermittedExternalAppsQuery = z.object({
limit: z.int().gte(1).lte(200).optional().default(20),
mode: z.string().optional(),
mode: z
.enum([
'advanced-chat',
'agent',
'agent-chat',
'channel',
'chat',
'completion',
'rag-pipeline',
'workflow',
])
.optional(),
name: z.string().max(200).optional(),
page: z.int().gte(1).optional().default(1),
})

View File

@ -1424,7 +1424,7 @@ export type GetConversationsData = {
body?: never
path?: never
query?: {
last_id?: string | null
last_id?: string
limit?: number
sort_by?: '-created_at' | '-updated_at' | 'created_at' | 'updated_at'
}
@ -1514,9 +1514,9 @@ export type GetConversationsByCIdVariablesData = {
c_id: string
}
query?: {
last_id?: string | null
last_id?: string
limit?: number
variable_name?: string | null
variable_name?: string
}
url: '/conversations/{c_id}/variables'
}
@ -3236,7 +3236,7 @@ export type GetMessagesData = {
path?: never
query: {
conversation_id: string
first_id?: string | null
first_id?: string
limit?: number
}
url: '/messages'
@ -3466,14 +3466,14 @@ export type GetWorkflowsLogsData = {
body?: never
path?: never
query?: {
created_at__after?: string | null
created_at__before?: string | null
created_by_account?: string | null
created_by_end_user_session_id?: string | null
keyword?: string | null
created_at__after?: string
created_at__before?: string
created_by_account?: string
created_by_end_user_session_id?: string
keyword?: string
limit?: number
page?: number
status?: 'failed' | 'stopped' | 'succeeded' | null
status?: 'failed' | 'stopped' | 'succeeded'
}
url: '/workflows/logs'
}

View File

@ -1533,7 +1533,7 @@ export const zPostCompletionMessagesByTaskIdStopPath = z.object({
export const zPostCompletionMessagesByTaskIdStopResponse = zSimpleResultResponse
export const zGetConversationsQuery = z.object({
last_id: z.string().nullish(),
last_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
sort_by: z
.enum(['-created_at', '-updated_at', 'created_at', 'updated_at'])
@ -1571,9 +1571,9 @@ export const zGetConversationsByCIdVariablesPath = z.object({
})
export const zGetConversationsByCIdVariablesQuery = z.object({
last_id: z.string().nullish(),
last_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
variable_name: z.string().min(1).max(255).nullish(),
variable_name: z.string().min(1).max(255).optional(),
})
/**
@ -2226,7 +2226,7 @@ export const zGetInfoResponse = zAppInfoResponse
export const zGetMessagesQuery = z.object({
conversation_id: z.string(),
first_id: z.string().nullish(),
first_id: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
})
@ -2293,14 +2293,14 @@ export const zGetWorkflowByTaskIdEventsQuery = z.object({
export const zGetWorkflowByTaskIdEventsResponse = z.record(z.string(), z.unknown())
export const zGetWorkflowsLogsQuery = z.object({
created_at__after: z.string().nullish(),
created_at__before: z.string().nullish(),
created_by_account: z.string().nullish(),
created_by_end_user_session_id: z.string().nullish(),
keyword: z.string().nullish(),
created_at__after: z.string().optional(),
created_at__before: z.string().optional(),
created_by_account: z.string().optional(),
created_by_end_user_session_id: z.string().optional(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).lte(99999).optional().default(1),
status: z.enum(['failed', 'stopped', 'succeeded']).nullish(),
status: z.enum(['failed', 'stopped', 'succeeded']).optional(),
})
/**

View File

@ -431,21 +431,6 @@ const isNullSchema = (schema: SwaggerSchema) => {
return schema.type === 'null'
}
const resolveSchemaRef = (
schema: SwaggerSchema | undefined,
schemas: Record<string, SwaggerSchema>,
): SwaggerSchema | undefined => {
const ref = schema?.$ref
if (!ref)
return schema
const refName = schemaNameFromRef(ref)
if (!refName)
return schema
return schemas[refName] ?? schema
}
const withoutNullableWrapper = (schema: SwaggerSchema | undefined): SwaggerSchema => {
if (!schema)
return {}
@ -461,95 +446,6 @@ const withoutNullableWrapper = (schema: SwaggerSchema | undefined): SwaggerSchem
}
}
const queryParameterFromSchema = (
name: string,
schema: SwaggerSchema | undefined,
required: boolean,
): SwaggerParameter => {
const querySchema = withoutNullableWrapper(schema)
const parameter: SwaggerParameter = {
in: 'query',
name,
required,
schema: querySchema,
}
if (querySchema.description)
parameter.description = querySchema.description
return parameter
}
const mergeQueryParameter = (
parameters: SwaggerParameter[],
queryParameter: SwaggerParameter,
) => {
const existingIndex = parameters.findIndex((parameter) => {
return parameter.in === 'query' && parameter.name === queryParameter.name
})
if (existingIndex === -1) {
parameters.push(queryParameter)
return
}
const existingParameter = parameters[existingIndex]
if (!existingParameter) {
parameters.push(queryParameter)
return
}
parameters[existingIndex] = {
...existingParameter,
...queryParameter,
description: queryParameter.description ?? existingParameter.description,
required: Boolean(existingParameter.required) || Boolean(queryParameter.required),
}
}
const normalizeGetBodyParameters = (
operation: SwaggerOperation,
schemas: Record<string, SwaggerSchema>,
) => {
const bodyParameters: SwaggerParameter[] = []
const normalizedParameters: SwaggerParameter[] = []
for (const parameter of operation.parameters ?? []) {
if (parameter.in === 'body') {
bodyParameters.push(parameter)
continue
}
normalizedParameters.push(parameter)
}
const requestBodySchema = getRequestBodySchema(operation)
if (requestBodySchema) {
bodyParameters.push({
in: 'body',
name: 'payload',
required: Boolean(operation.requestBody?.required),
schema: requestBodySchema,
})
}
for (const parameter of bodyParameters) {
const schema = resolveSchemaRef(parameter.schema, schemas)
const properties = schema?.properties ?? {}
const required = new Set(schema?.required ?? [])
for (const [name, propertySchema] of Object.entries(properties)) {
mergeQueryParameter(
normalizedParameters,
queryParameterFromSchema(name, propertySchema, required.has(name)),
)
}
}
operation.parameters = normalizedParameters
delete operation.requestBody
}
const normalizeResponses = (operation: SwaggerOperation) => {
const responses = operation.responses ??= {}
@ -710,8 +606,6 @@ const normalizeOperations = (document: SwaggerDocument, surface: string) => {
const swaggerOperation = operation as SwaggerOperation
swaggerOperation.operationId = operationId(method, routePath)
if (method === 'get')
normalizeGetBodyParameters(swaggerOperation, schemas)
normalizeResponses(swaggerOperation)
const hasPossiblyInaccurateTypes = hasPossiblyInaccurateGeneratedContractTypes(swaggerOperation, schemas, {
method,