refactor(api): split files workflow routers

This commit is contained in:
2026-07-29 20:11:17 +02:00
parent 5b868272b9
commit 86a905a3a7
19 changed files with 4918 additions and 3565 deletions
@@ -0,0 +1,261 @@
from __future__ import annotations
import json
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from govoplan_core.auth import ApiPrincipal, require_any_scope
from govoplan_files.backend.change_tracking import (
FILES_CONNECTOR_PROFILES_COLLECTION,
)
from govoplan_files.backend.schemas import (
FileConnectorProfileResponse,
FileConnectorProfileUpdateRequest,
)
from govoplan_core.db.session import get_session
from govoplan_files.backend.storage.common import FileStorageError
from govoplan_files.backend.storage.connector_credential_deletion import (
delete_connector_profile_row,
)
from govoplan_files.backend.storage.connector_deployment import (
connector_effective_endpoint_url,
)
from govoplan_files.backend.storage.connector_profile_store import (
connector_profile_from_row,
get_connector_profile_row,
update_connector_profile_row,
)
from govoplan_files.backend.storage.connector_policy import (
ConnectorPolicyDenied,
)
from govoplan_files.backend.route_support import (
FILES_CONNECTOR_PROFILE_RESOURCE,
_can_read_disabled_connector_profiles,
_connector_policy_error,
_credential_row_for_profile,
_ensure_connector_configuration_allowed,
_ensure_connector_local_policy_allowed,
_http_error,
_record_connector_settings_change,
_require_connector_profile_write,
_visible_connector_profile,
)
router = APIRouter(prefix="/files", tags=["files"])
@router.get(
"/connectors/profiles/{profile_id}", response_model=FileConnectorProfileResponse
)
def get_connector_profile(
profile_id: str,
campaign_id: str | None = None,
include_disabled: bool = False,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(
require_any_scope(
"files:file:read",
"files:file:upload",
"files:file:download",
"files:file:admin",
"system:settings:read",
"admin:settings:read",
)
),
):
try:
profile = _visible_connector_profile(
session,
principal,
profile_id,
campaign_id=campaign_id,
include_disabled=include_disabled
and _can_read_disabled_connector_profiles(principal),
include_effective_policy=False,
)
except (OSError, ValueError, json.JSONDecodeError) as exc:
raise _http_error(exc) from exc
return FileConnectorProfileResponse(**profile.to_response())
@router.patch(
"/connectors/profiles/{profile_id}", response_model=FileConnectorProfileResponse
)
def update_connector_profile(
profile_id: str,
payload: FileConnectorProfileUpdateRequest,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(
require_any_scope(
"files:file:admin", "system:settings:write", "admin:settings:write"
)
),
):
try:
row = get_connector_profile_row(
session,
tenant_id=principal.tenant_id,
profile_id=profile_id,
include_disabled=True,
)
except FileStorageError as exc:
raise _http_error(exc, not_found=True) from exc
_require_connector_profile_write(principal, row.scope_type)
credentials = payload.credentials
try:
credential_profile_id = (
payload.credential_profile_id
if payload.credential_profile_id is not None
else row.credential_profile_id
)
provider = payload.provider if payload.provider is not None else row.provider
credential_row = _credential_row_for_profile(
session,
principal,
credential_profile_id=credential_profile_id,
provider=provider,
profile_id=row.id,
scope_type=row.scope_type,
scope_id=row.scope_id,
include_disabled=True,
)
_ensure_connector_configuration_allowed(
session,
principal,
connector_id=row.id,
credential_id=credential_profile_id,
provider=provider,
endpoint_url=connector_effective_endpoint_url(
provider=provider,
endpoint_url=payload.endpoint_url
if payload.endpoint_url is not None
else row.endpoint_url,
metadata=payload.metadata
if payload.metadata is not None
else row.metadata_,
),
base_path=payload.base_path
if payload.base_path is not None
else row.base_path,
scope_type=row.scope_type,
scope_id=row.scope_id,
operation="configure",
)
if payload.policy is not None:
_ensure_connector_local_policy_allowed(
session,
principal,
scope_type=row.scope_type,
scope_id=row.scope_id,
policy=payload.policy,
)
update_connector_profile_row(
session,
row,
user_id=principal.user.id,
label=payload.label,
provider=payload.provider,
endpoint_url=payload.endpoint_url,
base_path=payload.base_path,
enabled=payload.enabled,
credential_profile_id=payload.credential_profile_id,
credential_mode=payload.credential_mode,
username=credentials.username if credentials else None,
password=credentials.password if credentials else None,
token=credentials.token if credentials else None,
password_env=credentials.password_env if credentials else None,
token_env=credentials.token_env if credentials else None,
secret_ref=credentials.secret_ref if credentials else None,
clear_password=payload.clear_password,
clear_token=payload.clear_token,
capabilities=payload.capabilities,
policy=payload.policy,
metadata=payload.metadata,
)
_record_connector_settings_change(
session,
collection=FILES_CONNECTOR_PROFILES_COLLECTION,
resource_type=FILES_CONNECTOR_PROFILE_RESOURCE,
resource_id=row.id,
operation="updated",
principal=principal,
tenant_id=row.tenant_id,
payload={
"scope_type": row.scope_type,
"scope_id": row.scope_id,
"provider": row.provider,
},
)
session.commit()
session.refresh(row)
return FileConnectorProfileResponse(
**connector_profile_from_row(
row, credential_row=credential_row
).to_response()
)
except ConnectorPolicyDenied as exc:
session.rollback()
raise _connector_policy_error(exc) from exc
except (FileStorageError, ValueError, json.JSONDecodeError) as exc:
session.rollback()
raise _http_error(exc) from exc
@router.delete(
"/connectors/profiles/{profile_id}", response_model=FileConnectorProfileResponse
)
def deactivate_connector_profile(
profile_id: str,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(
require_any_scope(
"files:file:admin", "system:settings:write", "admin:settings:write"
)
),
):
try:
row = get_connector_profile_row(
session,
tenant_id=principal.tenant_id,
profile_id=profile_id,
include_disabled=True,
)
except FileStorageError as exc:
raise _http_error(exc, not_found=True) from exc
_require_connector_profile_write(principal, row.scope_type)
try:
changed = delete_connector_profile_row(
session,
row,
deletion_reason="api_delete",
user_id=principal.user.id,
api_key_id=principal.api_key.id if principal.api_key else None,
)
if changed:
_record_connector_settings_change(
session,
collection=FILES_CONNECTOR_PROFILES_COLLECTION,
resource_type=FILES_CONNECTOR_PROFILE_RESOURCE,
resource_id=row.id,
operation="deleted",
principal=principal,
tenant_id=row.tenant_id,
payload={
"scope_type": row.scope_type,
"scope_id": row.scope_id,
"provider": row.provider,
},
)
session.commit()
session.refresh(row)
return FileConnectorProfileResponse(
**connector_profile_from_row(row).to_response()
)
except FileStorageError as exc:
session.rollback()
raise _http_error(exc) from exc
except Exception:
session.rollback()
raise