Files
portfolio-hub/docs/srs/Interface/req-028_v1-portfolios-list.md
T
genichinandClaude Sonnet 4.6 4bf6b554a7 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>
2026-05-21 07:25:39 +00:00

7.1 KiB

REQ-028: GET /v1/portfolios 신설 (토큰 분기 조회)

  • Status: Implemented
  • Type: Interface
  • Priority: High
  • Owner: Justin Ko

Intent

  • v1 API에 portfolio 조회 경로(GET /v1/portfolios)를 신설해 service/user/admin 세 principal이 단일 엔드포인트로 분기 조회할 수 있도록 한다.
  • dcy-001 FR-5(REQ-013 Deprecated 처리)에서 회수된 GET /v1/assets 통합 endpoint 정의를 본 신규 path로 이전한다(dcy-003 옵션 B 결정).

Requirement

  • 신규 라우트 GET /v1/portfolios (Bearer 인증 필수).
  • 토큰 분기 규칙 (dcy-003 OQ-5 결정):
    • token_type='service': daemon-bound 조회. daemon_id is None이면 HTTP 403 fail-closed. 그 외 managed_by_daemon_id = auth.daemon_id 필터 + ?market=<code> 지원.
    • token_type='client': HTTP 403 fail-closed (dcy-004 REQ-016 폐지 확정 정합).
    • token_type ∈ {session, None(env)}:
      • permission='admin' → 기본 owner_user_id = auth.user_id(본인 portfolio) 반환. ?user_id=<id> 지정 시 해당 user의 portfolio 반환.
      • permission ∈ {read, trade-write} → owner_user_id = auth.user_id 필터, 본인 portfolio만 반환.
  • ?market=<code> 필터: 모든 토큰 타입에서 지원. <code> = ANY(assets.markets)인 portfolio만 반환(catalog와 join).
  • ?include_disabled=true 옵션 (dcy-003 R-2 완화): 기본 응답은 status='disabled'인 portfolio 제외. opt-in 시에만 포함.
  • 응답 항목 schema: 신규 PortfolioItem (portfolio_id, asset_id, owner_user_id, broker, status, cash_balance, ..., tag_ids, managed_by_daemon_id, ...). asset_id는 catalog의 assets.asset_id(UUID) 참조.
  • 응답: HTTP 200 + PortfolioListResponse (portfolios: list[PortfolioItem], total: int).

Acceptance Criteria

  1. Given service token(daemon-bound, daemon_id 있음), When GET /v1/portfolios, Then managed_by_daemon_id = auth.daemon_id인 portfolio 목록만 반환.
  2. Given service token(daemon_id 없음), When GET /v1/portfolios, Then HTTP 403 fail-closed.
  3. Given session/env token + permission ∈ {read, trade-write}, When GET /v1/portfolios, Then owner_user_id = auth.user_id인 portfolio 목록만 반환.
  4. Given admin token (no ?user_id), When GET /v1/portfolios, Then admin 본인의 portfolio 목록 반환.
  5. Given admin token + ?user_id=<target>, When GET /v1/portfolios, Then <target> user의 portfolio 목록 반환.
  6. Given 임의 토큰 + ?market=KRX, When GET /v1/portfolios?market=KRX, Then 'KRX' = ANY(assets.markets)인 portfolio만 반환.
  7. Given disabled portfolio 1건 + active portfolio 1건 보유 user, When GET /v1/portfolios (?include_disabled 없음), Then active 1건만 반환.
  8. Given AC-7 환경 + ?include_disabled=true, When GET /v1/portfolios?include_disabled=true, Then 2건 모두 반환.
  9. Given client token, When GET /v1/portfolios, Then HTTP 403 fail-closed.
  10. Given Bearer token 없음, When GET /v1/portfolios, Then HTTP 401.
  11. Given 응답 portfolio 항목, When asset_id 필드 확인, Then assets.asset_id(catalog UUID)를 참조한다.

Impacted Area

  • Docs:
    • docs/interface-contract.md — GET /v1/portfolios 명세 + ?market/?user_id/?include_disabled 파라미터 (REQ-032)
    • docs/runtime-flows.md — 토큰 분기 조회 flow + daemon-scoped 필터 (REQ-032)
  • Modules:
    • app/api/v1_portfolios.py — GET 라우트 (REQ-025의 인증 헬퍼 재사용 + GET 전용 service 토큰 분기 추가)
    • app/api/v1_assets.py — 기존 list_assets 함수에서 portfolio 조회 로직 제거(catalog 전용 회수). REQ-013 Deprecated 후속 정리.
    • app/schemas/v1.py — PortfolioItem, PortfolioListResponse 신규
    • app/services/portfolio_service.py:list_portfolios — token-type별 분기 + ?market/?include_disabled 처리. dcy-001 REQ-013 Deprecated 후속 작업 흡수.
    • app/repositories/postgres.py:list_portfolios_with_strategies — managed_by_daemon_id 필터 + markets array 필터 + include_disabled 분기
  • Tests:
    • 신규 tests/test_v1_portfolios_list.py — AC-1~11 검증

Notes

  • 본 REQ는 dcy-001 REQ-013 Deprecated(CHG-20260520-01)의 후속 신규 REQ. 토큰 분기 정책은 REQ-013의 본문을 path만 변경해 흡수.
  • GET /v1/assets(catalog 조회 의미)는 본 REQ 범위 밖이며 dcy-001 batch design 단계에서 REQ-010 보강 또는 신규 REQ로 처리.
  • service token이 NULL daemon_id로 호출하면 fail-closed. NULL daemon legacy token 운영 정리는 dcy-004 REQ-023(migrate-null-daemon CLI)로 처리.
  • ?include_disabled 기본 false 정책은 R-2 완화의 핵심. REQ-027 soft delete와 정합.
  • Source: dcy-003 FR-5, OQ-5, NFR-1, NFR-3, R-2 완화. dcy-001 REQ-013 후속.

Change Log

CHG-20260521-01

  • Date: 2026-05-21
  • Author: Justin Ko

Change Summary

  • Status: Approved → Implemented

Intent

  • BAT-009 confirm-batch-verification 완료. tests/test_v1_portfolios_list.py AC-1~11 전 케이스 pytest 통과 (2026-05-21, 298 PASSED). 토큰 분기 + status filter + daemon filter 모두 검증.

Acceptance Criteria Delta

  • No change

Impacted Area Delta

  • No change

Delivery Trace Delta

  • Batches: BAT-009 (release-candidate)
  • Releases: TBD

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: promote-to-implemented
  • Follow-up Action: 없음

CHG-20260520-02

  • Date: 2026-05-20
  • Author: Justin Ko

Change Summary

  • Status: Proposed → Approved

Intent

  • 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC 구체성 확인 완료, blocker 없음

Acceptance Criteria Delta

  • No change

Impacted Area Delta

  • No change

Delivery Trace Delta

  • Batches: none (미정)
  • Releases: none (미정)

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: promote-to-approved
  • Follow-up Action: batch 계획 편입

CHG-20260520-01

  • Date: 2026-05-20
  • Author: Justin Ko

Change Summary

  • 최초 생성 — dcy-003 confirm-discovery 후 draft-req 실행. dcy-003 FR-5 산출물 + REQ-013 Deprecated 후속.

Intent

  • v1 portfolio 조회 신설 + 토큰 분기 정책 path 이전 + R-2 disabled 가시성 완화.

Acceptance Criteria Delta

  • Added:
    • AC-1~11 최초 작성

Impacted Area Delta

  • Docs: docs/interface-contract.md, docs/runtime-flows.md (REQ-032)
  • Modules: app/api/v1_portfolios.py, app/api/v1_assets.py(catalog 회수), app/schemas/v1.py, app/services/portfolio_service.py, app/repositories/postgres.py — 최초 지정
  • Tests: tests/test_v1_portfolios_list.py (신규) — 최초 지정

Delivery Trace Delta

  • Batches: none (미정)
  • Releases: none (미정)

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: keep-current
  • Follow-up Action: confirm-req 후 batch 계획 편입; dcy-001 REQ-013 Deprecated 정리 후속

Validation Plan

  • pytest TestClient로 AC-1~11 검증; 토큰 분기 + ?market + ?user_id + ?include_disabled 시나리오