Files
dify/api/controllers/openapi/__init__.py
T
GareArc 94d9134719 feat(api): let agents edit, test and publish workflow apps over openapi
DSL export returns a draft_hash covering the graph, features, environment
and conversation variables; an import that sends an older hash fails
instead of wiping newer edits. A blank secret imported over an app keeps
the stored value. The incompatible-node error names the nodes.

New openapi ops: draft test runs, run history with node steps, publish,
version list and restore, and environment variables (secrets masked on
read). Publish shares one service method with the console.
2026-09-30 02:06:02 +00:00

226 lines
5.6 KiB
Python

from typing import override
from flask import Blueprint
from flask_restx import Namespace
from controllers.openapi._catalog import attach_catalog
from controllers.openapi._errors import ErrorBody, OpenApiErrorCode, OpenApiErrorFormatter
from controllers.openapi._upload import describe_multipart_bodies
from libs.device_flow_security import attach_anti_framing
from libs.external_api import ExternalApi
bp = Blueprint("openapi", __name__, url_prefix="/openapi/v1")
attach_anti_framing(bp)
attach_catalog(bp)
class _OpenApi(ExternalApi):
"""The surface's document also names the multipart form its file-bearing bodies accept."""
@override
def finish_document(self, document: dict[str, object]) -> dict[str, object]:
return describe_multipart_bodies(document)
api = _OpenApi(
bp,
version="1.0",
title="OpenAPI",
description="User-scoped programmatic API (bearer auth)",
error_body_formatter=OpenApiErrorFormatter(),
)
openapi_ns = Namespace("openapi", description="User-scoped operations", path="/")
# Register response/query models BEFORE importing controller modules so that
# @openapi_ns.response / @openapi_ns.expect decorators can resolve model names.
from controllers.common.fields import EventStreamResponse, SimpleResultResponse
from controllers.common.schema import register_enum_models, register_response_schema_models, register_schema_models
from controllers.openapi._models import (
AccountPayload,
AccountResponse,
AdvancedChatRunPayload,
AppDescribeInfo,
AppDescribeQuery,
AppDescribeResponse,
AppDslExportQuery,
AppDslExportResponse,
AppDslImportPayload,
AppDslImportResponse,
AppInfo,
AppListQuery,
AppListResponse,
AppListRow,
ChatRunPayload,
CompletionRunPayload,
DeviceCodeRequest,
DeviceCodeResponse,
DeviceLookupQuery,
DeviceLookupResponse,
DeviceMutateRequest,
DeviceMutateResponse,
DevicePollRequest,
DeviceTokenResponse,
DraftWorkflowRunPayload,
EnvVariableListResponse,
EnvVariableSetPayload,
FileUploadPayload,
FormSubmitResponse,
HealthResponse,
Hint,
HumanInputFormDefinitionResponse,
MemberActionResponse,
MemberInvitePayload,
MemberInviteResponse,
MemberListQuery,
MemberListResponse,
MemberResponse,
MemberRoleUpdatePayload,
MessageMetadata,
OpenApiFormSubmitPayload,
PermittedExternalAppsListQuery,
PermittedExternalAppsListResponse,
PublishPayload,
PublishResponse,
RestoreResponse,
RevokeResponse,
RunListQuery,
RunListResponse,
ServerVersionResponse,
SessionListQuery,
SessionListResponse,
SessionRow,
TaskStopResponse,
UsageInfo,
VersionListQuery,
VersionListResponse,
WorkflowRunData,
WorkflowRunPayload,
WorkspaceDetailResponse,
WorkspaceListQuery,
WorkspaceListResponse,
WorkspacePayload,
WorkspaceSummaryResponse,
)
from fields.file_fields import FileResponse
from fields.workflow_run_fields import WorkflowRunDetailResponse, WorkflowRunNodeExecutionListResponse
from services.entities.dsl_entities import CheckDependenciesResult, Import
register_schema_models(
openapi_ns,
AdvancedChatRunPayload,
AppDescribeQuery,
AppDslImportPayload,
AppDslExportQuery,
AppListQuery,
ChatRunPayload,
CompletionRunPayload,
DeviceCodeRequest,
DevicePollRequest,
DeviceLookupQuery,
DeviceMutateRequest,
DraftWorkflowRunPayload,
FileUploadPayload,
MemberInvitePayload,
MemberListQuery,
MemberRoleUpdatePayload,
OpenApiFormSubmitPayload,
EnvVariableSetPayload,
PermittedExternalAppsListQuery,
PublishPayload,
RunListQuery,
SessionListQuery,
VersionListQuery,
WorkflowRunPayload,
WorkspaceListQuery,
)
register_response_schema_models(
openapi_ns,
ErrorBody,
EventStreamResponse,
SimpleResultResponse,
UsageInfo,
MessageMetadata,
Hint,
AppListRow,
AppListResponse,
AppInfo,
AppDescribeInfo,
AppDescribeResponse,
AppDslExportResponse,
Import,
AppDslImportResponse,
CheckDependenciesResult,
WorkflowRunData,
AccountPayload,
WorkspacePayload,
AccountResponse,
SessionRow,
SessionListResponse,
PermittedExternalAppsListResponse,
RevokeResponse,
WorkspaceSummaryResponse,
WorkspaceListResponse,
WorkspaceDetailResponse,
MemberResponse,
MemberListResponse,
MemberInviteResponse,
MemberActionResponse,
TaskStopResponse,
FormSubmitResponse,
HumanInputFormDefinitionResponse,
DeviceCodeResponse,
DeviceTokenResponse,
DeviceLookupResponse,
DeviceMutateResponse,
FileResponse,
ServerVersionResponse,
HealthResponse,
RunListResponse,
PublishResponse,
VersionListResponse,
RestoreResponse,
EnvVariableListResponse,
WorkflowRunDetailResponse,
WorkflowRunNodeExecutionListResponse,
)
# Standalone definition for contract codegen; ErrorBody.code stays an open
# string on the wire so old clients keep parsing future codes.
register_enum_models(openapi_ns, OpenApiErrorCode)
from . import (
account,
app_dsl,
app_run,
app_workflow,
apps,
apps_permitted_external,
files,
human_input_form,
index,
oauth_device,
oauth_device_sso,
workflow_events,
workspaces,
)
# Request models are imported from _models.py and registered above.
__all__ = [
"account",
"app_dsl",
"app_run",
"app_workflow",
"apps",
"apps_permitted_external",
"files",
"human_input_form",
"index",
"oauth_device",
"oauth_device_sso",
"workflow_events",
"workspaces",
]
api.add_namespace(openapi_ns)