Coverage for nexusLIMS/db/migrations/versions/v2_5_0a_add_external_user_identifiers.py: 100%
15 statements
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
1"""Add external_user_identifiers table.
3Adds table for mapping NexusLIMS usernames to external system user IDs using
4a star topology design with nexuslims_username as the canonical identifier.
6Supports integration with:
7- NEMO (lab management system)
8- LabArchives ELN (electronic lab notebook)
9- LabArchives Scheduler (reservation system)
10- CDCS (Configurable Data Curation System)
12Revision ID: v2_5_0a
13Revises: v2_4_0b
14Create Date: 2026-02-08 14:00:00.000000
16"""
18from typing import Sequence, Union
20import sqlalchemy as sa
21from alembic import op
23# revision identifiers, used by Alembic.
24revision: str = "v2_5_0a"
25down_revision: Union[str, Sequence[str], None] = "v2_4_0b"
26branch_labels: Union[str, Sequence[str], None] = None
27depends_on: Union[str, Sequence[str], None] = None
30def upgrade() -> None:
31 """Create external_user_identifiers table."""
32 # Create table with CHECK constraint and UNIQUE constraints
33 # NOTE: CHECK constraint values are hardcoded to preserve this migration's
34 # historical accuracy. If ExternalSystem enum changes in the future, a new
35 # migration should be created to update the constraint.
36 op.create_table(
37 "external_user_identifiers",
38 sa.Column("id", sa.Integer(), nullable=False),
39 sa.Column("nexuslims_username", sa.String(), nullable=False),
40 sa.Column("external_system", sa.String(), nullable=False),
41 sa.Column("external_id", sa.String(), nullable=False),
42 sa.Column("email", sa.String(), nullable=True),
43 sa.Column("created_at", sa.String(), nullable=False),
44 sa.Column("last_verified_at", sa.String(), nullable=True),
45 sa.Column("notes", sa.String(), nullable=True),
46 sa.CheckConstraint(
47 "external_system IN ('nemo', 'labarchives_eln', "
48 "'labarchives_scheduler', 'cdcs')",
49 name="valid_external_system",
50 ),
51 sa.PrimaryKeyConstraint("id"),
52 sa.UniqueConstraint(
53 "nexuslims_username",
54 "external_system",
55 name="nexuslims_username_system_UNIQUE",
56 ),
57 sa.UniqueConstraint(
58 "external_system", "external_id", name="system_external_id_UNIQUE"
59 ),
60 )
62 # Create indexes
63 op.create_index(
64 "idx_external_lookup",
65 "external_user_identifiers",
66 ["external_system", "external_id"],
67 unique=False,
68 )
69 op.create_index(
70 "idx_nexuslims_username",
71 "external_user_identifiers",
72 ["nexuslims_username"],
73 unique=False,
74 )
77def downgrade() -> None:
78 """Drop external_user_identifiers table."""
79 # Drop indexes
80 op.drop_index("idx_nexuslims_username", table_name="external_user_identifiers")
81 op.drop_index("idx_external_lookup", table_name="external_user_identifiers")
83 # Drop table
84 op.drop_table("external_user_identifiers")