feat: add auto-accept logic for team memberships based on org email domain (#26860)

## 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
This commit is contained in:
Hariom Balhara
2026-01-20 18:40:44 +05:30
committed by GitHub
parent bea542b310
commit 39ae54d682
8 changed files with 326 additions and 43 deletions
@@ -1,9 +1,11 @@
import { Logger, Module } from "@nestjs/common";
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
import { OrganizationsEventTypesPrivateLinksController } from "@/ee/event-types-private-links/controllers/organizations-event-types-private-links.controller";
import { EventTypesPrivateLinksModule } from "@/ee/event-types-private-links/event-types-private-links.module";
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
import { SchedulesModule_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/schedules.module";
import { InputSchedulesService_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/services/input-schedules.service";
import { SchedulesService_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/services/schedules.service";
import { OrganizationMembershipService } from "@/lib/services/organization-membership.service";
import { AppsRepository } from "@/modules/apps/apps.repository";
import { ConferencingRepository } from "@/modules/conferencing/repositories/conferencing.repository";
import { ConferencingService } from "@/modules/conferencing/services/conferencing.service";
@@ -13,7 +15,6 @@ import { ZoomVideoService } from "@/modules/conferencing/services/zoom-video.ser
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
import { EmailModule } from "@/modules/email/email.module";
import { EmailService } from "@/modules/email/email.service";
import { OrganizationMembershipService } from "@/lib/services/organization-membership.service";
import { MembershipsModule } from "@/modules/memberships/memberships.module";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
import { UserOOORepository } from "@/modules/ooo/repositories/ooo.repository";
@@ -37,8 +38,8 @@ import { OrganizationsRepository } from "@/modules/organizations/index/organizat
import { OrganizationsService } from "@/modules/organizations/index/organizations.service";
import { OrganizationsMembershipsController } from "@/modules/organizations/memberships/organizations-membership.controller";
import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository";
import { OrganizationsMembershipOutputService } from "@/modules/organizations/memberships/services/organizations-membership-output.service";
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
import { OrganizationsMembershipOutputService } from "@/modules/organizations/memberships/services/organizations-membership-output.service";
import { OrganizationsOrganizationsModule } from "@/modules/organizations/organizations/organizations-organizations.module";
import { OrganizationsRolesModule } from "@/modules/organizations/roles/organizations-roles.module";
import { OrganizationsSchedulesController } from "@/modules/organizations/schedules/organizations-schedules.controller";
@@ -84,7 +85,6 @@ import { TeamRoutingFormWorkflowsService } from "@/modules/workflows/services/te
import { WorkflowsInputService } from "@/modules/workflows/services/workflows.input.service";
import { WorkflowsOutputService } from "@/modules/workflows/services/workflows.output.service";
import { WorkflowsRepository } from "@/modules/workflows/workflows.repository";
import { Module } from "@nestjs/common";
@Module({
imports: [
@@ -162,6 +162,7 @@ import { Module } from "@nestjs/common";
TeamsMembershipsService,
TeamsMembershipsRepository,
OAuthClientRepository,
Logger,
],
exports: [
OrganizationsService,
@@ -184,6 +185,7 @@ import { Module } from "@nestjs/common";
OrganizationsEventTypesService,
OrganizationsConferencingService,
OrganizationsStripeService,
OrganizationMembershipService,
],
controllers: [
OrganizationsTeamsController,
@@ -202,4 +204,4 @@ import { Module } from "@nestjs/common";
OrganizationsEventTypesPrivateLinksController,
],
})
export class OrganizationsModule { }
export class OrganizationsModule {}
@@ -505,6 +505,226 @@ describe("Teams Memberships Endpoints", () => {
.expect(400);
});
// Auto-accept tests for sub-teams of organizations
describe("auto-accept based on email domain for org sub-teams", () => {
let orgWithAutoAccept: Team;
let subteamWithAutoAccept: Team;
let subteamEventType: EventType;
let userWithMatchingEmail: User;
let userWithUppercaseEmail: User;
let userWithMatchingEmailForOverride: User;
let userWithNonMatchingEmail: User;
beforeAll(async () => {
// Create org with auto-accept settings
orgWithAutoAccept = await teamsRepositoryFixture.create({
name: `auto-accept-org-${randomString()}`,
isOrganization: true,
});
// Create organization settings with orgAutoAcceptEmail
await teamsRepositoryFixture.createOrgSettings(orgWithAutoAccept.id, {
orgAutoAcceptEmail: "acme.com",
isOrganizationVerified: true,
isOrganizationConfigured: true,
isAdminAPIEnabled: true,
});
// Create subteam
subteamWithAutoAccept = await teamsRepositoryFixture.create({
name: `auto-accept-subteam-${randomString()}`,
isOrganization: false,
parent: { connect: { id: orgWithAutoAccept.id } },
});
// Create event type with assignAllTeamMembers
subteamEventType = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "COLLECTIVE",
team: { connect: { id: subteamWithAutoAccept.id } },
title: "Auto Accept Event Type",
slug: "auto-accept-event-type",
length: 30,
assignAllTeamMembers: true,
bookingFields: [],
locations: [],
});
// Create users with different email domains
userWithMatchingEmail = await userRepositoryFixture.create({
email: `alice-${randomString()}@acme.com`,
username: `alice-${randomString()}`,
});
userWithUppercaseEmail = await userRepositoryFixture.create({
email: `bob-${randomString()}@ACME.COM`,
username: `bob-${randomString()}`,
});
userWithMatchingEmailForOverride = await userRepositoryFixture.create({
email: `david-${randomString()}@acme.com`,
username: `david-${randomString()}`,
});
userWithNonMatchingEmail = await userRepositoryFixture.create({
email: `charlie-${randomString()}@external.com`,
username: `charlie-${randomString()}`,
});
// Add users to org
await membershipsRepositoryFixture.create({
role: "MEMBER",
accepted: true,
user: { connect: { id: userWithMatchingEmail.id } },
team: { connect: { id: orgWithAutoAccept.id } },
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
accepted: true,
user: { connect: { id: userWithUppercaseEmail.id } },
team: { connect: { id: orgWithAutoAccept.id } },
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
accepted: true,
user: { connect: { id: userWithMatchingEmailForOverride.id } },
team: { connect: { id: orgWithAutoAccept.id } },
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
accepted: true,
user: { connect: { id: userWithNonMatchingEmail.id } },
team: { connect: { id: orgWithAutoAccept.id } },
});
// Create profiles for users
await profileRepositoryFixture.create({
uid: `usr-${userWithMatchingEmail.id}`,
username: userWithMatchingEmail.username || `user-${userWithMatchingEmail.id}`,
organization: { connect: { id: orgWithAutoAccept.id } },
user: { connect: { id: userWithMatchingEmail.id } },
});
await profileRepositoryFixture.create({
uid: `usr-${userWithUppercaseEmail.id}`,
username: userWithUppercaseEmail.username || `user-${userWithUppercaseEmail.id}`,
organization: { connect: { id: orgWithAutoAccept.id } },
user: { connect: { id: userWithUppercaseEmail.id } },
});
await profileRepositoryFixture.create({
uid: `usr-${userWithMatchingEmailForOverride.id}`,
username:
userWithMatchingEmailForOverride.username || `user-${userWithMatchingEmailForOverride.id}`,
organization: { connect: { id: orgWithAutoAccept.id } },
user: { connect: { id: userWithMatchingEmailForOverride.id } },
});
await profileRepositoryFixture.create({
uid: `usr-${userWithNonMatchingEmail.id}`,
username: userWithNonMatchingEmail.username || `user-${userWithNonMatchingEmail.id}`,
organization: { connect: { id: orgWithAutoAccept.id } },
user: { connect: { id: userWithNonMatchingEmail.id } },
});
// Make teamAdmin an admin of the org and subteam for API access
await membershipsRepositoryFixture.create({
role: "ADMIN",
accepted: true,
user: { connect: { id: teamAdmin.id } },
team: { connect: { id: orgWithAutoAccept.id } },
});
await membershipsRepositoryFixture.create({
role: "ADMIN",
accepted: true,
user: { connect: { id: teamAdmin.id } },
team: { connect: { id: subteamWithAutoAccept.id } },
});
await profileRepositoryFixture.create({
uid: `usr-org-${teamAdmin.id}`,
username: teamAdmin.username || `admin-${teamAdmin.id}`,
organization: { connect: { id: orgWithAutoAccept.id } },
user: { connect: { id: teamAdmin.id } },
});
});
it("should auto-accept when email matches orgAutoAcceptEmail for sub-team", async () => {
const response = await request(app.getHttpServer())
.post(`/v2/teams/${subteamWithAutoAccept.id}/memberships`)
.send({
userId: userWithMatchingEmail.id,
role: "MEMBER",
} satisfies CreateTeamMembershipInput)
.expect(201);
const responseBody: CreateTeamMembershipOutput = response.body;
expect(responseBody.data.accepted).toBe(true);
// Verify EventTypes assignment
const eventTypes = await eventTypesRepositoryFixture.getAllTeamEventTypes(subteamWithAutoAccept.id);
const eventTypeWithAssignAll = eventTypes.find((et) => et.assignAllTeamMembers);
expect(eventTypeWithAssignAll).toBeTruthy();
const userIsHost = eventTypeWithAssignAll?.hosts.some((h) => h.userId === userWithMatchingEmail.id);
expect(userIsHost).toBe(true);
});
it("should handle case-insensitive email domain matching for sub-team", async () => {
// User with email="bob@ACME.COM" should match orgAutoAcceptEmail="acme.com"
const response = await request(app.getHttpServer())
.post(`/v2/teams/${subteamWithAutoAccept.id}/memberships`)
.send({
userId: userWithUppercaseEmail.id,
role: "MEMBER",
} satisfies CreateTeamMembershipInput)
.expect(201);
const responseBody: CreateTeamMembershipOutput = response.body;
expect(responseBody.data.accepted).toBe(true);
});
it("should ALWAYS auto-accept when email matches, even if accepted:false for sub-team", async () => {
const response = await request(app.getHttpServer())
.post(`/v2/teams/${subteamWithAutoAccept.id}/memberships`)
.send({
userId: userWithMatchingEmailForOverride.id,
role: "MEMBER",
accepted: false,
} satisfies CreateTeamMembershipInput)
.expect(201);
const responseBody: CreateTeamMembershipOutput = response.body;
// Should override to true because email matches
expect(responseBody.data.accepted).toBe(true);
});
it("should NOT auto-accept when email does not match orgAutoAcceptEmail for sub-team", async () => {
const response = await request(app.getHttpServer())
.post(`/v2/teams/${subteamWithAutoAccept.id}/memberships`)
.send({
userId: userWithNonMatchingEmail.id,
role: "MEMBER",
} satisfies CreateTeamMembershipInput)
.expect(201);
const responseBody: CreateTeamMembershipOutput = response.body;
expect(responseBody.data.accepted).toBe(false);
});
afterAll(async () => {
await userRepositoryFixture.deleteByEmail(userWithMatchingEmail.email);
await userRepositoryFixture.deleteByEmail(userWithUppercaseEmail.email);
await userRepositoryFixture.deleteByEmail(userWithMatchingEmailForOverride.email);
await userRepositoryFixture.deleteByEmail(userWithNonMatchingEmail.email);
await teamsRepositoryFixture.deleteOrgSettings(orgWithAutoAccept.id);
await teamsRepositoryFixture.delete(subteamWithAutoAccept.id);
await teamsRepositoryFixture.delete(orgWithAutoAccept.id);
});
});
afterAll(async () => {
await userRepositoryFixture.deleteByEmail(teamAdmin.email);
await userRepositoryFixture.deleteByEmail(teammateInvitedViaApi.email);
@@ -1,3 +1,22 @@
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { updateNewTeamMemberEventTypes } from "@calcom/platform-libraries/event-types";
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Logger,
Param,
ParseIntPipe,
Patch,
Post,
Query,
UseGuards,
} from "@nestjs/common";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
@@ -13,26 +32,6 @@ import { GetTeamMembershipsOutput } from "@/modules/teams/memberships/outputs/ge
import { TeamMembershipOutput } from "@/modules/teams/memberships/outputs/team-membership.output";
import { UpdateTeamMembershipOutput } from "@/modules/teams/memberships/outputs/update-team-membership.output";
import { TeamsMembershipsService } from "@/modules/teams/memberships/services/teams-memberships.service";
import {
Controller,
UseGuards,
Get,
Param,
ParseIntPipe,
Query,
Delete,
Patch,
Post,
Body,
HttpCode,
HttpStatus,
Logger,
} from "@nestjs/common";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { updateNewTeamMemberEventTypes } from "@calcom/platform-libraries/event-types";
@Controller({
path: "/v2/teams/:teamId/memberships",
@@ -55,13 +54,6 @@ export class TeamsMembershipsController {
@Body() body: CreateTeamMembershipInput
): Promise<CreateTeamMembershipOutput> {
const membership = await this.teamsMembershipsService.createTeamMembership(teamId, body);
if (membership.accepted) {
try {
await updateNewTeamMemberEventTypes(body.userId, teamId);
} catch (err) {
this.logger.error("Could not update new team member eventTypes", err);
}
}
return {
status: SUCCESS_STATUS,
data: plainToClass(TeamMembershipOutput, membership, { strategy: "excludeAll" }),
@@ -1,10 +1,13 @@
import { TeamService } from "@calcom/platform-libraries";
import { updateNewTeamMemberEventTypes } from "@calcom/platform-libraries/event-types";
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
import { OrganizationMembershipService } from "@/lib/services/organization-membership.service";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
import { CreateTeamMembershipInput } from "@/modules/teams/memberships/inputs/create-team-membership.input";
import { UpdateTeamMembershipInput } from "@/modules/teams/memberships/inputs/update-team-membership.input";
import { TeamsMembershipsRepository } from "@/modules/teams/memberships/teams-memberships.repository";
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { TeamService } from "@calcom/platform-libraries";
import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
import { UsersRepository } from "@/modules/users/users.repository";
export const PLATFORM_USER_BEING_ADDED_TO_REGULAR_TEAM_ERROR = `Can't add user to team - the user is platform managed user but team is not because team probably was not created using OAuth credentials.`;
export const REGULAR_USER_BEING_ADDED_TO_PLATFORM_TEAM_ERROR = `Can't add user to team - the user is not platform managed user but team is platform managed. Both have to be created using OAuth credentials.`;
@@ -14,11 +17,44 @@ export const PLATFORM_USER_AND_PLATFORM_TEAM_CREATED_WITH_DIFFERENT_OAUTH_CLIENT
export class TeamsMembershipsService {
constructor(
private readonly teamsMembershipsRepository: TeamsMembershipsRepository,
private readonly oAuthClientsRepository: OAuthClientRepository
private readonly oAuthClientsRepository: OAuthClientRepository,
private readonly teamsRepository: TeamsRepository,
private readonly usersRepository: UsersRepository,
private readonly orgMembershipService: OrganizationMembershipService,
private readonly logger: Logger
) {}
private async shouldAutoAccept({ teamId, userId }: { teamId: number; userId: number }): Promise<boolean> {
const team = await this.teamsRepository.getById(teamId);
if (team?.parentId) {
const user = await this.usersRepository.findById(userId);
if (user) {
const shouldAutoAccept = await this.orgMembershipService.shouldAutoAccept({
organizationId: team.parentId,
userEmail: user.email,
});
return shouldAutoAccept;
}
}
return false;
}
async createTeamMembership(teamId: number, data: CreateTeamMembershipInput) {
await this.canUserBeAddedToTeam(data.userId, teamId);
const shouldAutoAccept = await this.shouldAutoAccept({ teamId, userId: data.userId });
if (shouldAutoAccept) {
data = { ...data, accepted: true };
}
if (data.accepted) {
try {
await updateNewTeamMemberEventTypes(data.userId, teamId);
} catch (err) {
this.logger.error("Could not update new team member eventTypes", err);
}
}
const teamMembership = await this.teamsMembershipsRepository.createTeamMembership(teamId, data);
return teamMembership;
}
@@ -6,11 +6,21 @@ import { TeamsEventTypesModule } from "@/modules/teams/event-types/teams-event-t
import { TeamsMembershipsController } from "@/modules/teams/memberships/controllers/teams-memberships.controller";
import { TeamsMembershipsService } from "@/modules/teams/memberships/services/teams-memberships.service";
import { TeamsMembershipsRepository } from "@/modules/teams/memberships/teams-memberships.repository";
import { Module } from "@nestjs/common";
import { TeamsModule } from "@/modules/teams/teams/teams.module";
import { UsersModule } from "@/modules/users/users.module";
import { Logger, Module } from "@nestjs/common";
@Module({
imports: [PrismaModule, RedisModule, OrganizationsModule, MembershipsModule, TeamsEventTypesModule],
providers: [TeamsMembershipsRepository, TeamsMembershipsService],
imports: [
PrismaModule,
RedisModule,
OrganizationsModule,
MembershipsModule,
TeamsEventTypesModule,
TeamsModule,
UsersModule,
],
providers: [TeamsMembershipsRepository, TeamsMembershipsService, Logger],
controllers: [TeamsMembershipsController],
exports: [TeamsMembershipsService],
})
@@ -10,7 +10,6 @@ import { PrismaModule } from "@/modules/prisma/prisma.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { TeamsEventTypesModule } from "@/modules/teams/event-types/teams-event-types.module";
import { StripeService } from "@/modules/stripe/stripe.service";
import { TeamsMembershipsService } from "@/modules/teams/memberships/services/teams-memberships.service";
import { TeamsMembershipsRepository } from "@/modules/teams/memberships/teams-memberships.repository";
import { TeamsSchedulesController } from "@/modules/teams/schedules/controllers/teams-schedules.controller";
import { TeamsSchedulesService } from "@/modules/teams/schedules/services/teams-schedules.service";
@@ -26,7 +25,6 @@ import { Module } from "@nestjs/common";
TeamsRepository,
TeamsService,
TeamsMembershipsRepository,
TeamsMembershipsService,
OutputSchedulesService_2024_06_11,
OrganizationSchedulesRepository,
StripeService,
@@ -2,7 +2,6 @@ import { MembershipsModule } from "@/modules/memberships/memberships.module";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { StripeModule } from "@/modules/stripe/stripe.module";
import { TeamsMembershipsService } from "@/modules/teams/memberships/services/teams-memberships.service";
import { TeamsMembershipsRepository } from "@/modules/teams/memberships/teams-memberships.repository";
import { TeamsController } from "@/modules/teams/teams/controllers/teams.controller";
import { TeamsService } from "@/modules/teams/teams/services/teams.service";
@@ -11,7 +10,7 @@ import { Module } from "@nestjs/common";
@Module({
imports: [PrismaModule, MembershipsModule, RedisModule, StripeModule],
providers: [TeamsRepository, TeamsService, TeamsMembershipsRepository, TeamsMembershipsService],
providers: [TeamsRepository, TeamsService, TeamsMembershipsRepository],
controllers: [TeamsController],
exports: [TeamsRepository],
})
@@ -33,4 +33,30 @@ export class TeamRepositoryFixture {
},
});
}
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 },
});
}
}