feat: v1 portfolio mutation, sub-resources, ledger path migration (BAT-008~011)
dcy-003/004 기반 v1 API surface 완성 및 BAT-008~011 구현·검증 완료:
- BAT-008 (REQ-019~021,024): 토큰 surface baseline 문서 갱신
- BAT-009 (REQ-025~028): POST/PATCH/DELETE/GET /v1/portfolios CRUD 신설
- _require_write_token, _get_portfolio_or_404, soft delete, daemon-scoped list
- BAT-010 (REQ-030~032): portfolio tag/comment 서브 라우트 신설 + baseline 갱신
- GET/POST/DELETE /v1/portfolios/{id}/tags
- POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments
- BAT-011 (REQ-029): ledger path migration
- POST /v1/assets/{asset_id}/ledger/trades → /v1/portfolios/{portfolio_id}/ledger/trades
- v1_ledger.py 제거, disabled 차단(409) 추가
- test_v1.py / test_daemon_assignment.py 구 path 갱신
- 신규 tests: test_v1_portfolios_crud.py, test_v1_portfolios_list.py,
test_v1_portfolios_tags_comments.py, test_v1_portfolios_ledger.py
- 전체 suite: 340 passed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# BAT-011 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-011
|
||||
|
||||
## Architecture Summary
|
||||
- `app/api/v1_portfolios.py`에 `POST /{portfolio_id}/ledger/trades` 서브 라우트를 추가한다. 기존 `app/api/v1_ledger.py`의 daemon 인가 3단계 + idempotency 로직을 신 path로 이식하고, disabled 차단을 daemon 인가 이전에 삽입한다.
|
||||
- 구 path는 파일 전체 제거(`app/api/v1_ledger.py` 삭제) + `app/main.py`에서 include 제거로 처리한다. 별칭/redirect 없음.
|
||||
- trex-basic 동시 배포 순서: trex-basic 사전 빌드 완료 확인 → portfolio-hub 배포 → trex-basic 배포. rollback 시 portfolio-hub + trex-basic 동반 rollback.
|
||||
|
||||
## Changed Areas
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — REQ-032에서 이미 "pending REQ-029" 마킹됨. 본 batch에서 실제 명세 반영.
|
||||
- `docs/runtime-flows.md` — ledger trade path 이전 흐름 + daemon assignment 3단계 인가 갱신 (REQ-032 기반 기존 흐름 보강).
|
||||
- Modules:
|
||||
- `app/api/v1_portfolios.py` — 신 path 라우트 추가
|
||||
- `app/api/v1_ledger.py` — 제거 (구 path 라우트 삭제)
|
||||
- `app/main.py` — 구 ledger 라우터 include 제거
|
||||
- `app/services/ledger_service.py` — disabled 차단 검증 추가 (또는 라우터 계층에서 처리)
|
||||
- Interfaces:
|
||||
- `POST /v1/portfolios/{portfolio_id}/ledger/trades` (신설)
|
||||
- `POST /v1/assets/{asset_id}/ledger/trades` (제거 → HTTP 404)
|
||||
|
||||
## Key Decisions
|
||||
1. **구 `v1_ledger.py` 완전 제거**: 라우트만 제거하는 대신 파일 전체를 삭제해 잔재 코드가 남지 않도록 한다. `app/main.py`에서 해당 라우터 include를 제거.
|
||||
2. **disabled 차단 위치**: 라우터 계층에서 `_get_portfolio_or_404` 직후, daemon 인가 3단계 이전에 `if portfolio.status == "disabled": raise HTTPException(409)` 삽입. 서비스 계층 변경 최소화.
|
||||
3. **인가 순서**: (0) Bearer 없음 → 401, (1) token `daemon_id IS NULL` → 403, (2) portfolio not found → 404, (3) `portfolio.status == "disabled"` → 409, (4) `portfolio.managed_by_daemon_id IS NULL` → 403, (5) `portfolio.managed_by_daemon_id != auth.daemon_id` → 403, (6) idempotency 분기 → 201/200/409.
|
||||
4. **trex-basic 동시 배포**: release plan에 배포 순서 잠금 명시. 한시적 별칭/redirect 없음.
|
||||
|
||||
## Edge Cases
|
||||
- `portfolio_id`가 비존재 → HTTP 404 (daemon 인가보다 우선).
|
||||
- `status='disabled'` portfolio → HTTP 409 (daemon 인가 체크 전에 차단하여 daemon 미배정 오류와 구분).
|
||||
- 동일 `(portfolio_id, order_id, execution_id)` + 동일 payload 재호출 → HTTP 200 + `status="duplicate"`.
|
||||
- 동일 key + 다른 payload → HTTP 409 (idempotency conflict).
|
||||
- Bearer token 없음 → HTTP 401.
|
||||
|
||||
## Architecture Impact
|
||||
- runtime-flows
|
||||
|
||||
## Reference Doc Update Plan
|
||||
- project-structure.md: `v1_ledger.py` 제거 반영
|
||||
- runtime-flows.md: ledger path 이전 흐름 + daemon 인가 3단계 + disabled 차단 순서 갱신
|
||||
Reference in New Issue
Block a user