345 lines
14 KiB
Python
345 lines
14 KiB
Python
|
|
"""infrastructure/container.py:use_cases service 装配容器。
|
|||
|
|
|
|||
|
|
本模块是六边形架构的"唯一装配点"(设计方案 §8.1),负责:
|
|||
|
|
- 将 persistence 仓储 / runtime 横切关注点 / framework 单例 装配为 24 个 use_cases service
|
|||
|
|
- 对外暴露两个 factory:
|
|||
|
|
* ``create_use_cases``:底层 factory,由调用方注入全部依赖
|
|||
|
|
* ``create_use_cases_from_db``:请求级 factory,封装三层创建(repos + runtime + use_cases)
|
|||
|
|
|
|||
|
|
边界规范:
|
|||
|
|
- 本模块是**唯一允许 import use_cases service 实现类**的地方
|
|||
|
|
- ``UseCases`` dataclass 字段类型使用驱动端口 Protocol(非实现类)
|
|||
|
|
- service 构造调用使用**实际构造函数签名**(已逐个核对 use_cases/*.py 的 ``__init__``)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from dataclasses import dataclass
|
|||
|
|
from typing import Any
|
|||
|
|
|
|||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
|
|
|||
|
|
from yuxi.external_systems.adapters.persistence import (
|
|||
|
|
Repositories,
|
|||
|
|
SqlAlchemyUnitOfWork,
|
|||
|
|
create_repositories,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.core.contracts import (
|
|||
|
|
AdapterRegistry,
|
|||
|
|
EventPublisher,
|
|||
|
|
ToolExecutor,
|
|||
|
|
UnitOfWork,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.exceptions import EntityNotFoundError
|
|||
|
|
from yuxi.external_systems.framework.runtime import (
|
|||
|
|
InMemoryEventPublisher,
|
|||
|
|
RepositoryPorts,
|
|||
|
|
RuntimeServices,
|
|||
|
|
TestCaseRunnerImpl,
|
|||
|
|
create_runtime_services,
|
|||
|
|
create_tool_executor,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.context_builder import ContextBuilder
|
|||
|
|
|
|||
|
|
# 驱动端口类型(供 UseCases dataclass 字段标注,非实现类)
|
|||
|
|
from yuxi.external_systems.use_cases.ports import (
|
|||
|
|
AccessRuleServicePort,
|
|||
|
|
AdapterStatsServicePort,
|
|||
|
|
AlertServicePort,
|
|||
|
|
AssetServicePort,
|
|||
|
|
AuditLogServicePort,
|
|||
|
|
ContextBuilderPort,
|
|||
|
|
DashboardServicePort,
|
|||
|
|
EnvironmentServicePort,
|
|||
|
|
ExecutionServicePort,
|
|||
|
|
HealthCheckServicePort,
|
|||
|
|
ImportServicePort,
|
|||
|
|
IntegrationToolServicePort,
|
|||
|
|
MetricServicePort,
|
|||
|
|
NotificationChannelServicePort,
|
|||
|
|
NotificationServicePort,
|
|||
|
|
QuotaServicePort,
|
|||
|
|
SecretRotationServicePort,
|
|||
|
|
SystemServicePort,
|
|||
|
|
TestCaseServicePort,
|
|||
|
|
TestRegressionServicePort,
|
|||
|
|
TokenServicePort,
|
|||
|
|
ToolServicePort,
|
|||
|
|
WebhookEventHandlerPort,
|
|||
|
|
WebhookSubscriptionServicePort,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.ports.trash_service_port import TrashServicePort
|
|||
|
|
|
|||
|
|
# 唯一允许 import service 实现类的地方(设计方案 §8.1)
|
|||
|
|
from yuxi.external_systems.use_cases.services.access_rule_service import AccessRuleService
|
|||
|
|
from yuxi.external_systems.use_cases.services.adapter_stats_service import AdapterStatsService
|
|||
|
|
from yuxi.external_systems.use_cases.services.alert_service import AlertService
|
|||
|
|
from yuxi.external_systems.use_cases.services.asset_service import AssetService
|
|||
|
|
from yuxi.external_systems.use_cases.services.audit_log_service import AuditLogService
|
|||
|
|
from yuxi.external_systems.use_cases.services.dashboard_service import DashboardService
|
|||
|
|
from yuxi.external_systems.use_cases.services.environment_service import EnvironmentService
|
|||
|
|
from yuxi.external_systems.use_cases.services.execution_service import ExecutionService
|
|||
|
|
from yuxi.external_systems.use_cases.services.health_check_service import HealthCheckService
|
|||
|
|
from yuxi.external_systems.use_cases.services.import_service import ImportService
|
|||
|
|
from yuxi.external_systems.use_cases.services.integration_tool_service import (
|
|||
|
|
IntegrationToolService,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.services.metric_service import MetricService
|
|||
|
|
from yuxi.external_systems.use_cases.services.notification_channel_service import (
|
|||
|
|
NotificationChannelService,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.services.notification_service import NotificationService
|
|||
|
|
from yuxi.external_systems.use_cases.services.quota_service import QuotaService
|
|||
|
|
from yuxi.external_systems.use_cases.services.secret_rotation_service import (
|
|||
|
|
SecretRotationService,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.services.system_service import SystemService
|
|||
|
|
from yuxi.external_systems.use_cases.services.test_case_service import TestCaseService
|
|||
|
|
from yuxi.external_systems.use_cases.services.test_regression_service import (
|
|||
|
|
TestRegressionService,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.services.token_service import TokenService
|
|||
|
|
from yuxi.external_systems.use_cases.services.tool_service import ToolService
|
|||
|
|
from yuxi.external_systems.use_cases.services.trash_service import TrashService
|
|||
|
|
from yuxi.external_systems.use_cases.services.webhook_event_handler import (
|
|||
|
|
WebhookEventHandler,
|
|||
|
|
)
|
|||
|
|
from yuxi.external_systems.use_cases.services.webhook_subscription_service import (
|
|||
|
|
WebhookSubscriptionService,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
from .config import default_adapter_registry
|
|||
|
|
|
|||
|
|
__all__ = [
|
|||
|
|
"UseCases",
|
|||
|
|
"create_use_cases",
|
|||
|
|
"create_use_cases_from_db",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class UseCases:
|
|||
|
|
"""一组 use_cases service 实例(以端口类型标注),由 factory 创建。
|
|||
|
|
|
|||
|
|
字段类型使用驱动端口 Protocol(非实现类),保证调用方只依赖端口。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
context_builder: ContextBuilderPort
|
|||
|
|
system_service: SystemServicePort
|
|||
|
|
tool_service: ToolServicePort
|
|||
|
|
environment_service: EnvironmentServicePort
|
|||
|
|
execution_service: ExecutionServicePort
|
|||
|
|
token_service: TokenServicePort
|
|||
|
|
audit_log_service: AuditLogServicePort
|
|||
|
|
asset_service: AssetServicePort
|
|||
|
|
import_service: ImportServicePort
|
|||
|
|
integration_tool_service: IntegrationToolServicePort
|
|||
|
|
webhook_subscription_service: WebhookSubscriptionServicePort
|
|||
|
|
webhook_event_handler: WebhookEventHandlerPort
|
|||
|
|
quota_service: QuotaServicePort
|
|||
|
|
alert_service: AlertServicePort
|
|||
|
|
notification_service: NotificationServicePort
|
|||
|
|
notification_channel_service: NotificationChannelServicePort
|
|||
|
|
secret_rotation_service: SecretRotationServicePort
|
|||
|
|
access_rule_service: AccessRuleServicePort
|
|||
|
|
adapter_stats_service: AdapterStatsServicePort
|
|||
|
|
test_case_service: TestCaseServicePort
|
|||
|
|
test_regression_service: TestRegressionServicePort
|
|||
|
|
metric_service: MetricServicePort
|
|||
|
|
health_check_service: HealthCheckServicePort
|
|||
|
|
dashboard_service: DashboardServicePort
|
|||
|
|
trash_service: TrashServicePort
|
|||
|
|
|
|||
|
|
|
|||
|
|
def create_use_cases(
|
|||
|
|
repos: Repositories,
|
|||
|
|
runtime: RuntimeServices,
|
|||
|
|
uow: UnitOfWork,
|
|||
|
|
event_publisher: EventPublisher,
|
|||
|
|
*,
|
|||
|
|
adapter_registry: AdapterRegistry,
|
|||
|
|
tool_executor: ToolExecutor,
|
|||
|
|
context_builder: ContextBuilderPort | None = None,
|
|||
|
|
) -> UseCases:
|
|||
|
|
"""factory 函数:创建一组 use_cases service 实例。
|
|||
|
|
|
|||
|
|
由 infrastructure 层调用,注入所有依赖。实现类仅在此处 import(规范 §8.1)。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
repos: 仓储聚合(persistence 层创建,共享 db 会话)。
|
|||
|
|
runtime: 请求级 runtime 横切关注点集合。
|
|||
|
|
uow: 工作单元(事务边界)。
|
|||
|
|
event_publisher: 事件发布器。
|
|||
|
|
adapter_registry: 适配器注册表(全局只读单例)。
|
|||
|
|
tool_executor: 工具执行器(请求级实例)。
|
|||
|
|
context_builder: 可选,上下文构造器。未传入时由本函数创建。
|
|||
|
|
``create_use_cases_from_db`` 传入已创建的实例以复用给 ToolExecutor。
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
装配完成的 ``UseCases`` 实例(24 个 service + 1 个 context_builder)。
|
|||
|
|
"""
|
|||
|
|
context_builder = context_builder or ContextBuilder(repos, runtime)
|
|||
|
|
system_service = SystemService(repos, runtime, adapter_registry, uow, event_publisher)
|
|||
|
|
tool_service = ToolService(
|
|||
|
|
repos,
|
|||
|
|
runtime,
|
|||
|
|
adapter_registry,
|
|||
|
|
tool_executor,
|
|||
|
|
uow,
|
|||
|
|
event_publisher,
|
|||
|
|
context_builder,
|
|||
|
|
)
|
|||
|
|
environment_service = EnvironmentService(repos, runtime, uow, event_publisher)
|
|||
|
|
execution_service = ExecutionService(
|
|||
|
|
repos,
|
|||
|
|
runtime,
|
|||
|
|
uow,
|
|||
|
|
event_publisher,
|
|||
|
|
tool_executor,
|
|||
|
|
)
|
|||
|
|
token_service = TokenService(repos, runtime, uow, event_publisher)
|
|||
|
|
audit_log_service = AuditLogService(repos, runtime)
|
|||
|
|
asset_service = AssetService(repos, runtime, adapter_registry, uow, event_publisher)
|
|||
|
|
import_service = ImportService(repos, runtime, adapter_registry, uow, event_publisher, tool_service)
|
|||
|
|
integration_tool_service = IntegrationToolService(
|
|||
|
|
repos,
|
|||
|
|
runtime,
|
|||
|
|
uow,
|
|||
|
|
event_publisher,
|
|||
|
|
tool_service,
|
|||
|
|
)
|
|||
|
|
webhook_event_handler = WebhookEventHandler(repos, runtime, uow, event_publisher)
|
|||
|
|
webhook_subscription_service = WebhookSubscriptionService(
|
|||
|
|
repos,
|
|||
|
|
runtime,
|
|||
|
|
uow,
|
|||
|
|
event_publisher,
|
|||
|
|
webhook_event_handler,
|
|||
|
|
)
|
|||
|
|
quota_service = QuotaService(repos, runtime, uow, event_publisher)
|
|||
|
|
alert_service = AlertService(repos, runtime, uow, event_publisher)
|
|||
|
|
notification_service = NotificationService(repos, runtime, uow, event_publisher)
|
|||
|
|
notification_channel_service = NotificationChannelService(
|
|||
|
|
repos,
|
|||
|
|
runtime,
|
|||
|
|
uow,
|
|||
|
|
event_publisher,
|
|||
|
|
)
|
|||
|
|
secret_rotation_service = SecretRotationService(repos, runtime, uow, event_publisher)
|
|||
|
|
access_rule_service = AccessRuleService(repos, runtime, uow, event_publisher)
|
|||
|
|
test_case_service = TestCaseService(repos, runtime, uow, event_publisher)
|
|||
|
|
test_regression_service = TestRegressionService(repos, runtime, uow, event_publisher)
|
|||
|
|
metric_service = MetricService(repos.metric)
|
|||
|
|
health_check_service = HealthCheckService(repos.health_check)
|
|||
|
|
adapter_stats_service = AdapterStatsService(repos)
|
|||
|
|
# 纯查询用例,仅注入 repos(对齐 AdapterStatsService / MetricService / HealthCheckService)
|
|||
|
|
dashboard_service = DashboardService(repos)
|
|||
|
|
trash_service = TrashService(repos, runtime, uow, event_publisher)
|
|||
|
|
|
|||
|
|
return UseCases(
|
|||
|
|
context_builder=context_builder,
|
|||
|
|
system_service=system_service,
|
|||
|
|
tool_service=tool_service,
|
|||
|
|
environment_service=environment_service,
|
|||
|
|
execution_service=execution_service,
|
|||
|
|
token_service=token_service,
|
|||
|
|
audit_log_service=audit_log_service,
|
|||
|
|
asset_service=asset_service,
|
|||
|
|
import_service=import_service,
|
|||
|
|
integration_tool_service=integration_tool_service,
|
|||
|
|
webhook_subscription_service=webhook_subscription_service,
|
|||
|
|
webhook_event_handler=webhook_event_handler,
|
|||
|
|
quota_service=quota_service,
|
|||
|
|
alert_service=alert_service,
|
|||
|
|
notification_service=notification_service,
|
|||
|
|
notification_channel_service=notification_channel_service,
|
|||
|
|
secret_rotation_service=secret_rotation_service,
|
|||
|
|
access_rule_service=access_rule_service,
|
|||
|
|
adapter_stats_service=adapter_stats_service,
|
|||
|
|
test_case_service=test_case_service,
|
|||
|
|
test_regression_service=test_regression_service,
|
|||
|
|
metric_service=metric_service,
|
|||
|
|
health_check_service=health_check_service,
|
|||
|
|
dashboard_service=dashboard_service,
|
|||
|
|
trash_service=trash_service,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def create_use_cases_from_db(db: AsyncSession) -> UseCases:
|
|||
|
|
"""请求级工厂:从 db session 创建完整的 use_cases 实例。
|
|||
|
|
|
|||
|
|
封装三层创建逻辑(repos + runtime + use_cases),对调用方隐藏内部依赖结构。
|
|||
|
|
由 router 在请求开始时调用。
|
|||
|
|
|
|||
|
|
注意:``ToolExecutor`` 是请求级实例(依赖请求级 recorder + metrics_collector),
|
|||
|
|
在此处创建,不使用全局单例。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
db: SQLAlchemy 异步会话(请求级)。
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
装配完成的 ``UseCases`` 实例(24 个 service + 1 个 context_builder)。
|
|||
|
|
"""
|
|||
|
|
# 1. 请求级 repos + UnitOfWork
|
|||
|
|
repos = create_repositories(db)
|
|||
|
|
uow = SqlAlchemyUnitOfWork(db)
|
|||
|
|
|
|||
|
|
# 2. 请求级 runtime services(先不传 test_execute_callback:ToolExecutor 需
|
|||
|
|
# runtime.metrics_collector,而回调需 ToolExecutor,存在循环依赖,步骤 4 回填)
|
|||
|
|
runtime_repos = RepositoryPorts(
|
|||
|
|
token=repos.token,
|
|||
|
|
audit_log=repos.audit_log,
|
|||
|
|
metric=repos.metric,
|
|||
|
|
alert=repos.alert,
|
|||
|
|
quota_usage=repos.quota_usage,
|
|||
|
|
tool_access_rule=repos.tool_access_rule,
|
|||
|
|
webhook_subscription=repos.webhook_subscription,
|
|||
|
|
webhook_event=repos.webhook_event,
|
|||
|
|
tool_test_case=repos.tool_test_case,
|
|||
|
|
secret_rotation_policy=repos.secret_rotation_policy,
|
|||
|
|
)
|
|||
|
|
runtime = create_runtime_services(runtime_repos)
|
|||
|
|
event_publisher = InMemoryEventPublisher()
|
|||
|
|
|
|||
|
|
# 3. 请求级 ContextBuilder(ToolExecutor 系统级路径依赖它构造 RuntimeContext)
|
|||
|
|
context_builder = ContextBuilder(repos, runtime)
|
|||
|
|
|
|||
|
|
# 4. 请求级 ToolExecutor(注入请求级 recorder + metrics_collector + context_builder)
|
|||
|
|
tool_executor = create_tool_executor(
|
|||
|
|
recorder=repos.execution_recorder,
|
|||
|
|
metrics_collector=runtime.metrics_collector,
|
|||
|
|
context_builder=context_builder,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 5. 回填 TestCaseRunner 执行回调(两阶段创建,打破循环依赖)
|
|||
|
|
# 将 ToolExecutor.execute_external_tool 包装为 ExecuteCallback:
|
|||
|
|
# (tool_slug, parameters, env_key) → 执行结果。
|
|||
|
|
# 回调签名与执行器不直接兼容(回调收 slug,执行器需 ExecutableTool),
|
|||
|
|
# 需通过 repos.tool.get_by_slug 解析 slug,对齐 tool_service 执行路径。
|
|||
|
|
async def _execute_callback(
|
|||
|
|
tool_slug: str,
|
|||
|
|
parameters: dict[str, Any],
|
|||
|
|
env_key: str | None,
|
|||
|
|
) -> Any:
|
|||
|
|
tool = await repos.tool.get_by_slug(tool_slug)
|
|||
|
|
if tool is None:
|
|||
|
|
raise EntityNotFoundError(f"外部工具 {tool_slug} 不存在")
|
|||
|
|
env = {"YUXI_ACTIVE_ENV": env_key} if env_key else None
|
|||
|
|
return await tool_executor.execute_external_tool(tool, parameters, env=env)
|
|||
|
|
|
|||
|
|
runtime.test_case_runner = TestCaseRunnerImpl(
|
|||
|
|
repos.tool_test_case,
|
|||
|
|
_execute_callback,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 6. 装配 use_cases(复用步骤 3 创建的 context_builder,避免重复实例化)
|
|||
|
|
return create_use_cases(
|
|||
|
|
repos,
|
|||
|
|
runtime,
|
|||
|
|
uow,
|
|||
|
|
event_publisher,
|
|||
|
|
adapter_registry=default_adapter_registry,
|
|||
|
|
tool_executor=tool_executor,
|
|||
|
|
context_builder=context_builder,
|
|||
|
|
)
|