chore: sync GovOPlaN module split state

This commit is contained in:
2026-07-10 12:51:21 +02:00
parent 18e6c3eb9b
commit 13fd7fc3bd
50 changed files with 12678 additions and 1109 deletions

View File

@@ -0,0 +1,248 @@
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from sqlalchemy import false, or_
from sqlalchemy.orm import Session
from govoplan_files.backend.db.models import FileConnectorSpace
from govoplan_files.backend.storage.access import ensure_owner_access, user_group_ids
from govoplan_files.backend.storage.common import FileStorageError, utcnow
from govoplan_files.backend.storage.connector_browse import normalize_connector_browse_path
from govoplan_files.backend.storage.connector_profiles import ConnectorProfile
SYNC_MODES = {"manual"}
def connector_space_owner_id(space: FileConnectorSpace) -> str:
return space.owner_user_id if space.owner_type == "user" else space.owner_group_id # type: ignore[return-value]
def create_connector_space(
session: Session,
*,
tenant_id: str,
owner_type: str,
owner_id: str,
user_id: str,
label: str,
profile: ConnectorProfile,
library_id: str | None = None,
remote_path: str | None = None,
sync_mode: str = "manual",
read_only: bool = True,
metadata: Mapping[str, Any] | None = None,
is_admin: bool = False,
) -> FileConnectorSpace:
owner_type = owner_type.lower().strip()
ensure_owner_access(session, tenant_id=tenant_id, owner_type=owner_type, owner_id=owner_id, user_id=user_id, is_admin=is_admin)
label = _normalize_label(label)
sync_mode = _normalize_sync_mode(sync_mode)
remote_path = normalize_connector_browse_path(remote_path)
library_id = _clean_optional(library_id)
existing = _owned_query(session, tenant_id=tenant_id, owner_type=owner_type, owner_id=owner_id).filter(
FileConnectorSpace.label == label,
FileConnectorSpace.deleted_at.is_(None),
).first()
if existing is not None:
raise FileStorageError(f"Connector space already exists: {label}")
space = FileConnectorSpace(
tenant_id=tenant_id,
owner_type=owner_type,
owner_user_id=owner_id if owner_type == "user" else None,
owner_group_id=owner_id if owner_type == "group" else None,
label=label,
connector_profile_id=profile.id,
provider=profile.provider,
library_id=library_id,
remote_path=remote_path,
sync_mode=sync_mode,
read_only=read_only,
is_active=True,
created_by_user_id=user_id,
metadata_=dict(metadata or {}),
)
session.add(space)
session.flush()
return space
def list_connector_spaces_for_user(
session: Session,
*,
tenant_id: str,
user_id: str,
owner_type: str | None = None,
owner_id: str | None = None,
include_inactive: bool = False,
is_admin: bool = False,
) -> list[FileConnectorSpace]:
query = session.query(FileConnectorSpace).filter(
FileConnectorSpace.tenant_id == tenant_id,
FileConnectorSpace.deleted_at.is_(None),
)
if not include_inactive:
query = query.filter(FileConnectorSpace.is_active.is_(True))
if owner_type:
if not owner_id:
raise FileStorageError("owner_id is required when owner_type is set")
owner_type = owner_type.lower().strip()
ensure_owner_access(session, tenant_id=tenant_id, owner_type=owner_type, owner_id=owner_id, user_id=user_id, is_admin=is_admin)
query = _owner_filter(query, owner_type, owner_id)
elif not is_admin:
group_ids = user_group_ids(session, tenant_id=tenant_id, user_id=user_id)
query = query.filter(
or_(
FileConnectorSpace.owner_user_id == user_id,
FileConnectorSpace.owner_group_id.in_(group_ids) if group_ids else false(),
)
)
return query.order_by(FileConnectorSpace.owner_type.asc(), FileConnectorSpace.label.asc()).all()
def get_connector_space_for_user(
session: Session,
*,
tenant_id: str,
user_id: str,
space_id: str,
include_inactive: bool = False,
is_admin: bool = False,
) -> FileConnectorSpace:
query = session.query(FileConnectorSpace).filter(
FileConnectorSpace.id == space_id,
FileConnectorSpace.tenant_id == tenant_id,
FileConnectorSpace.deleted_at.is_(None),
)
if not include_inactive:
query = query.filter(FileConnectorSpace.is_active.is_(True))
space = query.one_or_none()
if space is None:
raise FileStorageError("Connector space not found")
ensure_owner_access(
session,
tenant_id=tenant_id,
owner_type=space.owner_type,
owner_id=connector_space_owner_id(space),
user_id=user_id,
is_admin=is_admin,
)
return space
def update_connector_space(
session: Session,
space: FileConnectorSpace,
*,
user_id: str,
label: str | None = None,
library_id: str | None = None,
remote_path: str | None = None,
sync_mode: str | None = None,
is_active: bool | None = None,
metadata: Mapping[str, Any] | None = None,
is_admin: bool = False,
) -> FileConnectorSpace:
ensure_owner_access(
session,
tenant_id=space.tenant_id,
owner_type=space.owner_type,
owner_id=connector_space_owner_id(space),
user_id=user_id,
is_admin=is_admin,
)
if label is not None:
new_label = _normalize_label(label)
existing = _owned_query(
session,
tenant_id=space.tenant_id,
owner_type=space.owner_type,
owner_id=connector_space_owner_id(space),
).filter(
FileConnectorSpace.id != space.id,
FileConnectorSpace.label == new_label,
FileConnectorSpace.deleted_at.is_(None),
).first()
if existing is not None:
raise FileStorageError(f"Connector space already exists: {new_label}")
space.label = new_label
if library_id is not None:
space.library_id = _clean_optional(library_id)
if remote_path is not None:
space.remote_path = normalize_connector_browse_path(remote_path)
if sync_mode is not None:
space.sync_mode = _normalize_sync_mode(sync_mode)
if is_active is not None:
space.is_active = bool(is_active)
if metadata is not None:
space.metadata_ = dict(metadata)
session.add(space)
session.flush()
return space
def soft_delete_connector_space(
session: Session,
space: FileConnectorSpace,
*,
user_id: str,
is_admin: bool = False,
) -> FileConnectorSpace:
ensure_owner_access(
session,
tenant_id=space.tenant_id,
owner_type=space.owner_type,
owner_id=connector_space_owner_id(space),
user_id=user_id,
is_admin=is_admin,
)
space.deleted_at = utcnow()
space.is_active = False
session.add(space)
session.flush()
return space
def _owned_query(session: Session, *, tenant_id: str, owner_type: str, owner_id: str):
query = session.query(FileConnectorSpace).filter(
FileConnectorSpace.tenant_id == tenant_id,
FileConnectorSpace.owner_type == owner_type,
)
return _owner_filter(query, owner_type, owner_id)
def _owner_filter(query, owner_type: str, owner_id: str):
if owner_type == "user":
return query.filter(FileConnectorSpace.owner_user_id == owner_id)
if owner_type == "group":
return query.filter(FileConnectorSpace.owner_group_id == owner_id)
raise FileStorageError("Files must be owned by a user or group")
def _normalize_label(value: str) -> str:
label = value.strip()
if not label:
raise FileStorageError("Connector space label is required")
if len(label) > 255:
raise FileStorageError("Connector space label is too long")
return label
def _normalize_sync_mode(value: str) -> str:
mode = value.strip().casefold()
if mode not in SYNC_MODES:
raise FileStorageError(f"Unsupported connector space sync mode: {value}")
return mode
def _clean_optional(value: object) -> str | None:
if value is None:
return None
text = str(value).strip()
return text or None