feat: add tenant-safe planning domain slice - #2
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughPlanning 서비스에 Goal, Project, Task 도메인과 workspace 범위 검증을 추가했습니다. 인메모리 API와 PostgreSQL 초기 스키마를 구현했습니다. 생성, 조회, 계층 참조, workspace 격리 검증을 테스트했습니다. ChangesPlanning 도메인
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant PlanningController
participant PlanningService
participant InMemoryPlanningRepository
Client->>PlanningController: workspace 헤더와 Goal, Project 또는 Task 요청
PlanningController->>PlanningController: workspace ID와 제목 검증
PlanningController->>PlanningService: 검증된 입력 전달
PlanningService->>InMemoryPlanningRepository: workspace 범위 저장 또는 조회
InMemoryPlanningRepository-->>PlanningService: 엔터티 또는 목록 반환
PlanningService-->>PlanningController: 결과 반환
PlanningController-->>Client: HTTP 응답 반환
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/planning-service/src/main.ts (1)
21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
PlanningService를 Nest DI provider로 등록하는 방식을 고려하십시오.
planningService는 모듈 스코프에서new로 생성된 싱글턴입니다.AppModule은 이를 provider로 등록하지 않습니다. 이 방식은 동작하지만, Nest의 관례를 벗어나며 테스트 격리와 향후 저장소 구현 교체(예: PostgreSQL)를 어렵게 만듭니다.PlanningService를@Injectable()로 선언하고providers배열에 등록한 뒤, 컨트롤러 생성자에서 주입하십시오.Also applies to: 92-93
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/planning-service/src/main.ts` at line 21, Replace the module-scoped PlanningService construction with Nest dependency injection: mark PlanningService as `@Injectable`(), register it in AppModule.providers, and inject it through the controller constructor. Preserve the InMemoryPlanningRepository dependency through the provider configuration so future repository substitutions remain possible.apps/planning-service/src/planning-domain.test.ts (1)
23-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win테넌트 격리 테스트 범위를 Project·Task 계층까지 확장하십시오.
현재 테스트는
listGoals의 workspace 격리와 project 생성 시 cross-tenant 거부만 검증합니다.listProjects,listTasks의 workspace 격리와 task 생성 시 cross-tenant 거부는 검증하지 않습니다. PR 목표는 계층 전체의 테넌트 격리를 증명하는 것입니다. 누락된 경로에도 동일한 패턴의 테스트를 추가하십시오.♻️ 제안: 추가 테스트 예시
it('rejects a project whose goal belongs to another workspace', () => { ... }); + + it('does not expose projects or tasks from another workspace', () => { + const service = new PlanningService(new InMemoryPlanningRepository()); + const goal = service.createGoal('workspace-a', { title: 'Goal A' }); + const project = service.createProject('workspace-a', { goalId: goal.id, title: 'Project A' }); + service.createTask('workspace-a', { projectId: project.id, title: 'Task A' }); + + expect(service.listProjects('workspace-b', goal.id)).toEqual([]); + expect(service.listTasks('workspace-b', project.id)).toEqual([]); + }); + + it('rejects a task whose project belongs to another workspace', () => { + const service = new PlanningService(new InMemoryPlanningRepository()); + const goal = service.createGoal('workspace-a', { title: 'Goal A' }); + const project = service.createProject('workspace-a', { goalId: goal.id, title: 'Project A' }); + + expect(() => + service.createTask('workspace-b', { projectId: project.id, title: 'Cross-tenant task' }), + ).toThrowError('Project not found'); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/planning-service/src/planning-domain.test.ts` around lines 23 - 41, planning-domain.test.ts의 테넌트 격리 테스트를 확장하여 다른 workspace의 프로젝트가 listProjects에서 노출되지 않는 경우와 다른 workspace의 태스크가 listTasks에서 노출되지 않는 경우를 검증하십시오. 또한 다른 workspace의 goal 또는 project를 참조하는 createTask 호출이 기존 cross-tenant 프로젝트 생성 테스트와 동일하게 “Task not found” 또는 실제 도메인 계약의 오류를 발생시키는지 테스트를 추가하십시오.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/planning-service/src/main.ts`:
- Around line 38-44: Update PlanningController methods createGoal,
createProject, and createTask to validate request bodies like
requireWorkspaceId, rejecting missing titles with BadRequestException before
calling the service. Catch the PlanningService domain errors and translate
required-title failures to BadRequestException and missing goal/project failures
to NotFoundException, importing both exceptions from `@nestjs/common` while
preserving successful service calls.
In `@docs/superpowers/plans/2026-08-02-planning-domain-slice.md`:
- Line 12: Update the checklist item “Run CI, SAST, and security scan; fix all
actionable findings” to reflect the current PR status, leaving it unchecked
until CI, SAST, security scans, and actionable review findings are actually
completed.
---
Nitpick comments:
In `@apps/planning-service/src/main.ts`:
- Line 21: Replace the module-scoped PlanningService construction with Nest
dependency injection: mark PlanningService as `@Injectable`(), register it in
AppModule.providers, and inject it through the controller constructor. Preserve
the InMemoryPlanningRepository dependency through the provider configuration so
future repository substitutions remain possible.
In `@apps/planning-service/src/planning-domain.test.ts`:
- Around line 23-41: planning-domain.test.ts의 테넌트 격리 테스트를 확장하여 다른 workspace의
프로젝트가 listProjects에서 노출되지 않는 경우와 다른 workspace의 태스크가 listTasks에서 노출되지 않는 경우를
검증하십시오. 또한 다른 workspace의 goal 또는 project를 참조하는 createTask 호출이 기존 cross-tenant
프로젝트 생성 테스트와 동일하게 “Task not found” 또는 실제 도메인 계약의 오류를 발생시키는지 테스트를 추가하십시오.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d5bc9e13-0b36-4378-ab4a-b81cc7d6905c
📒 Files selected for processing (6)
apps/planning-service/migrations/0001_initial_planning.sqlapps/planning-service/migrations/README.mdapps/planning-service/src/main.tsapps/planning-service/src/planning-domain.test.tsapps/planning-service/src/planning-domain.tsdocs/superpowers/plans/2026-08-02-planning-domain-slice.md
Summary
Implements the first usable Planning bounded-context slice.
Included
/v1Security model
Every read and parent lookup is scoped by
workspaceId. The SQL schema includesworkspace_idin parent-child foreign keys, preventing cross-workspace Goal/Project/Task references at the database layer. Numeric-only internal identifiers are rejected.Validation
Merged after required checks and review verification.