From caaace14f3b0e19dd5338010e9635fa1b1041d99 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Fri, 10 Jul 2026 17:33:43 +0200 Subject: [PATCH] Use capability-based module boundaries --- src/govoplan_mail/backend/db/models.py | 9 +++------ src/govoplan_mail/backend/dev_router.py | 2 +- src/govoplan_mail/backend/mail_profiles.py | 2 +- src/govoplan_mail/backend/manifest.py | 3 ++- .../versions/3d4e5f708192_mail_profile_policies.py | 8 +++++++- src/govoplan_mail/backend/router.py | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/govoplan_mail/backend/db/models.py b/src/govoplan_mail/backend/db/models.py index 074dc90..4a62815 100644 --- a/src/govoplan_mail/backend/db/models.py +++ b/src/govoplan_mail/backend/db/models.py @@ -4,7 +4,7 @@ import uuid from typing import Any from sqlalchemy import Boolean, ForeignKey, Index, JSON, String, Text, UniqueConstraint -from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.orm import Mapped, mapped_column from govoplan_core.db.base import Base, TimestampMixin @@ -21,7 +21,7 @@ class MailServerProfile(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str | None] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=True, index=True) + tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), default="tenant", nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) name: Mapped[str] = mapped_column(String(255), nullable=False) @@ -37,8 +37,6 @@ class MailServerProfile(Base, TimestampMixin): created_by_user_id: Mapped[str | None] = mapped_column(ForeignKey("access_users.id", ondelete="SET NULL"), nullable=True, index=True) updated_by_user_id: Mapped[str | None] = mapped_column(ForeignKey("access_users.id", ondelete="SET NULL"), nullable=True, index=True) - tenant: Mapped["Tenant"] = relationship() - class MailProfilePolicy(Base, TimestampMixin): __tablename__ = "mail_profile_policies" @@ -48,8 +46,7 @@ class MailProfilePolicy(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str | None] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=True, index=True) + tenant_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) scope_type: Mapped[str] = mapped_column(String(20), nullable=False, index=True) scope_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) policy: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False) - diff --git a/src/govoplan_mail/backend/dev_router.py b/src/govoplan_mail/backend/dev_router.py index cf9af8b..487f7a7 100644 --- a/src/govoplan_mail/backend/dev_router.py +++ b/src/govoplan_mail/backend/dev_router.py @@ -5,7 +5,7 @@ from typing import Any from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, ConfigDict, Field -from govoplan_access.auth import ApiPrincipal, require_scope +from govoplan_core.auth import ApiPrincipal, require_scope from govoplan_mail.backend.dev.mock_mailbox import ( clear_records, get_failures, diff --git a/src/govoplan_mail/backend/mail_profiles.py b/src/govoplan_mail/backend/mail_profiles.py index 2caf912..1f275d3 100644 --- a/src/govoplan_mail/backend/mail_profiles.py +++ b/src/govoplan_mail/backend/mail_profiles.py @@ -281,7 +281,7 @@ def _campaign_policy_context(session: Session, *, tenant_id: str, campaign_id: s def _legacy_tenant_settings(session: Session, tenant_id: str) -> dict[str, Any] | None: - row = session.execute(text("select settings from tenancy_tenants where id = :tenant_id"), {"tenant_id": tenant_id}).first() + row = session.execute(text("select settings from core_scopes where id = :tenant_id"), {"tenant_id": tenant_id}).first() return _json_object(row[0]) if row else None diff --git a/src/govoplan_mail/backend/manifest.py b/src/govoplan_mail/backend/manifest.py index 0b99c78..33d269c 100644 --- a/src/govoplan_mail/backend/manifest.py +++ b/src/govoplan_mail/backend/manifest.py @@ -2,6 +2,7 @@ from __future__ import annotations from pathlib import Path +from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER from govoplan_core.core.module_guards import drop_table_retirement_provider, persistent_table_uninstall_guard from govoplan_core.core.modules import ( DocumentationCondition, @@ -87,7 +88,7 @@ manifest = ModuleManifest( id="mail", name="Mail", version="0.1.6", - dependencies=("access",), + required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR), optional_dependencies=(), permissions=PERMISSIONS, route_factory=_mail_router, diff --git a/src/govoplan_mail/backend/migrations/versions/3d4e5f708192_mail_profile_policies.py b/src/govoplan_mail/backend/migrations/versions/3d4e5f708192_mail_profile_policies.py index f364ae4..f074e46 100644 --- a/src/govoplan_mail/backend/migrations/versions/3d4e5f708192_mail_profile_policies.py +++ b/src/govoplan_mail/backend/migrations/versions/3d4e5f708192_mail_profile_policies.py @@ -16,10 +16,16 @@ branch_labels = None depends_on = None +def _scope_fk_target(inspector) -> str: + tables = set(inspector.get_table_names()) + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) if "mail_profile_policies" in inspector.get_table_names(): return + scope_fk_target = _scope_fk_target(inspector) op.create_table( "mail_profile_policies", sa.Column("id", sa.String(length=36), nullable=False), @@ -29,7 +35,7 @@ def upgrade() -> None: sa.Column("policy", sa.JSON(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_mail_profile_policies_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_mail_profile_policies_tenant_id_scopes"), ondelete="CASCADE"), sa.PrimaryKeyConstraint("id", name=op.f("pk_mail_profile_policies")), sa.UniqueConstraint("tenant_id", "scope_type", "scope_id", name="uq_mail_profile_policies_scope"), ) diff --git a/src/govoplan_mail/backend/router.py b/src/govoplan_mail/backend/router.py index db85384..bfcd0c1 100644 --- a/src/govoplan_mail/backend/router.py +++ b/src/govoplan_mail/backend/router.py @@ -23,7 +23,7 @@ from govoplan_mail.backend.schemas import ( MailServerProfileUpdateRequest, MailSmtpTestRequest, ) -from govoplan_access.auth import ApiPrincipal, get_api_principal, has_scope +from govoplan_core.auth import ApiPrincipal, get_api_principal, has_scope from govoplan_core.api.v1.schemas import DeltaDeletedItem from govoplan_core.core.change_sequence import ( ChangeSequenceEntry,