Files
portfolio-hub/docs/batches/bat-002_20260519_asset-catalog-api-completion/design.md
T

5.9 KiB

BAT-002 Design

  • Status: verified
  • Batch: BAT-002

Architecture Summary

  • REQ-010: app/api/v1_assets.py에 admin 전용 엔드포인트 3개(POST /v1/assets, PATCH /v1/assets/{id}, PATCH /v1/assets/{id}/deactivate)를 추가하고, app/cli/commands/asset.py 신규 파일로 pfhub asset 커맨드 그룹을 구현한다. 내부 로직은 BAT-001에서 구현된 postgres.py의 insert_asset / update_asset / list_assets를 직접 호출한다.
  • REQ-013: 현재 service token 전용인 GET /v1/assets list_assets 핸들러를 auth.token_type 분기로 재작성한다. service/user/admin 각 경로에서 동일한 list_portfolios_with_strategies repository 메서드를 호출하되 필터 파라미터를 토큰 타입별로 다르게 설정한다.
  • REQ-012: PortfolioService.create_portfolio()에 repository.get_asset_by_id(asset_id) 호출을 추가하여 active 상태를 검증한다. CLI에 --symbol 옵션을 추가해 symbol→asset_id 조회 경로를 제공한다.
  • REQ-015: 코드 변경 없음. 구현 완료 후 baseline 문서 4종 갱신.

Changed Areas

  • Docs:
    • docs/data-model.md — assets 엔티티 정식 추가, portfolios asset_id FK 반영 (REQ-015)
    • docs/interface-contract.md — POST/PATCH /v1/assets 신규, GET /v1/assets 토큰 분기 명세 (REQ-015)
    • docs/runtime-flows.md — GET /v1/assets 토큰 분기 흐름, admin asset CRUD 흐름 (REQ-015)
    • docs/project-structure.md — pfhub asset 커맨드 그룹, 신규 asset.py 모듈 (REQ-015)
  • Modules:
    • app/cli/commands/asset.py (신규) — pfhub asset create/list/update/deactivate
    • app/cli/main.py — asset_app typer 그룹 등록
    • app/api/v1_assets.py — admin 엔드포인트 3개 추가 + list_assets 재작성
    • app/schemas/v1.py — AssetCreateRequest, AssetUpdateRequest, AssetResponse 추가
    • app/services/portfolio_service.py — create_portfolio catalog 검증 로직 추가
    • app/cli/commands/portfolio.py — create 커맨드에 --symbol 옵션 추가
    • app/repositories/postgres.py — list_portfolios_with_strategies market 필터 파라미터 추가
  • Interfaces:
    • POST /v1/assets — admin token 필요, AssetCreateRequest body, 201 AssetResponse
    • PATCH /v1/assets/{asset_id} — admin token 필요, AssetUpdateRequest body, 200 AssetResponse
    • PATCH /v1/assets/{asset_id}/deactivate — admin token 필요, 200 AssetResponse
    • GET /v1/assets — service/user/admin 토큰 분기, ?market=<code> 파라미터 지원

Key Decisions

  1. REQ-013 분기 구조: auth.token_type 필드로 분기

    • service 토큰: auth.daemon_id 추출 → None이면 403 fail-closed (기존 REQ-006/008 동작 유지). list_portfolios_with_strategies(managed_by_daemon_id=auth.daemon_id)
    • user 토큰: list_portfolios_with_strategies(owner_user_id=auth.user_id)
    • admin 토큰: 기본 list_portfolios_with_strategies(owner_user_id=auth.user_id); ?user_id=<id> 파라미터 시 해당 user id로 필터링
    • ?market=<code> 파라미터: 모든 토큰에서 지원 — list_portfolios_with_strategies에 market 필터 파라미터 추가
    • 이유: 기존 daemon 인증 흐름 test_daemon_assignment.py와 호환성 유지, token_type은 이미 TokenPrincipal에 존재
  2. REQ-010 admin 권한 검사: Permission.ADMIN으로 require_permission 재사용

    • 기존 app/core/auth.py의 require_permission 의존성 또는 AuthContext 확인 패턴 활용
    • non-admin이 POST/PATCH /v1/assets 호출 시 403
    • 이유: 인증 패턴 일관성 유지, 신규 미들웨어 불필요
  3. REQ-012 검증 위치: PortfolioService.create_portfolio() 서비스 레이어

    • CLI와 HTTP API 양쪽이 PortfolioService를 통하므로 한 곳에서 커버
    • repository.get_asset_by_id(payload.asset_id) → None이면 "asset not found" ValueError
    • asset.status != "active"이면 "asset is inactive" ValueError
    • --symbol 옵션은 CLI에만 추가 — CLI에서 repository.get_asset_by_symbol(symbol) 조회 후 asset_id 획득 → CreatePortfolioRequest에 전달
    • 이유: 서비스 레이어 검증으로 HTTP/CLI 중복 없음, symbol→id 변환은 CLI 관심사
  4. REQ-010 CLI 구조: 별도 파일 app/cli/commands/asset.py 분리

    • BAT-001의 portfolio.py 패턴과 동일하게 asset_app = typer.Typer(...) 생성
    • app/cli/main.py에 app.add_typer(asset_app, name="asset")으로 등록
    • 이유: 커맨드 그룹별 파일 분리 — 기존 패턴 일관성
  5. 구현 순서: REQ-010 → REQ-013 → REQ-012 → REQ-015

    • REQ-010이 v1_assets.py에 신규 엔드포인트를 추가하고, REQ-013이 같은 파일의 list_assets를 재작성 — 순서 엄수로 충돌 방지
    • REQ-012는 서비스 레이어만 수정 — 독립적이지만 REQ-010 완료 후 테스트 환경 정비 후 진행

Edge Cases

  • REQ-013: admin token + ?user_id 파라미터에서 해당 user가 존재하지 않는 경우 → 빈 배열 반환 (user 존재 여부 비공개, 404 미사용)
  • REQ-013: service token + daemon_id=None → 403 (기존 REQ-008 동작 그대로)
  • REQ-012: --symbol 조회 시 해당 symbol asset이 없는 경우 → CLI에서 즉시 오류 출력 후 종료
  • REQ-012: portfolio create 시 get_asset_by_id() 호출 비용 — 로컬 DB에서 단순 PK 조회로 성능 영향 없음
  • REQ-010: deactivate된 asset을 참조하는 기존 portfolio → 기존 portfolio 유지, 신규 create만 REQ-012에서 차단

Architecture Impact

  • runtime-flows: GET /v1/assets 분기 구조 신규 (service/user/admin 3경로)
  • project-structure: pfhub asset 커맨드 그룹 신규, app/cli/commands/asset.py 모듈 신규

Reference Doc Update Plan

  • project-structure.md: app/cli/commands/asset.py 추가, pfhub asset 커맨드 그룹 추가 (REQ-015)
  • runtime-flows.md: GET /v1/assets 토큰 분기 흐름 교체, admin asset CRUD 흐름 신규 추가 (REQ-015)