Newer
Older
gn-synapse / alembic / versions / 20261003_0007_soft_delete.py
"""Архив вместо удаления: deleted_at на источниках/типах/целях/правилах

Revision ID: b7d3e8f04a92
Revises: e2f6a9b3c4d7
Create Date: 2026-10-03
"""

from alembic import op
import sqlalchemy as sa


revision = "b7d3e8f04a92"
down_revision = "e2f6a9b3c4d7"
branch_labels = None
depends_on = None

TABLES = ("sources", "notification_types", "channel_targets", "routing_rules")

# Частичные индексы: живые строки (архив исключён) — уникальность/подчистка
# имен живых записей делает код, индекс ускоряет списки и дабл-чеки.
# Для notification_types — составной (source_id, subject, action) — дабл-чек
# create_type ищет по тройке; subject/action в индексе.
PARTIAL = {
    "sources": ("ix_sources_live", ["name"]),
    "notification_types": ("ix_ntypes_live", ["source_id", "subject", "action"]),
    "channel_targets": ("ix_targets_live", ["channel", "name"]),
    "routing_rules": ("ix_rules_live", ["enabled"]),
}


def upgrade() -> None:
    for table in TABLES:
        op.add_column(
            table,
            sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
        )
    for table, (iname, cols) in PARTIAL.items():
        op.create_index(
            iname,
            table,
            cols,
            unique=False,
            postgresql_where=sa.text("deleted_at IS NULL"),
        )


def downgrade() -> None:
    for table, (iname, _cols) in PARTIAL.items():
        op.drop_index(iname, table)
    for table in TABLES:
        op.drop_column(table, "deleted_at")