master/adapter/di/container.py
2026-04-02 13:27:44 +03:00

121 lines
3.6 KiB
Python

from collections.abc import Mapping
from dataclasses import dataclass, field
from datetime import UTC, datetime, timedelta
from pathlib import Path
import docker
from docker import DockerClient
from adapter.config.loader import load_config
from adapter.config.model import AppConfig
from adapter.docker.runtime import DockerSandboxRuntime
from adapter.observability.factory import build_observability
from adapter.observability.runtime import ObservabilityRuntime
from domain.user import User
from repository.sandbox_session import InMemorySandboxSessionRepository
from repository.user import InMemoryUserRepository
from usecase.interface import Clock
from usecase.sandbox import CleanupExpiredSandboxes, CreateSandbox
from usecase.user import GetUser
@dataclass(frozen=True, slots=True)
class AppRepositories:
user: InMemoryUserRepository
sandbox_session: InMemorySandboxSessionRepository
@dataclass(frozen=True, slots=True)
class AppUsecases:
get_user: GetUser
create_sandbox: CreateSandbox
cleanup_expired_sandboxes: CleanupExpiredSandboxes
@dataclass(slots=True)
class AppContainer:
config: AppConfig
observability: ObservabilityRuntime
repositories: AppRepositories
usecases: AppUsecases
_docker_client: DockerClient = field(repr=False)
_is_shutdown: bool = field(default=False, init=False, repr=False)
def shutdown(self) -> None:
if self._is_shutdown:
return
self._is_shutdown = True
errors: list[Exception] = []
for action in (self._docker_client.close, self.observability.shutdown):
try:
action()
except Exception as exc:
errors.append(exc)
if errors:
raise ExceptionGroup('shutdown failed', errors)
class SystemClock(Clock):
def now(self) -> datetime:
return datetime.now(tz=UTC)
def build_container(
config_path: Path | str | None = None,
env_path: Path | str | None = None,
environ: Mapping[str, str] | None = None,
config: AppConfig | None = None,
) -> AppContainer:
app_config = config
if app_config is None:
app_config = load_config(
config_path=config_path,
env_path=env_path,
environ=environ,
)
observability = build_observability(app_config)
docker_client: DockerClient = docker.from_env()
clock = SystemClock()
user_repository = InMemoryUserRepository(
observability.tracer, [User(id='123', email='aza@gglamer.ru', name='gglamer')]
)
sandbox_repository = InMemorySandboxSessionRepository()
sandbox_runtime = DockerSandboxRuntime(app_config.sandbox, docker_client)
repositories = AppRepositories(
user=user_repository,
sandbox_session=sandbox_repository,
)
usecases = AppUsecases(
get_user=GetUser(
repository=user_repository,
logger=observability.logger,
tracer=observability.tracer,
),
create_sandbox=CreateSandbox(
repository=sandbox_repository,
runtime=sandbox_runtime,
clock=clock,
logger=observability.logger,
ttl=timedelta(seconds=app_config.sandbox.ttl_seconds),
),
cleanup_expired_sandboxes=CleanupExpiredSandboxes(
repository=sandbox_repository,
runtime=sandbox_runtime,
clock=clock,
logger=observability.logger,
),
)
return AppContainer(
config=app_config,
observability=observability,
repositories=repositories,
usecases=usecases,
_docker_client=docker_client,
)