fix(call-recorder): listen proper updated fileds event (#23135)
## 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.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@twentyhq/call-recorder",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
|
||||
+18
-1
@@ -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();
|
||||
|
||||
|
||||
-1
@@ -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,
|
||||
}: {
|
||||
|
||||
-1
@@ -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(),
|
||||
|
||||
+1
@@ -125,5 +125,6 @@ export default defineLogicFunction({
|
||||
handler: scheduleRecallBotOnCallRecordingUpdateHandler,
|
||||
databaseEventTriggerSettings: {
|
||||
eventName: `${CALL_RECORDING_OBJECT_NAME}.updated`,
|
||||
updatedFields: PENDING_TRANSITION_FIELDS,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user