You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
6.3 KiB
Python

"""phase13 connectors: connector_account/sync_state/external_link/oauth_state/log + finance_tx/health_sample/chat_message
Revision ID: p13a1b2c3d4e5
Revises: p12f6a7b8c9d0
Create Date: 2026-06-15 06:00:00.000000
"""
from typing import Sequence, Union
import sqlalchemy as sa
import sqlmodel
from alembic import op
revision: str = "p13a1b2c3d4e5"
down_revision: Union[str, Sequence[str], None] = "p12f6a7b8c9d0"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
S = sqlmodel.sql.sqltypes.AutoString
def upgrade() -> None:
op.create_table(
"connector_account",
sa.Column("id", S(), nullable=False),
sa.Column("user_id", S(), nullable=False),
sa.Column("domain", S(), nullable=False),
sa.Column("mode", S(), nullable=False),
sa.Column("provider", S(), nullable=False),
sa.Column("external_account_id", S(), nullable=False),
sa.Column("name", S(), nullable=False),
sa.Column("kind", S(), nullable=False),
sa.Column("tone", S(), nullable=False),
sa.Column("state", S(), nullable=False),
sa.Column("last_synced_at", sa.DateTime(), nullable=True),
sa.Column("last_label", S(), nullable=False),
sa.Column("token_enc", S(), nullable=False),
sa.Column("scopes", S(), nullable=False),
sa.Column("error_detail", S(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["user_id"], ["person.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"connector_sync_state",
sa.Column("id", S(), nullable=False),
sa.Column("account_id", S(), nullable=False),
sa.Column("cursor", S(), nullable=False),
sa.Column("page_token", S(), nullable=False),
sa.Column("last_full_sync_at", sa.DateTime(), nullable=True),
sa.Column("last_delta_sync_at", sa.DateTime(), nullable=True),
sa.Column("items_seen", sa.Integer(), nullable=False),
sa.Column("items_upserted", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["account_id"], ["connector_account.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"external_link",
sa.Column("id", S(), nullable=False),
sa.Column("account_id", S(), nullable=False),
sa.Column("external_id", S(), nullable=False),
sa.Column("entity_type", S(), nullable=False),
sa.Column("entity_id", S(), nullable=False),
sa.Column("etag", S(), nullable=False),
sa.Column("external_updated_at", sa.DateTime(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["account_id"], ["connector_account.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_external_link_account_id", "external_link", ["account_id"])
op.create_index("ix_external_link_external_id", "external_link", ["external_id"])
# 멱등성 핵심: (account_id, external_id) 복합 UNIQUE
op.create_index(
"ux_extlink_acct_ext", "external_link", ["account_id", "external_id"], unique=True
)
op.create_table(
"oauth_state",
sa.Column("id", S(), nullable=False),
sa.Column("domain", S(), nullable=False),
sa.Column("provider", S(), nullable=False),
sa.Column("code_verifier", S(), nullable=False),
sa.Column("redirect_after", S(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"connector_account_log",
sa.Column("id", S(), nullable=False),
sa.Column("account_id", S(), nullable=False),
sa.Column("action", S(), nullable=False),
sa.Column("detail", S(), nullable=False),
sa.Column("items", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["account_id"], ["connector_account.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"finance_tx",
sa.Column("id", S(), nullable=False),
sa.Column("account_id", S(), nullable=False),
sa.Column("date", S(), nullable=False),
sa.Column("merchant", S(), nullable=False),
sa.Column("amount", sa.Integer(), nullable=False),
sa.Column("category", S(), nullable=False),
sa.Column("category_id", S(), nullable=False),
sa.Column("account_label", S(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["account_id"], ["connector_account.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"health_sample",
sa.Column("id", S(), nullable=False),
sa.Column("account_id", S(), nullable=False),
sa.Column("kind", S(), nullable=False),
sa.Column("value", sa.Float(), nullable=False),
sa.Column("unit", S(), nullable=False),
sa.Column("day", S(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["account_id"], ["connector_account.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"chat_message",
sa.Column("id", S(), nullable=False),
sa.Column("account_id", S(), nullable=False),
sa.Column("sender", S(), nullable=False),
sa.Column("text", S(), nullable=False),
sa.Column("channel", S(), nullable=False),
sa.Column("at", S(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["account_id"], ["connector_account.id"]),
sa.PrimaryKeyConstraint("id"),
)
def downgrade() -> None:
op.drop_table("chat_message")
op.drop_table("health_sample")
op.drop_table("finance_tx")
op.drop_table("connector_account_log")
op.drop_table("oauth_state")
op.drop_index("ux_extlink_acct_ext", table_name="external_link")
op.drop_index("ix_external_link_external_id", table_name="external_link")
op.drop_index("ix_external_link_account_id", table_name="external_link")
op.drop_table("external_link")
op.drop_table("connector_sync_state")
op.drop_table("connector_account")