add subscriptionType + refactor code
parent
0198285ccc
commit
4bfcefbc68
|
@ -29,8 +29,7 @@ export default function WebhookDialogForm(props: {
|
|||
subscriberUrl: "",
|
||||
active: true,
|
||||
payloadTemplate: null,
|
||||
isZapierSubscription,
|
||||
} as Omit<TWebhook, "userId" | "createdAt" | "eventTypeId">,
|
||||
} as Omit<TWebhook, "userId" | "createdAt" | "eventTypeId" | "subscriptionType">,
|
||||
} = props;
|
||||
|
||||
const [useCustomPayloadTemplate, setUseCustomPayloadTemplate] = useState(!!defaultValues.payloadTemplate);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { SubscriptionType } from "@prisma/client";
|
||||
import { compile } from "handlebars";
|
||||
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
@ -29,6 +30,7 @@ const sendPayload = async (
|
|||
metadata?: { [key: string]: string };
|
||||
rescheduleUid?: string;
|
||||
},
|
||||
subscriptionType: SubscriptionType,
|
||||
template?: string | null
|
||||
) => {
|
||||
if (!subscriberUrl || !data) {
|
||||
|
@ -38,13 +40,21 @@ const sendPayload = async (
|
|||
const contentType =
|
||||
!template || jsonParse(template) ? "application/json" : "application/x-www-form-urlencoded";
|
||||
|
||||
const body = template
|
||||
? applyTemplate(template, data, contentType)
|
||||
: JSON.stringify({
|
||||
triggerEvent: triggerEvent,
|
||||
createdAt: createdAt,
|
||||
payload: data,
|
||||
});
|
||||
data.description = data.description || data.additionalNotes;
|
||||
|
||||
let body;
|
||||
|
||||
if (subscriptionType === SubscriptionType.ZAPIER) {
|
||||
body = JSON.stringify(data);
|
||||
} else if (template) {
|
||||
body = applyTemplate(template, data, contentType);
|
||||
} else {
|
||||
body = JSON.stringify({
|
||||
triggerEvent: triggerEvent,
|
||||
createdAt: createdAt,
|
||||
payload: data,
|
||||
});
|
||||
}
|
||||
|
||||
const response = await fetch(subscriberUrl, {
|
||||
method: "POST",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { WebhookTriggerEvents } from "@prisma/client";
|
||||
import { SubscriptionType } from "@prisma/client";
|
||||
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
|
@ -6,10 +7,11 @@ export type GetSubscriberOptions = {
|
|||
userId: number;
|
||||
eventTypeId: number;
|
||||
triggerEvent: WebhookTriggerEvents;
|
||||
subscriptionType: SubscriptionType;
|
||||
};
|
||||
|
||||
const getSubscribers = async (options: GetSubscriberOptions) => {
|
||||
const { userId, eventTypeId } = options;
|
||||
const { userId, eventTypeId, subscriptionType } = options;
|
||||
const allWebhooks = await prisma.webhook.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
|
@ -27,14 +29,13 @@ const getSubscribers = async (options: GetSubscriberOptions) => {
|
|||
active: {
|
||||
equals: true,
|
||||
},
|
||||
isZapierSubscription: {
|
||||
equals: false,
|
||||
},
|
||||
subscriptionType: subscriptionType,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
subscriberUrl: true,
|
||||
payloadTemplate: true,
|
||||
subscriptionType: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import { WebhookTriggerEvents } from "@prisma/client";
|
||||
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
export type GetSubscriberOptions = {
|
||||
userId: number;
|
||||
triggerEvent: WebhookTriggerEvents;
|
||||
};
|
||||
|
||||
const getZapierSubscribers = async (options: GetSubscriberOptions) => {
|
||||
const { userId, triggerEvent } = options;
|
||||
const allSubscribers = await prisma.webhook.findMany({
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
userId,
|
||||
},
|
||||
{
|
||||
isZapierSubscription: true,
|
||||
},
|
||||
{
|
||||
eventTriggers: {
|
||||
has: triggerEvent,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
subscriberUrl: true,
|
||||
payloadTemplate: true,
|
||||
},
|
||||
});
|
||||
|
||||
return allSubscribers;
|
||||
};
|
||||
|
||||
export default getZapierSubscribers;
|
|
@ -6,6 +6,7 @@ import {
|
|||
SchedulingType,
|
||||
WebhookTriggerEvents,
|
||||
} from "@prisma/client";
|
||||
import { SubscriptionType } from "@prisma/client";
|
||||
import async from "async";
|
||||
import dayjs from "dayjs";
|
||||
import dayjsBusinessTime from "dayjs-business-time";
|
||||
|
@ -40,7 +41,6 @@ import prisma from "@lib/prisma";
|
|||
import { BookingCreateBody } from "@lib/types/booking";
|
||||
import sendPayload from "@lib/webhooks/sendPayload";
|
||||
import getSubscribers from "@lib/webhooks/subscriptions";
|
||||
import getZapierSubscribers from "@lib/zapier/subscriptions";
|
||||
|
||||
import { getTranslation } from "@server/lib/i18n";
|
||||
|
||||
|
@ -750,11 +750,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
});
|
||||
|
||||
// Send Webhook call if hooked to BOOKING_CREATED & BOOKING_RESCHEDULED
|
||||
const subscribers = await getSubscribers(subscriberOptions);
|
||||
//todo: check if zapier is installed
|
||||
const subscribers = await getSubscribers({
|
||||
...subscriberOptions,
|
||||
subscriptionType: SubscriptionType.WEBHOOK,
|
||||
});
|
||||
|
||||
if (zapierAppInstalled) {
|
||||
const zapierSubscribers = await getZapierSubscribers(subscriberOptions);
|
||||
const zapierSubscribers = await getSubscribers({
|
||||
...subscriberOptions,
|
||||
subscriptionType: SubscriptionType.ZAPIER,
|
||||
});
|
||||
subscribers.push(...zapierSubscribers);
|
||||
}
|
||||
|
||||
|
@ -772,6 +777,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
rescheduleUid,
|
||||
metadata: reqBody.metadata,
|
||||
},
|
||||
sub.subscriptionType,
|
||||
sub.payloadTemplate
|
||||
).catch((e) => {
|
||||
console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${sub.subscriberUrl}`, e);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { BookingStatus, Credential, WebhookTriggerEvents } from "@prisma/client";
|
||||
import { SubscriptionType } from "@prisma/client";
|
||||
import async from "async";
|
||||
import dayjs from "dayjs";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
@ -15,7 +16,6 @@ import { sendCancelledEmails } from "@lib/emails/email-manager";
|
|||
import prisma from "@lib/prisma";
|
||||
import sendPayload from "@lib/webhooks/sendPayload";
|
||||
import getSubscribers from "@lib/webhooks/subscriptions";
|
||||
import getZapierSubscribers from "@lib/zapier/subscriptions";
|
||||
|
||||
import { getTranslation } from "@server/lib/i18n";
|
||||
|
||||
|
@ -149,19 +149,34 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
},
|
||||
});
|
||||
|
||||
const subscribers = await getSubscribers(subscriberOptions);
|
||||
const subscribers = await getSubscribers({
|
||||
...subscriberOptions,
|
||||
subscriptionType: SubscriptionType.WEBHOOK,
|
||||
});
|
||||
//const allSubscribersPromises = [getSubscribers(subscriberOptions)]
|
||||
|
||||
if (zapierAppInstalled) {
|
||||
const zapierSubscribers = await getZapierSubscribers(subscriberOptions);
|
||||
const zapierSubscribers = await getSubscribers({
|
||||
...subscriberOptions,
|
||||
subscriptionType: SubscriptionType.ZAPIER,
|
||||
});
|
||||
subscribers.push(...zapierSubscribers);
|
||||
//allSubscribersPromises.push(getZapierSubscribers(subscriberOptions));
|
||||
}
|
||||
|
||||
//await Promise.all(allSubscribersPromises)
|
||||
|
||||
const promises = subscribers.map((sub) =>
|
||||
sendPayload(eventTrigger, new Date().toISOString(), sub.subscriberUrl, evt, sub.payloadTemplate).catch(
|
||||
(e) => {
|
||||
console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${sub.subscriberUrl}`, e);
|
||||
}
|
||||
)
|
||||
sendPayload(
|
||||
eventTrigger,
|
||||
new Date().toISOString(),
|
||||
sub.subscriberUrl,
|
||||
evt,
|
||||
sub.subscriptionType,
|
||||
sub.payloadTemplate
|
||||
).catch((e) => {
|
||||
console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${sub.subscriberUrl}`, e);
|
||||
})
|
||||
);
|
||||
await Promise.all(promises);
|
||||
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import prisma from "@lib/prisma";
|
||||
import findValidApiKey from "@lib/zapier/findValidApiKey";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.query.apiKey as string;
|
||||
|
||||
if (!apiKey) {
|
||||
return res.status(401).json({ message: "No API key provided" });
|
||||
}
|
||||
|
||||
const validKey = await findValidApiKey(apiKey);
|
||||
|
||||
if (!validKey) {
|
||||
return res.status(401).json({ message: "API not valid" });
|
||||
}
|
||||
|
||||
const subscriptionId = req.query.subscriptionId as string;
|
||||
|
||||
if (req.method === "DELETE") {
|
||||
try {
|
||||
await prisma.webhook.delete({
|
||||
where: {
|
||||
id: subscriptionId,
|
||||
},
|
||||
});
|
||||
res.status(201).json({ message: "Subscription deleted." });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({ message: "Unable to delete subscription." });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import { SubscriptionType } from "@prisma/client";
|
||||
import { v4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
|
@ -21,14 +22,18 @@ export const webhookRouter = createProtectedRouter()
|
|||
return await ctx.prisma.webhook.findMany({
|
||||
where: {
|
||||
eventTypeId: input.eventTypeId,
|
||||
isZapierSubscription: false,
|
||||
NOT: {
|
||||
subscriptionType: SubscriptionType.ZAPIER,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return await ctx.prisma.webhook.findMany({
|
||||
where: {
|
||||
userId: ctx.user.id,
|
||||
isZapierSubscription: false,
|
||||
NOT: {
|
||||
subscriptionType: SubscriptionType.ZAPIER,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
|
@ -40,13 +45,15 @@ export const webhookRouter = createProtectedRouter()
|
|||
active: z.boolean(),
|
||||
payloadTemplate: z.string().nullable(),
|
||||
eventTypeId: z.number().optional(),
|
||||
subscriptionType: z.nativeEnum(SubscriptionType).optional(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const subscriptionType = input.subscriptionType || SubscriptionType.WEBHOOK;
|
||||
if (input.eventTypeId) {
|
||||
return await ctx.prisma.webhook.create({
|
||||
data: {
|
||||
id: v4(),
|
||||
isZapierSubscription: false,
|
||||
subscriptionType,
|
||||
...input,
|
||||
},
|
||||
});
|
||||
|
@ -54,7 +61,7 @@ export const webhookRouter = createProtectedRouter()
|
|||
return await ctx.prisma.webhook.create({
|
||||
data: {
|
||||
id: v4(),
|
||||
isZapierSubscription: false,
|
||||
subscriptionType,
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
},
|
||||
|
@ -69,6 +76,7 @@ export const webhookRouter = createProtectedRouter()
|
|||
active: z.boolean().optional(),
|
||||
payloadTemplate: z.string().nullable(),
|
||||
eventTypeId: z.number().optional(),
|
||||
subscriptionType: z.nativeEnum(SubscriptionType).optional(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const { id, ...data } = input;
|
||||
|
@ -174,7 +182,14 @@ export const webhookRouter = createProtectedRouter()
|
|||
};
|
||||
|
||||
try {
|
||||
return await sendPayload(type, new Date().toISOString(), url, data, payloadTemplate);
|
||||
return await sendPayload(
|
||||
type,
|
||||
new Date().toISOString(),
|
||||
url,
|
||||
data,
|
||||
SubscriptionType.WEBHOOK,
|
||||
payloadTemplate
|
||||
);
|
||||
} catch (_err) {
|
||||
const error = getErrorFromUnknown(_err);
|
||||
return {
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
export { default as add } from "./add";
|
||||
export { default as listBookings } from "./subscriptions/listBookings";
|
||||
export { default as deleteSubscription } from "./subscriptions/deleteSubscription";
|
||||
export { default as addSubscription } from "./subscriptions/addSubscription";
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
import prisma from "@lib/prisma";
|
||||
import findValidApiKey from "@lib/zapier/findValidApiKey";
|
||||
import findValidApiKey from "@calcom/ee/lib/api/findValidApiKey";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { SubscriptionType } from "@prisma/client";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.query.apiKey as string;
|
||||
|
@ -28,13 +29,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
eventTriggers: [triggerEvent],
|
||||
subscriberUrl,
|
||||
active: true,
|
||||
isZapierSubscription: true,
|
||||
subscriptionType: SubscriptionType.ZAPIER,
|
||||
},
|
||||
});
|
||||
res.status(201).json(createSubscription);
|
||||
res.status(200).json(createSubscription);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({ message: "Unable to create subscription." });
|
||||
return res.status(500).json({ message: "Could not create subscription." });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import findValidApiKey from "@calcom/ee/lib/api/findValidApiKey";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.query.apiKey as string;
|
||||
|
||||
if (!apiKey) {
|
||||
return res.status(401).json({ message: "No API key provided" });
|
||||
}
|
||||
|
||||
const validKey = await findValidApiKey(apiKey);
|
||||
|
||||
if (!validKey) {
|
||||
return res.status(401).json({ message: "API not valid" });
|
||||
}
|
||||
|
||||
const id = req.query.id as string; //maybe change that again
|
||||
|
||||
if (req.method === "DELETE") {
|
||||
await prisma.webhook.delete({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
res.status(204).json({ message: "Subscription is deleted." });
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import prisma from "@lib/prisma";
|
||||
import findValidApiKey from "@lib/zapier/findValidApiKey";
|
||||
import findValidApiKey from "@calcom/ee/lib/api/findValidApiKey";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.query.apiKey as string;
|
||||
|
@ -20,7 +18,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
|
||||
if (req.method === "GET") {
|
||||
try {
|
||||
const bookings = await prisma.booking.findFirst({
|
||||
const bookings = await prisma.booking.findMany({
|
||||
take:3,
|
||||
where: {
|
||||
userId: validKey.userId,
|
||||
},
|
||||
|
@ -41,9 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
},
|
||||
});
|
||||
|
||||
const payload = [{ payload: { ...bookings } }];
|
||||
|
||||
res.status(201).json(payload);
|
||||
res.status(201).json(bookings);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({ message: "Unable to get bookings." });
|
|
@ -1,20 +1,14 @@
|
|||
import { hashAPIKey } from "@calcom/ee/lib/api/apiKeys";
|
||||
|
||||
import prisma from "@lib/prisma";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
const findValidApiKey = async (apiKey: string) => {
|
||||
const hashedKey = hashAPIKey(apiKey.substring(4));
|
||||
const hashedKey = hashAPIKey(apiKey.substring(process.env.API_KEY_PREFIX?.length || 0));
|
||||
const validKey = await prisma.apiKey.findFirst({
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
hashedKey,
|
||||
},
|
||||
{
|
||||
createdAt: {
|
||||
lt: new Date(Date.now()),
|
||||
},
|
||||
},
|
||||
{
|
||||
expiresAt: {
|
||||
gte: new Date(Date.now()),
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `isZapierSubscription` to the `Webhook` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Webhook" ADD COLUMN "isZapierSubscription" BOOLEAN NOT NULL;
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `subscriptionType` to the `Webhook` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SubscriptionType" AS ENUM ('WEBHOOK', 'ZAPIER');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Webhook" ADD COLUMN "subscriptionType" "SubscriptionType" NOT NULL;
|
|
@ -363,18 +363,23 @@ enum WebhookTriggerEvents {
|
|||
BOOKING_CANCELLED
|
||||
}
|
||||
|
||||
enum SubscriptionType {
|
||||
WEBHOOK
|
||||
ZAPIER
|
||||
}
|
||||
|
||||
model Webhook {
|
||||
id String @id @unique
|
||||
userId Int?
|
||||
eventTypeId Int?
|
||||
subscriberUrl String
|
||||
payloadTemplate String?
|
||||
createdAt DateTime @default(now())
|
||||
active Boolean @default(true)
|
||||
eventTriggers WebhookTriggerEvents[]
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
|
||||
isZapierSubscription Boolean
|
||||
id String @id @unique
|
||||
userId Int?
|
||||
eventTypeId Int?
|
||||
subscriberUrl String
|
||||
payloadTemplate String?
|
||||
createdAt DateTime @default(now())
|
||||
active Boolean @default(true)
|
||||
eventTriggers WebhookTriggerEvents[]
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
|
||||
subscriptionType SubscriptionType @default(WEBHOOK)
|
||||
}
|
||||
|
||||
model ApiKey {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as z from "zod"
|
||||
import * as imports from "../zod-utils"
|
||||
import { WebhookTriggerEvents } from "@prisma/client"
|
||||
import { WebhookTriggerEvents, SubscriptionType } from "@prisma/client"
|
||||
import { CompleteUser, UserModel, CompleteEventType, EventTypeModel } from "./index"
|
||||
|
||||
export const _WebhookModel = z.object({
|
||||
|
@ -12,7 +12,7 @@ export const _WebhookModel = z.object({
|
|||
createdAt: z.date(),
|
||||
active: z.boolean(),
|
||||
eventTriggers: z.nativeEnum(WebhookTriggerEvents).array(),
|
||||
isZapierSubscription: z.boolean(),
|
||||
subscriptionType: z.nativeEnum(SubscriptionType),
|
||||
})
|
||||
|
||||
export interface CompleteWebhook extends z.infer<typeof _WebhookModel> {
|
||||
|
|
Loading…
Reference in New Issue