diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 3852af3408..96e261ea54 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -26,7 +26,7 @@ "dependencies": { "@calcom/platform-constants": "*", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", - "@calcom/platform-libraries-0.0.21": "npm:@calcom/platform-libraries@0.0.21", + "@calcom/platform-libraries-0.0.22": "npm:@calcom/platform-libraries@0.0.22", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", "@calcom/prisma": "*", diff --git a/apps/api/v2/src/ee/bookings/controllers/bookings.controller.e2e-spec.ts b/apps/api/v2/src/ee/bookings/controllers/bookings.controller.e2e-spec.ts index d7fc779d7a..67e2c78e5b 100644 --- a/apps/api/v2/src/ee/bookings/controllers/bookings.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/bookings/controllers/bookings.controller.e2e-spec.ts @@ -19,7 +19,7 @@ import { UserRepositoryFixture } from "test/fixtures/repository/users.repository import { withApiAuth } from "test/utils/withApiAuth"; import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants"; -import { handleNewBooking } from "@calcom/platform-libraries-0.0.21"; +import { handleNewBooking } from "@calcom/platform-libraries-0.0.22"; import { ApiSuccessResponse, ApiResponse } from "@calcom/platform-types"; describe("Bookings Endpoints", () => { diff --git a/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts index c539f7a531..6e47027425 100644 --- a/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts +++ b/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts @@ -46,7 +46,7 @@ import { getBookingInfo, handleCancelBooking, getBookingForReschedule, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; import { GetBookingsInput, CancelBookingInput, Status } from "@calcom/platform-types"; import { ApiResponse } from "@calcom/platform-types"; import { PrismaClient } from "@calcom/prisma"; diff --git a/apps/api/v2/src/ee/bookings/inputs/create-recurring-booking.input.ts b/apps/api/v2/src/ee/bookings/inputs/create-recurring-booking.input.ts index 043ce80c24..02e004f513 100644 --- a/apps/api/v2/src/ee/bookings/inputs/create-recurring-booking.input.ts +++ b/apps/api/v2/src/ee/bookings/inputs/create-recurring-booking.input.ts @@ -1,7 +1,7 @@ import { CreateBookingInput } from "@/ee/bookings/inputs/create-booking.input"; import { IsBoolean, IsNumber, IsOptional } from "class-validator"; -import type { AppsStatus } from "@calcom/platform-libraries-0.0.21"; +import type { AppsStatus } from "@calcom/platform-libraries-0.0.22"; export class CreateRecurringBookingInput extends CreateBookingInput { @IsBoolean() diff --git a/apps/api/v2/src/ee/calendars/services/apple-calendar.service.ts b/apps/api/v2/src/ee/calendars/services/apple-calendar.service.ts index a75d8faac2..2025c7323d 100644 --- a/apps/api/v2/src/ee/calendars/services/apple-calendar.service.ts +++ b/apps/api/v2/src/ee/calendars/services/apple-calendar.service.ts @@ -5,7 +5,7 @@ import { BadRequestException, UnauthorizedException } from "@nestjs/common"; import { Injectable } from "@nestjs/common"; import { SUCCESS_STATUS, APPLE_CALENDAR_TYPE, APPLE_CALENDAR_ID } from "@calcom/platform-constants"; -import { symmetricEncrypt, CalendarService } from "@calcom/platform-libraries-0.0.21"; +import { symmetricEncrypt, CalendarService } from "@calcom/platform-libraries-0.0.22"; @Injectable() export class AppleCalendarService implements CredentialSyncCalendarApp { diff --git a/apps/api/v2/src/ee/calendars/services/calendars.service.ts b/apps/api/v2/src/ee/calendars/services/calendars.service.ts index 42a78970a9..c24478ae9d 100644 --- a/apps/api/v2/src/ee/calendars/services/calendars.service.ts +++ b/apps/api/v2/src/ee/calendars/services/calendars.service.ts @@ -18,7 +18,7 @@ import { User } from "@prisma/client"; import { DateTime } from "luxon"; import { z } from "zod"; -import { getConnectedDestinationCalendars, getBusyCalendarTimes } from "@calcom/platform-libraries-0.0.21"; +import { getConnectedDestinationCalendars, getBusyCalendarTimes } from "@calcom/platform-libraries-0.0.22"; import { Calendar } from "@calcom/platform-types"; import { PrismaClient } from "@calcom/prisma"; diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.e2e-spec.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.e2e-spec.ts index af85596b89..8a6cf0de51 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.e2e-spec.ts @@ -35,7 +35,7 @@ import { EventTypesPublic, eventTypeBookingFields, eventTypeLocations, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; import { ApiSuccessResponse } from "@calcom/platform-types"; describe("Event types Endpoints", () => { diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/event-types.repository.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/event-types.repository.ts index 4fe2c46580..a79040d6ef 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/event-types.repository.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/event-types.repository.ts @@ -4,7 +4,7 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { UserWithProfile } from "@/modules/users/users.repository"; import { Injectable } from "@nestjs/common"; -import { getEventTypeById } from "@calcom/platform-libraries-0.0.21"; +import { getEventTypeById } from "@calcom/platform-libraries-0.0.22"; import type { PrismaClient } from "@calcom/prisma"; @Injectable() diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts index 0092bc6bf1..a8bb51b997 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts @@ -14,7 +14,7 @@ import { updateEventType, EventTypesPublic, getEventTypesPublic, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; import { EventType } from "@calcom/prisma/client"; @Injectable() diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts index 4807be6ae5..436c4f65a6 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts @@ -7,7 +7,7 @@ import { getEventTypeById, transformApiEventTypeBookingFields, transformApiEventTypeLocations, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; import { CreateEventTypeInput_2024_06_14 } from "@calcom/platform-types"; import type { PrismaClient } from "@calcom/prisma"; diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/event-types.service.ts index 7ff490ec9c..650412725a 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/event-types.service.ts @@ -10,9 +10,9 @@ import { UsersService } from "@/modules/users/services/users.service"; import { UserWithProfile, UsersRepository } from "@/modules/users/users.repository"; import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; -import { createEventType, updateEventType } from "@calcom/platform-libraries-0.0.21"; -import { getEventTypesPublic, EventTypesPublic } from "@calcom/platform-libraries-0.0.21"; -import { dynamicEvent } from "@calcom/platform-libraries-0.0.21"; +import { createEventType, updateEventType } from "@calcom/platform-libraries-0.0.22"; +import { getEventTypesPublic, EventTypesPublic } from "@calcom/platform-libraries-0.0.22"; +import { dynamicEvent } from "@calcom/platform-libraries-0.0.22"; import { CreateEventTypeInput_2024_06_14, UpdateEventTypeInput_2024_06_14, diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts index 88ecd3923c..bf59c381fd 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts @@ -3,7 +3,7 @@ import { Injectable } from "@nestjs/common"; import { transformApiEventTypeBookingFields, transformApiEventTypeLocations, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; import { CreateEventTypeInput_2024_06_14, UpdateEventTypeInput_2024_06_14 } from "@calcom/platform-types"; @Injectable() diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts index 0d558814dd..e62da1d364 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts @@ -9,7 +9,7 @@ import { parseRecurringEvent, TransformedLocationsSchema, BookingFieldsSchema, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; type EventTypeRelations = { users: User[]; schedule: Schedule | null }; type DatabaseEventType = EventType & EventTypeRelations; diff --git a/apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/input-schedules.service.ts b/apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/input-schedules.service.ts index 67d5aeabb8..cef53e1815 100644 --- a/apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/input-schedules.service.ts +++ b/apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/input-schedules.service.ts @@ -3,7 +3,7 @@ import { Injectable } from "@nestjs/common"; import { transformApiScheduleOverrides, transformApiScheduleAvailability, -} from "@calcom/platform-libraries-0.0.21"; +} from "@calcom/platform-libraries-0.0.22"; import { CreateScheduleInput_2024_06_11, ScheduleAvailabilityInput_2024_06_11 } from "@calcom/platform-types"; import { ScheduleOverrideInput_2024_06_11 } from "@calcom/platform-types"; diff --git a/apps/api/v2/src/filters/trpc-exception.filter.ts b/apps/api/v2/src/filters/trpc-exception.filter.ts index d2f8edb6bc..626210bf9f 100644 --- a/apps/api/v2/src/filters/trpc-exception.filter.ts +++ b/apps/api/v2/src/filters/trpc-exception.filter.ts @@ -2,7 +2,7 @@ import { ArgumentsHost, Catch, ExceptionFilter, Logger } from "@nestjs/common"; import { Request } from "express"; import { ERROR_STATUS } from "@calcom/platform-constants"; -import { TRPCError } from "@calcom/platform-libraries-0.0.21"; +import { TRPCError } from "@calcom/platform-libraries-0.0.22"; import { Response } from "@calcom/platform-types"; @Catch(TRPCError) diff --git a/apps/api/v2/src/modules/email/email.service.ts b/apps/api/v2/src/modules/email/email.service.ts index 848067f50a..310ae86554 100644 --- a/apps/api/v2/src/modules/email/email.service.ts +++ b/apps/api/v2/src/modules/email/email.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@nestjs/common"; -import { sendSignupToOrganizationEmail, getTranslation } from "@calcom/platform-libraries-0.0.21"; +import { sendSignupToOrganizationEmail, getTranslation } from "@calcom/platform-libraries-0.0.22"; @Injectable() export class EmailService { diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts index 51443c6259..9fac542ed5 100644 --- a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts @@ -14,7 +14,7 @@ import { UsersModule } from "@/modules/users/users.module"; import { INestApplication } from "@nestjs/common"; import { NestExpressApplication } from "@nestjs/platform-express"; import { Test } from "@nestjs/testing"; -import { PlatformOAuthClient, Team, User } from "@prisma/client"; +import { PlatformOAuthClient, Team, User, EventType } from "@prisma/client"; import * as request from "supertest"; import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture"; @@ -204,10 +204,16 @@ describe("OAuth Client Users Endpoints", () => { // note(Lauris): to determine count see default event types created in EventTypesService.createUserDefaultEventTypes expect(defaultEventTypes?.length).toEqual(4); expect( - defaultEventTypes?.find((eventType) => eventType.length === DEFAULT_EVENT_TYPES.thirtyMinutes.length) + defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.thirtyMinutes.slug) ).toBeTruthy(); expect( - defaultEventTypes?.find((eventType) => eventType.length === DEFAULT_EVENT_TYPES.sixtyMinutes.length) + defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.sixtyMinutes.slug) + ).toBeTruthy(); + expect( + defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.thirtyMinutesVideo.slug) + ).toBeTruthy(); + expect( + defaultEventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.sixtyMinutesVideo.slug) ).toBeTruthy(); } @@ -301,4 +307,243 @@ describe("OAuth Client Users Endpoints", () => { await app.close(); }); }); + + describe("User team even-types", () => { + let app: INestApplication; + + let oAuthClient1: PlatformOAuthClient; + let oAuthClient2: PlatformOAuthClient; + + let organization: Team; + let team1: Team; + let team2: Team; + let owner: User; + + let managedEventType1: EventType; + let userRepositoryFixture: UserRepositoryFixture; + let oauthClientRepositoryFixture: OAuthClientRepositoryFixture; + let teamRepositoryFixture: TeamRepositoryFixture; + let eventTypesRepositoryFixture: EventTypesRepositoryFixture; + let profileRepositoryFixture: ProfileRepositoryFixture; + + let postResponseData: CreateUserResponse; + + const userEmail = "oauth-client-users-user@gmail.com"; + const userTimeZone = "Europe/Rome"; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [PrismaExceptionFilter, HttpExceptionFilter], + imports: [AppModule, UsersModule], + }).compile(); + + app = moduleRef.createNestApplication(); + bootstrap(app as NestExpressApplication); + + oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef); + userRepositoryFixture = new UserRepositoryFixture(moduleRef); + teamRepositoryFixture = new TeamRepositoryFixture(moduleRef); + eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef); + profileRepositoryFixture = new ProfileRepositoryFixture(moduleRef); + + organization = await teamRepositoryFixture.create({ + name: "Testy Organization", + isOrganization: true, + }); + + owner = await userRepositoryFixture.create({ + email: userEmail, + username: userEmail, + organization: { connect: { id: organization.id } }, + }); + + await profileRepositoryFixture.create({ + uid: `usr-${owner.id}`, + username: userEmail, + organization: { + connect: { + id: organization.id, + }, + }, + user: { + connect: { + id: owner.id, + }, + }, + }); + + oAuthClient1 = await createOAuthClient(organization.id); + oAuthClient2 = await createOAuthClient(organization.id); + + team1 = await teamRepositoryFixture.create({ + name: "Testy org team", + isOrganization: false, + parent: { connect: { id: organization.id } }, + createdByOAuthClient: { connect: { id: oAuthClient1.id } }, + }); + + team2 = await teamRepositoryFixture.create({ + name: "Testy org team 2", + isOrganization: false, + parent: { connect: { id: organization.id } }, + createdByOAuthClient: { connect: { id: oAuthClient2.id } }, + }); + + // note(Lauris): team1 team event-types + await eventTypesRepositoryFixture.createTeamEventType({ + schedulingType: "COLLECTIVE", + team: { + connect: { id: team1.id }, + }, + title: "Collective Event Type", + slug: "collective-event-type", + length: 30, + assignAllTeamMembers: true, + bookingFields: [], + locations: [], + }); + + managedEventType1 = await eventTypesRepositoryFixture.createTeamEventType({ + schedulingType: "MANAGED", + team: { + connect: { id: team1.id }, + }, + title: "Managed Event Type", + slug: "managed-event-type", + length: 60, + assignAllTeamMembers: true, + bookingFields: [], + locations: [], + }); + + // note(Lauris): team2 team event-types + await eventTypesRepositoryFixture.createTeamEventType({ + schedulingType: "COLLECTIVE", + team: { + connect: { id: team2.id }, + }, + title: "Collective Event Type team 2", + slug: "collective-event-type-team-2", + length: 30, + assignAllTeamMembers: true, + bookingFields: [], + locations: [], + }); + + await eventTypesRepositoryFixture.createTeamEventType({ + schedulingType: "MANAGED", + team: { + connect: { id: team2.id }, + }, + title: "Managed Event Type team 2", + slug: "managed-event-type-team-2", + length: 60, + assignAllTeamMembers: true, + bookingFields: [], + locations: [], + }); + + await app.init(); + }); + + async function createOAuthClient(organizationId: number) { + const data = { + logo: "logo-url", + name: "name", + redirectUris: [CLIENT_REDIRECT_URI], + permissions: 32, + }; + const secret = "secret"; + + const client = await oauthClientRepositoryFixture.create(organizationId, data, secret); + return client; + } + + it("should be defined", () => { + expect(oauthClientRepositoryFixture).toBeDefined(); + expect(userRepositoryFixture).toBeDefined(); + expect(oAuthClient1).toBeDefined(); + }); + + it(`should create managed user and update team event-types of OAuthClient marked as assignAllTeamMembers: true`, async () => { + const requestBody: CreateManagedUserInput = { + email: userEmail, + timeZone: userTimeZone, + weekStart: "Monday", + timeFormat: 24, + locale: Locales.FR, + }; + + const response = await request(app.getHttpServer()) + .post(`/api/v2/oauth-clients/${oAuthClient1.id}/users`) + .set("x-cal-secret-key", oAuthClient1.secret) + .send(requestBody) + .expect(201); + + const responseBody: ApiSuccessResponse<{ + user: Omit; + accessToken: string; + refreshToken: string; + }> = response.body; + + postResponseData = responseBody.data; + + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + + await userHasCorrectEventTypes(responseBody.data.user.id); + await teamHasCorrectEventTypes(team1.id); + }); + + async function userHasCorrectEventTypes(userId: number) { + const eventTypes = await eventTypesRepositoryFixture.getAllUserEventTypes(userId); + + expect(eventTypes?.length).toEqual(5); + + // note(Lauris): managed event-types with assignAllTeamMembers: true + expect(eventTypes?.find((eventType) => eventType.slug === managedEventType1.slug)).toBeTruthy(); + + // note(Lauris): default event types + expect( + eventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.thirtyMinutes.slug) + ).toBeTruthy(); + expect( + eventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.sixtyMinutes.slug) + ).toBeTruthy(); + expect( + eventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.thirtyMinutesVideo.slug) + ).toBeTruthy(); + expect( + eventTypes?.find((eventType) => eventType.slug === DEFAULT_EVENT_TYPES.sixtyMinutesVideo.slug) + ).toBeTruthy(); + } + + async function teamHasCorrectEventTypes(teamId: number) { + const eventTypes = await eventTypesRepositoryFixture.getAllTeamEventTypes(teamId); + + expect(eventTypes?.length).toEqual(2); + + // note(Lauris): managed event-types with assignAllTeamMembers: true + expect(eventTypes?.find((eventType) => eventType.slug === managedEventType1.slug)).toBeTruthy(); + + // note(Lauris): check if managed user added to collective event-type hosts given that it has assignAllTeamMembers: true + const collective = eventTypes?.find((eventType) => eventType.schedulingType === "COLLECTIVE"); + expect(collective).toBeTruthy(); + expect(collective?.hosts).toBeDefined(); + expect(collective?.hosts?.length).toEqual(1); + expect(collective?.hosts[0].userId).toEqual(postResponseData.user.id); + } + + afterAll(async () => { + await oauthClientRepositoryFixture.delete(oAuthClient1.id); + await teamRepositoryFixture.delete(organization.id); + await userRepositoryFixture.delete(owner.id); + try { + await userRepositoryFixture.delete(postResponseData.user.id); + } catch (e) { + // User might have been deleted by the test + } + await app.close(); + }); + }); }); diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts index ab22b43822..9bfed76a9e 100644 --- a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts +++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts @@ -78,6 +78,7 @@ export class OAuthClientUsersController { `Creating user with data: ${JSON.stringify(body, null, 2)} for OAuth Client with ID ${oAuthClientId}` ); const client = await this.oauthRepository.getOAuthClient(oAuthClientId); + console.log("asap createUser client", JSON.stringify(client, null, 2)); const isPlatformManaged = true; const { user, tokens } = await this.oAuthClientUsersService.createOauthClientUser( diff --git a/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts b/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts index 7e286b252e..d757748d62 100644 --- a/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts +++ b/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts @@ -10,6 +10,7 @@ import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repo import { OAuthClientUsersService } from "@/modules/oauth-clients/services/oauth-clients-users.service"; import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service"; import { OrganizationsModule } from "@/modules/organizations/organizations.module"; +import { OrganizationsTeamsService } from "@/modules/organizations/services/organizations-teams.service"; import { PrismaModule } from "@/modules/prisma/prisma.module"; import { RedisModule } from "@/modules/redis/redis.module"; import { StripeModule } from "@/modules/stripe/stripe.module"; @@ -33,7 +34,13 @@ import { Global, Module } from "@nestjs/common"; BillingModule, SchedulesModule_2024_04_15, ], - providers: [OAuthClientRepository, TokensRepository, OAuthFlowService, OAuthClientUsersService], + providers: [ + OAuthClientRepository, + TokensRepository, + OAuthFlowService, + OAuthClientUsersService, + OrganizationsTeamsService, + ], controllers: [OAuthClientUsersController, OAuthClientsController, OAuthFlowController], exports: [OAuthClientRepository], }) diff --git a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts index 98a7d08dfd..dea2e796f6 100644 --- a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts +++ b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts @@ -1,5 +1,6 @@ import { EventTypesService_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/services/event-types.service"; import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service"; +import { OrganizationsTeamsService } from "@/modules/organizations/services/organizations-teams.service"; import { TokensRepository } from "@/modules/tokens/tokens.repository"; import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-user.input"; import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input"; @@ -7,7 +8,7 @@ import { UsersRepository } from "@/modules/users/users.repository"; import { BadRequestException, Injectable } from "@nestjs/common"; import { User } from "@prisma/client"; -import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries-0.0.21"; +import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries-0.0.22"; @Injectable() export class OAuthClientUsersService { @@ -15,7 +16,8 @@ export class OAuthClientUsersService { private readonly userRepository: UsersRepository, private readonly tokensRepository: TokensRepository, private readonly eventTypesService: EventTypesService_2024_04_15, - private readonly schedulesService: SchedulesService_2024_04_15 + private readonly schedulesService: SchedulesService_2024_04_15, + private readonly organizationsTeamsService: OrganizationsTeamsService ) {} async createOauthClientUser( @@ -78,6 +80,8 @@ export class OAuthClientUsersService { user.defaultScheduleId = defaultSchedule.id; } + await this.organizationsTeamsService.addUserToPlatformTeamEvents(user.id, organizationId, oAuthClientId); + return { user, tokens: { diff --git a/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.e2e-spec.ts b/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.e2e-spec.ts index 7f795a4ccb..2c4857c259 100644 --- a/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.e2e-spec.ts +++ b/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.e2e-spec.ts @@ -248,7 +248,9 @@ describe("Organizations Event Types Endpoints", () => { options: ["javascript", "python", "cobol"], }, ], - schedulingType: "COLLECTIVE", + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + schedulingType: "collective", hosts: [ { userId: teammate1.id, @@ -270,6 +272,7 @@ describe("Organizations Event Types Endpoints", () => { const data = responseBody.data; expect(data.title).toEqual(body.title); expect(data.hosts.length).toEqual(2); + expect(data.schedulingType).toEqual("COLLECTIVE"); evaluateHost(body.hosts[0], data.hosts[0]); evaluateHost(body.hosts[1], data.hosts[1]); @@ -385,7 +388,6 @@ describe("Organizations Event Types Endpoints", () => { expect(responseBody.status).toEqual(SUCCESS_STATUS); const data = responseBody.data; - console.log("asap responseBody.data", JSON.stringify(responseBody.data, null, 2)); expect(data.length).toEqual(2); const eventTypeCollective = data.find((eventType) => eventType.schedulingType === "COLLECTIVE"); diff --git a/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.e2e-spec.ts b/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.e2e-spec.ts index ce7677f1d8..0a487bc7f3 100644 --- a/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.e2e-spec.ts @@ -11,20 +11,20 @@ import { Test } from "@nestjs/testing"; import { User } from "@prisma/client"; import * as request from "supertest"; import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture"; +import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture"; import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture"; import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; import { withApiAuth } from "test/utils/withApiAuth"; -import { SUCCESS_STATUS } from "@calcom/platform-constants"; +import { SUCCESS_STATUS, X_CAL_CLIENT_ID, X_CAL_SECRET_KEY } from "@calcom/platform-constants"; import { ApiSuccessResponse } from "@calcom/platform-types"; -import { Team } from "@calcom/prisma/client"; +import { PlatformOAuthClient, Team } from "@calcom/prisma/client"; describe("Organizations Team Endpoints", () => { describe("User Authentication - User is Org Admin", () => { let app: INestApplication; let userRepositoryFixture: UserRepositoryFixture; - let organizationsRepositoryFixture: TeamRepositoryFixture; let teamsRepositoryFixture: TeamRepositoryFixture; let membershipsRepositoryFixture: MembershipRepositoryFixture; @@ -32,6 +32,7 @@ describe("Organizations Team Endpoints", () => { let team: Team; let team2: Team; let teamCreatedViaApi: Team; + let teamCreatedViaApi2: Team; const userEmail = "org-admin-teams-controller-e2e@api.com"; let user: User; @@ -45,7 +46,6 @@ describe("Organizations Team Endpoints", () => { ).compile(); userRepositoryFixture = new UserRepositoryFixture(moduleRef); - organizationsRepositoryFixture = new TeamRepositoryFixture(moduleRef); teamsRepositoryFixture = new TeamRepositoryFixture(moduleRef); membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef); @@ -54,7 +54,7 @@ describe("Organizations Team Endpoints", () => { username: userEmail, }); - org = await organizationsRepositoryFixture.create({ + org = await teamsRepositoryFixture.create({ name: "Test Organization", isOrganization: true, }); @@ -85,7 +85,7 @@ describe("Organizations Team Endpoints", () => { it("should be defined", () => { expect(userRepositoryFixture).toBeDefined(); - expect(organizationsRepositoryFixture).toBeDefined(); + expect(teamsRepositoryFixture).toBeDefined(); expect(user).toBeDefined(); expect(org).toBeDefined(); }); @@ -147,6 +147,7 @@ describe("Organizations Team Endpoints", () => { teamCreatedViaApi.id ); expect(membership?.role ?? "").toEqual("OWNER"); + expect(membership?.accepted).toEqual(true); }); }); @@ -199,11 +200,35 @@ describe("Organizations Team Endpoints", () => { return request(app.getHttpServer()).get(`/v2/organizations/${org.id}/teams/123132145`).expect(404); }); + it("should create the team of the org without auto-accepting creator", async () => { + return request(app.getHttpServer()) + .post(`/v2/organizations/${org.id}/teams`) + .send({ + name: "Team II created via API", + autoAcceptCreator: false, + } satisfies CreateOrgTeamDto) + .expect(201) + .then(async (response) => { + const responseBody: ApiSuccessResponse = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + teamCreatedViaApi2 = responseBody.data; + expect(teamCreatedViaApi2.name).toEqual("Team II created via API"); + expect(teamCreatedViaApi2.parentId).toEqual(org.id); + const membership = await membershipsRepositoryFixture.getUserMembershipByTeamId( + user.id, + teamCreatedViaApi2.id + ); + expect(membership?.role ?? "").toEqual("OWNER"); + expect(membership?.accepted).toEqual(false); + }); + }); + afterAll(async () => { await userRepositoryFixture.deleteByEmail(user.email); await teamsRepositoryFixture.delete(team.id); await teamsRepositoryFixture.delete(team2.id); - await organizationsRepositoryFixture.delete(org.id); + await teamsRepositoryFixture.delete(teamCreatedViaApi2.id); + await teamsRepositoryFixture.delete(org.id); await app.close(); }); }); @@ -214,7 +239,6 @@ describe("Organizations Team Endpoints", () => { let app: INestApplication; let userRepositoryFixture: UserRepositoryFixture; - let organizationsRepositoryFixture: TeamRepositoryFixture; let teamsRepositoryFixture: TeamRepositoryFixture; let membershipsRepositoryFixture: MembershipRepositoryFixture; @@ -234,7 +258,6 @@ describe("Organizations Team Endpoints", () => { ).compile(); userRepositoryFixture = new UserRepositoryFixture(moduleRef); - organizationsRepositoryFixture = new TeamRepositoryFixture(moduleRef); teamsRepositoryFixture = new TeamRepositoryFixture(moduleRef); membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef); @@ -243,7 +266,7 @@ describe("Organizations Team Endpoints", () => { username: userEmail, }); - org = await organizationsRepositoryFixture.create({ + org = await teamsRepositoryFixture.create({ name: "Test Organization", isOrganization: true, }); @@ -274,7 +297,7 @@ describe("Organizations Team Endpoints", () => { it("should be defined", () => { expect(userRepositoryFixture).toBeDefined(); - expect(organizationsRepositoryFixture).toBeDefined(); + expect(teamsRepositoryFixture).toBeDefined(); expect(user).toBeDefined(); expect(org).toBeDefined(); }); @@ -317,7 +340,7 @@ describe("Organizations Team Endpoints", () => { await userRepositoryFixture.deleteByEmail(user.email); await teamsRepositoryFixture.delete(team.id); await teamsRepositoryFixture.delete(team2.id); - await organizationsRepositoryFixture.delete(org.id); + await teamsRepositoryFixture.delete(org.id); await app.close(); }); }); @@ -448,3 +471,158 @@ describe("Organizations Team Endpoints", () => { }); }); }); + +describe("Organizations Team Endpoints", () => { + describe("Platform teams", () => { + let app: INestApplication; + + let userRepositoryFixture: UserRepositoryFixture; + let teamsRepositoryFixture: TeamRepositoryFixture; + let membershipsRepositoryFixture: MembershipRepositoryFixture; + let oauthClientRepositoryFixture: OAuthClientRepositoryFixture; + + let oAuthClient1: PlatformOAuthClient; + let oAuthClient2: PlatformOAuthClient; + let org: Team; + let team1: Team; + let team2: Team; + + const userEmail = "platform-org-member-teams-owner-controller-e2e@api.com"; + let user: User; + + beforeAll(async () => { + const moduleRef = await withApiAuth( + userEmail, + Test.createTestingModule({ + imports: [AppModule, PrismaModule, UsersModule, TokensModule], + }) + ).compile(); + + userRepositoryFixture = new UserRepositoryFixture(moduleRef); + teamsRepositoryFixture = new TeamRepositoryFixture(moduleRef); + membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef); + oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef); + + user = await userRepositoryFixture.create({ + email: userEmail, + username: userEmail, + }); + + org = await teamsRepositoryFixture.create({ + name: "Platform Test Organization", + isOrganization: true, + }); + + await membershipsRepositoryFixture.create({ + role: "ADMIN", + user: { connect: { id: user.id } }, + team: { connect: { id: org.id } }, + }); + + oAuthClient1 = await createOAuthClient(org.id); + oAuthClient2 = await createOAuthClient(org.id); + + app = moduleRef.createNestApplication(); + bootstrap(app as NestExpressApplication); + + await app.init(); + }); + + async function createOAuthClient(organizationId: number) { + const data = { + logo: "logo-url", + name: "name", + redirectUris: ["redirect-uri"], + permissions: 32, + }; + const secret = "secret"; + + return await oauthClientRepositoryFixture.create(organizationId, data, secret); + } + + it("should be defined", () => { + expect(userRepositoryFixture).toBeDefined(); + expect(teamsRepositoryFixture).toBeDefined(); + expect(user).toBeDefined(); + expect(org).toBeDefined(); + }); + + it("should create first oAuth client team", async () => { + const teamName = "Platform team created via API"; + return request(app.getHttpServer()) + .post(`/v2/organizations/${org.id}/teams`) + .set(X_CAL_CLIENT_ID, oAuthClient1.id) + .send({ + name: teamName, + } satisfies CreateOrgTeamDto) + .expect(201) + .then(async (response) => { + const responseBody: ApiSuccessResponse = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + team1 = responseBody.data; + expect(team1.name).toEqual(teamName); + expect(team1.parentId).toEqual(org.id); + + const membership = await membershipsRepositoryFixture.getUserMembershipByTeamId(user.id, team1.id); + + expect(membership?.role ?? "").toEqual("OWNER"); + expect(membership?.accepted).toEqual(true); + }); + }); + + it("should create second oAuth client team", async () => { + const teamName = "Platform team II created via API"; + return request(app.getHttpServer()) + .post(`/v2/organizations/${org.id}/teams`) + .set(X_CAL_CLIENT_ID, oAuthClient2.id) + .set(X_CAL_SECRET_KEY, oAuthClient2.secret) + .send({ + name: teamName, + } satisfies CreateOrgTeamDto) + .expect(201) + .then(async (response) => { + const responseBody: ApiSuccessResponse = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + team2 = responseBody.data; + expect(team2.name).toEqual(teamName); + expect(team2.parentId).toEqual(org.id); + + const membership = await membershipsRepositoryFixture.getUserMembershipByTeamId(user.id, team2.id); + + expect(membership?.role ?? "").toEqual("OWNER"); + expect(membership?.accepted).toEqual(true); + }); + }); + + it("should get all the platform teams correctly tied to OAuth clients", async () => { + return request(app.getHttpServer()) + .get(`/v2/organizations/${org.id}/teams`) + .expect(200) + .then(async (response) => { + const responseBody: ApiSuccessResponse = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data[0].id).toEqual(team1.id); + expect(responseBody.data[1].id).toEqual(team2.id); + + const oAuthClientTeams = await teamsRepositoryFixture.getPlatformOrgTeams(org.id, oAuthClient1.id); + expect(oAuthClientTeams.length).toEqual(1); + const oAuthClientTeam = oAuthClientTeams[0]; + expect(oAuthClientTeam.id).toEqual(team1.id); + expect(oAuthClientTeam.name).toEqual(team1.name); + + const oAuthClient2Teams = await teamsRepositoryFixture.getPlatformOrgTeams(org.id, oAuthClient2.id); + expect(oAuthClient2Teams.length).toEqual(1); + const oAuthClientTeam2 = oAuthClient2Teams[0]; + expect(oAuthClientTeam2.id).toEqual(team2.id); + expect(oAuthClientTeam2.name).toEqual(team2.name); + }); + }); + + afterAll(async () => { + await userRepositoryFixture.deleteByEmail(user.email); + await teamsRepositoryFixture.delete(team1.id); + await teamsRepositoryFixture.delete(org.id); + await app.close(); + }); + }); +}); diff --git a/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts b/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts index e672244694..87fc4a7049 100644 --- a/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts +++ b/apps/api/v2/src/modules/organizations/controllers/teams/organizations-teams.controller.ts @@ -9,6 +9,7 @@ import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard"; import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; import { IsTeamInOrg } from "@/modules/auth/guards/teams/is-team-in-org.guard"; import { CreateOrgTeamDto } from "@/modules/organizations/inputs/create-organization-team.input"; +import { UpdateOrgTeamDto } from "@/modules/organizations/inputs/update-organization-team.input"; import { OrgMeTeamOutputDto, OrgMeTeamsOutputResponseDto, @@ -28,11 +29,12 @@ import { Patch, Post, Body, + Headers, } from "@nestjs/common"; import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; import { plainToClass } from "class-transformer"; -import { SUCCESS_STATUS } from "@calcom/platform-constants"; +import { SUCCESS_STATUS, X_CAL_CLIENT_ID } from "@calcom/platform-constants"; import { OrgTeamOutputDto } from "@calcom/platform-types"; import { SkipTakePagination } from "@calcom/platform-types"; import { Team } from "@calcom/prisma/client"; @@ -126,7 +128,7 @@ export class OrganizationsTeamsController { async updateTeam( @Param("orgId", ParseIntPipe) orgId: number, @Param("teamId", ParseIntPipe) teamId: number, - @Body() body: CreateOrgTeamDto + @Body() body: UpdateOrgTeamDto ): Promise { const team = await this.organizationsTeamsService.updateOrgTeam(orgId, teamId, body); return { @@ -142,9 +144,13 @@ export class OrganizationsTeamsController { async createTeam( @Param("orgId", ParseIntPipe) orgId: number, @Body() body: CreateOrgTeamDto, - @GetUser() user: UserWithProfile + @GetUser() user: UserWithProfile, + @Headers(X_CAL_CLIENT_ID) oAuthClientId?: string ): Promise { - const team = await this.organizationsTeamsService.createOrgTeam(orgId, body, user); + const team = oAuthClientId + ? await this.organizationsTeamsService.createPlatformOrgTeam(orgId, oAuthClientId, body, user) + : await this.organizationsTeamsService.createOrgTeam(orgId, body, user); + return { status: SUCCESS_STATUS, data: plainToClass(OrgTeamOutputDto, team, { strategy: "excludeAll" }), diff --git a/apps/api/v2/src/modules/organizations/controllers/users/organizations-users.e2e-spec.ts b/apps/api/v2/src/modules/organizations/controllers/users/organizations-users.e2e-spec.ts index 2dad8a9e5b..0a977de9c3 100644 --- a/apps/api/v2/src/modules/organizations/controllers/users/organizations-users.e2e-spec.ts +++ b/apps/api/v2/src/modules/organizations/controllers/users/organizations-users.e2e-spec.ts @@ -8,6 +8,7 @@ import { INestApplication } from "@nestjs/common"; import { NestExpressApplication } from "@nestjs/platform-express"; import { Test } from "@nestjs/testing"; import * as request from "supertest"; +import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture"; import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture"; import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture"; @@ -15,7 +16,7 @@ import { UserRepositoryFixture } from "test/fixtures/repository/users.repository import { withApiAuth } from "test/utils/withApiAuth"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import { User, Team, Membership } from "@calcom/prisma/client"; +import { User, Team, EventType } from "@calcom/prisma/client"; describe("Organizations Users Endpoints", () => { describe("Member role", () => { @@ -29,7 +30,6 @@ describe("Organizations Users Endpoints", () => { const userEmail = "member1@org.com"; let user: User; let org: Team; - let membership: Membership; beforeAll(async () => { const moduleRef = await withApiAuth( @@ -70,7 +70,7 @@ describe("Organizations Users Endpoints", () => { }, }); - membership = await membershipFixtures.addUserToOrg(user, org, "MEMBER", true); + await membershipFixtures.addUserToOrg(user, org, "MEMBER", true); app = moduleRef.createNestApplication(); bootstrap(app as NestExpressApplication); @@ -344,4 +344,165 @@ describe("Organizations Users Endpoints", () => { await app.close(); }); }); + + describe("Member event-types", () => { + let app: INestApplication; + + let userRepositoryFixture: UserRepositoryFixture; + let organizationsRepositoryFixture: TeamRepositoryFixture; + let eventTypesRepositoryFixture: EventTypesRepositoryFixture; + let membershipFixtures: MembershipRepositoryFixture; + let profileRepositoryFixture: ProfileRepositoryFixture; + + const authEmail = "auth@org.com"; + let user: User; + let org: Team; + let team: Team; + let managedEventType: EventType; + let createdUser: User; + + beforeAll(async () => { + const moduleRef = await withApiAuth( + authEmail, + Test.createTestingModule({ + imports: [AppModule, PrismaModule, UsersModule, TokensModule], + }) + ).compile(); + + userRepositoryFixture = new UserRepositoryFixture(moduleRef); + organizationsRepositoryFixture = new TeamRepositoryFixture(moduleRef); + eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef); + membershipFixtures = new MembershipRepositoryFixture(moduleRef); + profileRepositoryFixture = new ProfileRepositoryFixture(moduleRef); + + org = await organizationsRepositoryFixture.create({ + name: "Test org 4", + isOrganization: true, + }); + + team = await organizationsRepositoryFixture.create({ + name: "Test org 4 team", + isOrganization: false, + parent: { connect: { id: org.id } }, + }); + + user = await userRepositoryFixture.create({ + email: authEmail, + username: authEmail, + organization: { connect: { id: org.id } }, + }); + + await profileRepositoryFixture.create({ + uid: `usr-${user.id}`, + username: authEmail, + organization: { + connect: { + id: org.id, + }, + }, + user: { + connect: { + id: user.id, + }, + }, + }); + + await membershipFixtures.addUserToOrg(user, org, "ADMIN", true); + + await eventTypesRepositoryFixture.createTeamEventType({ + schedulingType: "COLLECTIVE", + team: { + connect: { id: team.id }, + }, + title: "Collective Event Type", + slug: "collective-event-type", + length: 30, + assignAllTeamMembers: true, + bookingFields: [], + locations: [], + }); + + managedEventType = await eventTypesRepositoryFixture.createTeamEventType({ + schedulingType: "MANAGED", + team: { + connect: { id: team.id }, + }, + title: "Managed Event Type", + slug: "managed-event-type", + length: 60, + assignAllTeamMembers: true, + bookingFields: [], + locations: [], + }); + + app = moduleRef.createNestApplication(); + bootstrap(app as NestExpressApplication); + + await app.init(); + }); + + it("should be defined", () => { + expect(userRepositoryFixture).toBeDefined(); + expect(organizationsRepositoryFixture).toBeDefined(); + expect(user).toBeDefined(); + expect(org).toBeDefined(); + }); + + it("should create a new org user with team event-types", async () => { + const newOrgUser = { + email: "new-org-member-d@org.com", + organizationRole: "MEMBER", + autoAccept: true, + }; + + const { body } = await request(app.getHttpServer()) + .post(`/v2/organizations/${org.id}/users`) + .send({ + email: newOrgUser.email, + }) + .set("Content-Type", "application/json") + .set("Accept", "application/json"); + + const userData = body.data; + expect(body.status).toBe(SUCCESS_STATUS); + userHasCorrectEventTypes(userData.id); + createdUser = userData; + teamHasCorrectEventTypes(team.id); + }); + + async function userHasCorrectEventTypes(userId: number) { + const eventTypes = await eventTypesRepositoryFixture.getAllUserEventTypes(userId); + + expect(eventTypes?.length).toEqual(1); + + // note(Lauris): managed event-types with assignAllTeamMembers: true + expect(eventTypes?.find((eventType) => eventType.slug === managedEventType.slug)).toBeTruthy(); + } + + async function teamHasCorrectEventTypes(teamId: number) { + const eventTypes = await eventTypesRepositoryFixture.getAllTeamEventTypes(teamId); + + expect(eventTypes?.length).toEqual(2); + + // note(Lauris): managed event-types with assignAllTeamMembers: true + expect(eventTypes?.find((eventType) => eventType.slug === managedEventType.slug)).toBeTruthy(); + + // note(Lauris): check if managed user added to collective event-type hosts given that it has assignAllTeamMembers: true + const collective = eventTypes?.find((eventType) => eventType.schedulingType === "COLLECTIVE"); + expect(collective).toBeTruthy(); + expect(collective?.hosts).toBeDefined(); + expect(collective?.hosts?.length).toEqual(1); + expect(collective?.hosts[0].userId).toEqual(createdUser.id); + } + + afterAll(async () => { + // await membershipFixtures.delete(membership.id); + await userRepositoryFixture.deleteByEmail(user.email); + await userRepositoryFixture.deleteByEmail(createdUser.email); + await organizationsRepositoryFixture.delete(org.id); + await app.close(); + + await app.close(); + }); + }); }); diff --git a/apps/api/v2/src/modules/organizations/inputs/create-organization-team.input.ts b/apps/api/v2/src/modules/organizations/inputs/create-organization-team.input.ts index 2167b95c50..6c454dce6e 100644 --- a/apps/api/v2/src/modules/organizations/inputs/create-organization-team.input.ts +++ b/apps/api/v2/src/modules/organizations/inputs/create-organization-team.input.ts @@ -72,4 +72,8 @@ export class CreateOrgTeamDto { @IsOptional() @IsString() readonly weekStart?: string = "Sunday"; + + @IsOptional() + @IsBoolean() + readonly autoAcceptCreator?: boolean = true; } diff --git a/apps/api/v2/src/modules/organizations/inputs/update-organization-team.input.ts b/apps/api/v2/src/modules/organizations/inputs/update-organization-team.input.ts new file mode 100644 index 0000000000..fdaa201111 --- /dev/null +++ b/apps/api/v2/src/modules/organizations/inputs/update-organization-team.input.ts @@ -0,0 +1,75 @@ +import { IsBoolean, IsOptional, IsString, IsUrl, Length } from "class-validator"; + +export class UpdateOrgTeamDto { + @IsString() + @Length(1) + readonly name?: string; + + @IsOptional() + @IsString() + readonly slug?: string; + + @IsOptional() + @IsUrl() + readonly logoUrl?: string; + + @IsOptional() + @IsUrl() + readonly calVideoLogo?: string; + + @IsOptional() + @IsUrl() + readonly appLogo?: string; + + @IsOptional() + @IsUrl() + readonly appIconLogo?: string; + + @IsOptional() + @IsString() + readonly bio?: string; + + @IsOptional() + @IsBoolean() + readonly hideBranding?: boolean = false; + + @IsOptional() + @IsBoolean() + readonly isPrivate?: boolean; + + @IsOptional() + @IsBoolean() + readonly hideBookATeamMember?: boolean; + + @IsOptional() + @IsString() + readonly metadata?: string; // Assuming metadata is a JSON string. Adjust accordingly if it's a nested object. + + @IsOptional() + @IsString() + readonly theme?: string; + + @IsOptional() + @IsString() + readonly brandColor?: string; + + @IsOptional() + @IsString() + readonly darkBrandColor?: string; + + @IsOptional() + @IsUrl() + readonly bannerUrl?: string; + + @IsOptional() + @IsString() + readonly timeFormat?: number; + + @IsOptional() + @IsString() + readonly timeZone?: string = "Europe/London"; + + @IsOptional() + @IsString() + readonly weekStart?: string = "Sunday"; +} diff --git a/apps/api/v2/src/modules/organizations/repositories/organizations-teams.repository.ts b/apps/api/v2/src/modules/organizations/repositories/organizations-teams.repository.ts index d5cbb97e19..b81489aa6b 100644 --- a/apps/api/v2/src/modules/organizations/repositories/organizations-teams.repository.ts +++ b/apps/api/v2/src/modules/organizations/repositories/organizations-teams.repository.ts @@ -1,4 +1,5 @@ import { CreateOrgTeamDto } from "@/modules/organizations/inputs/create-organization-team.input"; +import { UpdateOrgTeamDto } from "@/modules/organizations/inputs/update-organization-team.input"; import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; import { Injectable } from "@nestjs/common"; @@ -16,6 +17,14 @@ export class OrganizationsTeamsRepository { }); } + async findOrgTeams(organizationId: number) { + return this.dbRead.prisma.team.findMany({ + where: { + parentId: organizationId, + }, + }); + } + async deleteOrgTeam(organizationId: number, teamId: number) { return this.dbRead.prisma.team.delete({ where: { @@ -32,7 +41,26 @@ export class OrganizationsTeamsRepository { }); } - async updateOrgTeam(organizationId: number, teamId: number, data: CreateOrgTeamDto) { + async createPlatformOrgTeam(organizationId: number, oAuthClientId: string, data: CreateOrgTeamDto) { + return this.dbRead.prisma.team.create({ + data: { + ...data, + parentId: organizationId, + createdByOAuthClientId: oAuthClientId, + }, + }); + } + + async getPlatformOrgTeams(organizationId: number, oAuthClientId: string) { + return this.dbRead.prisma.team.findMany({ + where: { + parentId: organizationId, + createdByOAuthClientId: oAuthClientId, + }, + }); + } + + async updateOrgTeam(organizationId: number, teamId: number, data: UpdateOrgTeamDto) { return this.dbRead.prisma.team.update({ data: { ...data }, where: { id: teamId, parentId: organizationId, isOrganization: false }, diff --git a/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts b/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts index fabcdcdf6c..81a0b06101 100644 --- a/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts +++ b/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts @@ -9,7 +9,7 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { UserWithProfile } from "@/modules/users/users.repository"; import { Injectable, NotFoundException } from "@nestjs/common"; -import { createEventType, updateEventType } from "@calcom/platform-libraries-0.0.21"; +import { createEventType, updateEventType } from "@calcom/platform-libraries-0.0.22"; import { CreateTeamEventTypeInput_2024_06_14, UpdateTeamEventTypeInput_2024_06_14, diff --git a/apps/api/v2/src/modules/organizations/services/organizations-teams.service.ts b/apps/api/v2/src/modules/organizations/services/organizations-teams.service.ts index 8e9b7fc895..32036a0552 100644 --- a/apps/api/v2/src/modules/organizations/services/organizations-teams.service.ts +++ b/apps/api/v2/src/modules/organizations/services/organizations-teams.service.ts @@ -1,9 +1,12 @@ import { MembershipsRepository } from "@/modules/memberships/memberships.repository"; import { CreateOrgTeamDto } from "@/modules/organizations/inputs/create-organization-team.input"; +import { UpdateOrgTeamDto } from "@/modules/organizations/inputs/update-organization-team.input"; import { OrganizationsTeamsRepository } from "@/modules/organizations/repositories/organizations-teams.repository"; import { UserWithProfile } from "@/modules/users/users.repository"; import { Injectable } from "@nestjs/common"; +import { updateNewTeamMemberEventTypes } from "@calcom/platform-libraries-0.0.22"; + @Injectable() export class OrganizationsTeamsService { constructor( @@ -31,16 +34,58 @@ export class OrganizationsTeamsService { return team; } - async updateOrgTeam(organizationId: number, teamId: number, data: CreateOrgTeamDto) { + async updateOrgTeam(organizationId: number, teamId: number, data: UpdateOrgTeamDto) { const team = await this.organizationsTeamRepository.updateOrgTeam(organizationId, teamId, data); return team; } async createOrgTeam(organizationId: number, data: CreateOrgTeamDto, user: UserWithProfile) { - const team = await this.organizationsTeamRepository.createOrgTeam(organizationId, data); + const { autoAcceptCreator, ...rest } = data; + + const team = await this.organizationsTeamRepository.createOrgTeam(organizationId, rest); + if (user.role !== "ADMIN") { - await this.membershipsRepository.createMembership(team.id, user.id, "OWNER", true); + await this.membershipsRepository.createMembership(team.id, user.id, "OWNER", !!autoAcceptCreator); } return team; } + + async createPlatformOrgTeam( + organizationId: number, + oAuthClientId: string, + data: CreateOrgTeamDto, + user: UserWithProfile + ) { + const { autoAcceptCreator, ...rest } = data; + + const team = await this.organizationsTeamRepository.createPlatformOrgTeam( + organizationId, + oAuthClientId, + rest + ); + + if (user.role !== "ADMIN") { + await this.membershipsRepository.createMembership(team.id, user.id, "OWNER", !!autoAcceptCreator); + } + return team; + } + + async addUserToTeamEvents(userId: number, organizationId: number) { + const orgTeams = await this.organizationsTeamRepository.findOrgTeams(organizationId); + + for (const team of orgTeams) { + await updateNewTeamMemberEventTypes(userId, team.id); + } + } + + async addUserToPlatformTeamEvents(userId: number, organizationId: number, oAuthClientId: string) { + const oAuthClientTeams = await this.organizationsTeamRepository.getPlatformOrgTeams( + organizationId, + oAuthClientId + ); + + for (const team of oAuthClientTeams) { + await updateNewTeamMemberEventTypes(userId, team.id); + } + } } diff --git a/apps/api/v2/src/modules/organizations/services/organizations-users-service.ts b/apps/api/v2/src/modules/organizations/services/organizations-users-service.ts index f28ac9e21a..f689f3493a 100644 --- a/apps/api/v2/src/modules/organizations/services/organizations-users-service.ts +++ b/apps/api/v2/src/modules/organizations/services/organizations-users-service.ts @@ -2,17 +2,19 @@ import { EmailService } from "@/modules/email/email.service"; import { CreateOrganizationUserInput } from "@/modules/organizations/inputs/create-organization-user.input"; import { UpdateOrganizationUserInput } from "@/modules/organizations/inputs/update-organization-user.input"; import { OrganizationsUsersRepository } from "@/modules/organizations/repositories/organizations-users.repository"; +import { OrganizationsTeamsService } from "@/modules/organizations/services/organizations-teams.service"; import { CreateUserInput } from "@/modules/users/inputs/create-user.input"; import { Injectable, ConflictException } from "@nestjs/common"; import { plainToInstance } from "class-transformer"; -import { createNewUsersConnectToOrgIfExists } from "@calcom/platform-libraries-0.0.21"; +import { createNewUsersConnectToOrgIfExists } from "@calcom/platform-libraries-0.0.22"; import { Team } from "@calcom/prisma/client"; @Injectable() export class OrganizationsUsersService { constructor( private readonly organizationsUsersRepository: OrganizationsUsersRepository, + private readonly organizationsTeamsService: OrganizationsTeamsService, private readonly emailService: EmailService ) {} @@ -77,6 +79,8 @@ export class OrganizationsUsersService { updateUserBody ); + await this.organizationsTeamsService.addUserToTeamEvents(user.id, org.id); + // Need to send email to new user to create password await this.emailService.sendSignupToOrganizationEmail({ usernameOrEmail, diff --git a/apps/api/v2/src/modules/slots/controllers/slots.controller.ts b/apps/api/v2/src/modules/slots/controllers/slots.controller.ts index 604bf23c55..7ac775e648 100644 --- a/apps/api/v2/src/modules/slots/controllers/slots.controller.ts +++ b/apps/api/v2/src/modules/slots/controllers/slots.controller.ts @@ -5,8 +5,8 @@ import { ApiTags as DocsTags } from "@nestjs/swagger"; import { Response as ExpressResponse, Request as ExpressRequest } from "express"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import { getAvailableSlots } from "@calcom/platform-libraries-0.0.21"; -import type { AvailableSlotsType } from "@calcom/platform-libraries-0.0.21"; +import { getAvailableSlots } from "@calcom/platform-libraries-0.0.22"; +import type { AvailableSlotsType } from "@calcom/platform-libraries-0.0.22"; import { RemoveSelectedSlotInput, ReserveSlotInput } from "@calcom/platform-types"; import { ApiResponse, GetAvailableSlotsInput } from "@calcom/platform-types"; diff --git a/apps/api/v2/src/modules/slots/slots.repository.ts b/apps/api/v2/src/modules/slots/slots.repository.ts index 0181eebc8b..a2cbaf833e 100644 --- a/apps/api/v2/src/modules/slots/slots.repository.ts +++ b/apps/api/v2/src/modules/slots/slots.repository.ts @@ -3,7 +3,7 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { Injectable } from "@nestjs/common"; import { DateTime } from "luxon"; -import { MINUTES_TO_BOOK } from "@calcom/platform-libraries-0.0.21"; +import { MINUTES_TO_BOOK } from "@calcom/platform-libraries-0.0.22"; import { ReserveSlotInput } from "@calcom/platform-types"; @Injectable() diff --git a/apps/api/v2/src/modules/timezones/controllers/timezones.controller.ts b/apps/api/v2/src/modules/timezones/controllers/timezones.controller.ts index ecbbb9132e..f15231851e 100644 --- a/apps/api/v2/src/modules/timezones/controllers/timezones.controller.ts +++ b/apps/api/v2/src/modules/timezones/controllers/timezones.controller.ts @@ -4,7 +4,7 @@ import { Controller, Get } from "@nestjs/common"; import { ApiTags as DocsTags } from "@nestjs/swagger"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import type { CityTimezones } from "@calcom/platform-libraries-0.0.21"; +import type { CityTimezones } from "@calcom/platform-libraries-0.0.22"; import { ApiResponse } from "@calcom/platform-types"; @Controller({ diff --git a/apps/api/v2/src/modules/timezones/services/timezones.service.ts b/apps/api/v2/src/modules/timezones/services/timezones.service.ts index a885219b1f..8b9889106e 100644 --- a/apps/api/v2/src/modules/timezones/services/timezones.service.ts +++ b/apps/api/v2/src/modules/timezones/services/timezones.service.ts @@ -1,8 +1,8 @@ import { RedisService } from "@/modules/redis/redis.service"; import { Injectable } from "@nestjs/common"; -import { cityTimezonesHandler } from "@calcom/platform-libraries-0.0.21"; -import type { CityTimezones } from "@calcom/platform-libraries-0.0.21"; +import { cityTimezonesHandler } from "@calcom/platform-libraries-0.0.22"; +import type { CityTimezones } from "@calcom/platform-libraries-0.0.22"; @Injectable() export class TimezonesService { diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index c821991eef..1cc0411be5 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -962,6 +962,14 @@ "schema": { "type": "number" } + }, + { + "name": "x-cal-client-id", + "required": true, + "in": "header", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -1126,7 +1134,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateOrgTeamDto" + "$ref": "#/components/schemas/UpdateOrgTeamDto" } } } @@ -5224,6 +5232,69 @@ "data" ] }, + "UpdateOrgTeamDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "slug": { + "type": "string" + }, + "logoUrl": { + "type": "string" + }, + "calVideoLogo": { + "type": "string" + }, + "appLogo": { + "type": "string" + }, + "appIconLogo": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "hideBranding": { + "type": "boolean", + "default": false + }, + "isPrivate": { + "type": "boolean" + }, + "hideBookATeamMember": { + "type": "boolean" + }, + "metadata": { + "type": "string" + }, + "theme": { + "type": "string" + }, + "brandColor": { + "type": "string" + }, + "darkBrandColor": { + "type": "string" + }, + "bannerUrl": { + "type": "string" + }, + "timeFormat": { + "type": "number" + }, + "timeZone": { + "type": "string", + "default": "Europe/London" + }, + "weekStart": { + "type": "string", + "default": "Sunday" + } + } + }, "CreateOrgTeamDto": { "type": "object", "properties": { @@ -5284,6 +5355,10 @@ "weekStart": { "type": "string", "default": "Sunday" + }, + "autoAcceptCreator": { + "type": "boolean", + "default": true } }, "required": [ diff --git a/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts index d802800a64..b033bcec3a 100644 --- a/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts +++ b/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts @@ -4,6 +4,8 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { TestingModule } from "@nestjs/testing"; import { EventType } from "@prisma/client"; +import { Prisma } from "@calcom/prisma/client"; + export class EventTypesRepositoryFixture { private primaReadClient: PrismaReadService["prisma"]; private prismaWriteClient: PrismaWriteService["prisma"]; @@ -26,6 +28,9 @@ export class EventTypesRepositoryFixture { where: { teamId, }, + include: { + hosts: true, + }, }); } @@ -41,6 +46,10 @@ export class EventTypesRepositoryFixture { }); } + async createTeamEventType(data: Prisma.EventTypeCreateInput) { + return this.prismaWriteClient.eventType.create({ data }); + } + async delete(eventTypeId: EventType["id"]) { return this.prismaWriteClient.eventType.delete({ where: { id: eventTypeId } }); } diff --git a/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts index 1d05908731..16f60078ae 100644 --- a/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts +++ b/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts @@ -23,4 +23,13 @@ export class TeamRepositoryFixture { async delete(teamId: Team["id"]) { return this.prismaWriteClient.team.delete({ where: { id: teamId } }); } + + async getPlatformOrgTeams(organizationId: number, oAuthClientId: string) { + return this.primaReadClient.team.findMany({ + where: { + parentId: organizationId, + createdByOAuthClientId: oAuthClientId, + }, + }); + } } diff --git a/apps/web/lib/orgMigration.test.ts b/apps/web/lib/orgMigration.test.ts index 81b713a39a..ad3d71cfaa 100644 --- a/apps/web/lib/orgMigration.test.ts +++ b/apps/web/lib/orgMigration.test.ts @@ -1574,6 +1574,7 @@ async function createOrg( metadata: { ...(data.metadata || {}), }, + createdByOAuthClient: undefined, }, }); } @@ -1591,6 +1592,7 @@ async function createTeamOutsideOrg( ...(data.metadata || {}), isOrganization: false, }, + createdByOAuthClient: undefined, }, }); } diff --git a/packages/platform/libraries/CHANGELOG.md b/packages/platform/libraries/CHANGELOG.md index 4bb2717858..3a558cff6f 100644 --- a/packages/platform/libraries/CHANGELOG.md +++ b/packages/platform/libraries/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.22 +Export `updateNewTeamMemberEventTypes` from `"@calcom/lib/server/queries"` so that we can assign newly created organizations +teams members to event-types that have been marked as "assign all team members" + ## 0.0.20 In event-types create handler (packages/trpc/server/routers/viewer/eventTypes/create.handler.ts) enable passing scheduleId so that when an event type is created it can be connected to a specific schedule. diff --git a/packages/platform/libraries/index.ts b/packages/platform/libraries/index.ts index 830b7f893d..677739e04e 100644 --- a/packages/platform/libraries/index.ts +++ b/packages/platform/libraries/index.ts @@ -109,3 +109,5 @@ export { CalendarService }; export { getCalendar }; export { getTranslation }; + +export { updateNewTeamMemberEventTypes } from "@calcom/lib/server/queries"; diff --git a/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts b/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts index 3ee014b583..0c131832e7 100644 --- a/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts +++ b/packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts @@ -1,5 +1,5 @@ import { ApiProperty as DocsProperty } from "@nestjs/swagger"; -import { Type } from "class-transformer"; +import { Type, Transform } from "class-transformer"; import { IsString, IsInt, @@ -148,6 +148,18 @@ export class CreateTeamEventTypeInput_2024_06_14 { @DocsProperty() afterEventBuffer?: number; + @Transform(({ value }) => { + if (value === "collective") { + return SchedulingType.COLLECTIVE; + } + if (value === "roundRobin") { + return SchedulingType.ROUND_ROBIN; + } + if (value === "managed") { + return SchedulingType.MANAGED; + } + return value; + }) @IsEnum(SchedulingType) @DocsProperty() schedulingType!: keyof typeof SchedulingType; diff --git a/packages/prisma/migrations/20240730122536_add_created_by_oauth_client_to_org_teams/migration.sql b/packages/prisma/migrations/20240730122536_add_created_by_oauth_client_to_org_teams/migration.sql new file mode 100644 index 0000000000..13227fcdf0 --- /dev/null +++ b/packages/prisma/migrations/20240730122536_add_created_by_oauth_client_to_org_teams/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Team" ADD COLUMN "createdByOAuthClientId" TEXT; + +-- AddForeignKey +ALTER TABLE "Team" ADD CONSTRAINT "Team_createdByOAuthClientId_fkey" FOREIGN KEY ("createdByOAuthClientId") REFERENCES "PlatformOAuthClient"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/prisma/migrations/20240730142744_delete_platform_team_when_oauth_client_deleted/migration.sql b/packages/prisma/migrations/20240730142744_delete_platform_team_when_oauth_client_deleted/migration.sql new file mode 100644 index 0000000000..e154306030 --- /dev/null +++ b/packages/prisma/migrations/20240730142744_delete_platform_team_when_oauth_client_deleted/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "Team" DROP CONSTRAINT "Team_createdByOAuthClientId_fkey"; + +-- AddForeignKey +ALTER TABLE "Team" ADD CONSTRAINT "Team_createdByOAuthClientId_fkey" FOREIGN KEY ("createdByOAuthClientId") REFERENCES "PlatformOAuthClient"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 416bc1e48d..f7ece8c1da 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -359,55 +359,59 @@ model Profile { } model Team { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) /// @zod.min(1) - name String + name String /// @zod.min(1) - slug String? - logoUrl String? - calVideoLogo String? - appLogo String? - appIconLogo String? - bio String? - hideBranding Boolean @default(false) - isPrivate Boolean @default(false) - hideBookATeamMember Boolean @default(false) - members Membership[] - eventTypes EventType[] - workflows Workflow[] - createdAt DateTime @default(now()) + slug String? + logoUrl String? + calVideoLogo String? + appLogo String? + appIconLogo String? + bio String? + hideBranding Boolean @default(false) + isPrivate Boolean @default(false) + hideBookATeamMember Boolean @default(false) + members Membership[] + eventTypes EventType[] + workflows Workflow[] + createdAt DateTime @default(now()) /// @zod.custom(imports.teamMetadataSchema) - metadata Json? - theme String? - brandColor String? - darkBrandColor String? - verifiedNumbers VerifiedNumber[] - verifiedEmails VerifiedEmail[] - bannerUrl String? - parentId Int? - parent Team? @relation("organization", fields: [parentId], references: [id], onDelete: Cascade) - children Team[] @relation("organization") - orgUsers User[] @relation("scope") - inviteTokens VerificationToken[] - webhooks Webhook[] - timeFormat Int? - timeZone String @default("Europe/London") - weekStart String @default("Sunday") - routingForms App_RoutingForms_Form[] - apiKeys ApiKey[] - credentials Credential[] - accessCodes AccessCode[] - isOrganization Boolean @default(false) - organizationSettings OrganizationSettings? - instantMeetingTokens InstantMeetingToken[] - orgProfiles Profile[] - pendingPayment Boolean @default(false) - dsyncTeamGroupMapping DSyncTeamGroupMapping[] - isPlatform Boolean @default(false) - platformOAuthClient PlatformOAuthClient[] - smsLockState SMSLockState @default(UNLOCKED) - platformBilling PlatformBilling? - activeOrgWorkflows WorkflowsOnTeams[] + metadata Json? + theme String? + brandColor String? + darkBrandColor String? + verifiedNumbers VerifiedNumber[] + verifiedEmails VerifiedEmail[] + bannerUrl String? + parentId Int? + parent Team? @relation("organization", fields: [parentId], references: [id], onDelete: Cascade) + children Team[] @relation("organization") + orgUsers User[] @relation("scope") + inviteTokens VerificationToken[] + webhooks Webhook[] + timeFormat Int? + timeZone String @default("Europe/London") + weekStart String @default("Sunday") + routingForms App_RoutingForms_Form[] + apiKeys ApiKey[] + credentials Credential[] + accessCodes AccessCode[] + isOrganization Boolean @default(false) + organizationSettings OrganizationSettings? + instantMeetingTokens InstantMeetingToken[] + orgProfiles Profile[] + pendingPayment Boolean @default(false) + dsyncTeamGroupMapping DSyncTeamGroupMapping[] + isPlatform Boolean @default(false) + // Organization's OAuth clients. Organization has them but a team does not. + platformOAuthClient PlatformOAuthClient[] + // OAuth client used to create team of an organization. Team has it but organization does not. + createdByOAuthClient PlatformOAuthClient? @relation("CreatedByOAuthClient", fields: [createdByOAuthClientId], references: [id], onDelete: Cascade) + createdByOAuthClientId String? + smsLockState SMSLockState @default(UNLOCKED) + platformBilling PlatformBilling? + activeOrgWorkflows WorkflowsOnTeams[] @@unique([slug, parentId]) @@index([parentId]) @@ -1287,6 +1291,7 @@ model PlatformOAuthClient { redirectUris String[] organizationId Int organization Team @relation(fields: [organizationId], references: [id], onDelete: Cascade) + teams Team[] @relation("CreatedByOAuthClient") accessTokens AccessToken[] refreshToken RefreshToken[] diff --git a/yarn.lock b/yarn.lock index 2bb45cf3db..90278c4e5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4066,12 +4066,13 @@ __metadata: dependencies: "@calcom/platform-constants": "*" "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2" - "@calcom/platform-libraries-0.0.20": "npm:@calcom/platform-libraries@0.0.20" + "@calcom/platform-libraries-0.0.22": "npm:@calcom/platform-libraries@0.0.22" "@calcom/platform-types": "*" "@calcom/platform-utils": "*" "@calcom/prisma": "*" "@golevelup/ts-jest": ^0.4.0 "@microsoft/microsoft-graph-types-beta": ^0.42.0-preview + "@nestjs/bull": ^10.1.1 "@nestjs/cli": ^10.0.0 "@nestjs/common": ^10.0.0 "@nestjs/config": ^3.1.1 @@ -4093,6 +4094,7 @@ __metadata: "@types/passport-jwt": ^3.0.13 "@types/supertest": ^2.0.12 body-parser: ^1.20.2 + bull: ^4.12.4 class-transformer: ^0.5.1 class-validator: ^0.14.0 cookie-parser: ^1.4.6 @@ -4216,7 +4218,7 @@ __metadata: languageName: unknown linkType: soft -"@calcom/atoms@*, @calcom/atoms@workspace:^, @calcom/atoms@workspace:packages/platform/atoms": +"@calcom/atoms@*, @calcom/atoms@workspace:packages/platform/atoms": version: 0.0.0-use.local resolution: "@calcom/atoms@workspace:packages/platform/atoms" dependencies: @@ -5025,14 +5027,14 @@ __metadata: languageName: unknown linkType: soft -"@calcom/platform-libraries-0.0.20@npm:@calcom/platform-libraries@0.0.20": - version: 0.0.20 - resolution: "@calcom/platform-libraries@npm:0.0.20" +"@calcom/platform-libraries-0.0.22@npm:@calcom/platform-libraries@0.0.22": + version: 0.0.22 + resolution: "@calcom/platform-libraries@npm:0.0.22" dependencies: "@calcom/core": "*" "@calcom/features": "*" "@calcom/lib": "*" - checksum: f17e9bd51429a1a70240944b8f3836be41790a8c0eba34ddeaa7336c4f98503c0ff8c70c4d79b15809895c3af8462cbf10cff14ca4ba0453cc751febf4dd86cb + checksum: 137238600e8c19e5dcd77f340addb5e1284c5dee46b4dfb20c8fb37808251d1752a6c870120f394093c3d503987820c20a10b9a6f2bd4221fbf9109f71a08d87 languageName: node linkType: hard @@ -5673,7 +5675,6 @@ __metadata: dependencies: "@algora/sdk": ^0.1.2 "@calcom/app-store": "*" - "@calcom/atoms": "workspace:^" "@calcom/config": "*" "@calcom/dayjs": "*" "@calcom/embed-react": "workspace:^" @@ -5719,7 +5720,6 @@ __metadata: "@vercel/og": ^0.5.0 autoprefixer: ^10.4.12 bcryptjs: ^2.4.3 - class-variance-authority: ^0.7.0 clsx: ^1.2.1 cobe: ^0.4.1 concurrently: ^7.6.0 @@ -5732,7 +5732,6 @@ __metadata: env-cmd: ^10.1.0 eslint: ^8.34.0 fathom-client: ^3.5.0 - framer-motion: ^11.0.25 globby: ^13.1.3 graphql: ^16.8.0 graphql-codegen: ^0.4.0 @@ -5742,7 +5741,7 @@ __metadata: i18n-unused: ^0.13.0 iframe-resizer-react: ^1.1.0 keen-slider: ^6.8.0 - lucide-react: ^0.364.0 + lucide-react: ^0.171.0 micro: ^10.0.1 next: ^14.1.3 next-auth: ^4.22.1 @@ -5754,7 +5753,7 @@ __metadata: prism-react-renderer: ^1.3.5 react: ^18.2.0 react-confetti: ^6.0.1 - react-datocms: ^5.0.3 + react-datocms: ^3.1.0 react-device-detect: ^2.2.2 react-dom: ^18.2.0 react-fast-marquee: ^1.6.4 @@ -5764,10 +5763,8 @@ __metadata: react-live-chat-loader: ^2.8.1 react-markdown: ^9.0.1 react-merge-refs: 1.1.0 - react-parallax-tilt: ^1.7.226 react-resize-detector: ^9.1.0 react-twemoji: ^0.3.0 - react-twitter-embed: ^4.0.4 react-use-measure: ^2.1.1 react-wrap-balancer: ^1.0.0 remark: ^14.0.2 @@ -9497,6 +9494,48 @@ __metadata: languageName: node linkType: hard +"@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.3" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.3" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@mswjs/cookies@npm:^0.2.0": version: 0.2.1 resolution: "@mswjs/cookies@npm:0.2.1" @@ -9521,59 +9560,6 @@ __metadata: languageName: node linkType: hard -"@mux/mux-player-react@npm:*": - version: 2.8.0 - resolution: "@mux/mux-player-react@npm:2.8.0" - dependencies: - "@mux/mux-player": 2.8.0 - "@mux/playback-core": 0.25.0 - prop-types: ^15.7.2 - peerDependencies: - "@types/react": ^17.0.0 || ^18 - react: ^17.0.2 || ^18 - react-dom: ^17.0.2 || ^18 - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: b4891a151ff92bbee3b9d2e0411c21be07b976c30f6e13c30b30cda97eeb5d4a88bb3d78fd58dac447b05bb12563234a9a5fbba3bdf79e0c274575cf9bb020cd - languageName: node - linkType: hard - -"@mux/mux-player@npm:2.8.0": - version: 2.8.0 - resolution: "@mux/mux-player@npm:2.8.0" - dependencies: - "@mux/mux-video": 0.20.0 - "@mux/playback-core": 0.25.0 - media-chrome: ~3.2.3 - checksum: 6a8e410456a325c2e59ef3ebb4f3efba69596a4df3e38e9ccb332af23fe16c571a8271c51e97ea5f003352f09f63fdb311ff548c0b2f613b7ca4c47fa68fcade - languageName: node - linkType: hard - -"@mux/mux-video@npm:0.20.0": - version: 0.20.0 - resolution: "@mux/mux-video@npm:0.20.0" - dependencies: - "@mux/playback-core": 0.25.0 - castable-video: ~1.0.9 - custom-media-element: ~1.3.1 - media-tracks: ~0.3.2 - checksum: 2f316a6697ff63dbcc5428216600ced1db14e22c195567c69e826b29b2752b9aeee785f073bc2eef71d7422b696370608d9d081b8e24b5332dfeb57bf0839791 - languageName: node - linkType: hard - -"@mux/playback-core@npm:0.25.0": - version: 0.25.0 - resolution: "@mux/playback-core@npm:0.25.0" - dependencies: - hls.js: ~1.5.11 - mux-embed: ~5.2.0 - checksum: 37175985f7ec7df0b2f24e9b6e3df9c39f94da81b0f1e59b989dd9d04d9451dae2ea6e06d4208a673e532ea2351c39c2d2ec0e149cde60a9a09b9dbd00ea2866 - languageName: node - linkType: hard - "@ndelangen/get-tarball@npm:^3.0.7": version: 3.0.9 resolution: "@ndelangen/get-tarball@npm:3.0.9" @@ -9585,6 +9571,32 @@ __metadata: languageName: node linkType: hard +"@nestjs/bull-shared@npm:^10.1.1": + version: 10.1.1 + resolution: "@nestjs/bull-shared@npm:10.1.1" + dependencies: + tslib: 2.6.2 + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + "@nestjs/core": ^8.0.0 || ^9.0.0 || ^10.0.0 + checksum: 492c6bfd8d77ded163913aa8fced3c6a52f8b6b40ae2e35b722cde98d89d3a0136497233ea8d60436cc842107cb61fc1f2a5ddc3fa01dd85aa68f08a350f8c47 + languageName: node + linkType: hard + +"@nestjs/bull@npm:^10.1.1": + version: 10.1.1 + resolution: "@nestjs/bull@npm:10.1.1" + dependencies: + "@nestjs/bull-shared": ^10.1.1 + tslib: 2.6.2 + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + "@nestjs/core": ^8.0.0 || ^9.0.0 || ^10.0.0 + bull: ^3.3 || ^4.0.0 + checksum: 3f3cf1a119324f2e7721fedc3d1484a60ffb55381a2d291040953770e0aded825cd14ee2a2526e68929f79fee56053ae9ac0858f8a427cbb11dbffb73191be36 + languageName: node + linkType: hard + "@nestjs/cli@npm:^10.0.0": version: 10.3.2 resolution: "@nestjs/cli@npm:10.3.2" @@ -21178,6 +21190,21 @@ __metadata: languageName: node linkType: hard +"bull@npm:^4.12.4": + version: 4.15.1 + resolution: "bull@npm:4.15.1" + dependencies: + cron-parser: ^4.2.1 + get-port: ^5.1.1 + ioredis: ^5.3.2 + lodash: ^4.17.21 + msgpackr: ^1.10.1 + semver: ^7.5.2 + uuid: ^8.3.0 + checksum: 9ac743e32bd68d6f52ec0dcb3696ad4d81ecd994faddc1b6fe48ceb5047ffaae4e45da1211b3b68f6c1dfd719c84405cc132c611fccd0a92ef8c44217b81a82f + languageName: node + linkType: hard + "bundle-name@npm:^4.1.0": version: 4.1.0 resolution: "bundle-name@npm:4.1.0" @@ -21335,6 +21362,7 @@ __metadata: husky: ^8.0.0 i18n-unused: ^0.13.0 jest-diff: ^29.5.0 + jest-summarizing-reporter: ^1.1.4 jsdom: ^22.0.0 lint-staged: ^12.5.0 mailhog: ^4.16.0 @@ -21569,15 +21597,6 @@ __metadata: languageName: node linkType: hard -"castable-video@npm:~1.0.9": - version: 1.0.10 - resolution: "castable-video@npm:1.0.10" - dependencies: - custom-media-element: ~1.3.2 - checksum: 5b7a27aacf305f40c6867e96e773f4290260f6eb8b01fe1f209506f8f6dec4cd099f3e35ee57de098829e4bcc688ccdc2cbb2fc86c509712dd573eadf66d42cc - languageName: node - linkType: hard - "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -22042,15 +22061,6 @@ __metadata: languageName: node linkType: hard -"class-variance-authority@npm:^0.7.0": - version: 0.7.0 - resolution: "class-variance-authority@npm:0.7.0" - dependencies: - clsx: 2.0.0 - checksum: e7fd1fab433ef06f52a1b7b241b70b4a185864deef199d3b0a2c3412f1cc179517288264c383f3b971a00d76811625fc8f7ffe709e6170219e88cd7368f08a20 - languageName: node - linkType: hard - "classnames@npm:^2.2.5, classnames@npm:^2.2.6": version: 2.3.2 resolution: "classnames@npm:2.3.2" @@ -22323,13 +22333,6 @@ __metadata: languageName: node linkType: hard -"clsx@npm:2.0.0": - version: 2.0.0 - resolution: "clsx@npm:2.0.0" - checksum: a2cfb2351b254611acf92faa0daf15220f4cd648bdf96ce369d729813b85336993871a4bf6978ddea2b81b5a130478339c20d9d0b5c6fc287e5147f0c059276e - languageName: node - linkType: hard - "clsx@npm:^1.1.1": version: 1.1.1 resolution: "clsx@npm:1.1.1" @@ -23289,6 +23292,15 @@ __metadata: languageName: node linkType: hard +"cron-parser@npm:^4.2.1": + version: 4.9.0 + resolution: "cron-parser@npm:4.9.0" + dependencies: + luxon: ^3.2.1 + checksum: 3cf248fc5cae6c19ec7124962b1cd84b76f02b9bc4f58976b3bd07624db3ef10aaf1548efcc2d2dcdab0dad4f12029d640a55ecce05ea5e1596af9db585502cf + languageName: node + linkType: hard + "cross-env@npm:^7.0.3": version: 7.0.3 resolution: "cross-env@npm:7.0.3" @@ -23635,13 +23647,6 @@ __metadata: languageName: node linkType: hard -"custom-media-element@npm:~1.3.1, custom-media-element@npm:~1.3.2": - version: 1.3.2 - resolution: "custom-media-element@npm:1.3.2" - checksum: 9ec2ff88c3c5ba7d20b9484c0359b4cfe08ad6804f3116934ffbae2f2e0e04d2a832beb6f0bec56027a969ab366727d217406f750890613c988de6de5330cb4f - languageName: node - linkType: hard - "d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:^3.1.6": version: 3.2.3 resolution: "d3-array@npm:3.2.3" @@ -24334,6 +24339,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.1": + version: 2.0.3 + resolution: "detect-libc@npm:2.0.3" + checksum: 2ba6a939ae55f189aea996ac67afceb650413c7a34726ee92c40fb0deb2400d57ef94631a8a3f052055eea7efb0f99a9b5e6ce923415daa3e68221f963cfc27d + languageName: node + linkType: hard + "detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" @@ -27689,26 +27701,6 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:^11.0.25": - version: 11.3.12 - resolution: "framer-motion@npm:11.3.12" - dependencies: - tslib: ^2.4.0 - peerDependencies: - "@emotion/is-prop-valid": "*" - react: ^18.0.0 - react-dom: ^18.0.0 - peerDependenciesMeta: - "@emotion/is-prop-valid": - optional: true - react: - optional: true - react-dom: - optional: true - checksum: d13fa5fd2a21e73846a1171b8e6cad0be214b48f0c9b91070397975977758a98c66ea55d3e8dc027d959831027d76d0d8044cf41c49942cc3362a7b0cce7d4a0 - languageName: node - linkType: hard - "fresh@npm:0.5.2, fresh@npm:^0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -29381,13 +29373,6 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:~1.5.11": - version: 1.5.13 - resolution: "hls.js@npm:1.5.13" - checksum: 534b48b18638fefe7788fd31d92b0b4e3820271b470fbd18c29cd46d51e6381b6f011d732f6196d4cda3148c81d8cb7740e1d1d0f2da0031e808b468198142f3 - languageName: node - linkType: hard - "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -32080,6 +32065,13 @@ __metadata: languageName: node linkType: hard +"jest-summarizing-reporter@npm:^1.1.4": + version: 1.1.4 + resolution: "jest-summarizing-reporter@npm:1.1.4" + checksum: 46be3e78146a86ba2b75b3e85f17b854f0da42587bf90e664b35d1b56cd51607c3a431da2c3b6a0a1fe5b3d3429ea493aedb51d2d7ce615919643e7e7dea3cbe + languageName: node + linkType: hard + "jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" @@ -34208,6 +34200,15 @@ __metadata: languageName: node linkType: hard +"lucide-react@npm:^0.171.0": + version: 0.171.0 + resolution: "lucide-react@npm:0.171.0" + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + checksum: 768ffe368c52a518ee339203d86ff4479989ab4d79c0716f721900c4bb7392ef6ff7a14807f6a685abd74d27f4c1778170bff77a0ab4c3e06c17944b557d8300 + languageName: node + linkType: hard + "lucide-react@npm:^0.364.0": version: 0.364.0 resolution: "lucide-react@npm:0.364.0" @@ -34238,7 +34239,7 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.4.4": +"luxon@npm:^3.2.1, luxon@npm:^3.4.4": version: 3.4.4 resolution: "luxon@npm:3.4.4" checksum: 36c1f99c4796ee4bfddf7dc94fa87815add43ebc44c8934c924946260a58512f0fd2743a629302885df7f35ccbd2d13f178c15df046d0e3b6eb71db178f1c60c @@ -34777,20 +34778,6 @@ __metadata: languageName: node linkType: hard -"media-chrome@npm:~3.2.3": - version: 3.2.4 - resolution: "media-chrome@npm:3.2.4" - checksum: 6401f509f4075cd7d02f8a479e81bcb084cd06104aa17fd7ebe6623bd171eaa52e1e1333c1895ad84c1f794ea3889cca13db52dd49b64129d67dd8be558d6eb2 - languageName: node - linkType: hard - -"media-tracks@npm:~0.3.2": - version: 0.3.3 - resolution: "media-tracks@npm:0.3.3" - checksum: 4795af3f171d7ad3a68d1ac1c1e8166a735244fe57d3fc0ec53b1c7410799e524756fc0bfb389632aebb148436b03baade36ca34a3f5377776c3968f6d2cc580 - languageName: node - linkType: hard - "media-typer@npm:0.3.0": version: 0.3.0 resolution: "media-typer@npm:0.3.0" @@ -36062,6 +36049,49 @@ __metadata: languageName: node linkType: hard +"msgpackr-extract@npm:^3.0.2": + version: 3.0.3 + resolution: "msgpackr-extract@npm:3.0.3" + dependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": 3.0.3 + "@msgpackr-extract/msgpackr-extract-darwin-x64": 3.0.3 + "@msgpackr-extract/msgpackr-extract-linux-arm": 3.0.3 + "@msgpackr-extract/msgpackr-extract-linux-arm64": 3.0.3 + "@msgpackr-extract/msgpackr-extract-linux-x64": 3.0.3 + "@msgpackr-extract/msgpackr-extract-win32-x64": 3.0.3 + node-gyp: latest + node-gyp-build-optional-packages: 5.2.2 + dependenciesMeta: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-darwin-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-win32-x64": + optional: true + bin: + download-msgpackr-prebuilds: bin/download-prebuilds.js + checksum: 3b5ae152821feff843380f0b091afbebd80bd224e644f4410abd33d05da3159eb8b0d45c7dcf7d5226ce1d5c71cd68052f066788f46ea7a3cd8791a1c740a079 + languageName: node + linkType: hard + +"msgpackr@npm:^1.10.1": + version: 1.11.0 + resolution: "msgpackr@npm:1.11.0" + dependencies: + msgpackr-extract: ^3.0.2 + dependenciesMeta: + msgpackr-extract: + optional: true + checksum: 20a1941e1a281c10a1d43d04349ae7a79bef1cbd0644f1e5acef7f98f6a6e6cd83388ee82689799f9e65623591b11f849ef2f86c8eac83492449bee5a2b055b2 + languageName: node + linkType: hard + "mssql@npm:10.0.2": version: 10.0.2 resolution: "mssql@npm:10.0.2" @@ -36168,13 +36198,6 @@ __metadata: languageName: node linkType: hard -"mux-embed@npm:~5.2.0": - version: 5.2.1 - resolution: "mux-embed@npm:5.2.1" - checksum: ec34d3e003e2520be6d1386ca2383daa198a1fa3a469ca3f06c11a742d6ab136eef4944c37e8f2dcaa0585e1b0a6dfc119e3cf2302c7eb0e7f2ebc7c1fc95fbd - languageName: node - linkType: hard - "mysql2@npm:3.9.1": version: 3.9.1 resolution: "mysql2@npm:3.9.1" @@ -36867,6 +36890,19 @@ __metadata: languageName: node linkType: hard +"node-gyp-build-optional-packages@npm:5.2.2": + version: 5.2.2 + resolution: "node-gyp-build-optional-packages@npm:5.2.2" + dependencies: + detect-libc: ^2.0.1 + bin: + node-gyp-build-optional-packages: bin.js + node-gyp-build-optional-packages-optional: optional.js + node-gyp-build-optional-packages-test: build-test.js + checksum: 3c10d7380901ab5febcd153d2632917fe7507edb15a3405e9ef19801834a4c2162459a67b9944887f737f8718baeb4aaf0002c829a8214011930f2de80e4b42f + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 9.3.1 resolution: "node-gyp@npm:9.3.1" @@ -40483,21 +40519,20 @@ __metadata: languageName: node linkType: hard -"react-datocms@npm:^5.0.3": - version: 5.0.3 - resolution: "react-datocms@npm:5.0.3" +"react-datocms@npm:^3.1.0": + version: 3.1.4 + resolution: "react-datocms@npm:3.1.4" dependencies: - "@mux/mux-player-react": "*" datocms-listen: ^0.1.9 datocms-structured-text-generic-html-renderer: ^2.0.1 datocms-structured-text-utils: ^2.0.1 - react-intersection-observer: ^9.4.3 + react-intersection-observer: ^8.33.1 react-string-replace: ^1.1.0 universal-base64: ^2.1.0 use-deep-compare-effect: ^1.6.1 peerDependencies: react: ">= 16.12.0" - checksum: 22c20152afb54424acfe967a2c8c525cd9f132a33374f2aba0231f16ea64dade389b096e2dac8de9ffded612bc32e9891d725609ee947639fe1cef907cb143f5 + checksum: 54aba12aef4937175c2011548a8a576c96c8d8a596e84d191826910624c1d596e76a49782689dc236388a10803b02e700ac820cb7500cca7fd147a81f6c544c3 languageName: node linkType: hard @@ -40749,16 +40784,12 @@ __metadata: languageName: node linkType: hard -"react-intersection-observer@npm:^9.4.3": - version: 9.13.0 - resolution: "react-intersection-observer@npm:9.13.0" +"react-intersection-observer@npm:^8.33.1": + version: 8.34.0 + resolution: "react-intersection-observer@npm:8.34.0" peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - react-dom: - optional: true - checksum: ee2a163b078923c1556814834f83d7c3ea3e8a9d3ceef974351fd52afe12163518ec57cb2eb0f6544ac255ac4b64d2e7652bc5d60c255159c0f25bda57a565b6 + react: ^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0 + checksum: 7713fecfd1512c7f5a60f9f0bf15403b8f8bbd4110bcafaeaea6de36a0e0eb60368c3638f99e9c97b75ad8fc787ea48c241dcb5c694f821d7f2976f709082cc5 languageName: node linkType: hard @@ -40868,16 +40899,6 @@ __metadata: languageName: node linkType: hard -"react-parallax-tilt@npm:^1.7.226": - version: 1.7.232 - resolution: "react-parallax-tilt@npm:1.7.232" - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: 8c866c9c06315aeaf1eddd4e5265db706e010d788667c337be552cbf8786e8b39f19cafc529be6c69d925e25b31705c52398e90bd89a805bf8914bbb46a8ebdb - languageName: node - linkType: hard - "react-phone-input-2@npm:^2.15.1": version: 2.15.1 resolution: "react-phone-input-2@npm:2.15.1" @@ -41292,18 +41313,6 @@ __metadata: languageName: node linkType: hard -"react-twitter-embed@npm:^4.0.4": - version: 4.0.4 - resolution: "react-twitter-embed@npm:4.0.4" - dependencies: - scriptjs: ^2.5.9 - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: cdb3c5bd04c4da0efa767476be47c0a3865fb6335f2a1b9e242170167b51615c38164223278cef60c77143c4bac27ba582cbea054d0af3f138104fa5ec537c4c - languageName: node - linkType: hard - "react-universal-interface@npm:^0.6.2": version: 0.6.2 resolution: "react-universal-interface@npm:0.6.2" @@ -43128,13 +43137,6 @@ __metadata: languageName: node linkType: hard -"scriptjs@npm:^2.5.9": - version: 2.5.9 - resolution: "scriptjs@npm:2.5.9" - checksum: fc84cb6b60b6fb9aa6f1b3bc59fc94b233bd5241ed3a04233579014382b5eb60640269c87d8657902acc09f9b785ee33230c218627cea00e653564bda8f5acb6 - languageName: node - linkType: hard - "scuid@npm:^1.1.0": version: 1.1.0 resolution: "scuid@npm:1.1.0"