Migration 057 inserts 6 AD script templates with NULL team_id and NULL created_by. Neither backfill path (created_by→users, team_id→team admin) could attribute them to an account, causing the verify check to fail. Fix: pre-create the platform sentinel account (ON CONFLICT DO NOTHING, safe since 3a40fe11b427 also creates it idempotently) and add a final fallback UPDATE assigning any remaining NULL script_templates to it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
"""add account_id to script_builder_sessions, script_templates, script_generations
|
|
|
|
Revision ID: 78fc200abac1
|
|
Revises: 7f136778f5a8
|
|
Create Date: 2026-04-09 00:00:00.000000
|
|
"""
|
|
from typing import Sequence, Union
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision: str = '78fc200abac1'
|
|
down_revision: Union[str, None] = '7f136778f5a8'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
PLATFORM_ACCOUNT_ID = '00000000-0000-0000-0000-000000000001'
|
|
|
|
|
|
def upgrade() -> None:
|
|
# Ensure the platform sentinel account exists before any fallback assignments.
|
|
# Migration 3a40fe11b427 also inserts this with ON CONFLICT DO NOTHING — safe.
|
|
op.execute(f"""
|
|
INSERT INTO accounts (id, name, display_code, created_at, updated_at)
|
|
VALUES (
|
|
'{PLATFORM_ACCOUNT_ID}',
|
|
'ResolutionFlow Platform',
|
|
'PLATFORM',
|
|
NOW(),
|
|
NOW()
|
|
)
|
|
ON CONFLICT (id) DO NOTHING
|
|
""")
|
|
|
|
for table in ('script_builder_sessions', 'script_templates', 'script_generations'):
|
|
op.add_column(table, sa.Column('account_id', sa.UUID(), nullable=True))
|
|
op.create_foreign_key(
|
|
f'fk_{table}_account_id', table, 'accounts',
|
|
['account_id'], ['id'], ondelete='CASCADE',
|
|
)
|
|
|
|
# script_builder_sessions: user_id → users.account_id
|
|
op.execute("""
|
|
UPDATE script_builder_sessions sbs
|
|
SET account_id = u.account_id
|
|
FROM users u
|
|
WHERE sbs.user_id = u.id
|
|
AND sbs.account_id IS NULL
|
|
""")
|
|
|
|
# script_templates: created_by → users.account_id (nullable created_by)
|
|
op.execute("""
|
|
UPDATE script_templates st
|
|
SET account_id = u.account_id
|
|
FROM users u
|
|
WHERE st.created_by = u.id
|
|
AND st.account_id IS NULL
|
|
""")
|
|
# Fallback: team_id → team admin user
|
|
op.execute("""
|
|
UPDATE script_templates st
|
|
SET account_id = u.account_id
|
|
FROM users u
|
|
WHERE u.team_id = st.team_id
|
|
AND u.is_team_admin = TRUE
|
|
AND u.account_id IS NOT NULL
|
|
AND st.account_id IS NULL
|
|
""")
|
|
# Final fallback: platform-seeded templates with NULL team_id AND NULL created_by
|
|
# (e.g. the 6 AD templates inserted by migration 057) → platform sentinel account
|
|
op.execute(f"""
|
|
UPDATE script_templates
|
|
SET account_id = '{PLATFORM_ACCOUNT_ID}'
|
|
WHERE account_id IS NULL
|
|
""")
|
|
|
|
# script_generations: user_id → users.account_id
|
|
op.execute("""
|
|
UPDATE script_generations sg
|
|
SET account_id = u.account_id
|
|
FROM users u
|
|
WHERE sg.user_id = u.id
|
|
AND sg.account_id IS NULL
|
|
""")
|
|
|
|
# VERIFY
|
|
for table in ('script_builder_sessions', 'script_templates', 'script_generations'):
|
|
result = op.get_bind().execute(
|
|
sa.text(f"SELECT COUNT(*) FROM {table} WHERE account_id IS NULL")
|
|
)
|
|
count = result.scalar()
|
|
if count > 0:
|
|
raise RuntimeError(f"ROLLBACK: {count} NULL account_id rows in {table}.")
|
|
|
|
for table in ('script_builder_sessions', 'script_templates', 'script_generations'):
|
|
op.alter_column(table, 'account_id', nullable=False)
|
|
op.create_index(f'ix_{table}_account_id', table, ['account_id'])
|
|
|
|
|
|
def downgrade() -> None:
|
|
for table in ('script_builder_sessions', 'script_templates', 'script_generations'):
|
|
op.drop_index(f'ix_{table}_account_id', table_name=table)
|
|
op.drop_constraint(f'fk_{table}_account_id', table, type_='foreignkey')
|
|
op.drop_column(table, 'account_id')
|