refactor: async constructor is an anti-pattern, use init instead (#21785)

This commit is contained in:
Alex van Andel
2025-06-12 13:13:44 +00:00
committed by GitHub
parent 74234b4ed1
commit 86912e0cbc
5 changed files with 39 additions and 28 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ import logger from "@calcom/lib/logger";
import { getTranslation } from "@calcom/lib/server/i18n";
import { BookingRepository } from "@calcom/lib/server/repository/booking";
import { prisma } from "@calcom/prisma";
import { WebhookTriggerEvents } from "@calcom/prisma/client";
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
import type { PlatformClientParams } from "@calcom/prisma/zod-utils";
import type { TNoShowInputSchema } from "@calcom/trpc/server/routers/loggedInViewer/markNoShow.schema";
@@ -208,7 +208,7 @@ const getWebhooksService = async (bookingUid: string, platformClientId?: string)
memberId: booking?.eventType?.userId,
teamId: booking?.eventType?.teamId,
});
const webhooks = await new WebhookService({
const webhooks = await WebhookService.init({
teamId: booking?.eventType?.teamId,
userId: booking?.eventType?.userId,
eventTypeId: booking?.eventType?.id,
@@ -2,11 +2,12 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { WebhookService } from "../WebhookService";
import getWebhooks from "../getWebhooks";
import { WebhookService } from "./WebhookService";
import type { GetWebhooksReturnType } from "./getWebhooks";
import getWebhooks from "./getWebhooks";
vi.mock("../getWebhooks");
vi.mock("../sendOrSchedulePayload");
vi.mock("./getWebhooks");
vi.mock("./sendOrSchedulePayload");
vi.mock("@calcom/lib/logger", async () => {
const actual = await vi.importActual<typeof import("@calcom/lib/logger")>("@calcom/lib/logger");
@@ -36,19 +37,22 @@ describe("WebhookService", () => {
});
it("should initialize with options and webhooks", async () => {
const mockWebhooks = [
const mockWebhooks: GetWebhooksReturnType = [
{
id: "webhookId",
subscriberUrl: "url",
secret: "secret",
appId: "appId",
payloadTemplate: "payloadTemplate",
eventTriggers: [WebhookTriggerEvents.BOOKING_CREATED],
timeUnit: "MINUTE",
time: 5,
},
];
vi.mocked(getWebhooks).mockResolvedValue(mockWebhooks);
// Has to be called with await due to the iffi being async
const service = await new WebhookService(mockOptions);
const service = await WebhookService.init(mockOptions);
expect(service).toBeInstanceOf(WebhookService);
expect(await service.getWebhooks()).toEqual(mockWebhooks);
@@ -2,20 +2,17 @@ import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import getWebhooks from "./getWebhooks";
import type { GetSubscriberOptions, GetWebhooksReturnType } from "./getWebhooks";
import sendOrSchedulePayload from "./sendOrSchedulePayload";
const log = logger.getSubLogger({ prefix: ["[WebhookService] "] });
/** This is a WIP. With minimal methods until the API matures and stabilizes */
export class WebhookService {
private options = {} as Parameters<typeof getWebhooks>[0];
private webhooks: Awaited<ReturnType<typeof getWebhooks>> = [];
constructor(options: Parameters<typeof getWebhooks>[0]) {
return (async (): Promise<WebhookService> => {
this.options = options;
this.webhooks = await getWebhooks(options);
return this;
})() as unknown as WebhookService;
private constructor(private options: GetSubscriberOptions, private webhooks: GetWebhooksReturnType) {}
static async init(options: GetSubscriberOptions) {
const webhooks = await getWebhooks(options);
return new WebhookService(options, webhooks);
}
getWebhooks() {
return this.webhooks;
+21 -11
View File
@@ -1,6 +1,7 @@
import { withReporting } from "@calcom/lib/sentryWrapper";
import defaultPrisma from "@calcom/prisma";
import type { PrismaClient } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
export type GetSubscriberOptions = {
@@ -12,7 +13,25 @@ export type GetSubscriberOptions = {
oAuthClientId?: string | null;
};
const getWebhooks = async (options: GetSubscriberOptions, prisma: PrismaClient = defaultPrisma) => {
const webhookSelect = {
id: true,
subscriberUrl: true,
payloadTemplate: true,
appId: true,
secret: true,
time: true,
timeUnit: true,
eventTriggers: true,
} satisfies Prisma.WebhookSelect;
export type GetWebhooksReturnType = Prisma.WebhookGetPayload<{
select: typeof webhookSelect;
}>[];
const getWebhooks = async (
options: GetSubscriberOptions,
prisma: PrismaClient = defaultPrisma
): Promise<GetWebhooksReturnType> => {
const teamId = options.teamId;
const userId = options.userId ?? 0;
const eventTypeId = options.eventTypeId ?? 0;
@@ -66,16 +85,7 @@ const getWebhooks = async (options: GetSubscriberOptions, prisma: PrismaClient =
},
},
},
select: {
id: true,
subscriberUrl: true,
payloadTemplate: true,
appId: true,
secret: true,
time: true,
timeUnit: true,
eventTriggers: true,
},
select: webhookSelect,
});
return allWebhooks;
@@ -129,7 +129,7 @@ export const paymentsRouter = router({
const userId = ctx.user.id || 0;
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: userId });
const eventTypeId = booking.eventTypeId || 0;
const webhooks = await new WebhookService({
const webhooks = await WebhookService.init({
userId,
eventTypeId,
triggerEvent: WebhookTriggerEvents.BOOKING_PAID,