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
5.3 KiB
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/assetsasset_idUUID 직렬화, 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_tradestests/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.pyensure_schema(): replacedADD CONSTRAINT IF NOT EXISTS(not valid PostgreSQL syntax) withDO $$ IF NOT EXISTS ... ALTER TABLE ADD CONSTRAINT $$pattern for uq_portfolios_owner_namecreate_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—PortfolioDomainRepositoryProtocol updated:create_portfolionew signature,get_portfolio_by_name_and_useradded,list_portfoliosProtocol params alignedapp/schemas/portfolio.py—CreatePortfolioRequest: removedportfolio_idfield;name: strnow required with regex validation[a-zA-Z0-9._:-]{1,100}app/services/portfolio_service.py—create_portfolio(): call site updated toself._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;updateuses--new-namewith--forceguard;comment/tagsub-commands keepportfolio_id(UUID) directlydocs/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 documenteddocs/project-structure.md— Addedget_portfolio_by_name_and_user()to repositories sectiondocs/runtime-flows.md— Added CLI portfolio name+user resolution flow; GET /v1/assets asset_id UUID notedocs/interface-contract.md— Added v1 consumer API section with simultaneous deployment constraint; CLI portfolio inputs updatedtests/test_cli.py— FakeRepo:create_portfolionew signature (generates UUID),get_portfolio_by_name_and_useradded;_portfolio(): name changed to "005930:tv"; 5 tests updated to pass --user and adjust assertionstests/test_portfolio_ownership.py— Fixed POST body to usenameinstead ofportfolio_id
Execution Log
- Created Alembic migration
20260511_0015_portfolio_uuid_migration.py(REQ-016) - 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) - Updated
app/domain/services.pyProtocol (REQ-016) - Updated
app/schemas/portfolio.py: removed portfolio_id, name required + validated (REQ-016, REQ-017) - Updated
app/services/portfolio_service.py: create_portfolio call site (REQ-016) - Rewrote
app/cli/commands/portfolio.py: name+user pattern for all mutation commands (REQ-017) - Added dcy-002 A-3 철회 주석 (REQ-018)
- Updated 4 baseline docs (REQ-019)
- Created
tests/test_portfolio_uuid.pywith 14 tests - Fixed
tests/test_cli.pyFakeRepo + 5 test cases - Fixed
tests/test_portfolio_ownership.pyPOST body field - 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_0015not 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
commentandtagsub-commands still takeportfolio_id(UUID) directly; operators must obtain UUID fromportfolio list --useroutput