diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions.ts b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions.ts
index fd9836607c..adab4c4a7e 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions.ts
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions.ts
@@ -1,7 +1,7 @@
"use server";
-import { revalidatePath } from "next/cache";
+import { revalidateTag } from "next/cache";
-export async function revalidateWebhooksList() {
- revalidatePath("/settings/developer/webhooks");
+export async function revalidateWebhooksListGetByViewer() {
+ revalidateTag("viewer.webhook.getByViewer");
}
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/page.tsx
index 93174bcd5f..aedc702dfb 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/page.tsx
@@ -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,6 +20,17 @@ 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) {
@@ -27,8 +38,9 @@ const WebhooksViewServerWrapper = async () => {
}
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
- const caller = await createRouterCaller(webhookRouter);
- const data = await caller.getByViewer();
+ const userRole = session.user.role !== "INACTIVE_ADMIN" ? session.user.role : undefined;
+
+ const data = await getCachedWebhooksList({ userId: session.user.id, userRole });
return ;
};
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/actions.ts b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/actions.ts
new file mode 100644
index 0000000000..0c0de6176c
--- /dev/null
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/actions.ts
@@ -0,0 +1,7 @@
+"use server";
+
+import { revalidateTag } from "next/cache";
+
+export async function revalidateWebhookById(id: string) {
+ revalidateTag(`viewer.webhook.get:${id}`);
+}
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/page.tsx
index 173de93ab2..37fce01dce 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/[id]/page.tsx
@@ -1,5 +1,6 @@
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";
@@ -15,12 +16,24 @@ 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 WebhookRepository.findByWebhookId(id);
+ const webhook = await getCachedWebhook(id);
return (
await _generateMetadata(
@@ -15,15 +21,25 @@ 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 [appsCaller, webhookCaller] = await Promise.all([
- createRouterCaller(appsRouter),
- createRouterCaller(webhookRouter),
- ]);
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
+ if (!session?.user?.id) {
+ redirect("/auth/login");
+ }
+
+ const appsCaller = await createRouterCaller(appsRouter);
const [installedApps, webhooks] = await Promise.all([
appsCaller.integrations({ variant: "other", onlyInstalled: true }),
- webhookCaller.list(),
+ getCachedWebhooksList({ userId: session.user.id }),
]);
return ;
diff --git a/packages/features/webhooks/components/WebhookListItem.tsx b/packages/features/webhooks/components/WebhookListItem.tsx
index 1d354da071..aa9177d853 100644
--- a/packages/features/webhooks/components/WebhookListItem.tsx
+++ b/packages/features/webhooks/components/WebhookListItem.tsx
@@ -18,7 +18,8 @@ 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 { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/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";
type WebhookProps = {
id: string;
@@ -46,21 +47,19 @@ export default function WebhookListItem(props: {
const deleteWebhook = trpc.viewer.webhook.delete.useMutation({
async onSuccess() {
if (webhook.eventTypeId) revalidateEventTypeEditPage(webhook.eventTypeId);
- revalidateWebhooksList();
+ revalidateWebhooksListGetByViewer();
+ revalidateWebhookList();
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);
- revalidateWebhooksList();
+ revalidateWebhooksListGetByViewer();
+ revalidateWebhookList();
// 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();
},
});
diff --git a/packages/features/webhooks/pages/webhook-edit-view.tsx b/packages/features/webhooks/pages/webhook-edit-view.tsx
index 48d87332cb..c09eb4cd57 100644
--- a/packages/features/webhooks/pages/webhook-edit-view.tsx
+++ b/packages/features/webhooks/pages/webhook-edit-view.tsx
@@ -7,7 +7,9 @@ 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 { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/actions";
+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 type { WebhookFormSubmitData } from "../components/WebhookForm";
import WebhookForm from "../components/WebhookForm";
@@ -43,10 +45,10 @@ export function EditWebhookView({ webhook }: { webhook?: WebhookProps }) {
});
const editWebhookMutation = trpc.viewer.webhook.edit.useMutation({
async onSuccess() {
- await utils.viewer.webhook.list.invalidate();
- await utils.viewer.webhook.get.invalidate({ webhookId: webhook?.id });
+ revalidateWebhookById(webhook?.id ?? "");
+ revalidateWebhookList();
showToast(t("webhook_updated_successfully"), "success");
- revalidateWebhooksList();
+ revalidateWebhooksListGetByViewer();
router.push("/settings/developer/webhooks");
},
onError(error) {
diff --git a/packages/features/webhooks/pages/webhook-new-view.tsx b/packages/features/webhooks/pages/webhook-new-view.tsx
index c2f5551197..06cc853641 100644
--- a/packages/features/webhooks/pages/webhook-new-view.tsx
+++ b/packages/features/webhooks/pages/webhook-new-view.tsx
@@ -10,7 +10,8 @@ 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 { revalidateWebhooksList } from "@calcom/web/app/(use-page-wrapper)/settings/(settings-layout)/developer/webhooks/(with-loader)/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 type { WebhookFormSubmitData } from "../components/WebhookForm";
import WebhookForm from "../components/WebhookForm";
@@ -35,7 +36,8 @@ export const NewWebhookView = ({ webhooks, installedApps }: Props) => {
async onSuccess() {
showToast(t("webhook_created_successfully"), "success");
await utils.viewer.webhook.list.invalidate();
- revalidateWebhooksList();
+ revalidateWebhookList();
+ revalidateWebhooksListGetByViewer();
router.push("/settings/developer/webhooks");
},
onError(error) {
diff --git a/packages/lib/server/repository/webhook.ts b/packages/lib/server/repository/webhook.ts
index a17cf3f86a..48eaca8293 100644
--- a/packages/lib/server/repository/webhook.ts
+++ b/packages/lib/server/repository/webhook.ts
@@ -1,3 +1,7 @@
+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";
@@ -19,6 +23,24 @@ 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;
+
const filterWebhooks = (webhook: Webhook) => {
const appIds = [
"zapier",
@@ -180,4 +202,65 @@ 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,
+ });
+ }
}
diff --git a/packages/trpc/server/routers/viewer/webhook/list.handler.ts b/packages/trpc/server/routers/viewer/webhook/list.handler.ts
index 120223ec81..11205add04 100644
--- a/packages/trpc/server/routers/viewer/webhook/list.handler.ts
+++ b/packages/trpc/server/routers/viewer/webhook/list.handler.ts
@@ -1,6 +1,4 @@
-import type { Prisma } from "@prisma/client";
-
-import { prisma } from "@calcom/prisma";
+import { WebhookRepository } from "@calcom/lib/server/repository/webhook";
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
import type { TListInputSchema } from "./list.schema";
@@ -13,53 +11,5 @@ type ListOptions = {
};
export const listHandler = async ({ ctx, input }: ListOptions) => {
- 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,
- });
+ return await WebhookRepository.findWebhooksByFilters({ userId: ctx.user.id, input });
};