feat: api v2 team invite link endpoint#117
Conversation
Review Summary by QodoAdd API v2 team invite link endpoint with role-based access
WalkthroughsDescription• Add team invite link endpoint to API v2 with role-based access control • Implement TeamsInviteModule with controller and output DTOs • Add comprehensive e2e tests for admin, member, and non-member scenarios • Fix token generation logic to return identifier instead of token value • Correct invite link building logic for organization context Diagramflowchart LR
A["Client Request"] -->|POST /v2/teams/:teamId/invite| B["TeamsInviteController"]
B -->|Validate Auth & Role| C["ApiAuthGuard + RolesGuard"]
C -->|Create Invite| D["TeamService.createInvite"]
D -->|Generate Token| E["Prisma VerificationToken"]
D -->|Build Link| F["buildInviteLink"]
F -->|Return| G["CreateInviteOutputDto"]
G -->|Response| A
File Changes1. apps/api/v2/src/ee/platform-endpoints-module.ts
|
Code Review by Qodo
1. InviteDataDto example lines too long
|
| example: "f6a5c8b1d2e34c7f90a1b2c3d4e5f6a5b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2", | ||
| }) | ||
| token!: string; | ||
|
|
||
| @IsString() | ||
| @Expose() | ||
| @ApiProperty({ | ||
| description: | ||
| "Complete invitation URL that can be shared with prospective members. Opens the signup page with the token and redirects to getting started after signup.", | ||
| example: | ||
| "http://app.cal.com/signup?token=f6a5c8b1d2e34c7f90a1b2c3d4e5f6a5b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2&callbackUrl=/getting-started", |
There was a problem hiding this comment.
1. invitedatadto example lines too long 📘 Rule violation ✓ Correctness
The added ApiProperty example strings exceed the 110 character line-width requirement, deviating from the standardized Biome formatting configuration. This reduces formatting consistency and can cause formatter/linter CI failures.
Agent Prompt
## Issue description
The new Swagger `ApiProperty` `example` strings in `invite.output.ts` exceed the required 110 character line width, violating the Biome formatting standard.
## Issue Context
Formatting compliance requires 110 character max line width in TS/JS files.
## Fix Focus Areas
- apps/api/v2/src/modules/teams/invite/outputs/invite.output.ts[9-23]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| }); | ||
|
|
||
| it("should create a new invite on each request", async () => { | ||
| const first = await request(app.getHttpServer()).post(`/v2/teams/${team.id}/invite`).expect(200); |
There was a problem hiding this comment.
2. E2e test line too long 📘 Rule violation ✓ Correctness
The added chained request(...).post(...).expect(...) statement is on a single line and exceeds the 110 character line-width formatting requirement. This breaks the standardized Biome formatting rules and can fail format checks.
Agent Prompt
## Issue description
A newly added test statement exceeds the 110 character line-width requirement.
## Issue Context
Formatting compliance requires Biome-standard formatting, including 110 character max line width.
## Fix Focus Areas
- apps/api/v2/src/modules/teams/invite/controllers/teams-invite.controller.e2e-spec.ts[79-81]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| const token = randomBytes(32).toString("hex"); | ||
| await prisma.verificationToken.create({ | ||
| const newToken = await prisma.verificationToken.create({ | ||
| data: { | ||
| identifier: `invite-link-for-teamId-${teamId}`, | ||
| token, |
There was a problem hiding this comment.
3. Wrong invite token returned 🐞 Bug ✓ Correctness
TeamService.createInvite returns the verificationToken.identifier string instead of the generated verificationToken.token, so the API exposes a non-usable token that does not match the token embedded in inviteLink. This breaks clients/tests expecting inviteLink to contain the returned token and makes subsequent token-based operations fail.
Agent Prompt
### Issue description
`TeamService.createInvite()` returns `newToken.identifier` as the response `token`, but `inviteLink` is built using the generated `token`. This makes the returned token unusable and inconsistent.
### Issue Context
`identifier` is a deterministic string (e.g., `invite-link-for-teamId-<id>`), while the actual invite token is the random hex string stored in `verificationToken.token`.
### Fix Focus Areas
- packages/features/ee/teams/services/teamService.ts[87-101]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| private static async buildInviteLink(token: string, isOrgContext: boolean): Promise<string> { | ||
| const teamInviteLink = `${WEBAPP_URL}/teams?token=${token}`; | ||
| if (!isOrgContext) { | ||
| if (isOrgContext) { | ||
| return teamInviteLink; | ||
| } | ||
| const gettingStartedPath = await OnboardingPathService.getGettingStartedPathWhenInvited(prisma); |
There was a problem hiding this comment.
4. Invite link logic reversed 🐞 Bug ✓ Correctness
TeamService.buildInviteLink now returns a /teams?token=... link when isOrgContext is true and a /signup?token=... link when false, inverting the intended behavior. This produces incorrect invite URLs for org-context vs team-context invites and breaks onboarding/join flows.
Agent Prompt
### Issue description
`buildInviteLink()` uses an inverted `isOrgContext` check and returns the wrong URL shape for org-context invites vs normal team invites.
### Issue Context
`isOrgContext` is derived from `team.parentId || team.isOrganization` and is intended to signal organization-related invite flows.
### Fix Focus Areas
- packages/features/ee/teams/services/teamService.ts[104-111]
- packages/features/ee/teams/services/teamService.ts[67-68]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| @Post("/invite") | ||
| @Roles("TEAM_MEMBER") | ||
| @ApiOperation({ summary: "Create team invite link" }) |
There was a problem hiding this comment.
5. Invite creation privilege escalation 🐞 Bug ⛨ Security
TeamsInviteController is annotated with @Roles("TEAM_MEMBER"), which permits regular members to
create invite links. This contradicts the intended admin-only behavior (tests expect 403 for
members) and diverges from the existing org-team invite endpoint which requires TEAM_ADMIN.
Agent Prompt
### Issue description
`TeamsInviteController` currently allows any `TEAM_MEMBER` to create invite links, enabling regular members to generate invites.
### Issue Context
Existing org team invite endpoints require `TEAM_ADMIN`, and the newly added e2e tests expect 403 for non-admin team members.
### Fix Focus Areas
- apps/api/v2/src/modules/teams/invite/controllers/teams-invite.controller.ts[31-33]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Benchmark PR from agentic-review-benchmarks#6