1. 移除多个导出接口的显式response_model声明 2. 调整access_rule和test_case的创建接口位置,修复静态路径冲突 3. 优化适配器配置校验的异常处理逻辑 4. 重构集成路由的查询逻辑,统一使用get_integration_or_raise 5. 新增channels路由组下的capability、reports、dashboard、webhook、wizard、doctor、directory、session共8个子路由模块 6. 注册channels_router到全局路由列表
284 lines
11 KiB
Python
284 lines
11 KiB
Python
"""Integration 子域 Router.
|
||
|
||
外部系统限界上下文的厂商集成目录查询 API。所有端点直接读取
|
||
``IntegrationRegistry`` 与 ``IntegrationOperationRegistry`` 注册表全局单例
|
||
(代码注册的内存态对象),不引入 use_cases 层——数据源无持久化需求,与
|
||
adapter_router / auth_plugin_router 元数据端点保持一致。
|
||
|
||
设计依据:docs/vibe/v1.1/设计方案/RouterAPI扩展设计/15-integration_router扩展设计方案.md
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
from yuxi.external_systems.integrations.operation_registry import (
|
||
IntegrationOperationRegistry,
|
||
)
|
||
from yuxi.external_systems.integrations.registry import IntegrationRegistry
|
||
from yuxi.storage.postgres.models_business import User
|
||
|
||
from server.utils.auth_middleware import get_required_user
|
||
|
||
integration_router = APIRouter(
|
||
prefix="/integrations",
|
||
tags=["external-systems-integration"],
|
||
)
|
||
|
||
|
||
# 列表端点剔除的字段:Schema/配置/依赖说明,避免列表响应过重
|
||
_SUMMARY_EXCLUDED_FIELDS = ("connection_extra_schema", "example_config", "dependency_note")
|
||
|
||
|
||
# =============================================================================
|
||
# === 静态路径端点(必须在 /{key} 之前声明) ===
|
||
# =============================================================================
|
||
|
||
|
||
@integration_router.get("", response_model=dict)
|
||
async def list_integrations(
|
||
adapter_type: str | None = Query(None, description="按适配器类型精确过滤"),
|
||
tags: str | None = Query(None, description="按标签过滤,多个标签以逗号分隔(OR 关系)"),
|
||
keyword: str | None = Query(None, description="模糊匹配 display_name / description(大小写不敏感)"),
|
||
limit: int = Query(50, ge=1, le=200, description="分页大小,最大 200"),
|
||
offset: int = Query(0, ge=0, description="分页偏移"),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询厂商集成目录列表,支持按适配器类型、标签、关键词过滤与分页。
|
||
|
||
列表项返回精简字段(剔除 connection_extra_schema / example_config /
|
||
dependency_note),适合前端集成市场页表格展示。``total`` 为满足筛选
|
||
条件的全量总数(由 count_integrations 计算),非当前分页条数。
|
||
"""
|
||
tags_list: list[str] | None = None
|
||
if tags is not None:
|
||
tags_list = [t.strip() for t in tags.split(",") if t.strip()]
|
||
if not tags_list:
|
||
tags_list = None
|
||
|
||
metadata_list = IntegrationRegistry.list_integrations(
|
||
adapter_type=adapter_type,
|
||
tags=tags_list,
|
||
keyword=keyword,
|
||
limit=limit,
|
||
offset=offset,
|
||
)
|
||
total = IntegrationRegistry.count_integrations(
|
||
adapter_type=adapter_type,
|
||
tags=tags_list,
|
||
keyword=keyword,
|
||
)
|
||
items = [
|
||
{k: v for k, v in metadata.model_dump().items() if k not in _SUMMARY_EXCLUDED_FIELDS}
|
||
for metadata in metadata_list
|
||
]
|
||
return {
|
||
"success": True,
|
||
"data": {
|
||
"items": items,
|
||
"total": total,
|
||
"limit": limit,
|
||
"offset": offset,
|
||
},
|
||
}
|
||
|
||
|
||
@integration_router.get("/overview", response_model=dict)
|
||
async def get_integrations_overview(
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询厂商集成的分组总览(按 adapter_type 分组 + 标签统计)。
|
||
|
||
通过 filter_integrations 获取全量集成(无分页),按 ``adapter_type``
|
||
分组构造 ``by_adapter_type`` 字典(每组仅含精简字段),并统计每个标签
|
||
出现次数构造 ``by_tag`` 字典。适合前端管理页树形渲染与统计卡片。
|
||
"""
|
||
metadata_list = IntegrationRegistry.filter_integrations()
|
||
|
||
by_adapter_type: dict[str, list[dict[str, Any]]] = {}
|
||
by_tag: dict[str, int] = {}
|
||
for metadata in metadata_list:
|
||
by_adapter_type.setdefault(metadata.adapter_type, []).append(
|
||
{
|
||
"key": metadata.key,
|
||
"display_name": metadata.display_name,
|
||
"adapter_type": metadata.adapter_type,
|
||
"tags": metadata.tags,
|
||
}
|
||
)
|
||
for tag in metadata.tags:
|
||
by_tag[tag] = by_tag.get(tag, 0) + 1
|
||
|
||
return {
|
||
"success": True,
|
||
"data": {
|
||
"by_adapter_type": by_adapter_type,
|
||
"total": len(metadata_list),
|
||
"by_tag": by_tag,
|
||
},
|
||
}
|
||
|
||
|
||
@integration_router.get("/categories", response_model=dict)
|
||
async def get_integration_categories(
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询厂商集成的分类标签树(基于 tags 字段聚合)。
|
||
|
||
通过 filter_integrations 获取全量集成(无分页),每个标签返回
|
||
``count`` 与 ``integrations``(key 列表),按 ``count`` 降序、
|
||
``tag`` 升序排序。适合前端集成市场页分类筛选侧边栏。
|
||
"""
|
||
metadata_list = IntegrationRegistry.filter_integrations()
|
||
|
||
tag_to_integrations: dict[str, list[str]] = {}
|
||
for metadata in metadata_list:
|
||
for tag in metadata.tags:
|
||
tag_to_integrations.setdefault(tag, []).append(metadata.key)
|
||
|
||
items = [{"tag": tag, "count": len(keys), "integrations": keys} for tag, keys in tag_to_integrations.items()]
|
||
items.sort(key=lambda it: (-it["count"], it["tag"]))
|
||
|
||
return {
|
||
"success": True,
|
||
"data": {"items": items, "total": len(items)},
|
||
}
|
||
|
||
|
||
@integration_router.get("/adapter-types", response_model=dict)
|
||
async def get_integration_adapter_types(
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询所有厂商集成涉及的适配器类型(去重),用于前端过滤选项。
|
||
|
||
仅返回有厂商集成的适配器类型(当前全部为 http),与 adapter_router 的
|
||
``GET /adapters``(所有注册协议适配器)语义不同。
|
||
"""
|
||
adapter_types = IntegrationRegistry.list_adapter_types()
|
||
return {
|
||
"success": True,
|
||
"data": {"items": adapter_types, "total": len(adapter_types)},
|
||
}
|
||
|
||
|
||
@integration_router.get("/source-types", response_model=dict)
|
||
async def get_integration_source_types(
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询所有已注册操作 handler 的数据源类型(含操作支持矩阵)。
|
||
|
||
对每个 source_type 调用 ``list_operations`` 获取已注册操作,按
|
||
``source_type`` 升序排序。适合前端"工具生成"页的 source_type 选择。
|
||
仅返回厂商 source_type,不含 openapi / postman 等通用 source_type
|
||
(通用 source_type 走资产导入流程,未注册操作 handler)。
|
||
"""
|
||
source_types = IntegrationOperationRegistry.list_source_types()
|
||
items = [
|
||
{
|
||
"source_type": source_type,
|
||
"operations": IntegrationOperationRegistry.list_operations(source_type),
|
||
}
|
||
for source_type in sorted(source_types)
|
||
]
|
||
return {
|
||
"success": True,
|
||
"data": {"items": items, "total": len(items)},
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# === 路径参数端点(/{key}) ===
|
||
# =============================================================================
|
||
|
||
|
||
@integration_router.get("/{key}", response_model=dict)
|
||
async def get_integration_detail(
|
||
key: str,
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询指定厂商集成的完整详情(含所有 12 个字段)。
|
||
|
||
``get_integration_or_raise`` 未找到时抛 ``EntityNotFoundError`` (404),
|
||
由全局处理器统一映射。返回 ``model_dump()`` 完整字段,包含
|
||
connection_extra_schema / example_config / dependency_note。
|
||
"""
|
||
metadata = IntegrationRegistry.get_integration_or_raise(key)
|
||
return {"success": True, "data": metadata.model_dump()}
|
||
|
||
|
||
@integration_router.get("/{key}/operations", response_model=dict)
|
||
async def get_integration_operations(
|
||
key: str,
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询指定厂商集成已注册的操作 handler 列表(按 source_type 分组)。
|
||
|
||
遍历 ``metadata.source_types``,对每个 source_type 调用
|
||
``list_operations`` 获取已注册操作。``operations_by_source_type`` 为
|
||
``{source_type: [operations]}`` 字典;``supported_operations`` 为所有
|
||
source_type 操作的并集(去重,按字母升序排序)。
|
||
|
||
``get_integration_or_raise`` 未找到时抛 ``EntityNotFoundError`` (404)。
|
||
"""
|
||
metadata = IntegrationRegistry.get_integration_or_raise(key)
|
||
|
||
operations_by_source_type: dict[str, list[str]] = {}
|
||
for source_type in metadata.source_types:
|
||
operations_by_source_type[source_type] = IntegrationOperationRegistry.list_operations(source_type)
|
||
|
||
supported_operations = sorted(set(op for ops in operations_by_source_type.values() for op in ops))
|
||
|
||
return {
|
||
"success": True,
|
||
"data": {
|
||
"key": key,
|
||
"operations_by_source_type": operations_by_source_type,
|
||
"supported_operations": supported_operations,
|
||
},
|
||
}
|
||
|
||
|
||
@integration_router.get("/{key}/connection-schema", response_model=dict)
|
||
async def get_integration_connection_schema(
|
||
key: str,
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取厂商集成的连接配置 JSON Schema,用于前端动态表单渲染。
|
||
|
||
返回 ``connection_extra_schema`` 字段(可能为 None,表示该集成无额外
|
||
连接配置)。与详情端点的差异:本端点仅返回 Schema,适合前端表单渲染
|
||
时独立获取。
|
||
|
||
``get_integration_or_raise`` 未找到时抛 ``EntityNotFoundError`` (404)。
|
||
"""
|
||
metadata = IntegrationRegistry.get_integration_or_raise(key)
|
||
return {
|
||
"success": True,
|
||
"data": {
|
||
"key": key,
|
||
"connection_extra_schema": metadata.connection_extra_schema,
|
||
},
|
||
}
|
||
|
||
|
||
@integration_router.get("/{key}/example-config", response_model=dict)
|
||
async def get_integration_example_config(
|
||
key: str,
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取厂商集成的推荐配置示例,用于前端表单预填与配置引导。
|
||
|
||
返回 ``example_config`` 字段(可能为 None,表示该集成无推荐配置)。
|
||
|
||
``get_integration_or_raise`` 未找到时抛 ``EntityNotFoundError`` (404)。
|
||
"""
|
||
metadata = IntegrationRegistry.get_integration_or_raise(key)
|
||
return {
|
||
"success": True,
|
||
"data": {
|
||
"key": key,
|
||
"example_config": metadata.example_config,
|
||
},
|
||
}
|