Add governed tabular and LDAP address sources

This commit is contained in:
2026-08-02 15:39:34 +02:00
parent e60339a5bf
commit 2c421022d4
19 changed files with 3846 additions and 78 deletions
@@ -0,0 +1,95 @@
"""add versioned address import profiles and immutable run evidence
Revision ID: c5d7e8f9a0b1
Revises: b4c6d7e8f9a0
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "c5d7e8f9a0b1"
down_revision = "b4c6d7e8f9a0"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"addresses_import_profiles",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("profile_key", sa.String(length=36), nullable=False),
sa.Column("version", sa.Integer(), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=True),
sa.Column("scope_type", sa.String(length=20), nullable=False),
sa.Column("scope_id", sa.String(length=36), nullable=True),
sa.Column("name", sa.String(length=255), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("source_format", sa.String(length=20), nullable=False),
sa.Column("configuration", sa.JSON(), nullable=False),
sa.Column("is_current", sa.Boolean(), nullable=False),
sa.Column("created_by_account_id", sa.String(length=36), nullable=True),
sa.Column("superseded_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("profile_key", "version", name="uq_addresses_import_profile_version"),
)
op.create_index("ix_addresses_import_profiles_profile_key", "addresses_import_profiles", ["profile_key"])
op.create_index("ix_addresses_import_profiles_tenant_id", "addresses_import_profiles", ["tenant_id"])
op.create_index("ix_addresses_import_profiles_scope_type", "addresses_import_profiles", ["scope_type"])
op.create_index("ix_addresses_import_profiles_scope_id", "addresses_import_profiles", ["scope_id"])
op.create_index("ix_addresses_import_profiles_source_format", "addresses_import_profiles", ["source_format"])
op.create_index("ix_addresses_import_profiles_is_current", "addresses_import_profiles", ["is_current"])
op.create_index("ix_addresses_import_profiles_created_by_account_id", "addresses_import_profiles", ["created_by_account_id"])
op.create_index("ix_addresses_import_profiles_superseded_at", "addresses_import_profiles", ["superseded_at"])
op.create_index("ix_addresses_import_profiles_scope", "addresses_import_profiles", ["tenant_id", "scope_type", "scope_id", "is_current"])
op.create_index("ix_addresses_import_profiles_format", "addresses_import_profiles", ["tenant_id", "source_format"])
op.create_table(
"addresses_import_runs",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=True),
sa.Column("address_book_id", sa.String(length=36), nullable=False),
sa.Column("profile_id", sa.String(length=36), nullable=False),
sa.Column("source_filename", sa.String(length=500), nullable=False),
sa.Column("source_format", sa.String(length=20), nullable=False),
sa.Column("input_hash", sa.String(length=64), nullable=False),
sa.Column("plan_hash", sa.String(length=64), nullable=False),
sa.Column("status", sa.String(length=30), nullable=False),
sa.Column("row_count", sa.Integer(), nullable=False),
sa.Column("statistics", sa.JSON(), nullable=False),
sa.Column("diagnostics", sa.JSON(), nullable=False),
sa.Column("plan_data", sa.JSON(), nullable=False),
sa.Column("result_evidence", sa.JSON(), nullable=False),
sa.Column("created_by_account_id", sa.String(length=36), nullable=True),
sa.Column("applied_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("rolled_back_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["address_book_id"], ["addresses_address_books.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["profile_id"], ["addresses_import_profiles.id"], ondelete="RESTRICT"),
sa.PrimaryKeyConstraint("id"),
)
for column in (
"tenant_id",
"address_book_id",
"profile_id",
"source_format",
"input_hash",
"plan_hash",
"status",
"created_by_account_id",
"applied_at",
"rolled_back_at",
):
op.create_index(f"ix_addresses_import_runs_{column}", "addresses_import_runs", [column])
op.create_index("ix_addresses_import_runs_book_status", "addresses_import_runs", ["address_book_id", "status", "created_at"])
op.create_index("ix_addresses_import_runs_tenant_hash", "addresses_import_runs", ["tenant_id", "input_hash"])
def downgrade() -> None:
op.drop_table("addresses_import_runs")
op.drop_table("addresses_import_profiles")