Revert "perf: use repository for webhooks list query & caching in /settings/developer/webhooks/... RSC (#21781)" (#22963)

This commit is contained in:
Joe Au-Yeung
2025-08-07 11:55:13 -07:00
committed by GitHub
parent 815e13303b
commit cfefba6d4d
11 changed files with 79 additions and 170 deletions
@@ -1,7 +1,7 @@
"use server";
import { revalidateTag } from "next/cache";
import { revalidatePath } from "next/cache";
export async function revalidateWebhooksListGetByViewer() {
revalidateTag("viewer.webhook.getByViewer");
export async function revalidateWebhooksList() {
revalidatePath("/settings/developer/webhooks");
}
@@ -1,13 +1,13 @@
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import { unstable_cache } from "next/cache";
import { cookies, headers } from "next/headers";
import { redirect } from "next/navigation";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import WebhooksView from "@calcom/features/webhooks/pages/webhooks-view";
import { APP_NAME } from "@calcom/lib/constants";
import { WebhookRepository } from "@calcom/lib/server/repository/webhook";
import { UserPermissionRole } from "@calcom/prisma/enums";
import { webhookRouter } from "@calcom/trpc/server/routers/viewer/webhook/_router";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
@@ -20,17 +20,6 @@ export const generateMetadata = async () =>
"/settings/developer/webhooks"
);
const getCachedWebhooksList = unstable_cache(
async ({ userId, userRole }: { userId: number; userRole?: UserPermissionRole }) => {
return await WebhookRepository.getAllWebhooksByUserId({
userId,
userRole,
});
},
undefined,
{ revalidate: 3600, tags: ["viewer.webhook.getByViewer"] }
);
const WebhooksViewServerWrapper = async () => {
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
if (!session?.user?.id) {
@@ -38,9 +27,8 @@ const WebhooksViewServerWrapper = async () => {
}
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
const userRole = session.user.role !== "INACTIVE_ADMIN" ? session.user.role : undefined;
const data = await getCachedWebhooksList({ userId: session.user.id, userRole });
const caller = await createRouterCaller(webhookRouter);
const data = await caller.getByViewer();
return <WebhooksView data={data} isAdmin={isAdmin} />;
};
@@ -1,7 +0,0 @@
"use server";
import { revalidateTag } from "next/cache";
export async function revalidateWebhookById(id: string) {
revalidateTag(`viewer.webhook.get:${id}`);
}
@@ -1,6 +1,5 @@
import type { PageProps } from "app/_types";
import { getTranslate, _generateMetadata } from "app/_utils";
import { unstable_cache } from "next/cache";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { EditWebhookView } from "@calcom/features/webhooks/pages/webhook-edit-view";
@@ -16,24 +15,12 @@ export const generateMetadata = async ({ params }: { params: Promise<{ id: strin
`/settings/developer/webhooks/${(await params).id}`
);
const getCachedWebhook = (id?: string) => {
const fn = unstable_cache(
async () => {
return await WebhookRepository.findByWebhookId(id);
},
undefined,
{ revalidate: 3600, tags: [`viewer.webhook.get:${id}`] }
);
return fn();
};
const Page = async ({ params: _params }: PageProps) => {
const t = await getTranslate();
const params = await _params;
const id = typeof params?.id === "string" ? params.id : undefined;
const webhook = await getCachedWebhook(id);
const webhook = await WebhookRepository.findByWebhookId(id);
return (
<SettingsHeader
@@ -1,7 +0,0 @@
"use server";
import { revalidateTag } from "next/cache";
export async function revalidateWebhookList() {
revalidateTag("viewer.webhook.list");
}
@@ -1,16 +1,10 @@
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import { unstable_cache } from "next/cache";
import { cookies, headers } from "next/headers";
import { redirect } from "next/navigation";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { NewWebhookView } from "@calcom/features/webhooks/pages/webhook-new-view";
import { APP_NAME } from "@calcom/lib/constants";
import { WebhookRepository } from "@calcom/lib/server/repository/webhook";
import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
import { webhookRouter } from "@calcom/trpc/server/routers/viewer/webhook/_router";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -21,25 +15,15 @@ export const generateMetadata = async () =>
"/settings/developer/webhooks/new"
);
const getCachedWebhooksList = unstable_cache(
async ({ userId }: { userId: number }) => {
return await WebhookRepository.findWebhooksByFilters({ userId });
},
undefined,
{ revalidate: 3600, tags: ["viewer.webhook.list"] }
);
const Page = async () => {
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
if (!session?.user?.id) {
redirect("/auth/login");
}
const appsCaller = await createRouterCaller(appsRouter);
const [appsCaller, webhookCaller] = await Promise.all([
createRouterCaller(appsRouter),
createRouterCaller(webhookRouter),
]);
const [installedApps, webhooks] = await Promise.all([
appsCaller.integrations({ variant: "other", onlyInstalled: true }),
getCachedWebhooksList({ userId: session.user.id }),
webhookCaller.list(),
]);
return <NewWebhookView webhooks={webhooks} installedApps={installedApps} />;
@@ -18,8 +18,7 @@ import { Switch } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { revalidateEventTypeEditPage } from "@calcom/web/app/(use-page-wrapper)/event-types/[type]/actions";
import { revalidateWebhooksListGetByViewer } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
import { revalidateWebhookList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/new/actions";
import { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
type WebhookProps = {
id: string;
@@ -47,19 +46,21 @@ export default function WebhookListItem(props: {
const deleteWebhook = trpc.viewer.webhook.delete.useMutation({
async onSuccess() {
if (webhook.eventTypeId) revalidateEventTypeEditPage(webhook.eventTypeId);
revalidateWebhooksListGetByViewer();
revalidateWebhookList();
revalidateWebhooksList();
showToast(t("webhook_removed_successfully"), "success");
await utils.viewer.webhook.getByViewer.invalidate();
await utils.viewer.webhook.list.invalidate();
await utils.viewer.eventTypes.get.invalidate();
},
});
const toggleWebhook = trpc.viewer.webhook.edit.useMutation({
async onSuccess(data) {
if (webhook.eventTypeId) revalidateEventTypeEditPage(webhook.eventTypeId);
revalidateWebhooksListGetByViewer();
revalidateWebhookList();
revalidateWebhooksList();
// TODO: Better success message
showToast(t(data?.active ? "enabled" : "disabled"), "success");
await utils.viewer.webhook.getByViewer.invalidate();
await utils.viewer.webhook.list.invalidate();
await utils.viewer.eventTypes.get.invalidate();
},
});
@@ -7,9 +7,7 @@ import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
import { SkeletonContainer } from "@calcom/ui/components/skeleton";
import { showToast } from "@calcom/ui/components/toast";
import { revalidateWebhooksListGetByViewer } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
import { revalidateWebhookById } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/actions";
import { revalidateWebhookList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/new/actions";
import { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
import type { WebhookFormSubmitData } from "../components/WebhookForm";
import WebhookForm from "../components/WebhookForm";
@@ -45,10 +43,10 @@ export function EditWebhookView({ webhook }: { webhook?: WebhookProps }) {
});
const editWebhookMutation = trpc.viewer.webhook.edit.useMutation({
async onSuccess() {
revalidateWebhookById(webhook?.id ?? "");
revalidateWebhookList();
await utils.viewer.webhook.list.invalidate();
await utils.viewer.webhook.get.invalidate({ webhookId: webhook?.id });
showToast(t("webhook_updated_successfully"), "success");
revalidateWebhooksListGetByViewer();
revalidateWebhooksList();
router.push("/settings/developer/webhooks");
},
onError(error) {
@@ -10,8 +10,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { RouterOutputs } from "@calcom/trpc/react";
import { showToast } from "@calcom/ui/components/toast";
import { revalidateWebhooksListGetByViewer } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
import { revalidateWebhookList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/new/actions";
import { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
import type { WebhookFormSubmitData } from "../components/WebhookForm";
import WebhookForm from "../components/WebhookForm";
@@ -36,8 +35,7 @@ export const NewWebhookView = ({ webhooks, installedApps }: Props) => {
async onSuccess() {
showToast(t("webhook_created_successfully"), "success");
await utils.viewer.webhook.list.invalidate();
revalidateWebhookList();
revalidateWebhooksListGetByViewer();
revalidateWebhooksList();
router.push("/settings/developer/webhooks");
},
onError(error) {
-83
View File
@@ -1,7 +1,3 @@
import type { Prisma } from "@prisma/client";
import { z } from "zod";
import { WEBHOOK_TRIGGER_EVENTS } from "@calcom/features/webhooks/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { compareMembership } from "@calcom/lib/event-types/getEventTypesByViewer";
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
@@ -23,24 +19,6 @@ type WebhookGroup = {
webhooks: Webhook[];
};
const webhookIdAndEventTypeIdSchema = z.object({
// Webhook ID
id: z.string().optional(),
eventTypeId: z.number().optional(),
teamId: z.number().optional(),
});
const ZFindWebhooksByFiltersInputSchema = webhookIdAndEventTypeIdSchema
.extend({
appId: z.string().optional(),
teamId: z.number().optional(),
eventTypeId: z.number().optional(),
eventTriggers: z.enum(WEBHOOK_TRIGGER_EVENTS).array().optional(),
})
.optional();
export type TFindWebhooksByFiltersInputSchema = z.infer<typeof ZFindWebhooksByFiltersInputSchema>;
const filterWebhooks = (webhook: Webhook) => {
const appIds = [
"zapier",
@@ -202,65 +180,4 @@ export class WebhookRepository {
},
});
}
static async findWebhooksByFilters({
userId,
input,
}: {
userId: number;
input?: TFindWebhooksByFiltersInputSchema;
}) {
const where: Prisma.WebhookWhereInput = {
/* Don't mixup zapier webhooks with normal ones */
AND: [{ appId: !input?.appId ? null : input.appId }],
};
const user = await prisma.user.findUnique({
where: {
id: userId,
},
select: {
teams: true,
},
});
if (Array.isArray(where.AND)) {
if (input?.eventTypeId) {
const managedParentEvt = await prisma.eventType.findFirst({
where: {
id: input.eventTypeId,
parentId: {
not: null,
},
},
select: {
parentId: true,
},
});
if (managedParentEvt?.parentId) {
where.AND?.push({
OR: [
{ eventTypeId: input.eventTypeId },
{ eventTypeId: managedParentEvt.parentId, active: true },
],
});
} else {
where.AND?.push({ eventTypeId: input.eventTypeId });
}
} else {
where.AND?.push({
OR: [{ userId }, { teamId: { in: user?.teams.map((membership) => membership.teamId) } }],
});
}
if (input?.eventTriggers) {
where.AND?.push({ eventTriggers: { hasEvery: input.eventTriggers } });
}
}
return await prisma.webhook.findMany({
where,
});
}
}
@@ -1,4 +1,6 @@
import { WebhookRepository } from "@calcom/lib/server/repository/webhook";
import type { Prisma } from "@prisma/client";
import { prisma } from "@calcom/prisma";
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
import type { TListInputSchema } from "./list.schema";
@@ -11,5 +13,53 @@ type ListOptions = {
};
export const listHandler = async ({ ctx, input }: ListOptions) => {
return await WebhookRepository.findWebhooksByFilters({ userId: ctx.user.id, input });
const where: Prisma.WebhookWhereInput = {
/* Don't mixup zapier webhooks with normal ones */
AND: [{ appId: !input?.appId ? null : input.appId }],
};
const user = await prisma.user.findUnique({
where: {
id: ctx.user.id,
},
select: {
teams: true,
},
});
if (Array.isArray(where.AND)) {
if (input?.eventTypeId) {
const managedParentEvt = await prisma.eventType.findFirst({
where: {
id: input.eventTypeId,
parentId: {
not: null,
},
},
select: {
parentId: true,
},
});
if (managedParentEvt?.parentId) {
where.AND?.push({
OR: [{ eventTypeId: input.eventTypeId }, { eventTypeId: managedParentEvt.parentId, active: true }],
});
} else {
where.AND?.push({ eventTypeId: input.eventTypeId });
}
} else {
where.AND?.push({
OR: [{ userId: ctx.user.id }, { teamId: { in: user?.teams.map((membership) => membership.teamId) } }],
});
}
if (input?.eventTriggers) {
where.AND?.push({ eventTriggers: { hasEvery: input.eventTriggers } });
}
}
return await prisma.webhook.findMany({
where,
});
};