diff --git a/apps/web/app/api/routing-forms/queued-response/__tests__/queued-response.test.ts b/apps/web/app/api/routing-forms/queued-response/__tests__/queued-response.test.ts index 006c93b045..3e9f58ea55 100644 --- a/apps/web/app/api/routing-forms/queued-response/__tests__/queued-response.test.ts +++ b/apps/web/app/api/routing-forms/queued-response/__tests__/queued-response.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi } from "vitest"; +import { beforeEach, describe, it, expect, vi } from "vitest"; import { onSubmissionOfFormResponse } from "@calcom/app-store/routing-forms/lib/formSubmissionUtils"; import { getResponseToStore } from "@calcom/app-store/routing-forms/lib/getResponseToStore"; @@ -8,6 +8,11 @@ import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/for import { queuedResponseHandler } from "../route"; vi.mock("@calcom/lib/server/repository/formResponse"); + +const mockRoutingFormResponseRepository = { + getQueuedFormResponseFromId: vi.fn(), + recordFormResponse: vi.fn(), +}; vi.mock("@calcom/app-store/routing-forms/lib/getSerializableForm"); vi.mock("@calcom/app-store/routing-forms/lib/getResponseToStore"); vi.mock("@calcom/app-store/routing-forms/lib/formSubmissionUtils"); @@ -43,8 +48,14 @@ const mockQueuedFormResponse = { }; describe("queuedResponseHandler", () => { + beforeEach(() => { + vi.mocked(RoutingFormResponseRepository).mockImplementation( + () => mockRoutingFormResponseRepository as any + ); + }); + it("should process a queued form response", async () => { - vi.mocked(RoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue( + vi.mocked(mockRoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue( mockQueuedFormResponse ); @@ -90,7 +101,7 @@ describe("queuedResponseHandler", () => { team: null, } as unknown as Awaited>); - vi.mocked(RoutingFormResponseRepository.recordFormResponse).mockResolvedValue({ + vi.mocked(mockRoutingFormResponseRepository.recordFormResponse).mockResolvedValue({ id: "mock-form-id", name: "Test Form", description: "Test Form Description", @@ -102,7 +113,7 @@ describe("queuedResponseHandler", () => { }, team: null, chosenRouteId: "mock-chosen-route-id", - } as unknown as Awaited>); + } as unknown as Awaited>); const response = await queuedResponseHandler({ queuedFormResponseId: "1", @@ -115,7 +126,7 @@ describe("queuedResponseHandler", () => { }); it("if no queued form response is found, should return early", async () => { - vi.mocked(RoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(null); + vi.mocked(mockRoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(null); const response = await queuedResponseHandler({ queuedFormResponseId: "1", params: {}, @@ -127,7 +138,7 @@ describe("queuedResponseHandler", () => { }); it("should throw if form has no fields", async () => { - vi.mocked(RoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue( + vi.mocked(mockRoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue( mockQueuedFormResponse ); vi.mocked(getSerializableForm).mockResolvedValue({ diff --git a/apps/web/app/api/routing-forms/queued-response/route.ts b/apps/web/app/api/routing-forms/queued-response/route.ts index 47a889beca..e2f52acfb9 100644 --- a/apps/web/app/api/routing-forms/queued-response/route.ts +++ b/apps/web/app/api/routing-forms/queued-response/route.ts @@ -8,6 +8,7 @@ import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSeri import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse"; +import prisma from "@calcom/prisma"; import { defaultResponderForAppDir } from "../../defaultResponderForAppDir"; @@ -23,10 +24,10 @@ export const queuedResponseHandler = async ({ queuedFormResponseId: string; params: Record; }) => { + const formResponseRepo = new RoutingFormResponseRepository(prisma); + // Get the queued response - const queuedFormResponse = await RoutingFormResponseRepository.getQueuedFormResponseFromId( - queuedFormResponseId - ); + const queuedFormResponse = await formResponseRepo.getQueuedFormResponseFromId(queuedFormResponseId); if (!queuedFormResponse) { return { @@ -48,7 +49,7 @@ export const queuedResponseHandler = async ({ fieldsResponses: params, }); - const formResponse = await RoutingFormResponseRepository.recordFormResponse({ + const formResponse = await formResponseRepo.recordFormResponse({ formId: queuedFormResponse.formId, queuedFormResponseId: queuedFormResponse.id, // We record new response here as that might be different from the queued response depending on if the user changed something in b/w before clicking CTA and that something wasn't prerendered diff --git a/packages/app-store/routing-forms/lib/handleResponse.test.ts b/packages/app-store/routing-forms/lib/handleResponse.test.ts index d7fa9b40c1..0413d8f0a2 100644 --- a/packages/app-store/routing-forms/lib/handleResponse.test.ts +++ b/packages/app-store/routing-forms/lib/handleResponse.test.ts @@ -17,12 +17,12 @@ vi.mock("@calcom/lib/raqb/findTeamMembersMatchingAttributeLogic", () => ({ findTeamMembersMatchingAttributeLogic: vi.fn(), })); -vi.mock("@calcom/lib/server/repository/formResponse", () => ({ - RoutingFormResponseRepository: { - recordQueuedFormResponse: vi.fn(), - recordFormResponse: vi.fn(), - }, -})); +vi.mock("@calcom/lib/server/repository/formResponse"); + +const mockRoutingFormResponseRepository = { + recordQueuedFormResponse: vi.fn(), + recordFormResponse: vi.fn(), +}; vi.mock("./crmRouting/routerGetCrmContactOwnerEmail", () => ({ default: vi.fn(), @@ -107,6 +107,9 @@ const mockResponse: z.infer["response"] = { describe("handleResponse", () => { beforeEach(() => { vi.resetAllMocks(); + vi.mocked(RoutingFormResponseRepository).mockImplementation( + () => mockRoutingFormResponseRepository as any + ); }); it("should throw a TRPCError for missing required fields", async () => { @@ -150,7 +153,7 @@ describe("handleResponse", () => { createdAt: new Date(), updatedAt: new Date(), }; - vi.mocked(RoutingFormResponseRepository.recordFormResponse).mockResolvedValue(dbFormResponse); + vi.mocked(mockRoutingFormResponseRepository.recordFormResponse).mockResolvedValue(dbFormResponse); const result = await handleResponse({ response: mockResponse, @@ -161,7 +164,7 @@ describe("handleResponse", () => { isPreview: false, }); - expect(RoutingFormResponseRepository.recordFormResponse).toHaveBeenCalledWith({ + expect(mockRoutingFormResponseRepository.recordFormResponse).toHaveBeenCalledWith({ formId: mockForm.id, response: mockResponse, chosenRouteId: null, @@ -185,7 +188,7 @@ describe("handleResponse", () => { createdAt: new Date(), updatedAt: new Date(), }; - vi.mocked(RoutingFormResponseRepository.recordQueuedFormResponse).mockResolvedValue(queuedResponse); + vi.mocked(mockRoutingFormResponseRepository.recordQueuedFormResponse).mockResolvedValue(queuedResponse); const result = await handleResponse({ response: mockResponse, @@ -197,12 +200,12 @@ describe("handleResponse", () => { queueFormResponse: true, }); - expect(RoutingFormResponseRepository.recordQueuedFormResponse).toHaveBeenCalledWith({ + expect(mockRoutingFormResponseRepository.recordQueuedFormResponse).toHaveBeenCalledWith({ formId: mockForm.id, response: mockResponse, chosenRouteId: null, }); - expect(RoutingFormResponseRepository.recordFormResponse).not.toHaveBeenCalled(); + expect(mockRoutingFormResponseRepository.recordFormResponse).not.toHaveBeenCalled(); expect(onSubmissionOfFormResponse).not.toHaveBeenCalled(); expect(result.queuedFormResponse).toEqual(queuedResponse); expect(result.formResponse).toBeNull(); @@ -219,7 +222,7 @@ describe("handleResponse", () => { isPreview: true, }); - expect(RoutingFormResponseRepository.recordFormResponse).not.toHaveBeenCalled(); + expect(mockRoutingFormResponseRepository.recordFormResponse).not.toHaveBeenCalled(); expect(onSubmissionOfFormResponse).not.toHaveBeenCalled(); expect(result.isPreview).toBe(true); expect(result.formResponse).toBeDefined(); @@ -237,7 +240,7 @@ describe("handleResponse", () => { queueFormResponse: true, }); - expect(RoutingFormResponseRepository.recordQueuedFormResponse).not.toHaveBeenCalled(); + expect(mockRoutingFormResponseRepository.recordQueuedFormResponse).not.toHaveBeenCalled(); expect(result.isPreview).toBe(true); expect(result.queuedFormResponse).toBeDefined(); expect(result.queuedFormResponse?.id).toBe("00000000-0000-0000-0000-000000000000"); diff --git a/packages/app-store/routing-forms/lib/handleResponse.ts b/packages/app-store/routing-forms/lib/handleResponse.ts index 43db0e5539..5db85ae45b 100644 --- a/packages/app-store/routing-forms/lib/handleResponse.ts +++ b/packages/app-store/routing-forms/lib/handleResponse.ts @@ -7,6 +7,7 @@ import { findTeamMembersMatchingAttributeLogic } from "@calcom/lib/raqb/findTeam import { safeStringify } from "@calcom/lib/safeStringify"; import { withReporting } from "@calcom/lib/sentryWrapper"; import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse"; +import prisma from "@calcom/prisma"; import type { ZResponseInputSchema } from "@calcom/trpc/server/routers/viewer/routing-forms/response.schema"; import { TRPCError } from "@trpc/server"; @@ -161,15 +162,16 @@ const _handleResponse = async ({ } let dbFormResponse, queuedFormResponse; if (!isPreview) { + const formResponseRepo = new RoutingFormResponseRepository(prisma); if (queueFormResponse) { - queuedFormResponse = await RoutingFormResponseRepository.recordQueuedFormResponse({ + queuedFormResponse = await formResponseRepo.recordQueuedFormResponse({ formId: form.id, response, chosenRouteId, }); dbFormResponse = null; } else { - dbFormResponse = await RoutingFormResponseRepository.recordFormResponse({ + dbFormResponse = await formResponseRepo.recordFormResponse({ formId: form.id, response, chosenRouteId, diff --git a/packages/lib/server/repository/formResponse.ts b/packages/lib/server/repository/formResponse.ts index df42a1e4c0..a0854fde3d 100644 --- a/packages/lib/server/repository/formResponse.ts +++ b/packages/lib/server/repository/formResponse.ts @@ -1,4 +1,4 @@ -import prisma from "@calcom/prisma"; +import type { PrismaClient } from "@calcom/prisma"; import type { Prisma } from "@calcom/prisma/client"; interface RecordFormResponseInput { @@ -8,7 +8,9 @@ interface RecordFormResponseInput { } export class RoutingFormResponseRepository { - private static generateCreateFormResponseData( + constructor(private prismaClient: PrismaClient) {} + + private generateCreateFormResponseData( input: RecordFormResponseInput & { queuedFormResponseId?: string | null } ) { return { @@ -27,24 +29,24 @@ export class RoutingFormResponseRepository { }; } - static async recordFormResponse( + async recordFormResponse( input: RecordFormResponseInput & { queuedFormResponseId?: string | null; } ) { - return await prisma.app_RoutingForms_FormResponse.create({ + return await this.prismaClient.app_RoutingForms_FormResponse.create({ data: this.generateCreateFormResponseData(input), }); } - static async recordQueuedFormResponse(input: RecordFormResponseInput) { - return await prisma.app_RoutingForms_QueuedFormResponse.create({ + async recordQueuedFormResponse(input: RecordFormResponseInput) { + return await this.prismaClient.app_RoutingForms_QueuedFormResponse.create({ data: this.generateCreateFormResponseData(input), }); } - static async findFormResponseIncludeForm({ routingFormResponseId }: { routingFormResponseId: number }) { - return await prisma.app_RoutingForms_FormResponse.findUnique({ + async findFormResponseIncludeForm({ routingFormResponseId }: { routingFormResponseId: number }) { + return await this.prismaClient.app_RoutingForms_FormResponse.findUnique({ where: { id: routingFormResponseId, }, @@ -61,8 +63,8 @@ export class RoutingFormResponseRepository { }); } - static async findQueuedFormResponseIncludeForm({ queuedFormResponseId }: { queuedFormResponseId: string }) { - return await prisma.app_RoutingForms_QueuedFormResponse.findUnique({ + async findQueuedFormResponseIncludeForm({ queuedFormResponseId }: { queuedFormResponseId: string }) { + return await this.prismaClient.app_RoutingForms_QueuedFormResponse.findUnique({ where: { id: queuedFormResponseId, }, @@ -79,8 +81,8 @@ export class RoutingFormResponseRepository { }); } - static async getQueuedFormResponseFromId(id: string) { - return await prisma.app_RoutingForms_QueuedFormResponse.findUnique({ + async getQueuedFormResponseFromId(id: string) { + return await this.prismaClient.app_RoutingForms_QueuedFormResponse.findUnique({ where: { id, }, diff --git a/packages/trpc/server/routers/viewer/slots/util.ts b/packages/trpc/server/routers/viewer/slots/util.ts index 87be8eebc7..e8df46d1d0 100644 --- a/packages/trpc/server/routers/viewer/slots/util.ts +++ b/packages/trpc/server/routers/viewer/slots/util.ts @@ -142,7 +142,8 @@ export class AvailableSlotsService { } const dynamicEventType = getDefaultEvent(input.eventTypeSlug); - const usersForDynamicEventType = await new UserRepository(prisma).findManyUsersForDynamicEventType({ + const userRepo = new UserRepository(prisma); + const usersForDynamicEventType = await userRepo.findManyUsersForDynamicEventType({ currentOrgDomain: isValidOrgDomain ? currentOrgDomain : null, usernameList: Array.isArray(input.usernameList) ? input.usernameList @@ -249,7 +250,8 @@ export class AvailableSlotsService { ) { const { currentOrgDomain, isValidOrgDomain } = organizationDetails; log.info("getUserIdFromUsername", safeStringify({ organizationDetails, username })); - const [user] = await new UserRepository(prisma).findUsersByUsername({ + const userRepo = new UserRepository(prisma); + const [user] = await userRepo.findUsersByUsername({ usernameList: [username], orgSlug: isValidOrgDomain ? currentOrgDomain : null, }); @@ -938,11 +940,13 @@ export class AvailableSlotsService { let routingFormResponse = null; if (routingFormResponseId) { - routingFormResponse = await RoutingFormResponseRepository.findFormResponseIncludeForm({ + const formResponseRepo = new RoutingFormResponseRepository(prisma); + routingFormResponse = await formResponseRepo.findFormResponseIncludeForm({ routingFormResponseId, }); } else if (queuedFormResponseId) { - routingFormResponse = await RoutingFormResponseRepository.findQueuedFormResponseIncludeForm({ + const formResponseRepo = new RoutingFormResponseRepository(prisma); + routingFormResponse = await formResponseRepo.findQueuedFormResponseIncludeForm({ queuedFormResponseId, }); }