diff --git a/src/govoplan_calendar/backend/db/models.py b/src/govoplan_calendar/backend/db/models.py index 8ae6478..ad743c0 100644 --- a/src/govoplan_calendar/backend/db/models.py +++ b/src/govoplan_calendar/backend/db/models.py @@ -29,7 +29,7 @@ class CalendarCollection(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) slug: Mapped[str] = mapped_column(String(100), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[str | None] = mapped_column(Text) @@ -56,7 +56,7 @@ class CalendarEvent(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) calendar_id: Mapped[str] = mapped_column(ForeignKey("calendar_collections.id", ondelete="CASCADE"), nullable=False, index=True) uid: Mapped[str] = mapped_column(String(255), nullable=False, index=True) recurrence_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True) @@ -110,7 +110,7 @@ class CalendarSyncSource(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) calendar_id: Mapped[str] = mapped_column(ForeignKey("calendar_collections.id", ondelete="CASCADE"), nullable=False, index=True) source_kind: Mapped[str] = mapped_column(String(30), default="caldav", nullable=False, index=True) collection_url: Mapped[str] = mapped_column(String(1000), nullable=False) @@ -142,7 +142,7 @@ class CalendarSyncCredential(Base, TimestampMixin): ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - tenant_id: Mapped[str] = mapped_column(ForeignKey("tenancy_tenants.id", ondelete="CASCADE"), nullable=False, index=True) + tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) credential_kind: Mapped[str] = mapped_column(String(30), nullable=False, index=True) label: Mapped[str | None] = mapped_column(String(255)) secret_encrypted: Mapped[str | None] = mapped_column(Text) diff --git a/src/govoplan_calendar/backend/manifest.py b/src/govoplan_calendar/backend/manifest.py index a0eb4e4..d440553 100644 --- a/src/govoplan_calendar/backend/manifest.py +++ b/src/govoplan_calendar/backend/manifest.py @@ -3,6 +3,7 @@ from __future__ import annotations from pathlib import Path from govoplan_calendar.backend.db import models as calendar_models # noqa: F401 - populate Calendar ORM metadata +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 FrontendModule, MigrationSpec, ModuleContext, ModuleManifest, NavItem, PermissionDefinition, RoleTemplate from govoplan_core.db.base import Base @@ -88,7 +89,7 @@ manifest = ModuleManifest( id="calendar", name="Calendar", version="0.1.6", - dependencies=("access",), + required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR), optional_dependencies=("mail", "tasks", "scheduling", "appointments", "workflow", "notifications", "dms", "connectors"), permissions=PERMISSIONS, route_factory=_calendar_router, diff --git a/src/govoplan_calendar/backend/migrations/versions/7c8d9e0f1a2b_calendar_collections_events.py b/src/govoplan_calendar/backend/migrations/versions/7c8d9e0f1a2b_calendar_collections_events.py index 73e310e..e41678c 100644 --- a/src/govoplan_calendar/backend/migrations/versions/7c8d9e0f1a2b_calendar_collections_events.py +++ b/src/govoplan_calendar/backend/migrations/versions/7c8d9e0f1a2b_calendar_collections_events.py @@ -16,9 +16,14 @@ branch_labels = None depends_on = None +def _scope_fk_target(tables: set[str]) -> str: + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) tables = set(inspector.get_table_names()) + scope_fk_target = _scope_fk_target(tables) if "calendar_collections" not in tables: op.create_table( "calendar_collections", @@ -39,7 +44,7 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_calendar_collections_created_by_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_calendar_collections_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_calendar_collections_tenant_id_scopes"), ondelete="CASCADE"), sa.PrimaryKeyConstraint("id", name=op.f("pk_calendar_collections")), ) op.create_index(op.f("ix_calendar_collections_tenant_id"), "calendar_collections", ["tenant_id"], unique=False) @@ -101,7 +106,7 @@ def upgrade() -> None: sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["calendar_id"], ["calendar_collections.id"], name=op.f("fk_calendar_events_calendar_id_calendar_collections"), ondelete="CASCADE"), sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_calendar_events_created_by_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_calendar_events_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_calendar_events_tenant_id_scopes"), ondelete="CASCADE"), sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_calendar_events_updated_by_user_id_users"), ondelete="SET NULL"), sa.PrimaryKeyConstraint("id", name=op.f("pk_calendar_events")), sa.UniqueConstraint("calendar_id", "uid", "recurrence_id", name="uq_calendar_events_calendar_uid_recurrence"), diff --git a/src/govoplan_calendar/backend/migrations/versions/8d9e0f1a2b3c_caldav_sync_sources.py b/src/govoplan_calendar/backend/migrations/versions/8d9e0f1a2b3c_caldav_sync_sources.py index c98f282..6918008 100644 --- a/src/govoplan_calendar/backend/migrations/versions/8d9e0f1a2b3c_caldav_sync_sources.py +++ b/src/govoplan_calendar/backend/migrations/versions/8d9e0f1a2b3c_caldav_sync_sources.py @@ -16,11 +16,16 @@ branch_labels = None depends_on = None +def _scope_fk_target(tables: set[str]) -> str: + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: inspector = sa.inspect(op.get_bind()) tables = set(inspector.get_table_names()) if "calendar_sync_sources" in tables: return + scope_fk_target = _scope_fk_target(tables) op.create_table( "calendar_sync_sources", sa.Column("id", sa.String(length=36), nullable=False), @@ -42,7 +47,7 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["calendar_id"], ["calendar_collections.id"], name=op.f("fk_calendar_sync_sources_calendar_id_calendar_collections"), ondelete="CASCADE"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_calendar_sync_sources_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_calendar_sync_sources_tenant_id_scopes"), ondelete="CASCADE"), sa.PrimaryKeyConstraint("id", name=op.f("pk_calendar_sync_sources")), ) for column in ("tenant_id", "calendar_id", "source_kind", "deleted_at"): diff --git a/src/govoplan_calendar/backend/migrations/versions/9e0f1a2b3c4d_caldav_credentials_outbound_sync.py b/src/govoplan_calendar/backend/migrations/versions/9e0f1a2b3c4d_caldav_credentials_outbound_sync.py index cce842d..09dcfea 100644 --- a/src/govoplan_calendar/backend/migrations/versions/9e0f1a2b3c4d_caldav_credentials_outbound_sync.py +++ b/src/govoplan_calendar/backend/migrations/versions/9e0f1a2b3c4d_caldav_credentials_outbound_sync.py @@ -16,10 +16,15 @@ branch_labels = None depends_on = None +def _scope_fk_target(tables: set[str]) -> str: + return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id" + + def upgrade() -> None: bind = op.get_bind() inspector = sa.inspect(bind) tables = set(inspector.get_table_names()) + scope_fk_target = _scope_fk_target(tables) if "calendar_sync_sources" in tables: columns = {column["name"] for column in inspector.get_columns("calendar_sync_sources")} add_column_if_missing(columns, "sync_enabled", sa.Column("sync_enabled", sa.Boolean(), server_default=sa.true(), nullable=False)) @@ -48,7 +53,7 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_calendar_sync_credentials_created_by_user_id_users"), ondelete="SET NULL"), - sa.ForeignKeyConstraint(["tenant_id"], ["tenancy_tenants.id"], name=op.f("fk_calendar_sync_credentials_tenant_id_tenants"), ondelete="CASCADE"), + sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_calendar_sync_credentials_tenant_id_scopes"), ondelete="CASCADE"), sa.PrimaryKeyConstraint("id", name=op.f("pk_calendar_sync_credentials")), ) for column in ("tenant_id", "credential_kind", "created_by_user_id", "deleted_at"): diff --git a/src/govoplan_calendar/backend/router.py b/src/govoplan_calendar/backend/router.py index 2081dfe..e126ac1 100644 --- a/src/govoplan_calendar/backend/router.py +++ b/src/govoplan_calendar/backend/router.py @@ -5,7 +5,7 @@ from datetime import datetime from fastapi import APIRouter, Body, Depends, HTTPException, Query, Response, status from sqlalchemy.orm import Session -from govoplan_access.backend.auth.dependencies 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 decode_sequence_watermark, encode_sequence_watermark, max_sequence_id, sequence_entries_since, sequence_watermark_is_expired from govoplan_calendar.backend.db.models import CalendarEvent