Files
CIAgent/apps/api/migrations/versions/2c2a563a5edb_add_pending_provisionings_table.py
T
sakshamandClaude Sonnet 5 56b4a5404e Add pending account provisioning for demoing to not-yet-existing accounts
An admin can now queue up an email address in advance (POST/GET/DELETE
/system/pending-provisioning) with a source user and an optional admin
flag. The moment that email actually verifies a real account - not raw
registration, which proves nothing about ownership - it gets a deep copy
of the source user's per-user API keys and every company they own
(company profile, aliases, competitors, monitor config, sources, source
documents, monitoring runs, reports, snapshots, detected changes, and
enrichment - not just the company row), plus an email notification
destination for its own address linked to the copied companies.

The clone logic (_copy_row/_clone_company in provisioning_service.py) is
generic over every table it touches via column introspection, so it
doesn't need hand-maintained field lists per model.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-06 18:47:29 -04:00

46 lines
1.8 KiB
Python

"""add pending_provisionings table
Revision ID: 2c2a563a5edb
Revises: 9e6c80c11da7
Create Date: 2026-08-06 22:22:22.348339
"""
from __future__ import annotations
from collections.abc import Sequence
from alembic import op
import sqlalchemy as sa
revision: str = '2c2a563a5edb'
down_revision: str | None = '9e6c80c11da7'
branch_labels: Sequence[str] | str | None = None
depends_on: Sequence[str] | str | None = None
def upgrade() -> None:
# Autogenerate also proposed adding a redundant UNIQUE constraint on the
# `id` column of every other existing table (a cosmetic artifact of how
# this project's earlier migrations declared unnamed unique constraints,
# unrelated to this change) - stripped out, keeping only the new table.
op.create_table('pending_provisionings',
sa.Column('email', sa.String(length=320), nullable=False),
sa.Column('source_user_id', sa.Uuid(), nullable=False),
sa.Column('make_admin', sa.Boolean(), nullable=False),
sa.Column('id', sa.Uuid(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['source_user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
op.create_index(op.f('ix_pending_provisionings_email'), 'pending_provisionings', ['email'], unique=True)
op.create_index(op.f('ix_pending_provisionings_source_user_id'), 'pending_provisionings', ['source_user_id'], unique=False)
def downgrade() -> None:
op.drop_index(op.f('ix_pending_provisionings_source_user_id'), table_name='pending_provisionings')
op.drop_index(op.f('ix_pending_provisionings_email'), table_name='pending_provisionings')
op.drop_table('pending_provisionings')