Files
govoplan-files/src/govoplan_files/backend/provider_state.py
T

134 lines
4.3 KiB
Python

from __future__ import annotations
from collections import defaultdict
from datetime import UTC, datetime
from sqlalchemy import or_, select
from sqlalchemy.orm import Session
from govoplan_core.core.provider_governance import (
ExternalProviderRuntimeState,
ExternalProviderStateContext,
)
from govoplan_files.backend.db.models import FileConnectorProfile, FileConnectorSpace
from govoplan_files.backend.storage.connector_providers import (
ConnectorProviderDescriptor,
connector_provider_descriptors,
)
REMOTE_STORAGE_PROVIDER_ID = "files.remote_storage"
def remote_storage_provider_states(
context: ExternalProviderStateContext,
) -> tuple[ExternalProviderRuntimeState, ...]:
if not isinstance(context.session, Session):
raise RuntimeError("Files provider state requires a database session.")
statement = select(FileConnectorProfile)
if context.tenant_id is not None:
statement = statement.where(
or_(
FileConnectorProfile.tenant_id.is_(None),
FileConnectorProfile.tenant_id == context.tenant_id,
)
)
profiles = tuple(
context.session.scalars(
statement.order_by(
FileConnectorProfile.tenant_id,
FileConnectorProfile.id,
).limit(context.max_items + 1)
)
)
if not profiles:
return ()
profile_ids = tuple(item.id for item in profiles)
space_statement = select(FileConnectorSpace).where(
FileConnectorSpace.connector_profile_id.in_(profile_ids),
FileConnectorSpace.deleted_at.is_(None),
)
if context.tenant_id is not None:
space_statement = space_statement.where(
FileConnectorSpace.tenant_id == context.tenant_id
)
spaces_by_profile: dict[str, list[FileConnectorSpace]] = defaultdict(list)
for space in context.session.scalars(space_statement):
spaces_by_profile[space.connector_profile_id].append(space)
descriptors = {
item.provider: item for item in connector_provider_descriptors()
}
observed_at = datetime.now(UTC)
return tuple(
_profile_state(
profile,
spaces=spaces_by_profile.get(profile.id, []),
descriptor=descriptors.get(profile.provider),
observed_at=observed_at,
)
for profile in profiles
)
def _profile_state(
profile: FileConnectorProfile,
*,
spaces: list[FileConnectorSpace],
descriptor: ConnectorProviderDescriptor | None,
observed_at: datetime,
) -> ExternalProviderRuntimeState:
active_spaces = tuple(item for item in spaces if item.is_active)
active = bool(profile.enabled)
implementation_ready = bool(
descriptor is not None and descriptor.implemented and descriptor.installed
)
health = (
"inactive"
if not active
else "error"
if not implementation_ready
else "unknown"
)
recovery = (
"not_applicable"
if not active
else "unsupported"
if not implementation_ready
else "attention"
)
detail = (
"Remote-storage connector profile is disabled."
if not active
else "The configured provider is not available in this runtime."
if not implementation_ready
else "Software support is available; no live remote health observation is retained."
)
return ExternalProviderRuntimeState(
provider_id=REMOTE_STORAGE_PROVIDER_ID,
binding_ref=f"files:connector-profile:{profile.id}",
authority_mode="external_mirror",
observed_at=observed_at,
configured=True,
active=active,
health=health,
freshness="unknown" if active else "not_applicable",
conflict="not_applicable",
recovery=recovery,
detail=detail,
metrics={
"provider": profile.provider,
"configured_spaces": len(spaces),
"active_spaces": len(active_spaces),
"write_requested_spaces": sum(
1 for item in active_spaces if not item.read_only
),
"software_implemented": bool(descriptor and descriptor.implemented),
"optional_dependency_available": bool(descriptor and descriptor.installed),
},
)
__all__ = ["REMOTE_STORAGE_PROVIDER_ID", "remote_storage_provider_states"]