mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
## What does this PR do?
Adds auto-accept logic to the `/v2/teams/{teamId}/memberships` endpoint, similar to what exists in the `/v2/organizations/{orgId}/teams/{teamId}/memberships` endpoint.
When a user is added to a sub-team of an organization, the system now checks if the user's email domain matches the organization's `orgAutoAcceptEmail` setting. If it matches and the organization is verified, the membership is automatically accepted (overriding any `accepted: false` in the request).
### Changes:
- Modified `TeamsMembershipsService.createTeamMembership()` to check if the team has a parent organization and apply auto-accept logic using the existing `OrganizationMembershipService.shouldAutoAccept()` method
- Exported `OrganizationMembershipService` from `OrganizationsModule` for dependency injection
- Added `TeamsModule` and `UsersModule` imports to `TeamsMembershipsModule`
- Added required dependencies (`OrganizationMembershipService`, `OAuthClientRepository`, etc.) to `TeamsModule` and `TeamsSchedulesModule` since they provide `TeamsMembershipsService` directly
- Added comprehensive E2E tests for auto-accept scenarios
- Added `createOrgSettings` and `deleteOrgSettings` helper methods to test fixtures
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - no documentation changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
The E2E tests cover the following scenarios:
1. **Auto-accept when email matches** - User with `@acme.com` email is auto-accepted when org has `orgAutoAcceptEmail: "acme.com"`
2. **Case-insensitive matching** - User with `@ACME.COM` email matches `orgAutoAcceptEmail: "acme.com"`
3. **Override accepted:false** - Even if `accepted: false` is passed, it's overridden to `true` when email matches
4. **No auto-accept for non-matching emails** - User with `@external.com` email is NOT auto-accepted
5. **EventTypes assignment** - Verifies user is added to event types with `assignAllTeamMembers: true`
To run the tests:
```bash
TZ=UTC yarn test:e2e --testPathPattern="teams-memberships.controller.e2e-spec"
```
## Human Review Checklist
- [ ] Verify the auto-accept logic matches the existing implementation in `organizations-teams-memberships.controller.ts`
- [ ] Confirm all modules that provide `TeamsMembershipsService` directly (`TeamsModule`, `TeamsSchedulesModule`, `OrganizationsModule`) have the required dependencies
- [ ] Verify `updateNewTeamMemberEventTypes` is called when auto-accept triggers (controller checks `membership.accepted` after service returns)
- [ ] Consider if organization membership creation is needed when a user is auto-accepted into a sub-team but doesn't have org membership yet (not implemented in this PR)
## Checklist
- [x] I have read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- [x] My code follows the style guidelines of this project
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have checked if my changes generate no new warnings
---
Link to Devin run: https://app.devin.ai/sessions/578b8904a5a94385b59031f9616379cc
Requested by: @hariombalhara
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
|
import { TestingModule } from "@nestjs/testing";
|
|
|
|
import type { Prisma, Team } from "@calcom/prisma/client";
|
|
|
|
export class TeamRepositoryFixture {
|
|
private prismaReadClient: PrismaReadService["prisma"];
|
|
private prismaWriteClient: PrismaWriteService["prisma"];
|
|
|
|
constructor(module: TestingModule) {
|
|
this.prismaReadClient = module.get(PrismaReadService).prisma;
|
|
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
|
|
}
|
|
|
|
async get(teamId: Team["id"]) {
|
|
return this.prismaReadClient.team.findFirst({ where: { id: teamId } });
|
|
}
|
|
|
|
async create(data: Prisma.TeamCreateInput) {
|
|
return this.prismaWriteClient.team.create({ data });
|
|
}
|
|
|
|
async delete(teamId: Team["id"]) {
|
|
return this.prismaWriteClient.team.deleteMany({ where: { id: teamId } });
|
|
}
|
|
|
|
async getPlatformOrgTeams(organizationId: number, oAuthClientId: string) {
|
|
return this.prismaReadClient.team.findMany({
|
|
where: {
|
|
parentId: organizationId,
|
|
createdByOAuthClientId: oAuthClientId,
|
|
},
|
|
});
|
|
}
|
|
|
|
async createOrgSettings(
|
|
organizationId: number,
|
|
settings: {
|
|
orgAutoAcceptEmail: string;
|
|
isOrganizationVerified?: boolean;
|
|
isOrganizationConfigured?: boolean;
|
|
isAdminAPIEnabled?: boolean;
|
|
}
|
|
) {
|
|
return this.prismaWriteClient.organizationSettings.create({
|
|
data: {
|
|
organizationId,
|
|
orgAutoAcceptEmail: settings.orgAutoAcceptEmail,
|
|
isOrganizationVerified: settings.isOrganizationVerified,
|
|
isOrganizationConfigured: settings.isOrganizationConfigured,
|
|
isAdminAPIEnabled: settings.isAdminAPIEnabled,
|
|
},
|
|
});
|
|
}
|
|
|
|
async deleteOrgSettings(organizationId: number) {
|
|
return this.prismaWriteClient.organizationSettings.deleteMany({
|
|
where: { organizationId },
|
|
});
|
|
}
|
|
}
|