154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
"""remove qa role
|
|
|
|
Revision ID: 20260521_01
|
|
Revises: 20260520_01
|
|
Create Date: 2026-05-21 09:00:00.000000
|
|
|
|
"""
|
|
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
|
|
|
|
revision: str = "20260521_01"
|
|
down_revision: Union[str, None] = "20260520_01"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
USER_ROLE_VALUES = ("ADMIN", "PM", "CRA", "PV", "IMP", "MEDICAL_REVIEW")
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute("CREATE TEMP TABLE qa_user_ids AS SELECT id FROM users WHERE role = 'QA'")
|
|
op.execute("DELETE FROM permission_access_logs WHERE role = 'QA'")
|
|
op.execute("DELETE FROM api_endpoint_permissions WHERE role = 'QA'")
|
|
op.execute("DELETE FROM study_members WHERE role_in_study = 'QA'")
|
|
op.execute(
|
|
"""
|
|
UPDATE studies
|
|
SET active_roles = (
|
|
SELECT COALESCE(json_agg(role), '[]'::json)
|
|
FROM json_array_elements_text(active_roles) AS role
|
|
WHERE role <> 'QA'
|
|
)
|
|
WHERE active_roles::text LIKE '%QA%'
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
DELETE FROM permission_template_versions
|
|
WHERE template_id IN (
|
|
SELECT id FROM permission_templates
|
|
WHERE category = 'QA' OR recommended_roles = 'QA' OR permissions ? 'QA'
|
|
)
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
DELETE FROM permission_templates
|
|
WHERE category = 'QA' OR recommended_roles = 'QA' OR permissions ? 'QA'
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
UPDATE permission_templates
|
|
SET created_by = NULL
|
|
WHERE created_by IN (SELECT id FROM qa_user_ids)
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
DELETE FROM audit_logs
|
|
WHERE operator_role = 'QA'
|
|
OR operator_id IN (SELECT id FROM users WHERE role = 'QA')
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
DO $$
|
|
DECLARE
|
|
rel record;
|
|
BEGIN
|
|
FOR rel IN
|
|
SELECT
|
|
quote_ident(ns.nspname) AS schema_name,
|
|
quote_ident(cls.relname) AS table_name,
|
|
quote_ident(att.attname) AS column_name,
|
|
att.attnotnull AS not_null
|
|
FROM pg_constraint con
|
|
JOIN pg_class cls ON cls.oid = con.conrelid
|
|
JOIN pg_namespace ns ON ns.oid = cls.relnamespace
|
|
JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = ANY(con.conkey)
|
|
WHERE con.contype = 'f'
|
|
AND con.confrelid = 'users'::regclass
|
|
AND array_length(con.conkey, 1) = 1
|
|
LOOP
|
|
IF rel.table_name = 'users' AND rel.column_name = 'id' THEN
|
|
CONTINUE;
|
|
END IF;
|
|
|
|
IF rel.not_null THEN
|
|
EXECUTE format(
|
|
'DELETE FROM %s.%s WHERE %s IN (SELECT id FROM qa_user_ids)',
|
|
rel.schema_name,
|
|
rel.table_name,
|
|
rel.column_name
|
|
);
|
|
ELSE
|
|
EXECUTE format(
|
|
'UPDATE %s.%s SET %s = NULL WHERE %s IN (SELECT id FROM qa_user_ids)',
|
|
rel.schema_name,
|
|
rel.table_name,
|
|
rel.column_name,
|
|
rel.column_name
|
|
);
|
|
END IF;
|
|
END LOOP;
|
|
END $$;
|
|
"""
|
|
)
|
|
|
|
# Delete QA users after dependent project-role and audit rows have been removed.
|
|
op.execute("DELETE FROM users WHERE id IN (SELECT id FROM qa_user_ids)")
|
|
op.execute("DROP TABLE qa_user_ids")
|
|
|
|
allowed = ", ".join(f"'{role}'" for role in USER_ROLE_VALUES)
|
|
op.execute(
|
|
f"""
|
|
ALTER TABLE users
|
|
ALTER COLUMN role TYPE text
|
|
USING role::text
|
|
"""
|
|
)
|
|
op.execute("DROP TYPE user_role")
|
|
op.execute(f"CREATE TYPE user_role AS ENUM ({allowed})")
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE users
|
|
ALTER COLUMN role TYPE user_role
|
|
USING role::user_role
|
|
"""
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
allowed = ", ".join(f"'{role}'" for role in (*USER_ROLE_VALUES, "QA"))
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE users
|
|
ALTER COLUMN role TYPE text
|
|
USING role::text
|
|
"""
|
|
)
|
|
op.execute("DROP TYPE user_role")
|
|
op.execute(f"CREATE TYPE user_role AS ENUM ({allowed})")
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE users
|
|
ALTER COLUMN role TYPE user_role
|
|
USING role::user_role
|
|
"""
|
|
)
|