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
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user