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"]