Decouple scopes from tenancy schema
This commit is contained in:
@@ -13,6 +13,7 @@ from govoplan_core.db.base import Base
|
||||
from govoplan_core.server.default_config import get_server_config
|
||||
from govoplan_core.server.registry import build_platform_registry
|
||||
from govoplan_core.settings import settings
|
||||
from govoplan_core.tenancy.scope import scope_registry
|
||||
|
||||
config = context.config
|
||||
database_url = config.attributes.get("database_url") or settings.database_url
|
||||
@@ -30,7 +31,7 @@ def _target_metadata():
|
||||
enabled_modules,
|
||||
manifest_factories=manifest_factories,
|
||||
)
|
||||
plan = migration_metadata_plan(registry, extra_metadata=(Base.metadata,))
|
||||
plan = migration_metadata_plan(registry, extra_metadata=(scope_registry.metadata, Base.metadata))
|
||||
return tuple(dict.fromkeys(plan.metadata))
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"""rename tenancy scope table to core scopes
|
||||
|
||||
Revision ID: 4f2a9c8e7b6d
|
||||
Revises: 3f4a5b6c7d8e
|
||||
Create Date: 2026-07-10 00:00:00.000000
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = "4f2a9c8e7b6d"
|
||||
down_revision = "3f4a5b6c7d8e"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
LEGACY_SCOPE_TABLE = "tenancy_tenants"
|
||||
CORE_SCOPE_TABLE = "core_scopes"
|
||||
LEGACY_SLUG_INDEX = "ix_tenancy_tenants_slug"
|
||||
CORE_SLUG_INDEX = "ix_core_scopes_slug"
|
||||
|
||||
|
||||
def _row_count(bind: sa.Connection, table_name: str) -> int:
|
||||
quoted = bind.dialect.identifier_preparer.quote(table_name)
|
||||
return int(bind.execute(sa.text(f"SELECT COUNT(*) FROM {quoted}")).scalar_one())
|
||||
|
||||
|
||||
def _scope_tables(bind: sa.Connection) -> set[str]:
|
||||
return set(sa.inspect(bind).get_table_names())
|
||||
|
||||
|
||||
def _drop_table_if_empty(bind: sa.Connection, table_name: str) -> bool:
|
||||
if _row_count(bind, table_name) != 0:
|
||||
return False
|
||||
op.drop_table(table_name)
|
||||
return True
|
||||
|
||||
|
||||
def _ensure_slug_index(bind: sa.Connection, table_name: str, index_name: str, old_index_name: str) -> None:
|
||||
indexes = {index["name"] for index in sa.inspect(bind).get_indexes(table_name)}
|
||||
if old_index_name in indexes:
|
||||
op.drop_index(old_index_name, table_name=table_name)
|
||||
indexes.remove(old_index_name)
|
||||
if index_name not in indexes:
|
||||
op.create_index(op.f(index_name), table_name, ["slug"], unique=True)
|
||||
|
||||
|
||||
def _rename_scope_table(old_name: str, new_name: str) -> None:
|
||||
bind = op.get_bind()
|
||||
tables = _scope_tables(bind)
|
||||
if old_name not in tables:
|
||||
if new_name in tables:
|
||||
_ensure_slug_index(bind, new_name, CORE_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else LEGACY_SLUG_INDEX, LEGACY_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else CORE_SLUG_INDEX)
|
||||
return
|
||||
|
||||
if new_name in tables:
|
||||
if _drop_table_if_empty(bind, new_name):
|
||||
tables.remove(new_name)
|
||||
elif _drop_table_if_empty(bind, old_name):
|
||||
_ensure_slug_index(bind, new_name, CORE_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else LEGACY_SLUG_INDEX, LEGACY_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else CORE_SLUG_INDEX)
|
||||
return
|
||||
else:
|
||||
raise RuntimeError(f"Cannot reconcile non-empty {old_name} over non-empty {new_name}")
|
||||
|
||||
if old_name in tables and new_name not in tables:
|
||||
op.rename_table(old_name, new_name)
|
||||
_ensure_slug_index(bind, new_name, CORE_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else LEGACY_SLUG_INDEX, LEGACY_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else CORE_SLUG_INDEX)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
_rename_scope_table(LEGACY_SCOPE_TABLE, CORE_SCOPE_TABLE)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_rename_scope_table(CORE_SCOPE_TABLE, LEGACY_SCOPE_TABLE)
|
||||
Reference in New Issue
Block a user