docs(dcy-009): 사용자용 read 엔드포인트 discovery confirmed + REQ-044/045 approved
- dcy-009: GET /v1/assets require_read 완화 + 사용자 가용 데몬 목록 신규 (trex-web 포트폴리오 관리 발원) - REQ-044(assets 권한 완화) / REQ-045(사용자 데몬 목록) — Approved - 전부 read 추가(쓰기·데몬관리 제외) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# REQ-044: GET /v1/assets 권한 완화 — require_admin → require_read
|
||||
|
||||
- Status: Approved
|
||||
- Approved By: 팀장 (Claude), 2026-06-27 (confirm-req)
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- `GET /v1/assets`(asset 카탈로그 목록 조회) 엔드포인트의 의존성을 `require_admin`에서 `require_read`로 완화해, 일반 인증 사용자가 active asset 카탈로그를 조회할 수 있도록 한다.
|
||||
- trex-web 포트폴리오 관리 UI(dcy-007)에서 포트폴리오 생성·변경 시 종목 선택 드롭다운/검색이 이 엔드포인트를 소비한다. 현재 `require_admin`으로 막혀 있어 일반 사용자가 접근 불가한 상태다.
|
||||
- asset 쓰기(POST 생성, PATCH 수정/비활성, POST/DELETE 태그 적용/해제)는 `require_admin`을 유지한다 — 이번 완화는 읽기 하나에 한정한다.
|
||||
|
||||
## Requirement
|
||||
- 시스템은 `GET /v1/assets` 엔드포인트의 인증 의존성을 `require_admin`에서 `require_read`로 변경해야 한다.
|
||||
- 기존 동작(status 필터·market 필터·`AssetCatalogListResponse` 응답 스키마)은 변경 없이 유지해야 한다.
|
||||
- asset 쓰기 엔드포인트(`POST /v1/assets`, `PATCH /v1/assets/{asset_id}`, `PATCH /v1/assets/{asset_id}/deactivate`, `POST /v1/assets/{asset_id}/tags`, `DELETE /v1/assets/{asset_id}/tags/{tag_id}`)는 `require_admin`을 유지해야 한다.
|
||||
- 유효한 read 권한 이상 토큰(session, read service token, admin)으로 `GET /v1/assets`를 호출하면 HTTP 200과 카탈로그 목록을 반환해야 한다.
|
||||
- 인증 토큰 없는 요청은 HTTP 401, 권한이 없는 토큰은 기존 동작대로 HTTP 403을 반환해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given read 권한 세션 토큰(`permission="read"`)을 소지한 사용자, When `GET /v1/assets`를 호출, Then HTTP 200 + `AssetCatalogListResponse` 반환.
|
||||
2. Given admin 토큰, When `GET /v1/assets`를 호출, Then 기존과 동일하게 HTTP 200 반환(회귀 없음).
|
||||
3. Given read 권한 사용자, When `GET /v1/assets?status=active`·`?status=inactive`·`?status=all`로 호출, Then status 필터가 적용된 응답 반환(필터 동작 유지).
|
||||
4. Given read 권한 사용자, When `GET /v1/assets?market=KRX`로 호출, Then market 필터가 적용된 응답 반환(필터 동작 유지).
|
||||
5. Given read 권한 사용자, When `POST /v1/assets`(asset 생성)를 호출, Then HTTP 403(쓰기는 admin 유지).
|
||||
6. Given read 권한 사용자, When `PATCH /v1/assets/{asset_id}/deactivate`를 호출, Then HTTP 403.
|
||||
7. Given read 권한 사용자, When `POST /v1/assets/{asset_id}/tags`를 호출, Then HTTP 403.
|
||||
8. Given read 권한 사용자, When `DELETE /v1/assets/{asset_id}/tags/{tag_id}`를 호출, Then HTTP 403.
|
||||
9. Given 인증 토큰 없는 요청, When `GET /v1/assets`를 호출, Then HTTP 401.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: `docs/srs/Interface/req-044_get-v1-assets-require-read-relaxation.md` (신규)
|
||||
- Modules:
|
||||
- `app/api/v1_assets.py`: `list_catalog_assets` 함수의 `auth: AuthContext = Depends(require_admin)` → `Depends(require_read)` 변경. import에 `require_read` 추가.
|
||||
- Tests: 기존 `tests/`의 `GET /v1/assets` 관련 테스트에서 read 토큰으로 200 반환하는 케이스 추가. 쓰기 엔드포인트 403 회귀 확인.
|
||||
|
||||
## Notes
|
||||
- asset 카탈로그(거래 가능 종목 메타 — symbol·display_name·markets·status·태그)는 전 인증 사용자에게 read 공개해도 무방하다고 사장님이 확인(dcy-009 OQ-1 Answered 2026-06-27).
|
||||
- 코드 변경 범위는 `app/api/v1_assets.py` 단일 파일의 `Depends(require_admin)` → `Depends(require_read)` 한 줄이다. 응답 로직·필터·스키마는 변경 없다.
|
||||
- `require_read`는 `app/core/auth.py`에 이미 정의(`require_read = permission_dependency(Permission.READ)`)되어 있다.
|
||||
- NFR-1(기존 admin asset 관리·토큰 daemon 바인딩 회귀 0)은 AC-2, AC-5~8의 쓰기 403 확인으로 흡수한다. 별도 NFR REQ 미생성.
|
||||
- NFR-2(데몬 목록 caller 스코프)는 REQ-045에서 다룬다.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260627-01
|
||||
- Date: 2026-06-27
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 신규 생성 (Proposed) — dcy-009 FR-1 구현을 위한 첫 번째 REQ 초안.
|
||||
|
||||
#### Intent
|
||||
- dcy-009 confirmed Discovery의 FR-1을 REQ로 정규화한다. 코드·테스트 구현의 기준 문서로 사용.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1 ~ AC-9 전체 (신규 초안)
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/srs/Interface/req-044_get-v1-assets-require-read-relaxation.md` 신규 생성
|
||||
- Modules:
|
||||
- `app/api/v1_assets.py` (require_admin → require_read 1행)
|
||||
- Tests:
|
||||
- read 권한 GET 200 케이스 추가, 쓰기 403 회귀 케이스 확인
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 미정 (suggest-batch-reqs 대기)
|
||||
- Releases: 미정
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 배정
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 read 토큰 GET /v1/assets 200 확인, admin 쓰기 403 회귀 확인.
|
||||
@@ -0,0 +1,85 @@
|
||||
# REQ-045: 사용자 가용 데몬 목록 read 엔드포인트 신설
|
||||
|
||||
- Status: Approved
|
||||
- Approved By: 팀장 (Claude), 2026-06-27 (confirm-req)
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- caller(인증 사용자)가 자신이 소유한 데몬 목록을 조회할 수 있는 HTTP 엔드포인트를 신설한다.
|
||||
- trex-web 포트폴리오 관리 UI(dcy-007)에서 포트폴리오를 생성하거나 관리 데몬(`managed_by_daemon_id`)을 지정할 때, 사용자가 선택 가능한 데몬 후보를 이 엔드포인트로 불러온다.
|
||||
- 현재 도메인 `Daemon` 모델과 `list_daemons_for_user(user_id)` 서비스/리포지토리가 존재하나 HTTP 라우트로 노출되지 않는 상태다. 이 REQ는 그 노출 경로를 추가한다.
|
||||
|
||||
## Requirement
|
||||
- 시스템은 caller(인증 사용자) 소유 데몬 목록을 반환하는 GET 엔드포인트를 `require_read` 권한으로 제공해야 한다.
|
||||
- 응답은 caller(`auth.user_id`) 소유 데몬만 포함해야 한다 — 타 사용자 데몬은 절대 반환하지 않는다.
|
||||
- 각 응답 항목은 다음 필드를 포함해야 한다: `id`(str), `daemon_slug`(str), `display_name`(str | None), `is_active`(bool).
|
||||
- 엔드포인트 경로와 응답 스키마(`DaemonResponse`) 구체 형태는 draft-batch-design에서 기존 라우팅 컨벤션에 맞춰 확정한다(OQ-2 Deferred).
|
||||
- 인증 토큰 없는 요청은 HTTP 401을 반환해야 한다.
|
||||
- 해당 사용자에게 데몬이 없는 경우 빈 목록(`[]`)으로 HTTP 200을 반환해야 한다(404 아님).
|
||||
- 기존 서비스 메서드 `find_candidate_daemons_for_user(user_id)`(`AuthService`)를 활용한다. 이 메서드는 내부적으로 `repository.list_daemons_for_user(user_id)`를 호출한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given read 권한 토큰을 소지한 사용자 A(데몬 2개 소유), When 데몬 목록 엔드포인트를 호출, Then HTTP 200 + 사용자 A의 데몬 2개만 포함된 목록 반환.
|
||||
2. Given read 권한 토큰을 소지한 사용자 A, When 데몬 목록 엔드포인트를 호출, Then 응답 항목이 `id`, `daemon_slug`, `display_name`, `is_active` 필드를 모두 포함.
|
||||
3. Given read 권한 토큰을 소지한 사용자 A, When 데몬 목록 엔드포인트를 호출, Then 사용자 B의 데몬이 응답에 포함되지 않음(타 사용자 비노출).
|
||||
4. Given read 권한 사용자이고 자신에게 데몬이 없을 때, When 데몬 목록 엔드포인트를 호출, Then HTTP 200 + 빈 목록(`items: []`).
|
||||
5. Given 인증 토큰 없는 요청, When 데몬 목록 엔드포인트를 호출, Then HTTP 401.
|
||||
6. Given admin 토큰을 소지한 사용자, When 데몬 목록 엔드포인트를 호출, Then HTTP 200 + 해당 admin 사용자 소유 데몬만 반환(admin 권한이어도 caller 스코프 적용).
|
||||
7. Given read 권한 토큰으로 기존 `GET /v1/assets`(REQ-044)를 호출, Then 회귀 없음(데몬 라우터 추가가 기존 assets 라우터에 영향 없음).
|
||||
|
||||
## Impacted Area
|
||||
- Docs: `docs/srs/Interface/req-045_user-daemon-list-endpoint.md` (신규)
|
||||
- Modules:
|
||||
- 신규 라우터 파일 또는 기존 라우터에 엔드포인트 추가(경로·파일 위치는 design 확정 — OQ-2).
|
||||
- `app/schemas/v1.py` 또는 별도 스키마 파일: `DaemonResponse` Pydantic 모델 신규 정의.
|
||||
- `app/services/auth_service.py`: `find_candidate_daemons_for_user` 기존 활용(변경 없거나 최소).
|
||||
- `app/main.py` 또는 라우터 등록: 신규 라우터 include.
|
||||
- Tests: 2사용자 fixture로 caller 스코프 격리 테스트(AC-3). 빈 목록·401 케이스. 기존 assets/auth 회귀.
|
||||
|
||||
## Notes
|
||||
- OQ-2(엔드포인트 경로·응답 스키마 형태): design 단계에서 기존 라우팅 컨벤션(`/v1/daemons` vs `/v1/users/me/daemons` 등)을 분석해 확정. 이 REQ에서는 경로를 확정하지 않으며 design Deferred. confirm-req 시 blocker 아님 — design 단계 위임 가능.
|
||||
- OQ-3("가용 데몬" 의미): 현재 기본안은 사용자 소유 데몬 전체(`find_candidate_daemons_for_user`). active 여부 필터링이 필요한지(소유 active 전체 vs 미바인딩 후보) design에서 확정. 이 REQ에서는 "caller 소유 데몬"으로만 명시하며 active 필터는 design 위임.
|
||||
- env 기반 token(`token_source="env"`)은 `user_id`가 None이다. 이 경우 `auth.user_id`가 없어 caller 스코프 적용이 불가 — design에서 env 토큰의 처리 방침(거부 vs 전체 반환)을 확정할 것. 기본안: DB 토큰만 허용(user_id 없으면 HTTP 403).
|
||||
- NFR-1(기존 admin asset 관리·토큰 daemon 바인딩 회귀 0)은 AC-7 회귀 확인으로 흡수한다.
|
||||
- NFR-2(caller 스코프 정확)는 AC-1/3/6으로 흡수한다.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260627-01
|
||||
- Date: 2026-06-27
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 신규 생성 (Proposed) — dcy-009 FR-2 구현을 위한 REQ 초안.
|
||||
|
||||
#### Intent
|
||||
- dcy-009 confirmed Discovery의 FR-2를 REQ로 정규화한다. 코드·테스트 구현의 기준 문서로 사용.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1 ~ AC-7 전체 (신규 초안)
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/srs/Interface/req-045_user-daemon-list-endpoint.md` 신규 생성
|
||||
- Modules:
|
||||
- 신규 라우터 파일(경로 design 확정 후), `DaemonResponse` 스키마 신규, `app/main.py` 라우터 등록
|
||||
- Tests:
|
||||
- 2사용자 스코프 격리 테스트, 빈 목록·401 케이스 신규
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 미정 (suggest-batch-reqs 대기)
|
||||
- Releases: 미정
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 배정. OQ-2/OQ-3는 design 단계에서 해소.
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 AC-1~7 각 케이스 확인. 특히 AC-3(2사용자 fixture 사용해 타 사용자 데몬 비노출) 강제 검증.
|
||||
+4
-1
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-044`
|
||||
- Current: `REQ-046`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -124,9 +124,12 @@
|
||||
| REQ-041 | WAC 기반 cost_basis 산출 및 실현 PnL 계산 로직 (service/domain 계층 격리) | Integration | Implemented | High | Justin Ko | [req-041](./Integration/req-041_wac-cost-basis-pnl-calculation.md) |
|
||||
| REQ-042 | 체결 내역/실현 PnL API 테스트 커버리지 (WAC 시나리오 기댓값 대조, 날짜 필터, 422) | Testing | Implemented | High | Justin Ko | [req-042](./Testing/req-042_trade-pnl-api-test-coverage.md) |
|
||||
| REQ-043 | GET /v1/portfolios/{id}/holdings/cost-basis 현재 보유 평단(WAC) read 엔드포인트 신설 | Interface | Implemented | High | Justin Ko | [req-043](./Interface/req-043_holdings-cost-basis-read-endpoint.md) |
|
||||
| REQ-044 | GET /v1/assets 권한 완화 — require_admin → require_read | Interface | Approved | High | Justin Ko | [req-044](./Interface/req-044_get-v1-assets-require-read-relaxation.md) |
|
||||
| REQ-045 | 사용자 가용 데몬 목록 read 엔드포인트 신설 | Interface | Approved | High | Justin Ko | [req-045](./Interface/req-045_user-daemon-list-endpoint.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-06-27: REQ-044/045 신규 (Proposed) — dcy-009 FR-1(GET /v1/assets require_read 완화)·FR-2(사용자 가용 데몬 목록 엔드포인트 신설) REQ 초안 작성. 엔드포인트 경로·스키마(OQ-2) 및 가용 데몬 의미(OQ-3)는 design Deferred.
|
||||
- 2026-06-26: REQ-041 WAC 공식 change-req(CHG-20260626-01) — 매수 수수료 cost basis 편입(사장님 결정). 신 공식: `running_wac = (running_wac*qty + price*qty + buy_fee) / new_total_qty`. `calculate_realized_pnl`과 `calculate_current_wac` 양 함수 적용. AC-9/AC-10 추가. 신규 테스트 9개 포함 456 passed / 0 failed. REQ-040/REQ-043 Notes 참조 갱신.
|
||||
- 2026-06-26: REQ-043 `Approved` → `Implemented` — BAT-018 PR #14 squash-merge(d93c0bc) 팀장 QC PASS 후 origin/main 머지 완료. AC-1~11 전부 PASS(447 passed / 0 failed). 배포 별도(release-candidate).
|
||||
- 2026-06-25: REQ-043 `Proposed` → `Approved` (confirm-req 팀장 승인) — dcy-008 근거 Discovery 양방향 링크 확정. OQ-1(service token 소유권 blocker) 해소: cold-start는 `managed_by_daemon_id=daemon` 포트폴리오만 조회하므로 null 케이스 blocker 아님, daemon scope 소유권 정책(REQ-029 선례) 유지 확정. OQ-2(domain 함수) 구현 단계 재량 위임. AC-1~11 유지.
|
||||
|
||||
Reference in New Issue
Block a user