feat: REQ-002/REQ-010 implementation — assets.tag_ids column + GET /v1/assets admin catalog (BAT-012)
- REQ-002 scope redefinition: portfolios.tag_ids → assets.tag_ids UUID[] column with GIN index
- REQ-010 augmentation: GET /v1/assets redesigned as admin catalog endpoint (admin-only, no token_type branching)
- Asset tagging infrastructure: tag_ids column, archived flag synthesis for inactive tags, list_assets N+1 optimization
- DB migration 20260521_0023: assets.tag_ids UUID[] NOT NULL DEFAULT '{}', idx_assets_tag_ids_gin
- Test suite: 17 new test cases for assets.tag_ids + GET /v1/assets AC-9~12 (362 tests total)
- Documentation: runtime flows, release baseline, batch verification all updated
- BAT-012 added to REL-001 Included Batch and marked release-candidate
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""Add assets.tag_ids UUID[] column and GIN index (REQ-002 CHG-20260521-02)
|
||||
|
||||
Revision ID: 20260521_0023
|
||||
Revises: 20260520_0022
|
||||
Create Date: 2026-05-21 00:00:00
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import inspect
|
||||
|
||||
revision = "20260521_0023"
|
||||
down_revision = "20260520_0022"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _column_exists(inspector: sa.engine.Inspector, table_name: str, column_name: str) -> bool:
|
||||
return any(col["name"] == column_name for col in inspector.get_columns(table_name))
|
||||
|
||||
|
||||
def _index_exists(inspector: sa.engine.Inspector, table_name: str, index_name: str) -> bool:
|
||||
return any(idx["name"] == index_name for idx in inspector.get_indexes(table_name))
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
|
||||
if not _column_exists(inspector, "assets", "tag_ids"):
|
||||
op.add_column(
|
||||
"assets",
|
||||
sa.Column(
|
||||
"tag_ids",
|
||||
sa.ARRAY(sa.UUID()),
|
||||
nullable=False,
|
||||
server_default=sa.text("'{}'::uuid[]"),
|
||||
),
|
||||
)
|
||||
|
||||
inspector = inspect(bind)
|
||||
if _column_exists(inspector, "assets", "tag_ids") and not _index_exists(
|
||||
inspector, "assets", "idx_assets_tag_ids_gin"
|
||||
):
|
||||
op.create_index(
|
||||
"idx_assets_tag_ids_gin",
|
||||
"assets",
|
||||
["tag_ids"],
|
||||
postgresql_using="gin",
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
|
||||
if _index_exists(inspector, "assets", "idx_assets_tag_ids_gin"):
|
||||
op.drop_index("idx_assets_tag_ids_gin", table_name="assets")
|
||||
|
||||
if _column_exists(inspector, "assets", "tag_ids"):
|
||||
op.drop_column("assets", "tag_ids")
|
||||
Reference in New Issue
Block a user