diff --git a/apps/api/v1/pages/api/bookings/_post.ts b/apps/api/v1/pages/api/bookings/_post.ts index 1d43e41fd8..307591a9f2 100644 --- a/apps/api/v1/pages/api/bookings/_post.ts +++ b/apps/api/v1/pages/api/bookings/_post.ts @@ -5,6 +5,7 @@ import handleNewBooking from "@calcom/features/bookings/lib/handleNewBooking"; import { ErrorCode } from "@calcom/lib/errorCodes"; import { HttpError } from "@calcom/lib/http-error"; import { defaultResponder } from "@calcom/lib/server"; +import { CreationSource } from "@calcom/prisma/enums"; import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers"; @@ -213,6 +214,10 @@ import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers"; */ async function handler(req: NextApiRequest) { const { userId, isSystemWideAdmin, isOrganizationOwnerOrAdmin } = req; + req.body = { + ...req.body, + creationSource: CreationSource.API_V1, + }; if (isSystemWideAdmin) req.userId = req.body.userId || userId; if (isOrganizationOwnerOrAdmin) { diff --git a/apps/api/v1/pages/api/invites/_post.ts b/apps/api/v1/pages/api/invites/_post.ts index 7bc2a18d74..eabe62ba69 100644 --- a/apps/api/v1/pages/api/invites/_post.ts +++ b/apps/api/v1/pages/api/invites/_post.ts @@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { HttpError } from "@calcom/lib/http-error"; import { defaultResponder } from "@calcom/lib/server"; import prisma from "@calcom/prisma"; +import { CreationSource } from "@calcom/prisma/enums"; import { createContext } from "@calcom/trpc/server/createContext"; import { viewerTeamsRouter } from "@calcom/trpc/server/routers/viewer/teams/_router"; import type { TInviteMemberInputSchema } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema"; @@ -43,6 +44,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { language: data.language, teamId: data.teamId, usernameOrEmail: data.usernameOrEmail, + creationSource: CreationSource.API_V1, }); return { success: true, message: `${data.usernameOrEmail} has been invited.` }; diff --git a/apps/api/v1/pages/api/users/_post.ts b/apps/api/v1/pages/api/users/_post.ts index a8e3fbfae9..8331005acd 100644 --- a/apps/api/v1/pages/api/users/_post.ts +++ b/apps/api/v1/pages/api/users/_post.ts @@ -3,6 +3,7 @@ import type { NextApiRequest } from "next"; import { HttpError } from "@calcom/lib/http-error"; import { defaultResponder } from "@calcom/lib/server"; import prisma from "@calcom/prisma"; +import { CreationSource } from "@calcom/prisma/enums"; import { schemaUserCreateBodyParams } from "~/lib/validations/user"; @@ -92,7 +93,7 @@ async function postHandler(req: NextApiRequest) { // If user is not ADMIN, return unauthorized. if (!isSystemWideAdmin) throw new HttpError({ statusCode: 401, message: "You are not authorized" }); const data = await schemaUserCreateBodyParams.parseAsync(req.body); - const user = await prisma.user.create({ data }); + const user = await prisma.user.create({ data: { ...data, creationSource: CreationSource.API_V1 } }); req.statusCode = 201; return { user }; } diff --git a/apps/api/v1/test/lib/bookings/_post.test.ts b/apps/api/v1/test/lib/bookings/_post.test.ts index 7950446044..9aa9b516d7 100644 --- a/apps/api/v1/test/lib/bookings/_post.test.ts +++ b/apps/api/v1/test/lib/bookings/_post.test.ts @@ -12,6 +12,7 @@ import { ErrorCode } from "@calcom/lib/errorCodes"; import { buildBooking, buildEventType, buildWebhook } from "@calcom/lib/test/builder"; import prisma from "@calcom/prisma"; import type { Booking } from "@calcom/prisma/client"; +import { CreationSource } from "@calcom/prisma/enums"; import handler from "../../../pages/api/bookings/_post"; @@ -220,6 +221,34 @@ describe.skipIf(true)("POST /api/bookings", () => { }); expect(previousBooking?.status).toBe("cancelled"); }); + + test("Creates source as api_v1", async () => { + const { req, res } = createMocks({ + method: "POST", + body: { + name: "test", + start: dayjs().format(), + end: dayjs().add(1, "day").format(), + eventTypeId: 2, + email: "test@example.com", + location: "Cal.com Video", + timeZone: "America/Montevideo", + language: "en", + customInputs: [], + metadata: {}, + userId: 4, + }, + prisma, + }); + + prismaMock.eventType.findUniqueOrThrow.mockResolvedValue(buildEventType()); + prismaMock.booking.findMany.mockResolvedValue([]); + + await handler(req, res); + createdBooking = JSON.parse(res._getData()); + expect(createdBooking.creationSource).toEqual(CreationSource.API_V1); + expect(prismaMock.booking.create).toHaveBeenCalledTimes(1); + }); }); describe("Recurring event-type", () => { diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index d3c48f3772..3b62af5049 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -29,7 +29,7 @@ "@axiomhq/winston": "^1.2.0", "@calcom/platform-constants": "*", "@calcom/platform-enums": "*", - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.86", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.91", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", diff --git a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts index af70edb760..20f621e6ca 100644 --- a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts +++ b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts @@ -33,6 +33,7 @@ import { import { ConfigService } from "@nestjs/config"; import { ApiQuery, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger"; import { User } from "@prisma/client"; +import { CreationSource } from "@prisma/client"; import { Request } from "express"; import { NextApiRequest } from "next/types"; import { v4 as uuidv4 } from "uuid"; @@ -383,7 +384,11 @@ export class BookingsController_2024_04_15 { ...oAuthParams, }); Object.assign(clone, { userId, ...oAuthParams, platformBookingLocation }); - clone.body = { ...clone.body, noEmail: !oAuthParams.arePlatformEmailsEnabled }; + clone.body = { + ...clone.body, + noEmail: !oAuthParams.arePlatformEmailsEnabled, + creationSource: CreationSource.API_V2, + }; return clone as unknown as NextApiRequest & { userId?: number } & OAuthRequestParams; } @@ -411,6 +416,7 @@ export class BookingsController_2024_04_15 { ...oAuthParams, platformBookingLocation, noEmail: !oAuthParams.arePlatformEmailsEnabled, + creationSource: CreationSource.API_V2, }); return clone as unknown as NextApiRequest & { userId?: number } & OAuthRequestParams; } diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts index 17465cf751..9f4b56ff93 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts @@ -12,6 +12,7 @@ import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.se import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { Logger } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; +import { CreationSource } from "@prisma/client"; import { Request } from "express"; import { DateTime } from "luxon"; import { NextApiRequest } from "next/types"; @@ -97,10 +98,14 @@ export class InputBookingsService_2024_08_13 { if (oAuthClientParams) { Object.assign(newRequest, { userId, ...oAuthClientParams, platformBookingLocation: location }); - newRequest.body = { ...bodyTransformed, noEmail: !oAuthClientParams.arePlatformEmailsEnabled }; + newRequest.body = { + ...bodyTransformed, + noEmail: !oAuthClientParams.arePlatformEmailsEnabled, + creationSource: CreationSource.API_V2, + }; } else { Object.assign(newRequest, { userId, platformBookingLocation: location }); - newRequest.body = { ...bodyTransformed, noEmail: false }; + newRequest.body = { ...bodyTransformed, noEmail: false, creationSource: CreationSource.API_V2 }; } return newRequest as unknown as BookingRequest; @@ -220,6 +225,7 @@ export class InputBookingsService_2024_08_13 { newRequest.body = bodyTransformed.map((event) => ({ ...event, + creationSource: CreationSource.API_V2, })); return newRequest as unknown as BookingRequest; @@ -317,10 +323,14 @@ export class InputBookingsService_2024_08_13 { const location = await this.getRescheduleBookingLocation(bookingUid); if (oAuthClientParams) { Object.assign(newRequest, { userId, ...oAuthClientParams, platformBookingLocation: location }); - newRequest.body = { ...bodyTransformed, noEmail: !oAuthClientParams.arePlatformEmailsEnabled }; + newRequest.body = { + ...bodyTransformed, + noEmail: !oAuthClientParams.arePlatformEmailsEnabled, + creationSource: CreationSource.API_V2, + }; } else { Object.assign(newRequest, { userId, platformBookingLocation: location }); - newRequest.body = { ...bodyTransformed, noEmail: false }; + newRequest.body = { ...bodyTransformed, noEmail: false, creationSource: CreationSource.API_V2 }; } return newRequest as unknown as BookingRequest; 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 9e64bcf40c..338361ca78 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 @@ -6,7 +6,7 @@ import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-us import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input"; import { UsersRepository } from "@/modules/users/users.repository"; import { BadRequestException, ConflictException, Injectable } from "@nestjs/common"; -import { User } from "@prisma/client"; +import { User, CreationSource } from "@prisma/client"; import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries"; @@ -46,6 +46,7 @@ export class OAuthClientUsersService { role: "MEMBER", }, ], + creationSource: CreationSource.API_V2, teamId: organizationId, isOrg: true, parentId: null, diff --git a/apps/api/v2/src/modules/organizations/repositories/organizations-users.repository.ts b/apps/api/v2/src/modules/organizations/repositories/organizations-users.repository.ts index b126a001e4..d2d007ccae 100644 --- a/apps/api/v2/src/modules/organizations/repositories/organizations-users.repository.ts +++ b/apps/api/v2/src/modules/organizations/repositories/organizations-users.repository.ts @@ -3,6 +3,7 @@ import { UpdateOrganizationUserInput } from "@/modules/organizations/inputs/upda import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { Injectable } from "@nestjs/common"; +import { CreationSource } from "@prisma/client"; @Injectable() export class OrganizationsUsersRepository { @@ -70,7 +71,7 @@ export class OrganizationsUsersRepository { async createOrganizationUser(orgId: number, createUserBody: CreateOrganizationUserInput) { const createdUser = await this.dbWrite.prisma.user.create({ - data: createUserBody, + data: { ...createUserBody, creationSource: CreationSource.API_V2 }, }); return createdUser; 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 3a1003ad85..0fe739eb82 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 @@ -5,10 +5,10 @@ import { OrganizationsUsersRepository } from "@/modules/organizations/repositori 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 { Team, CreationSource } from "@prisma/client"; import { plainToInstance } from "class-transformer"; import { createNewUsersConnectToOrgIfExists } from "@calcom/platform-libraries"; -import { Team } from "@calcom/prisma/client"; @Injectable() export class OrganizationsUsersService { @@ -56,6 +56,7 @@ export class OrganizationsUsersService { }, ], teamId: org.id, + creationSource: CreationSource.API_V2, isOrg: true, parentId: null, autoAcceptEmailDomain: "not-required-for-this-endpoint", diff --git a/apps/api/v2/src/modules/users/users.repository.ts b/apps/api/v2/src/modules/users/users.repository.ts index f113708d3d..365bbf39e3 100644 --- a/apps/api/v2/src/modules/users/users.repository.ts +++ b/apps/api/v2/src/modules/users/users.repository.ts @@ -4,6 +4,7 @@ import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-us import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input"; import { Injectable, NotFoundException } from "@nestjs/common"; import type { Profile, User, Team, Prisma } from "@prisma/client"; +import { CreationSource } from "@prisma/client"; export type UserWithProfile = User & { movedToProfile?: (Profile & { organization: Pick }) | null; @@ -30,6 +31,7 @@ export class UsersRepository { connect: { id: oAuthClientId }, }, isPlatformManaged, + creationSource: CreationSource.API_V2, }, }); } diff --git a/apps/web/pages/api/auth/setup.ts b/apps/web/pages/api/auth/setup.ts index c092a7505a..f6a6222ed5 100644 --- a/apps/web/pages/api/auth/setup.ts +++ b/apps/web/pages/api/auth/setup.ts @@ -9,6 +9,7 @@ import { defaultHandler, defaultResponder } from "@calcom/lib/server"; import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; import { IdentityProvider } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; const querySchema = z.object({ username: z @@ -48,6 +49,7 @@ async function handler(req: NextApiRequest) { emailVerified: new Date(), locale: "en", // TODO: We should revisit this identityProvider: IdentityProvider.CAL, + creationSource: CreationSource.WEBAPP, }, }); diff --git a/apps/web/pages/api/auth/verify-email.test.ts b/apps/web/pages/api/auth/verify-email.test.ts index b278708744..eb4c9eabcb 100644 --- a/apps/web/pages/api/auth/verify-email.test.ts +++ b/apps/web/pages/api/auth/verify-email.test.ts @@ -3,6 +3,7 @@ import { organizationScenarios } from "@calcom/lib/server/repository/__mocks__/o import { describe, it, expect, beforeEach, vi } from "vitest"; import { MembershipRole } from "@calcom/prisma/client"; +import { CreationSource } from "@calcom/prisma/enums"; import { inviteMembersWithNoInviterPermissionCheck } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler"; import { moveUserToMatchingOrg } from "./verify-email"; @@ -29,6 +30,7 @@ describe("moveUserToMatchingOrg", () => { const argToInviteMembersWithNoInviterPermissionCheck = { inviterName: null, language: "en", + creationSource: CreationSource.WEBAPP, invitations: [ { usernameOrEmail: email, diff --git a/apps/web/pages/api/auth/verify-email.ts b/apps/web/pages/api/auth/verify-email.ts index c090e1de54..b900cf842b 100644 --- a/apps/web/pages/api/auth/verify-email.ts +++ b/apps/web/pages/api/auth/verify-email.ts @@ -8,6 +8,7 @@ import { IS_STRIPE_ENABLED } from "@calcom/lib/constants"; import { OrganizationRepository } from "@calcom/lib/server/repository/organization"; import { prisma } from "@calcom/prisma"; import { MembershipRole } from "@calcom/prisma/client"; +import { CreationSource } from "@calcom/prisma/enums"; import { userMetadata } from "@calcom/prisma/zod-utils"; import { inviteMembersWithNoInviterPermissionCheck } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler"; @@ -29,6 +30,7 @@ export async function moveUserToMatchingOrg({ email }: { email: string }) { inviterName: null, teamId: org.id, language: "en", + creationSource: CreationSource.WEBAPP, invitations: [ { usernameOrEmail: email, diff --git a/apps/web/pages/api/book/event.ts b/apps/web/pages/api/book/event.ts index 58477b43cd..ef860d6177 100644 --- a/apps/web/pages/api/book/event.ts +++ b/apps/web/pages/api/book/event.ts @@ -6,6 +6,7 @@ import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowE import getIP from "@calcom/lib/getIP"; import { defaultResponder } from "@calcom/lib/server"; import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken"; +import { CreationSource } from "@calcom/prisma/enums"; async function handler(req: NextApiRequest & { userId?: number }, res: NextApiResponse) { const userIp = getIP(req); @@ -25,6 +26,10 @@ async function handler(req: NextApiRequest & { userId?: number }, res: NextApiRe const session = await getServerSession({ req, res }); /* To mimic API behavior and comply with types */ req.userId = session?.user?.id || -1; + req.body = { + ...req.body, + creationSource: CreationSource.WEBAPP, + }; const booking = await handleNewBooking(req); return booking; } diff --git a/apps/web/pages/api/book/instant-event.ts b/apps/web/pages/api/book/instant-event.ts index 57fa795f5d..697007e7f1 100644 --- a/apps/web/pages/api/book/instant-event.ts +++ b/apps/web/pages/api/book/instant-event.ts @@ -5,6 +5,7 @@ import handleInstantMeeting from "@calcom/features/instant-meeting/handleInstant import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; import getIP from "@calcom/lib/getIP"; import { defaultResponder } from "@calcom/lib/server"; +import { CreationSource } from "@calcom/prisma/enums"; async function handler(req: NextApiRequest & { userId?: number }, res: NextApiResponse) { const userIp = getIP(req); @@ -16,6 +17,7 @@ async function handler(req: NextApiRequest & { userId?: number }, res: NextApiRe const session = await getServerSession({ req, res }); req.userId = session?.user?.id || -1; + req.body.creationSource = CreationSource.WEBAPP; const booking = await handleInstantMeeting(req); return booking; } diff --git a/apps/web/test/utils/bookingScenario/getMockRequestDataForBooking.ts b/apps/web/test/utils/bookingScenario/getMockRequestDataForBooking.ts index f610db925b..60e8ebf2fd 100644 --- a/apps/web/test/utils/bookingScenario/getMockRequestDataForBooking.ts +++ b/apps/web/test/utils/bookingScenario/getMockRequestDataForBooking.ts @@ -1,6 +1,7 @@ import { getDate } from "@calcom/web/test/utils/bookingScenario/bookingScenario"; import type { SchedulingType } from "@calcom/prisma/client"; +import type { CreationSource } from "@calcom/prisma/enums"; export const DEFAULT_TIMEZONE_BOOKER = "Asia/Kolkata"; export function getBasicMockRequestDataForBooking() { @@ -40,6 +41,7 @@ export function getMockRequestDataForBooking({ data: Partial> & { eventTypeId: number; user?: string; + creationSource?: CreationSource; } & CommonPropsMockRequestData; }) { return { diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index 9c69c37c14..82751db5e3 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -36,6 +36,7 @@ import { ProfileRepository } from "@calcom/lib/server/repository/profile"; import { UserRepository } from "@calcom/lib/server/repository/user"; import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; +import { CreationSource } from "@calcom/prisma/enums"; import { IdentityProvider, MembershipRole } from "@calcom/prisma/enums"; import { teamMetadataSchema, userMetadata } from "@calcom/prisma/zod-utils"; @@ -979,6 +980,7 @@ export const getOptions = ({ create: { role: MembershipRole.MEMBER, accepted: true, team: { connect: { id: orgId } } }, }, }), + creationSource: CreationSource.WEBAPP, }, }); diff --git a/packages/features/auth/signup/handlers/calcomHandler.ts b/packages/features/auth/signup/handlers/calcomHandler.ts index a4ef504b1d..b95a8d13d0 100644 --- a/packages/features/auth/signup/handlers/calcomHandler.ts +++ b/packages/features/auth/signup/handlers/calcomHandler.ts @@ -14,6 +14,7 @@ import logger from "@calcom/lib/logger"; import { usernameHandler, type RequestWithUsernameStatus } from "@calcom/lib/server/username"; import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUsername"; import { prisma } from "@calcom/prisma"; +import { CreationSource } from "@calcom/prisma/enums"; import { IdentityProvider } from "@calcom/prisma/enums"; import { signupSchema } from "@calcom/prisma/zod-utils"; @@ -199,6 +200,7 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { stripeCustomerId: customer.id, checkoutSessionId, }, + creationSource: CreationSource.WEBAPP, }, }); if (process.env.AVATARAPI_USERNAME && process.env.AVATARAPI_PASSWORD) { diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index f1e2a64077..3d0fde73fd 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -68,6 +68,7 @@ import { WorkflowRepository } from "@calcom/lib/server/repository/workflow"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; import prisma from "@calcom/prisma"; import { BookingStatus, SchedulingType, WebhookTriggerEvents } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; import { eventTypeAppMetadataOptionalSchema, eventTypeMetaDataSchemaWithTypedApps, @@ -277,6 +278,7 @@ const buildDryRunBooking = ({ ratingFeedback: null, noShowHost: null, cancelledBy: null, + creationSource: CreationSource.WEBAPP, } as CreatedBooking; /** @@ -1195,6 +1197,7 @@ async function handler( }, evt, originalRescheduledBooking, + creationSource: req.body.creationSource, }); if (booking?.userId) { diff --git a/packages/features/bookings/lib/handleNewBooking/createBooking.ts b/packages/features/bookings/lib/handleNewBooking/createBooking.ts index 8019d7875d..370e250ee1 100644 --- a/packages/features/bookings/lib/handleNewBooking/createBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking/createBooking.ts @@ -7,6 +7,7 @@ import dayjs from "@calcom/dayjs"; import { isPrismaObjOrUndefined } from "@calcom/lib"; import prisma from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; +import type { CreationSource } from "@calcom/prisma/enums"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { TgetBookingDataSchema } from "../getBookingDataSchema"; @@ -51,6 +52,7 @@ type CreateBookingParams = { }; evt: CalendarEvent; originalRescheduledBooking: OriginalRescheduledBooking; + creationSource?: CreationSource; }; function updateEventDetails( @@ -85,6 +87,7 @@ export async function createBooking({ routingFormResponseId, reroutingFormResponses, rescheduledBy, + creationSource, }: CreateBookingParams & { rescheduledBy: string | undefined }) { updateEventDetails(evt, originalRescheduledBooking, input.changedOrganizer); const associatedBookingForFormResponse = routingFormResponseId @@ -101,6 +104,7 @@ export async function createBooking({ input, evt, originalRescheduledBooking, + creationSource, }); return await saveBooking( @@ -211,6 +215,7 @@ function buildNewBookingData(params: CreateBookingParams) { routingFormResponseId, reroutingFormResponses, rescheduledBy, + creationSource, } = params; const attendeesData = getAttendeesData(evt); @@ -258,6 +263,7 @@ function buildNewBookingData(params: CreateBookingParams) { routedFromRoutingFormReponse: routingFormResponseId ? { connect: { id: routingFormResponseId } } : undefined, + creationSource, }; if (reqBody.recurringEventId) { diff --git a/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts b/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts index 6de0113f78..72f0213241 100644 --- a/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts +++ b/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts @@ -56,6 +56,7 @@ import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData"; import { WEBSITE_URL, WEBAPP_URL } from "@calcom/lib/constants"; import { ErrorCode } from "@calcom/lib/errorCodes"; import { resetTestEmails } from "@calcom/lib/testEmails"; +import { CreationSource } from "@calcom/prisma/enums"; import { BookingStatus } from "@calcom/prisma/enums"; import { test } from "@calcom/web/test/fixtures/fixtures"; @@ -1549,6 +1550,86 @@ describe("handleNewBooking", () => { ); }); + describe("Creation source tests", () => { + test( + `should create a booking with creation source as WEBAPP`, + async ({ emails }) => { + const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default; + const booker = getBooker({ + email: "booker@example.com", + name: "Booker", + }); + + const organizer = getOrganizer({ + name: "Organizer", + email: "organizer@example.com", + id: 101, + schedules: [TestData.schedules.IstWorkHours], + credentials: [getZoomAppCredential()], + selectedCalendars: [TestData.selectedCalendars.google], + }); + + const { req } = createMockNextJsRequest({ + method: "POST", + body: getMockRequestDataForBooking({ + data: { + eventTypeId: 1, + responses: { + email: booker.email, + name: booker.name, + }, + creationSource: CreationSource.WEBAPP, + }, + }), + }); + + const scenarioData = getScenarioData({ + webhooks: [ + { + userId: organizer.id, + eventTriggers: ["BOOKING_CREATED"], + subscriberUrl: "http://my-webhook.example.com", + active: true, + eventTypeId: 1, + appId: null, + }, + ], + eventTypes: [ + { + id: 1, + slotInterval: 30, + length: 30, + users: [ + { + id: 101, + }, + ], + locations: [ + { + type: BookingLocations.ZoomVideo, + }, + ], + }, + ], + organizer, + apps: [TestData.apps["zoomvideo"]], + }); + + mockSuccessfulVideoMeetingCreation({ + metadataLookupKey: "zoomvideo", + }); + await createBookingScenario(scenarioData); + const createdBooking = await handleNewBooking(req); + expect(createdBooking).toEqual( + expect.objectContaining({ + creationSource: CreationSource.WEBAPP, + }) + ); + }, + timeout + ); + }); + describe( "Availability Check during booking", () => { diff --git a/packages/features/ee/dsync/lib/createUserAndInviteToOrg.ts b/packages/features/ee/dsync/lib/createUserAndInviteToOrg.ts index 9533f7e90c..18cc3648ed 100644 --- a/packages/features/ee/dsync/lib/createUserAndInviteToOrg.ts +++ b/packages/features/ee/dsync/lib/createUserAndInviteToOrg.ts @@ -3,6 +3,7 @@ import type { TFunction } from "next-i18next"; import { ProfileRepository } from "@calcom/lib/server/repository/profile"; import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; +import { CreationSource } from "@calcom/prisma/enums"; import { MembershipRole } from "@calcom/prisma/enums"; import type { getTeamOrThrow } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/utils"; import { sendSignupToOrganizationEmail } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/utils"; @@ -46,6 +47,7 @@ const createUserAndInviteToOrg = async ({ ], }, }, + creationSource: CreationSource.WEBAPP, }, }); diff --git a/packages/features/ee/dsync/lib/users/createUsersAndConnectToOrg.ts b/packages/features/ee/dsync/lib/users/createUsersAndConnectToOrg.ts index 9bd07ce4a9..37710c12bc 100644 --- a/packages/features/ee/dsync/lib/users/createUsersAndConnectToOrg.ts +++ b/packages/features/ee/dsync/lib/users/createUsersAndConnectToOrg.ts @@ -2,6 +2,7 @@ import { createOrUpdateMemberships } from "@calcom/features/auth/signup/utils/cr import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; import type { IdentityProvider } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; import dSyncUserSelect from "./dSyncUserSelect"; @@ -37,6 +38,7 @@ const createUsersAndConnectToOrg = async ( organizationId, identityProvider, identityProviderId, + creationSource: CreationSource.WEBAPP, }; }), }); diff --git a/packages/features/ee/organizations/components/AddNewTeamsForm.tsx b/packages/features/ee/organizations/components/AddNewTeamsForm.tsx index c33a595288..5c51b9b925 100644 --- a/packages/features/ee/organizations/components/AddNewTeamsForm.tsx +++ b/packages/features/ee/organizations/components/AddNewTeamsForm.tsx @@ -10,6 +10,7 @@ import { classNames } from "@calcom/lib"; import { IS_TEAM_BILLING_ENABLED_CLIENT } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; +import { CreationSource } from "@calcom/prisma/enums"; import { UserPermissionRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import { Button, CheckboxField, Form, Icon, showToast, TextField } from "@calcom/ui"; @@ -151,7 +152,12 @@ const AddNewTeamsFormChild = ({ const handleFormSubmit = () => { const fields = getValues("teams"); const moveTeams = getValues("moveTeams"); - createTeamsMutation.mutate({ orgId: org.id, moveTeams, teamNames: fields.map((field) => field.name) }); + createTeamsMutation.mutate({ + orgId: org.id, + moveTeams, + teamNames: fields.map((field) => field.name), + creationSource: CreationSource.WEBAPP, + }); }; const moveTeams = watch("moveTeams"); diff --git a/packages/features/ee/organizations/components/CreateANewOrganizationForm.tsx b/packages/features/ee/organizations/components/CreateANewOrganizationForm.tsx index d03faceb8d..4c4fb14959 100644 --- a/packages/features/ee/organizations/components/CreateANewOrganizationForm.tsx +++ b/packages/features/ee/organizations/components/CreateANewOrganizationForm.tsx @@ -13,6 +13,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import slugify from "@calcom/lib/slugify"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { UserPermissionRole } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import type { Ensure } from "@calcom/types/utils"; import { Alert, Button, Form, Label, RadioGroup as RadioArea, TextField, ToggleGroup } from "@calcom/ui"; @@ -107,7 +108,7 @@ const CreateANewOrganizationFormChild = ({ handleSubmit={(v) => { if (!createOrganizationMutation.isPending) { setServerErrorMessage(null); - createOrganizationMutation.mutate(v); + createOrganizationMutation.mutate({ ...v, creationSource: CreationSource.WEBAPP }); } }}>
diff --git a/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx b/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx index ab56967706..f8e027579d 100644 --- a/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx +++ b/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx @@ -9,6 +9,7 @@ import { useState, useEffect } from "react"; import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback"; +import { CreationSource } from "@calcom/prisma/enums"; import { MembershipRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import type { RouterOutputs } from "@calcom/trpc/react"; @@ -216,6 +217,7 @@ const MembersView = () => { language: i18n.language, role: values.role, usernameOrEmail: values.emailOrUsername, + creationSource: CreationSource.WEBAPP, }, { onSuccess: async (data) => { diff --git a/packages/features/ee/platform/components/CreateANewPlatformForm.tsx b/packages/features/ee/platform/components/CreateANewPlatformForm.tsx index cfa886215b..6f4b4365b6 100644 --- a/packages/features/ee/platform/components/CreateANewPlatformForm.tsx +++ b/packages/features/ee/platform/components/CreateANewPlatformForm.tsx @@ -13,6 +13,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import slugify from "@calcom/lib/slugify"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { UserPermissionRole } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import type { Ensure } from "@calcom/types/utils"; import { Alert, Form, TextField, Button } from "@calcom/ui"; @@ -91,6 +92,7 @@ const CreateANewPlatformFormChild = ({ session }: { session: Ensure diff --git a/packages/features/ee/teams/components/MemberInvitationModal.tsx b/packages/features/ee/teams/components/MemberInvitationModal.tsx index d359901b7b..640fd0c27c 100644 --- a/packages/features/ee/teams/components/MemberInvitationModal.tsx +++ b/packages/features/ee/teams/components/MemberInvitationModal.tsx @@ -10,6 +10,7 @@ import { IS_TEAM_BILLING_ENABLED_CLIENT, MAX_NB_INVITES } from "@calcom/lib/cons import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { MembershipRole } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; import type { RouterOutputs } from "@calcom/trpc"; import { trpc } from "@calcom/trpc"; import { isEmail } from "@calcom/trpc/server/routers/viewer/teams/util"; @@ -512,6 +513,7 @@ export const MemberInvitationModalWithoutMembers = ({ language: i18n.language, role: values.role, usernameOrEmail: values.emailOrUsername, + creationSource: CreationSource.WEBAPP, }, { onSuccess: async (data) => { diff --git a/packages/features/ee/teams/components/TeamsListing.tsx b/packages/features/ee/teams/components/TeamsListing.tsx index a46680bf8e..ebd0441b9b 100644 --- a/packages/features/ee/teams/components/TeamsListing.tsx +++ b/packages/features/ee/teams/components/TeamsListing.tsx @@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from "react"; import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { CreationSource } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import { Alert, Button, ButtonGroup, EmptyScreen, Icon, Label, showToast } from "@calcom/ui"; @@ -95,7 +96,7 @@ export function TeamsListing() { useEffect(() => { if (!router) return; - if (token) inviteMemberByToken({ token }); + if (token) inviteMemberByToken({ token, creationSource: CreationSource.WEBAPP }); else setInviteTokenChecked(true); }, [router, inviteMemberByToken, setInviteTokenChecked, token]); diff --git a/packages/features/ee/users/server/trpc-router.ts b/packages/features/ee/users/server/trpc-router.ts index 4647604bca..20399911ce 100644 --- a/packages/features/ee/users/server/trpc-router.ts +++ b/packages/features/ee/users/server/trpc-router.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains"; +import { CreationSource } from "@calcom/prisma/enums"; import { RedirectType } from "@calcom/prisma/enums"; import { _UserModel as User } from "@calcom/prisma/zod"; import type { inferRouterOutputs } from "@calcom/trpc"; @@ -63,7 +64,7 @@ export const userAdminRouter = router({ }), add: authedAdminProcedure.input(userBodySchema).mutation(async ({ ctx, input }) => { const { prisma } = ctx; - const user = await prisma.user.create({ data: input }); + const user = await prisma.user.create({ data: { ...input, creationSource: CreationSource.WEBAPP } }); return { user, message: `User with id: ${user.id} added successfully` }; }), update: authedAdminProcedureWithRequestedUser diff --git a/packages/features/instant-meeting/handleInstantMeeting.ts b/packages/features/instant-meeting/handleInstantMeeting.ts index d06eee7028..1297c5b5eb 100644 --- a/packages/features/instant-meeting/handleInstantMeeting.ts +++ b/packages/features/instant-meeting/handleInstantMeeting.ts @@ -261,6 +261,7 @@ async function handler(req: NextApiRequest) { data: attendeesList, }, }, + creationSource: req.body.creationSource, }; const createBookingObj = { diff --git a/packages/features/users/components/UserTable/InviteMemberModal.tsx b/packages/features/users/components/UserTable/InviteMemberModal.tsx index d70b35036e..5c77b23ca8 100644 --- a/packages/features/users/components/UserTable/InviteMemberModal.tsx +++ b/packages/features/users/components/UserTable/InviteMemberModal.tsx @@ -3,6 +3,7 @@ import type { Dispatch } from "react"; import MemberInvitationModal from "@calcom/features/ee/teams/components/MemberInvitationModal"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { CreationSource } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc"; import { showToast } from "@calcom/ui"; import usePlatformMe from "@calcom/web/components/settings/platform/hooks/usePlatformMe"; @@ -70,6 +71,7 @@ export function InviteMemberModal(props: Props) { role: values.role, usernameOrEmail: values.emailOrUsername, isPlatform: platformUser?.organization.isPlatform, + creationSource: CreationSource.WEBAPP, }); }} /> diff --git a/packages/lib/server/repository/organization.ts b/packages/lib/server/repository/organization.ts index 23b63b2fed..43725c71a4 100644 --- a/packages/lib/server/repository/organization.ts +++ b/packages/lib/server/repository/organization.ts @@ -4,6 +4,7 @@ import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import { prisma } from "@calcom/prisma"; import { MembershipRole } from "@calcom/prisma/enums"; +import type { CreationSource } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { createAProfileForAnExistingUser } from "../../createAProfileForAnExistingUser"; @@ -64,6 +65,7 @@ export class OrganizationRepository { static async createWithNonExistentOwner({ orgData, owner, + creationSource, }: { orgData: { name: string; @@ -79,6 +81,7 @@ export class OrganizationRepository { owner: { email: string; }; + creationSource: CreationSource; }) { logger.debug("createWithNonExistentOwner", safeStringify({ orgData, owner })); const organization = await this.create(orgData); @@ -87,6 +90,7 @@ export class OrganizationRepository { email: owner.email, username: ownerUsernameInOrg, organizationId: organization.id, + creationSource, }); await prisma.membership.create({ diff --git a/packages/lib/server/repository/user.ts b/packages/lib/server/repository/user.ts index 6790097004..0449d05f2d 100644 --- a/packages/lib/server/repository/user.ts +++ b/packages/lib/server/repository/user.ts @@ -9,6 +9,7 @@ import prisma from "@calcom/prisma"; import { availabilityUserSelect } from "@calcom/prisma"; import { Prisma } from "@calcom/prisma/client"; import type { User as UserType } from "@calcom/prisma/client"; +import type { CreationSource } from "@calcom/prisma/enums"; import { MembershipRole } from "@calcom/prisma/enums"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import { userMetadata } from "@calcom/prisma/zod-utils"; @@ -584,11 +585,14 @@ export class UserRepository { email, username, organizationId, + creationSource, }: { email: string; username: string; organizationId: number | null; + creationSource: CreationSource; }) { + console.log("create user", { email, username, organizationId }); const password = createHash("md5").update(`${email}${process.env.CALENDSO_ENCRYPTION_KEY}`).digest("hex"); const hashedPassword = await hashPassword(password); const t = await getTranslation("en", "common"); @@ -624,6 +628,7 @@ export class UserRepository { }, } : undefined, + creationSource, }, }); } diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index c6de3193ad..2263f51e77 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -3,6 +3,7 @@ import type { Booking, EventType, Prisma, Webhook, BookingReference } from "@pri import type { TFunction } from "next-i18next"; import getICalUID from "@calcom/emails/lib/getICalUID"; +import { CreationSource } from "@calcom/prisma/enums"; import { BookingStatus } from "@calcom/prisma/enums"; import type { CalendarEvent, Person, VideoCallData } from "@calcom/types/Calendar"; @@ -76,6 +77,7 @@ export const buildBooking = ( ratingFeedback: null, attendees: [], oneTimePassword: null, + creationSource: CreationSource.WEBAPP, ...booking, }; }; diff --git a/packages/prisma/migrations/20250120135752_add_creation_source_for_bookings_and_users/migration.sql b/packages/prisma/migrations/20250120135752_add_creation_source_for_bookings_and_users/migration.sql new file mode 100644 index 0000000000..3e622ecd2a --- /dev/null +++ b/packages/prisma/migrations/20250120135752_add_creation_source_for_bookings_and_users/migration.sql @@ -0,0 +1,8 @@ +-- CreateEnum +CREATE TYPE "CreationSource" AS ENUM ('api_v1', 'api_v2', 'webapp'); + +-- AlterTable +ALTER TABLE "Booking" ADD COLUMN "creationSource" "CreationSource"; + +-- AlterTable +ALTER TABLE "users" ADD COLUMN "creationSource" "CreationSource"; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 4380dcd7a3..f74af5f01d 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -42,6 +42,12 @@ enum PeriodType { RANGE @map("range") } +enum CreationSource { + API_V1 @map("api_v1") + API_V2 @map("api_v2") + WEBAPP @map("webapp") +} + model Host { user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int @@ -367,6 +373,7 @@ model User { createdWatchlists Watchlist[] @relation("CreatedWatchlists") updatedWatchlists Watchlist[] @relation("UpdatedWatchlists") BookingInternalNote BookingInternalNote[] + creationSource CreationSource? @@unique([email]) @@unique([email, username]) @@ -671,6 +678,7 @@ model Booking { routedFromRoutingFormReponse App_RoutingForms_FormResponse? assignmentReason AssignmentReason[] internalNote BookingInternalNote[] + creationSource CreationSource? @@index([eventTypeId]) @@index([userId]) diff --git a/packages/trpc/server/routers/viewer/organizations/create.handler.ts b/packages/trpc/server/routers/viewer/organizations/create.handler.ts index 34ca79e53c..cff3cc24b2 100644 --- a/packages/trpc/server/routers/viewer/organizations/create.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/create.handler.ts @@ -89,6 +89,7 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => { pricePerSeat, isPlatform, billingPeriod: billingPeriodRaw, + creationSource, } = input; const loggedInUser = await prisma.user.findUnique({ @@ -229,6 +230,7 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => { owner: { email: orgOwnerEmail, }, + creationSource, }); orgOwner = data.orgOwner; diff --git a/packages/trpc/server/routers/viewer/organizations/create.schema.ts b/packages/trpc/server/routers/viewer/organizations/create.schema.ts index 69a37d262d..ae51aa80c6 100644 --- a/packages/trpc/server/routers/viewer/organizations/create.schema.ts +++ b/packages/trpc/server/routers/viewer/organizations/create.schema.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { emailSchema } from "@calcom/lib/emailSchema"; import slugify from "@calcom/lib/slugify"; +import { CreationSource } from "@calcom/prisma/enums"; export enum BillingPeriod { MONTHLY = "MONTHLY", @@ -17,6 +18,7 @@ export const ZCreateInputSchema = z.object({ pricePerSeat: z.number().optional(), isPlatform: z.boolean().default(false), billingPeriod: z.nativeEnum(BillingPeriod).default(BillingPeriod.MONTHLY).optional(), + creationSource: z.nativeEnum(CreationSource), }); export type TCreateInputSchema = z.infer; diff --git a/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts b/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts index 1c45f3e5dc..f68207a636 100644 --- a/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts @@ -8,6 +8,7 @@ import { UserRepository } from "@calcom/lib/server/repository/user"; import slugify from "@calcom/lib/slugify"; import { prisma } from "@calcom/prisma"; import { MembershipRole, RedirectType } from "@calcom/prisma/enums"; +import type { CreationSource } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -33,7 +34,7 @@ export const createTeamsHandler = async ({ ctx, input }: CreateTeamsOptions) => throw new NoUserError(); } - const { orgId, moveTeams } = input; + const { orgId, moveTeams, creationSource } = input; // Remove empty team names that could be there due to the default empty team name const teamNames = input.teamNames.filter((name) => name.trim().length > 0); @@ -106,6 +107,7 @@ export const createTeamsHandler = async ({ ctx, input }: CreateTeamsOptions) => ownerId: organizationOwner.id, }, ctx, + creationSource, }); }) ); @@ -173,6 +175,7 @@ async function moveTeam({ newSlug, org, ctx, + creationSource, }: { teamId: number; newSlug?: string | null; @@ -183,6 +186,7 @@ async function moveTeam({ metadata: Prisma.JsonValue; }; ctx: CreateTeamsOptions["ctx"]; + creationSource: CreationSource; }) { const team = await prisma.team.findUnique({ where: { @@ -240,6 +244,7 @@ async function moveTeam({ teamId: org.id, language: "en", usernameOrEmail: invitableMembers, + creationSource, }, }); } diff --git a/packages/trpc/server/routers/viewer/organizations/createTeams.schema.ts b/packages/trpc/server/routers/viewer/organizations/createTeams.schema.ts index 724a7cf4e2..66b9f69b4e 100644 --- a/packages/trpc/server/routers/viewer/organizations/createTeams.schema.ts +++ b/packages/trpc/server/routers/viewer/organizations/createTeams.schema.ts @@ -1,5 +1,7 @@ import { z } from "zod"; +import { CreationSource } from "@calcom/prisma/enums"; + export const ZCreateTeamsSchema = z.object({ teamNames: z.string().array(), orgId: z.number(), @@ -10,6 +12,7 @@ export const ZCreateTeamsSchema = z.object({ shouldMove: z.boolean(), }) ), + creationSource: z.nativeEnum(CreationSource), }); export type TCreateTeamsSchema = z.infer; diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts index 19c0403fcd..8ec42c3be8 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts @@ -8,6 +8,7 @@ import { getTranslation } from "@calcom/lib/server/i18n"; import { isOrganisationOwner } from "@calcom/lib/server/queries/organisations"; import { UserRepository } from "@calcom/lib/server/repository/user"; import { MembershipRole } from "@calcom/prisma/enums"; +import type { CreationSource } from "@calcom/prisma/enums"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { TRPCError } from "@trpc/server"; @@ -139,9 +140,10 @@ export const inviteMembersWithNoInviterPermissionCheck = async ( usernameOrEmail: string; role: MembershipRole; }[]; + creationSource: CreationSource; } & TargetTeam ) => { - const { inviterName, orgSlug, invitations, language } = data; + const { inviterName, orgSlug, invitations, language, creationSource } = data; const myLog = log.getSubLogger({ prefix: ["inviteMembers"] }); const translation = await getTranslation(language ?? "en", "common"); const team = "team" in data ? data.team : await getTeamOrThrow(data.teamId); @@ -188,6 +190,7 @@ export const inviteMembersWithNoInviterPermissionCheck = async ( isOrg: isTeamAnOrg, inviter, autoAcceptEmailDomain: orgState.autoAcceptEmailDomain, + creationSource, }); } @@ -235,7 +238,7 @@ export const inviteMembersWithNoInviterPermissionCheck = async ( const inviteMembers = async ({ ctx, input }: InviteMemberOptions) => { const { user: inviter } = ctx; - const { usernameOrEmail, role, isPlatform } = input; + const { usernameOrEmail, role, isPlatform, creationSource } = input; const team = await getTeamOrThrow(input.teamId); const requestedSlugForTeam = team?.metadata?.requestedSlug ?? null; @@ -267,11 +270,11 @@ const inviteMembers = async ({ ctx, input }: InviteMemberOptions) => { teamId: inviterOrgId && isInviterOrgAdmin ? inviterOrgId : input.teamId, isOrg: isTeamAnOrg, }); - const result = await inviteMembersWithNoInviterPermissionCheck({ inviterName: inviter.name, team, language: input.language, + creationSource, orgSlug, invitations, }); diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema.ts index 79059bd47a..4a33c6afcc 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema.ts @@ -3,6 +3,7 @@ import { z } from "zod"; import { MAX_NB_INVITES } from "@calcom/lib/constants"; import { emailSchema } from "@calcom/lib/emailSchema"; import { MembershipRole } from "@calcom/prisma/enums"; +import { CreationSource } from "@calcom/prisma/enums"; export const ZInviteMemberInputSchema = z.object({ teamId: z.number(), @@ -57,6 +58,7 @@ export const ZInviteMemberInputSchema = z.object({ role: z.nativeEnum(MembershipRole).optional(), language: z.string(), isPlatform: z.boolean().optional(), + creationSource: z.nativeEnum(CreationSource), }); export type TInviteMemberInputSchema = z.infer; diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts index 31b8005ea4..9129c6a865 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts @@ -20,6 +20,7 @@ import { prisma } from "@calcom/prisma"; import type { Membership, OrganizationSettings, Team } from "@calcom/prisma/client"; import { type User as UserType, type UserPassword, Prisma } from "@calcom/prisma/client"; import type { Profile as ProfileType } from "@calcom/prisma/client"; +import type { CreationSource } from "@calcom/prisma/enums"; import { MembershipRole } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -274,6 +275,7 @@ export async function createNewUsersConnectToOrgIfExists({ weekStart, timeZone, language, + creationSource, }: { invitations: Invitation[]; isOrg: boolean; @@ -286,6 +288,7 @@ export async function createNewUsersConnectToOrgIfExists({ weekStart?: string; timeZone?: string; language: string; + creationSource: CreationSource; }) { // fail if we have invalid emails invitations.forEach((invitation) => checkInputEmailIsValid(invitation.usernameOrEmail)); @@ -323,6 +326,7 @@ export async function createNewUsersConnectToOrgIfExists({ timeFormat, weekStart, timeZone, + creationSource, organizationId: orgId || null, // If the user is invited to a child team, they are automatically added to the parent org ...(orgId ? { @@ -911,6 +915,7 @@ export async function handleNewUsersInvites({ isOrg, autoAcceptEmailDomain, inviter, + creationSource, }: { invitationsForNewUsers: Invitation[]; teamId: number; @@ -922,6 +927,7 @@ export async function handleNewUsersInvites({ name: string | null; }; isOrg: boolean; + creationSource: CreationSource; }) { const translation = await getTranslation(language, "common"); @@ -933,6 +939,7 @@ export async function handleNewUsersInvites({ autoAcceptEmailDomain: autoAcceptEmailDomain, parentId: team.parentId, language, + creationSource, }); const sendVerifyEmailsPromises = invitationsForNewUsers.map((invitation) => { diff --git a/packages/trpc/server/routers/viewer/teams/inviteMemberByToken.schema.ts b/packages/trpc/server/routers/viewer/teams/inviteMemberByToken.schema.ts index 2b9ef811a8..910e559503 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMemberByToken.schema.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMemberByToken.schema.ts @@ -1,7 +1,10 @@ import { z } from "zod"; +import { CreationSource } from "@calcom/prisma/enums"; + export const ZInviteMemberByTokenSchemaInputSchema = z.object({ token: z.string(), + creationSource: z.nativeEnum(CreationSource), }); export type TInviteMemberByTokenSchemaInputSchema = z.infer;