fix: improve api/cron/webhookTriggers endpoint (#14188)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
Carina Wollendorfer
2024-04-04 10:01:57 -07:00
committed by GitHub
co-authored by CarinaWolli
parent 1132d9d0dc
commit ed0cd30092
5 changed files with 62 additions and 48 deletions
+30 -31
View File
@@ -31,20 +31,35 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
lte: dayjs().toISOString(),
},
},
select: {
id: true,
jobName: true,
payload: true,
subscriberUrl: true,
webhook: {
select: {
secret: true,
},
},
},
});
const fetchPromises: Promise<any>[] = [];
// run jobs
for (const job of jobsToRun) {
// Fetch the webhook configuration so that we can get the secret.
const [appId, subscriberId] = job.jobName.split("_");
let webhook = job.webhook;
let webhook;
try {
webhook = await prisma.webhook.findUniqueOrThrow({
where: { id: subscriberId, appId: appId !== "null" ? appId : null },
});
} catch {
logger.error(`Error finding webhook for subscriberId: ${subscriberId}, appId: ${appId}`);
if (!webhook) {
try {
webhook = await prisma.webhook.findUniqueOrThrow({
where: { id: subscriberId, appId: appId !== "null" ? appId : null },
});
} catch {
logger.error(`Error finding webhook for subscriberId: ${subscriberId}, appId: ${appId}`);
}
}
const headers: Record<string, string> = {
@@ -56,33 +71,15 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
headers["X-Cal-Signature-256"] = createWebhookSignature({ secret: webhook.secret, body: job.payload });
}
try {
await fetch(job.subscriberUrl, {
fetchPromises.push(
fetch(job.subscriberUrl, {
method: "POST",
body: job.payload,
headers,
});
} catch (error) {
console.log(`Error running webhook trigger (retry count: ${job.retryCount}): ${error}`);
// if job fails, retry again for 5 times.
if (job.retryCount < 5) {
await prisma.webhookScheduledTriggers.update({
where: {
id: job.id,
},
data: {
retryCount: {
increment: 1,
},
startAfter: dayjs()
.add(5 * (job.retryCount + 1), "minutes")
.toISOString(),
},
});
return res.json({ ok: false });
}
}
}).catch((error) => {
console.error(`Webhook trigger for subscriber url ${job.subscriberUrl} failed with error: ${error}`);
})
);
const parsedJobPayload = JSON.parse(job.payload) as {
id: number; // booking id
@@ -122,6 +119,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
});
}
Promise.allSettled(fetchPromises);
res.json({ ok: true });
}
@@ -297,6 +297,11 @@ export async function scheduleTrigger(
payload,
startAfter: triggerEvent === WebhookTriggerEvents.MEETING_ENDED ? booking.endTime : booking.startTime,
subscriberUrl,
webhook: {
connect: {
id: subscriber.id,
},
},
},
});
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "WebhookScheduledTriggers" ADD COLUMN "webhookId" TEXT;
-- AddForeignKey
ALTER TABLE "WebhookScheduledTriggers" ADD CONSTRAINT "WebhookScheduledTriggers_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "Webhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+19 -16
View File
@@ -691,23 +691,24 @@ enum WebhookTriggerEvents {
}
model Webhook {
id String @id @unique
userId Int?
teamId Int?
eventTypeId Int?
id String @id @unique
userId Int?
teamId Int?
eventTypeId Int?
/// @zod.url()
subscriberUrl String
payloadTemplate String?
createdAt DateTime @default(now())
active Boolean @default(true)
eventTriggers WebhookTriggerEvents[]
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
app App? @relation(fields: [appId], references: [slug], onDelete: Cascade)
appId String?
secret String?
platform Boolean @default(false)
subscriberUrl String
payloadTemplate String?
createdAt DateTime @default(now())
active Boolean @default(true)
eventTriggers WebhookTriggerEvents[]
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
app App? @relation(fields: [appId], references: [slug], onDelete: Cascade)
appId String?
secret String?
platform Boolean @default(false)
scheduledTriggers WebhookScheduledTriggers[]
@@unique([userId, subscriberUrl], name: "courseIdentifier")
}
@@ -989,6 +990,8 @@ model WebhookScheduledTriggers {
startAfter DateTime
retryCount Int @default(0)
createdAt DateTime? @default(now())
webhookId String?
webhook Webhook? @relation(fields: [webhookId], references: [id], onDelete: Cascade)
}
enum WorkflowTemplates {
+3 -1
View File
@@ -1,7 +1,7 @@
import * as z from "zod"
import * as imports from "../zod-utils"
import { WebhookTriggerEvents } from "@prisma/client"
import { CompleteUser, UserModel, CompleteTeam, TeamModel, CompleteEventType, EventTypeModel, CompleteApp, AppModel } from "./index"
import { CompleteUser, UserModel, CompleteTeam, TeamModel, CompleteEventType, EventTypeModel, CompleteApp, AppModel, CompleteWebhookScheduledTriggers, WebhookScheduledTriggersModel } from "./index"
export const _WebhookModel = z.object({
id: z.string(),
@@ -23,6 +23,7 @@ export interface CompleteWebhook extends z.infer<typeof _WebhookModel> {
team?: CompleteTeam | null
eventType?: CompleteEventType | null
app?: CompleteApp | null
scheduledTriggers: CompleteWebhookScheduledTriggers[]
}
/**
@@ -35,4 +36,5 @@ export const WebhookModel: z.ZodSchema<CompleteWebhook> = z.lazy(() => _WebhookM
team: TeamModel.nullish(),
eventType: EventTypeModel.nullish(),
app: AppModel.nullish(),
scheduledTriggers: WebhookScheduledTriggersModel.array(),
}))