refactor: Make service dependencies explicit and injectable
Remove hidden cross-service coupling by making dependencies explicit through dependency injection while maintaining backward compatibility via lazy imports. Key changes: - history_service and ban_service: Removed direct module-level imports of fail2ban_metadata_service, added optional service parameters to functions - Added get_fail2ban_metadata_service() provider to dependencies.py - Updated history router to inject Fail2BanMetadataService dependency - history_service functions now use lazy imports in fallback paths for backward compatibility when service is not explicitly injected - All test patches updated to use internal _get_fail2ban_db_path() helper - jail_config_service and jail_service already follow best practices This pattern prevents circular imports, makes services testable via explicit mocking, and documents service dependencies clearly. Fixes: Instructions.md #2 - Hidden cross-service coupling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@ directly — to keep coupling explicit and testable.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from collections.abc import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator, Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Annotated, cast
|
||||
|
||||
@@ -31,6 +31,7 @@ from app.repositories.protocols import (
|
||||
SettingsRepository,
|
||||
)
|
||||
from app.services.geo_cache import GeoCache
|
||||
from app.services.protocols import Fail2BanMetadataService
|
||||
from app.utils.constants import SESSION_COOKIE_NAME
|
||||
from app.utils.rate_limiter import RateLimiter
|
||||
from app.utils.runtime_state import ApplicationState, RuntimeState
|
||||
@@ -331,6 +332,31 @@ async def get_pending_recovery(
|
||||
"""Return the current pending recovery record from application context."""
|
||||
return app_context.pending_recovery
|
||||
|
||||
|
||||
async def get_health_probe() -> Callable[[str], Awaitable[ServerStatus]]:
|
||||
"""Provide the health probe function for checking fail2ban connectivity.
|
||||
|
||||
Returns:
|
||||
A callable that probes the fail2ban socket and returns ServerStatus.
|
||||
This allows explicit dependency injection to avoid hidden service coupling.
|
||||
"""
|
||||
from app.services import health_service # noqa: PLC0415
|
||||
|
||||
return health_service.probe
|
||||
|
||||
|
||||
async def get_fail2ban_metadata_service() -> object:
|
||||
"""Provide the Fail2BanMetadataService instance.
|
||||
|
||||
Returns:
|
||||
The singleton Fail2BanMetadataService for resolving fail2ban metadata
|
||||
(such as the database path) and caching results.
|
||||
"""
|
||||
from app.services.fail2ban_metadata_service import default_fail2ban_metadata_service # noqa: PLC0415
|
||||
|
||||
return default_fail2ban_metadata_service
|
||||
|
||||
|
||||
async def require_auth(
|
||||
request: Request,
|
||||
db: Annotated[aiosqlite.Connection, Depends(get_db)],
|
||||
@@ -413,6 +439,7 @@ Fail2BanStartCommandDep = Annotated[str, Depends(get_fail2ban_start_command)]
|
||||
GeoCacheDep = Annotated[GeoCache, Depends(get_geo_cache)]
|
||||
ServerStatusDep = Annotated[ServerStatus, Depends(get_server_status)]
|
||||
PendingRecoveryDep = Annotated[PendingRecovery | None, Depends(get_pending_recovery)]
|
||||
HealthProbeDep = Annotated[Callable[[str], Awaitable[ServerStatus]], Depends(get_health_probe)]
|
||||
SessionCacheDep = Annotated[SessionCache, Depends(get_session_cache)]
|
||||
SessionRepoDep = Annotated[SessionRepository, Depends(get_session_repo)]
|
||||
SettingsRepoDep = Annotated[SettingsRepository, Depends(get_settings_repo)]
|
||||
@@ -425,3 +452,4 @@ AppStateDep = Annotated[ApplicationContext, Depends(get_app_state)]
|
||||
AppDep = Annotated[FastAPI, Depends(get_app)]
|
||||
AuthDep = Annotated[Session, Depends(require_auth)]
|
||||
LoginRateLimiterDep = Annotated[RateLimiter, Depends(get_login_rate_limiter)]
|
||||
Fail2BanMetadataServiceDep = Annotated[Fail2BanMetadataService, Depends(get_fail2ban_metadata_service)]
|
||||
|
||||
Reference in New Issue
Block a user