Files
portfolio-hub/docs/srs/Interface/req-010_admin-asset-catalog-crud.md
T
genichinandClaude Opus 4.7 9038f41010 feat: REQ-002/REQ-010 implementation — assets.tag_ids column + GET /v1/assets admin catalog (BAT-012)
- REQ-002 scope redefinition: portfolios.tag_ids → assets.tag_ids UUID[] column with GIN index
- REQ-010 augmentation: GET /v1/assets redesigned as admin catalog endpoint (admin-only, no token_type branching)
- Asset tagging infrastructure: tag_ids column, archived flag synthesis for inactive tags, list_assets N+1 optimization
- DB migration 20260521_0023: assets.tag_ids UUID[] NOT NULL DEFAULT '{}', idx_assets_tag_ids_gin
- Test suite: 17 new test cases for assets.tag_ids + GET /v1/assets AC-9~12 (362 tests total)
- Documentation: runtime flows, release baseline, batch verification all updated
- BAT-012 added to REL-001 Included Batch and marked release-candidate

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:37:08 +09:00

12 KiB

REQ-010: admin asset catalog CRUD

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

Intent

  • admin이 CLI 및 HTTP API를 통해 거래 가능한 종목을 assets catalog에 등록·수정·비활성화할 수 있도록 한다.
  • admin이 아닌 user 또는 daemon의 catalog 직접 변경 시도를 HTTP 403으로 거부한다(NFR-3).

Requirement

  • CLI: pfhub asset create <symbol> [--markets <codes>] [--display-name <name>] — admin 권한 필요. markets 값은 쉼표 구분 MarketCode Enum 값.
  • CLI: pfhub asset list [--inactive] — active asset만 기본 출력; --inactive 지정 시 inactive 포함 전체 출력.
  • CLI: pfhub asset update <symbol> [--markets <codes>] [--display-name <name>] — admin 권한 필요.
  • CLI: pfhub asset deactivate <symbol> [--force] — admin 권한 필요. --force 없으면 확인 프롬프트 출력.
  • HTTP API: POST /v1/assets — admin token 필요. request body에 symbol, markets, display_name. 성공 시 HTTP 201 + 생성된 asset 반환.
  • HTTP API: PATCH /v1/assets/{asset_id} — admin token 필요. markets, display_name 변경 가능. 성공 시 HTTP 200 + 갱신된 asset 반환.
  • HTTP API: PATCH /v1/assets/{asset_id}/deactivate — admin token 필요. status='inactive'로 변경. 성공 시 HTTP 200.
  • HTTP API: GET /v1/assets — admin token 필요. query param status (active|inactive|all, 기본 active), market (선택, MarketCode 1개). 응답: { items: [{ asset_id, symbol, markets, display_name, status, created_at, updated_at }] } 형식. 응답 본문에 portfolio 관련 필드는 포함하지 않는다(path 의미 분리 — portfolio 조회는 GET /v1/portfolios).
  • admin 이외의 token으로 POST/PATCH/GET 요청 시 HTTP 403을 반환한다.
  • asset hard DELETE는 제공하지 않는다(soft delete 전용).
  • app/cli/commands/asset.py 신규 파일로 pfhub asset 커맨드 그룹을 구현한다.
  • app/cli/main.py에 app.add_typer(asset_app, name="asset")을 추가한다.
  • app/services/ 또는 관련 서비스에 asset create/update/deactivate/list 로직을 추가한다.

Acceptance Criteria

  1. Given admin 토큰, When pfhub asset create "005930.KS" --markets KRX,NXT, Then assets catalog에 symbol='005930.KS', markets=['KRX','NXT'], status='active' 항목이 생성된다.
  2. Given admin 토큰, When pfhub asset list, Then status='active'인 asset 목록이 출력된다.
  3. Given active/inactive asset 모두 존재, When pfhub asset list --inactive, Then inactive 포함 전체 목록이 출력된다.
  4. Given admin 토큰, When pfhub asset deactivate "005930.KS" --force, Then 해당 asset의 status가 'inactive'로 변경된다.
  5. Given admin 토큰, When POST /v1/assets with valid symbol and markets, Then HTTP 201과 생성된 asset_id가 반환된다.
  6. Given admin 토큰, When PATCH /v1/assets/{asset_id} with updated markets, Then HTTP 200과 변경된 asset이 반환된다.
  7. Given admin 토큰, When PATCH /v1/assets/{asset_id}/deactivate, Then HTTP 200과 status='inactive'가 반환된다.
  8. Given non-admin token (user 또는 service token), When POST /v1/assets 요청, Then HTTP 403이 반환된다.
  9. Given admin 토큰 + active/inactive asset 혼재, When GET /v1/assets, Then HTTP 200 + status='active' 항목만 포함된 {items: [...]} 응답. 각 item은 asset_id, symbol, markets, display_name, status, created_at, updated_at 필드만 포함하고 portfolio 관련 필드(portfolio_id, owner_user_id, cash_balance 등)는 포함하지 않는다.
  10. Given admin 토큰 + active/inactive asset 혼재, When GET /v1/assets?status=all, Then 두 상태 모두 포함; GET /v1/assets?status=inactive, Then inactive만 포함.
  11. Given admin 토큰 + 서로 다른 markets 값을 가진 asset 다수, When GET /v1/assets?market=KRX, Then markets에 KRX가 포함된 asset만 응답된다.
  12. Given non-admin token (read/trade-write 권한 또는 service token), When GET /v1/assets, Then HTTP 403이 반환된다.

Impacted Area

  • Docs:
    • docs/project-structure.md — app/cli/ Command groups에 asset 추가 (REQ-015에서 처리)
    • docs/interface-contract.md — POST/PATCH/GET /v1/assets 엔드포인트 명세 추가 (CHG-20260521-02에서 GET 추가; 후속 docs sync 필요 시 별도 REQ)
  • Modules:
    • app/cli/commands/asset.py — 신규 파일, pfhub asset 커맨드 그룹
    • app/cli/main.py — app.add_typer(asset_app, name="asset") 추가
    • app/services/ — asset create/update/deactivate/list 서비스 메서드 추가
    • app/api/v1_assets.py — POST, PATCH 라우트 추가; GET /v1/assets 라우트는 admin catalog 조회로 재정의(기존 token_type 분기 portfolio 조회 코드 제거)
    • app/schemas/v1.py — CreateAssetRequest, UpdateAssetRequest, AssetCatalogItem, AssetCatalogListResponse 스키마 추가
  • Tests:
    • tests/test_asset_catalog.py (신규) — admin CRUD CLI/API AC 검증, non-admin 403 검증, GET /v1/assets AC-9~12 검증
    • tests/test_v1.py (기존) — GET /v1/assets에 portfolio 응답을 기대하는 케이스가 있다면 GET /v1/portfolios로 이전 또는 catalog 응답 schema로 갱신

Notes

  • pfhub asset list의 HTTP 대응(catalog 전용 GET): dcy-003 옵션 B(path 명명 분리) 결정에 따라 REQ-013(GET /v1/assets 포트폴리오 조회)이 Deprecated(2026-05-20) 처리되어 path 충돌 우려 해소. GET /v1/assets는 admin catalog 조회 전용으로 본 REQ에서 다룬다(2026-05-21 CHG-20260521-02로 augmentation 적용). 포지션 조회 GET /v1/portfolios는 REQ-028에서 분리 구현됨.
  • deactivate 후 해당 asset을 참조하는 기존 portfolio는 보존된다. 신규 portfolio 생성만 거부(REQ-012에서 처리).
  • REQ-009 선행 필요.
  • Source: dcy-001 FR-2, NFR-3; CHG-20260521-02 augmentation는 dcy-003 옵션 B (REQ-013 Deprecation) 후속 작업.

Change Log

CHG-20260521-03

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

Change Summary

  • Status: Approved → Implemented (CHG-20260521-02 augmentation으로 신설된 AC-9~12가 BAT-012 verification에서 evidence 확보)

Intent

  • BAT-012 confirm-batch-verification 완료. GET /v1/assets admin catalog 재정의(AC-912) 신규 evidence + 기존 AC-18 회귀 통과 모두 확인.

Acceptance Criteria Delta

  • No change

Impacted Area Delta

  • No change

Delivery Trace Delta

  • Batches:
    • BAT-012 (release-candidate) — primary trace, AC-1~12 전체 evidence
    • BAT-002 (release-candidate) — augmentation superseded by BAT-012. BAT-002 본래 trace인 REQ-012/REQ-015는 영향 없음.
  • Releases:
    • REL-001 — CHG-20260521-02 augmentation scope는 BAT-012로 이관됨. REL-001 release 본문은 BAT-012 inclusion 정리 시 별도 갱신 필요.

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: implemented
  • Follow-up Action: REL-001 release 문서에 BAT-012 trace 반영 (또는 신규 REL에 BAT-012 포함)

Validation Plan

  • tests/test_asset_catalog.py AC-9~12 신규 8건: test_get_v1_assets_admin_returns_catalog_response, test_get_v1_assets_status_filter_*, test_get_v1_assets_market_filter, test_get_v1_assets_user_token_returns_403, test_get_v1_assets_service_token_returns_403
  • 기존 AC-1~8 회귀 9건: test_asset_cli_create_success / list / update / deactivate / test_asset_http_create_returns_201 / update_returns_200 / deactivate_returns_200 / non_admin_403
  • 전체 회귀: 362 passed in 13.06s

CHG-20260521-02

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

Change Summary

  • Status: Implemented → Approved (REL-001 smoke 검증에서 GET /v1/assets가 dcy-001 시점 portfolio 조회 코드를 유지 중인 불일치 확인. 본 REQ Notes의 1순위 경로(REQ-010 augmentation)에 따라 GET 명세를 추가하고 기존 구현을 재정의.)

Intent

  • dcy-003 옵션 B로 REQ-013 Deprecated 후 본 REQ Notes에 명시된 "GET /v1/assets는 admin catalog 조회 전용" 재정의가 미구현 상태로 잔존. REL-001 배포 직전 로컬 smoke(rel-001.md:50)에서 admin session token → HTTP 403 "Unsupported token type" 발생으로 release 체크리스트와 코드 상태 불일치 확인. 본 augmentation으로 GET /v1/assets를 admin catalog 조회로 재정의해 path 분리 정책(dcy-003 옵션 B)을 코드에 반영.

Acceptance Criteria Delta

  • Added:
    • AC-9 (admin token GET 200 + asset catalog 응답)
    • AC-10 (status=all/status=inactive 필터)
    • AC-11 (market=<code> 필터)
    • AC-12 (non-admin token → HTTP 403)
  • 기존 AC-1~8: 변경 없음.

Impacted Area Delta

  • Modules added:
    • app/api/v1_assets.py — GET /v1/assets 라우트를 portfolio 조회에서 asset catalog 조회로 재정의 (token_type 분기 제거, admin permission 검증 + list_assets 호출)
    • app/schemas/v1.py — AssetCatalogItem, AssetCatalogListResponse 신규 schema
  • Tests added/updated:
    • tests/test_asset_catalog.py — GET /v1/assets AC-9~12 검증 신규 케이스 (기존 portfolio 조회 의미의 GET /v1/assets 테스트가 있다면 제거/대체)
    • tests/test_v1.py — 기존 GET /v1/assets 호출 패턴이 portfolio 응답을 기대한다면 GET /v1/portfolios로 이전 또는 응답 schema 변경 반영
  • Repository:
    • app/repositories/postgres.py — list_assets 메서드가 이미 존재하면 재사용; 없으면 보강

Delivery Trace Delta

  • Batches:
    • BAT-002 (asset-catalog-api-completion) — augmentation 누적, verification 재실행 필요
  • Releases:
    • REL-001 — scope 잔존, 본 augmentation 포함 (docs/releases/rel-001_20260521_v1-api-full-surface.md)

Revalidation Impact

  • Reverification Needed: yes
  • Existing Implementation Invalidated: yes (기존 GET /v1/assets portfolio 조회 동작이 제거됨)
  • Status Recommendation: revert-to-approved
  • Follow-up Action: 구현 → pytest 회귀 → 로컬 smoke 재실행 → confirm-req-implemented 재진입

Validation Plan

  • pytest로 AC-9~12 검증; 로컬 smoke로 admin session token + GET /v1/assets → HTTP 200 + asset catalog 응답 확인

CHG-20260521-01

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

Change Summary

  • Status: Approved → Implemented

Intent

  • BAT-002 confirm-batch-verification 완료. admin asset catalog CRUD CLI/HTTP API(POST/PATCH/deactivate), non-admin 403 모든 AC 검증 통과.

Acceptance Criteria Delta

  • No change

Impacted Area Delta

  • No change

Delivery Trace Delta

  • Batches:
    • BAT-002
  • Releases:
    • REL-001

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: implemented
  • Follow-up Action: none

CHG-20260519-02

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

Change Summary

  • Status: Proposed → Approved

Intent

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

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-20260519-01

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

Change Summary

  • 최초 생성 — dcy-001 confirm-discovery 후 draft-req 실행

Intent

  • dcy-001 FR-2, NFR-3을 Interface REQ로 분리; admin catalog CRUD CLI + HTTP API 정의

Acceptance Criteria Delta

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

Impacted Area Delta

  • Docs:
    • docs/project-structure.md, docs/interface-contract.md — 최초 지정
  • Modules:
    • app/cli/commands/asset.py, app/cli/main.py, app/services/, app/api/v1_assets.py, app/schemas/ — 최초 지정
  • Tests:
    • tests/test_asset_catalog.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 계획 편입; REQ-009 선행 의존성 확인

Validation Plan

  • pytest로 AC-1~8 검증; CLI는 CliRunner, HTTP는 TestClient 사용