From e0debf87a71be75b5fbed1c24efdd838220d0180 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 22 Jul 2026 14:58:25 +0200 Subject: [PATCH] fix(call-recorder): listen proper updated fileds event (#23135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context Around meeting-end peaks (~6pm), Recall/Svix delivers event bursts for every recorded call across the 700+ workspaces the app is installed on. Each delivery was processed synchronously in the API request path, and internal failures surfaced to Svix as non-2xx, so it redelivered — a self-feeding storm of 500s and latency that only stopped when the webhook endpoint was disabled. ## What changed With #23134, server-route dispatch defaults to **queued** server-side: the API acks Svix with a 202 right after signature verification, `process-recall-webhook` runs on the worker queue, and failed runs retry there (resolver `retryLimit`, default 3). The resolver needs no change at all — `recall-webhook.ts` is back to main, and no SDK update is required. Remaining app changes: - `schedule-recall-bot-on-call-recording-update` declares `updatedFields` (the pending-transition fields) on its `callRecording.updated` trigger, so the server drops the app's own scheduling-progress and artifact writes **before** spawning a full execution instead of executing and returning "skipped". The in-handler check stays as a fallback. - Version bumped to 1.5.0. - Code comments introduced by earlier revisions of this PR removed per review. ## Tests - New test pins the trigger's `updatedFields` declaration. - `yarn test:unit` (507 tests), `yarn lint`, `yarn typecheck` all green. ## Notes - The `call-recorder (dockerhub-latest)` CI leg fails because main already requires `twenty >= 2.23.0` while the latest published image is 2.22.0 — pre-existing, clears when 2.23.0 images publish. - The 250s `import-call-recording-artifacts` route still runs in an API request slot behind the fire-and-forget own-route POST; moving it fully off the request path is a follow-up. --- .../public/call-recorder/package.json | 2 +- ...ecall-bot-on-call-recording-update.test.ts | 19 ++++++++++++++++++- .../flows/handle-recall-webhook.util.ts | 1 - .../logic-functions/process-recall-webhook.ts | 1 - ...ule-recall-bot-on-call-recording-update.ts | 1 + 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/twenty-apps/public/call-recorder/package.json b/packages/twenty-apps/public/call-recorder/package.json index 26f31c547c..550ae27ec5 100644 --- a/packages/twenty-apps/public/call-recorder/package.json +++ b/packages/twenty-apps/public/call-recorder/package.json @@ -1,6 +1,6 @@ { "name": "@twentyhq/call-recorder", - "version": "1.4.0", + "version": "1.5.0", "license": "MIT", "engines": { "node": "^24.5.0", diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/__tests__/schedule-recall-bot-on-call-recording-update.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/__tests__/schedule-recall-bot-on-call-recording-update.test.ts index d124f07539..e705c3e54c 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/__tests__/schedule-recall-bot-on-call-recording-update.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/__tests__/schedule-recall-bot-on-call-recording-update.test.ts @@ -1,6 +1,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { scheduleRecallBotOnCallRecordingUpdateHandler } from 'src/logic-functions/schedule-recall-bot-on-call-recording-update'; +import scheduleRecallBotOnCallRecordingUpdateLogicFunction, { + scheduleRecallBotOnCallRecordingUpdateHandler, +} from 'src/logic-functions/schedule-recall-bot-on-call-recording-update'; const queryMock = vi.hoisted(() => vi.fn()); const mutationMock = vi.hoisted(() => vi.fn()); @@ -130,6 +132,21 @@ describe('scheduleRecallBotOnCallRecordingUpdateHandler', () => { vi.restoreAllMocks(); }); + it('declares the pending-transition fields on the trigger so the server drops other updates', () => { + expect( + scheduleRecallBotOnCallRecordingUpdateLogicFunction.config + .databaseEventTriggerSettings, + ).toEqual({ + eventName: 'callRecording.updated', + updatedFields: [ + 'recordingRequestStatus', + 'status', + 'externalBotId', + 'calendarEventId', + ], + }); + }); + it('schedules a bot when an update clears the bot id of a requested recording', async () => { stubPendingCallRecordingQueries(); diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts index e840b1130d..691cf1bc00 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/handle-recall-webhook.util.ts @@ -178,7 +178,6 @@ const queueCallRecordingArtifactsImport = async ({ }; }; -// A throw bubbles to a non-2xx so Svix redelivers; the preceding status update re-applies idempotently. const requestCallRecordingArtifactsImportOrThrow = async ({ callRecordingId, }: { diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/process-recall-webhook.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/process-recall-webhook.ts index e5e6edb82e..b78e25eca6 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/process-recall-webhook.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/process-recall-webhook.ts @@ -5,7 +5,6 @@ import { PROCESS_RECALL_WEBHOOK_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/ import { handleRecallWebhook } from 'src/logic-functions/flows/handle-recall-webhook.util'; import { type RecallWebhookBody } from 'src/logic-functions/recall-api/parse-recall-webhook-event.util'; -// Dispatched by the recall-webhook resolver; runs in the resolved workspace so the client is workspace-scoped. export const processRecallWebhookHandler = (body: RecallWebhookBody) => handleRecallWebhook({ client: new CoreApiClient(), diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/schedule-recall-bot-on-call-recording-update.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/schedule-recall-bot-on-call-recording-update.ts index 78c7026129..be852fb941 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/schedule-recall-bot-on-call-recording-update.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/schedule-recall-bot-on-call-recording-update.ts @@ -125,5 +125,6 @@ export default defineLogicFunction({ handler: scheduleRecallBotOnCallRecordingUpdateHandler, databaseEventTriggerSettings: { eventName: `${CALL_RECORDING_OBJECT_NAME}.updated`, + updatedFields: PENDING_TRANSITION_FIELDS, }, });