Files
portfolio-hub/docs/batches/bat-006_20260511_portfolio-uuid-migration/implementation.md
T
genichin 829629fa8f feat: implement portfolio UUID migration and v1 API finalization (BAT-006, BAT-001 re-verified)
Core Changes:
- Migrate portfolio_id from string to UUID in schema and repository layer
- Update portfolio CLI commands to use name+user combination for identification
- Update HTTP API to accept portfolio_id as UUID path parameter
- Implement portfolio ownership validation in repository queries

Requirements Completed (REQ-016 to REQ-019):
- REQ-016: portfolios table UUID schema migration with data migration script
- REQ-017: CLI portfolio commands with name+user identification
- REQ-018: HTTP API portfolio path parameters and v1 asset_id UUID returns
- REQ-019: Portfolio UUID baseline documentation updates

BAT-001 Re-verification:
- REQ-004~010 marked as Implemented after verification evidence validation
- All v1 API endpoints verified with comprehensive test coverage

Documentation Updates:
- Update data-model.md with portfolio UUID structure
- Update interface-contract.md with UUID path parameters
- Update runtime-flows.md with ownership-based resolution flows
- Update project-structure.md with database layer changes
- Add migration script for portfolio UUID transition

Testing:
- Add test_portfolio_uuid.py for migration validation
- Update test_cli.py and test_portfolio_ownership.py for ownership checks
2026-05-11 09:27:57 +00:00

5.3 KiB

BAT-006 Implementation

  • Status: completed
  • Batch: BAT-006

Plan Summary

  • REQ-016: 신규 Alembic revision(name 컬럼 추가, portfolio_id UUID 교체, FK 4개 테이블 atomic 갱신, UNIQUE 제약 추가), Portfolio dataclass name 필드 추가, create_portfolio() UUID 생성, get_portfolio_by_name_and_user() 신규 메서드
  • REQ-017: CLI portfolio 명령 argument/option 전면 전환(portfolio_id → <name> --user), (user_id, name) → UUID 서비스 레이어, name 유효성 검사, update name guard
  • REQ-018: HTTP API path parameter UUID 전환, GET /v1/assets asset_id UUID 직렬화, dcy-002 A-3 철회 주석
  • REQ-019: baseline 문서 4개 갱신(project-structure.md, runtime-flows.md, data-model.md, interface-contract.md)

Changed Files

New Files

  • migrations/versions/20260511_0015_portfolio_uuid_migration.py — Alembic revision; idempotency guard via uq_portfolios_owner_name constraint presence check; atomic per-portfolio FK update across portfolio_ledger/portfolio_comments/portfolio_tags/ledger_trades
  • tests/test_portfolio_uuid.py — 14 tests covering UUID creation, name+user resolution, alice/bob isolation, buy/delete flow, name validation, unknown user/portfolio errors, update rename

Modified Files

  • app/repositories/postgres.py
    • ensure_schema(): replaced ADD CONSTRAINT IF NOT EXISTS (not valid PostgreSQL syntax) with DO $$ IF NOT EXISTS ... ALTER TABLE ADD CONSTRAINT $$ pattern for uq_portfolios_owner_name
    • create_portfolio(): signature changed from (portfolio_id: str, *, ..., name: str, ...) to (name: str, *, ...) with server-generated UUID; catches psycopg UniqueViolation → ValueError("이미 사용 중인 이름입니다: {name}")
    • Added get_portfolio_by_name_and_user(owner_user_id, name) -> Portfolio | None
    • update_portfolio(): catches UniqueViolation on name update → ValueError("이미 사용 중인 이름입니다: {new_name}")
  • app/domain/services.py — PortfolioDomainRepository Protocol updated: create_portfolio new signature, get_portfolio_by_name_and_user added, list_portfolios Protocol params aligned
  • app/schemas/portfolio.py — CreatePortfolioRequest: removed portfolio_id field; name: str now required with regex validation [a-zA-Z0-9._:-]{1,100}
  • app/services/portfolio_service.py — create_portfolio(): call site updated to self._repository.create_portfolio(payload.name, owner_user_id=...)
  • app/cli/commands/portfolio.py — Full rewrite for REQ-017: _NAME_RE, _validate_portfolio_name(), _resolve_portfolio_id() helpers; all 8 mutation commands switched to <name> --user; update uses --new-name with --force guard; comment/tag sub-commands keep portfolio_id (UUID) directly
  • docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md — A-3 철회 주석 추가
  • docs/data-model.md — portfolios.portfolio_id → UUID, name column, uq_portfolios_owner_name constraint documented
  • docs/project-structure.md — Added get_portfolio_by_name_and_user() to repositories section
  • docs/runtime-flows.md — Added CLI portfolio name+user resolution flow; GET /v1/assets asset_id UUID note
  • docs/interface-contract.md — Added v1 consumer API section with simultaneous deployment constraint; CLI portfolio inputs updated
  • tests/test_cli.py — FakeRepo: create_portfolio new signature (generates UUID), get_portfolio_by_name_and_user added; _portfolio(): name changed to "005930:tv"; 5 tests updated to pass --user and adjust assertions
  • tests/test_portfolio_ownership.py — Fixed POST body to use name instead of portfolio_id

Execution Log

  1. Created Alembic migration 20260511_0015_portfolio_uuid_migration.py (REQ-016)
  2. Updated app/repositories/postgres.py: ensure_schema UNIQUE constraint (DO $$ pattern), create_portfolio UUID generation, get_portfolio_by_name_and_user, update_portfolio UniqueViolation catch (REQ-016)
  3. Updated app/domain/services.py Protocol (REQ-016)
  4. Updated app/schemas/portfolio.py: removed portfolio_id, name required + validated (REQ-016, REQ-017)
  5. Updated app/services/portfolio_service.py: create_portfolio call site (REQ-016)
  6. Rewrote app/cli/commands/portfolio.py: name+user pattern for all mutation commands (REQ-017)
  7. Added dcy-002 A-3 철회 주석 (REQ-018)
  8. Updated 4 baseline docs (REQ-019)
  9. Created tests/test_portfolio_uuid.py with 14 tests
  10. Fixed tests/test_cli.py FakeRepo + 5 test cases
  11. Fixed tests/test_portfolio_ownership.py POST body field
  12. Fixed ensure_schema() syntax: ADD CONSTRAINT IF NOT EXISTS → DO $$ block (PostgreSQL compatibility)

Validation

  • Full test suite: 129 passed, 0 failed
  • tests/test_portfolio_uuid.py: 14/14 passed (UUID generation, name+user resolution, alice/bob isolation, name validation, error messages)
  • tests/test_cli.py: 49/49 passed (including updated tests for new CLI interface)
  • tests/test_portfolio_ownership.py: 4/4 passed

Remaining Risks

  • Alembic migration 20260511_0015 not verified against real PostgreSQL instance (unit tests use FakeRepo only)
  • trex-basic simultaneous deployment required: GET /v1/assets asset_id changes from TEXT to UUID format
  • Existing comment and tag sub-commands still take portfolio_id (UUID) directly; operators must obtain UUID from portfolio list --user output