Fix join at in the past (#23000)
as title, we floor the bot join at date 1second in the future <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23000?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@twentyhq/call-recorder",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
|
||||
+2
@@ -219,6 +219,8 @@ const buildFakeCoreApiClient = (
|
||||
|
||||
describe('reconcileCallRecorderForCalendarEventIds', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(NOW);
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
+2
@@ -181,6 +181,8 @@ const createBotCalls = () =>
|
||||
|
||||
describe('scheduleRecallBotsForPendingCallRecordings', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(NOW);
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
vi.stubEnv('RECALL_API_KEY', 'recall-api-key');
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { computeMaximumJoinAt } from 'src/logic-functions/recall-api/compute-maximum-join-at.utils';
|
||||
|
||||
const NOW = new Date('2026-07-17T10:00:00.000Z');
|
||||
|
||||
describe('computeMaximumJoinAt', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(NOW);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('returns joinAt when it is later than now plus one second', () => {
|
||||
const joinAt = '2026-07-17T10:00:10.000Z';
|
||||
|
||||
const result = computeMaximumJoinAt(joinAt);
|
||||
|
||||
expect(result).toBe(joinAt);
|
||||
});
|
||||
|
||||
it('returns now plus one second when joinAt is in the past', () => {
|
||||
const joinAt = '2026-07-17T09:59:00.000Z';
|
||||
|
||||
const result = computeMaximumJoinAt(joinAt);
|
||||
|
||||
expect(result).toBe('2026-07-17T10:00:01.000Z');
|
||||
});
|
||||
|
||||
it('returns now plus one second when joinAt is equal to now', () => {
|
||||
const joinAt = '2026-07-17T10:00:00.000Z';
|
||||
|
||||
const result = computeMaximumJoinAt(joinAt);
|
||||
|
||||
expect(result).toBe('2026-07-17T10:00:01.000Z');
|
||||
});
|
||||
|
||||
it('returns joinAt when it is equal to now plus one second', () => {
|
||||
const joinAt = '2026-07-17T10:00:01.000Z';
|
||||
|
||||
const result = computeMaximumJoinAt(joinAt);
|
||||
|
||||
expect(result).toBe('2026-07-17T10:00:01.000Z');
|
||||
});
|
||||
|
||||
it('normalizes an ISO 8601 value with a timezone offset to UTC', () => {
|
||||
const joinAt = '2026-07-17T12:00:10.000+02:00';
|
||||
|
||||
const result = computeMaximumJoinAt(joinAt);
|
||||
|
||||
expect(result).toBe('2026-07-17T10:00:10.000Z');
|
||||
});
|
||||
|
||||
it('throws when joinAt is not a valid date', () => {
|
||||
expect(() => computeMaximumJoinAt('not-a-date')).toThrow(RangeError);
|
||||
});
|
||||
});
|
||||
+4
@@ -15,6 +15,7 @@ import { CALL_RECORDER_RECORDING_RETENTION_HOURS_ENV_VAR_NAME } from 'src/logic-
|
||||
import { RECALL_API_KEY_ENV_VAR_NAME } from 'src/logic-functions/constants/recall-api-key-env-var-name';
|
||||
import { RECALL_REGION_ENV_VAR_NAME } from 'src/logic-functions/constants/recall-region-env-var-name';
|
||||
|
||||
const NOW = new Date('2026-01-01T12:00:00.000Z');
|
||||
const WORKSPACE_ID = '123e4567-e89b-12d3-a456-426614174000';
|
||||
const RECALL_ROUTING_METADATA = {
|
||||
twentyWorkspaceId: WORKSPACE_ID,
|
||||
@@ -34,6 +35,8 @@ describe('recall bot api', () => {
|
||||
const fetchMock = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(NOW);
|
||||
delete process.env[CALL_RECORDER_RECORDING_RETENTION_HOURS_ENV_VAR_NAME];
|
||||
process.env[RECALL_API_KEY_ENV_VAR_NAME] = 'recall-api-key';
|
||||
process.env[RECALL_REGION_ENV_VAR_NAME] = 'ap-northeast-1';
|
||||
@@ -56,6 +59,7 @@ describe('recall bot api', () => {
|
||||
}
|
||||
});
|
||||
vi.unstubAllGlobals();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('creates Recall bot requests with the Token authorization scheme', async () => {
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
export const computeMaximumJoinAt = (joinAt: string): string => {
|
||||
const joinAtDate = new Date(joinAt);
|
||||
const nowPlusOneSecond = new Date(Date.now() + 1_000);
|
||||
|
||||
return new Date(
|
||||
Math.max(joinAtDate.getTime(), nowPlusOneSecond.getTime()),
|
||||
).toISOString();
|
||||
};
|
||||
+2
-1
@@ -10,6 +10,7 @@ import {
|
||||
import { getRecallApiConfig } from 'src/logic-functions/recall-api/get-recall-api-config.util';
|
||||
import { recallBotApiRequest } from 'src/logic-functions/recall-api/recall-bot-api-request.util';
|
||||
import { type ScheduleRecallBotArgs } from 'src/logic-functions/recall-api/schedule-recall-bot.util';
|
||||
import { computeMaximumJoinAt } from 'src/logic-functions/recall-api/compute-maximum-join-at.utils';
|
||||
|
||||
type RescheduleRecallBotArgs = ScheduleRecallBotArgs & {
|
||||
externalBotId: string;
|
||||
@@ -35,7 +36,7 @@ export const rescheduleRecallBot = async ({
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
meeting_url: meetingUrl,
|
||||
join_at: joinAt,
|
||||
join_at: computeMaximumJoinAt(joinAt), // We can't join in the past, so we floor this date 1s in the future
|
||||
bot_name: configResult.config.botName,
|
||||
...(isUndefined(automaticLeave)
|
||||
? {}
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@ import {
|
||||
} from 'src/logic-functions/recall-api/extract-recall-bot-id.util';
|
||||
import { getRecallApiConfig } from 'src/logic-functions/recall-api/get-recall-api-config.util';
|
||||
import { recallBotApiRequest } from 'src/logic-functions/recall-api/recall-bot-api-request.util';
|
||||
import { computeMaximumJoinAt } from 'src/logic-functions/recall-api/compute-maximum-join-at.utils';
|
||||
|
||||
export type ScheduleRecallBotArgs = {
|
||||
meetingUrl: string;
|
||||
@@ -47,7 +48,7 @@ export const scheduleRecallBot = async ({
|
||||
idempotencyKey,
|
||||
body: {
|
||||
meeting_url: meetingUrl,
|
||||
join_at: joinAt,
|
||||
join_at: computeMaximumJoinAt(joinAt), // We can't join in the past, so we floor this date 1s in the future
|
||||
bot_name: configResult.config.botName,
|
||||
...(isUndefined(automaticLeave)
|
||||
? {}
|
||||
|
||||
Reference in New Issue
Block a user