# REQ-031: portfolio comment CRUD v1 라우트 - Status: Implemented - Type: Interface - Priority: Medium - Owner: Justin Ko ## Intent - v1 API에 portfolio comment CRUD 경로(`/v1/portfolios/{portfolio_id}/comments`)를 신설해 user/admin이 HTTP로 portfolio에 코멘트를 작성·관리할 수 있도록 한다. - ownership 규칙과 disabled 차단을 REQ-026/027과 동일 패턴으로 적용해 v1 portfolio surface 인가 일관성을 확보한다. ## Requirement - 신규 라우트 (Bearer 인증 필수): - `POST /v1/portfolios/{portfolio_id}/comments` — body: `{"content": ""}`. 응답 HTTP 201 + 생성된 comment. - `GET /v1/portfolios/{portfolio_id}/comments` — 응답 HTTP 200 + comment 목록(created_at desc 순). - `PATCH /v1/portfolios/{portfolio_id}/comments/{comment_id}` — body: `{"content": ""}`. 응답 HTTP 200 + 갱신된 comment. - `DELETE /v1/portfolios/{portfolio_id}/comments/{comment_id}` — 응답 HTTP 200 또는 204. - **ownership 규칙** (REQ-026/030과 동일): - `token_type ∈ {service, client}` → HTTP 403 fail-closed. - `permission='admin'` → 모든 portfolio. - `permission ∈ {read, trade-write}` → 자기 소유 portfolio만, 불일치 시 HTTP 403. - **disabled portfolio 차단** (REQ-027 정합): `status='disabled'`인 portfolio에 대해 POST/PATCH/DELETE는 HTTP 409 또는 422로 거부. GET은 허용. - 비존재 `portfolio_id` 또는 `comment_id`는 HTTP 404. - 기존 `CommentCreateRequest`/`CommentUpdateRequest`/`CommentResponse` schema 재사용 또는 v1 전용 래핑(`PortfolioCommentRequest`, `PortfolioCommentResponse`). - 기존 service 메서드(`create_comment`, `list_comments`, `update_comment`, `delete_comment`) 재사용. ## Acceptance Criteria 1. Given session token + 본인 active portfolio, When `POST /v1/portfolios/{id}/comments` body `{"content": "..."}`, Then HTTP 201 + 생성된 comment. 2. Given session token + 본인 portfolio + 적용된 comments, When `GET /v1/portfolios/{id}/comments`, Then HTTP 200 + 코멘트 목록(created_at desc). 3. Given session token + 본인 portfolio + 본인 comment, When `PATCH /v1/portfolios/{id}/comments/{comment_id}` body `{"content": "..."}`, Then HTTP 200 + 갱신된 comment. 4. Given session token + 본인 portfolio + 본인 comment, When `DELETE /v1/portfolios/{id}/comments/{comment_id}`, Then HTTP 200/204. 5. Given session token + 타 user portfolio, When 임의 동사 호출, Then HTTP 403 (ownership 불일치). 6. Given service/client token, When 임의 동사 호출, Then HTTP 403 fail-closed. 7. Given session token + disabled portfolio, When `POST`/`PATCH`/`DELETE`, Then HTTP 409 또는 422. 8. Given session token + disabled portfolio, When `GET /v1/portfolios/{id}/comments`, Then HTTP 200 + 기존 comments 목록 반환(조회는 허용). 9. Given admin token + 타 user portfolio, When 임의 동사 호출, Then HTTP 200/201/204 (admin은 모든 portfolio 허용). 10. Given Bearer token 없음, When 호출, Then HTTP 401. 11. Given 비존재 `portfolio_id` 또는 `comment_id`, When 호출, Then HTTP 404. ## Impacted Area - Docs: - `docs/interface-contract.md` — `/v1/portfolios/{id}/comments` 명세 (REQ-032) - Modules: - `app/api/v1_portfolios.py` — comment 서브 라우트 추가 - `app/schemas/v1.py` — comment 관련 스키마 (기존 `app/schemas/portfolio.py:CommentCreateRequest`/`CommentUpdateRequest`/`CommentResponse` 재사용 또는 v1 래핑) - `app/services/portfolio_service.py:create_comment`/`list_comments`/`update_comment`/`delete_comment` — 기존 메서드 재사용 + disabled 차단 검증 추가 - Tests: - 신규/갱신 `tests/test_v1_portfolios_tags_comments.py` — AC-1~11 (comment 부분) ## Notes - comment 작성자 ownership(본인 comment만 수정/삭제 가능)은 기존 portfolio_comments 테이블 구조에 따라 처리. dcy-003 본문은 portfolio ownership만 명시하고 comment 작성자 ownership은 별도 결정 사항 — 본 REQ에서는 portfolio ownership을 일관 적용하고 comment 작성자 ownership은 batch design 단계에서 확정. - 본 REQ는 dcy-003 FR-8 산출물. tag(REQ-030)와 별도 REQ로 분리해 동사 surface를 명확히 함. - Source: dcy-003 FR-8, NFR-1, NFR-3, NFR-4. ## Change Log ### CHG-20260521-01 - Date: 2026-05-21 - Author: Justin Ko #### Change Summary - Status: Approved → Implemented #### Intent - BAT-010 구현 완료: `app/api/v1_portfolios.py`에 POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments 4종 신설; `app/schemas/v1.py`에 comment schema 4종 추가; `tests/test_v1_portfolios_tags_comments.py` AC-1~11 커버; comment 작성자 ownership은 portfolio ownership만 적용(design decision #3) #### Acceptance Criteria Delta - No change #### Impacted Area Delta - No change #### Delivery Trace Delta - Batches: BAT-010 (release-candidate) - Releases: pending #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: implemented - Follow-up Action: BAT-010 release candidate → draft-release ### 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-8 산출물. #### Intent - v1 portfolio comment CRUD 라우트 신설 + ownership/disabled 검증 일관 적용. #### Acceptance Criteria Delta - Added: - AC-1~11 최초 작성 #### Impacted Area Delta - Docs: docs/interface-contract.md (REQ-032) - Modules: app/api/v1_portfolios.py, app/schemas/v1.py, app/services/portfolio_service.py — 최초 지정 - Tests: tests/test_v1_portfolios_tags_comments.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 계획 편입; comment 작성자 ownership 정책은 batch design 단계에서 확정 #### Validation Plan - pytest TestClient로 AC-1~11 검증; ownership + disabled 차단 + admin 분기 시나리오