fix IMAP onboarding (#15444)

This commit is contained in:
neo773
2025-11-02 13:33:03 +01:00
committed by GitHub
parent 53bb534af8
commit f9eb06f015
2 changed files with 27 additions and 122 deletions
@@ -123,8 +123,8 @@ describe('ImapSmtpCalDavAPIService', () => {
type: MessageChannelType.EMAIL,
handle: 'test@example.com',
isSyncEnabled: true,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
syncCursor: '',
syncStageStartedAt: null,
};
@@ -153,21 +153,15 @@ describe('ImapSmtpCalDavAPIService', () => {
type: MessageChannelType.EMAIL,
handle: 'test@example.com',
isSyncEnabled: true,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
syncCursor: '',
syncStageStartedAt: null,
},
{},
);
expect(mockMessageQueueService.add).toHaveBeenCalledWith(
'MessagingMessageListFetchJob',
{
workspaceId: 'workspace-id',
messageChannelId: 'mocked-uuid',
},
);
expect(mockMessageQueueService.add).not.toHaveBeenCalled();
});
it('should preserve existing channels when updating account credentials', async () => {
@@ -221,13 +215,7 @@ describe('ImapSmtpCalDavAPIService', () => {
expect(mockMessageChannelRepository.save).not.toHaveBeenCalled();
expect(mockCalendarChannelRepository.save).not.toHaveBeenCalled();
expect(mockMessageQueueService.add).toHaveBeenCalledWith(
'MessagingMessageListFetchJob',
{
workspaceId: 'workspace-id',
messageChannelId: 'existing-message-channel-id',
},
);
expect(mockMessageQueueService.add).not.toHaveBeenCalled();
});
it('should only create message channel when only IMAP is configured', async () => {
@@ -253,8 +241,8 @@ describe('ImapSmtpCalDavAPIService', () => {
type: MessageChannelType.EMAIL,
handle: 'test@example.com',
isSyncEnabled: true,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
};
mockMessageChannelRepository.save.mockResolvedValue(
@@ -265,13 +253,7 @@ describe('ImapSmtpCalDavAPIService', () => {
expect(mockMessageChannelRepository.save).toHaveBeenCalled();
expect(mockCalendarChannelRepository.save).not.toHaveBeenCalled();
expect(mockMessageQueueService.add).toHaveBeenCalledWith(
'MessagingMessageListFetchJob',
{
workspaceId: 'workspace-id',
messageChannelId: 'mocked-uuid',
},
);
expect(mockMessageQueueService.add).not.toHaveBeenCalled();
expect(mockCalendarQueueService.add).not.toHaveBeenCalled();
});
@@ -314,7 +296,7 @@ describe('ImapSmtpCalDavAPIService', () => {
handle: 'test@example.com',
isSyncEnabled: false,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: undefined,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
syncCursor: '',
syncStageStartedAt: null,
},
@@ -323,13 +305,7 @@ describe('ImapSmtpCalDavAPIService', () => {
expect(mockCalendarChannelRepository.save).toHaveBeenCalled();
expect(mockMessageQueueService.add).not.toHaveBeenCalled();
expect(mockCalendarQueueService.add).toHaveBeenCalledWith(
'CalendarEventListFetchJob',
{
workspaceId: 'workspace-id',
calendarChannelId: 'mocked-uuid',
},
);
expect(mockCalendarQueueService.add).not.toHaveBeenCalled();
});
it('should handle IMAP + SMTP configuration without CALDAV', async () => {
@@ -362,8 +338,8 @@ describe('ImapSmtpCalDavAPIService', () => {
type: MessageChannelType.EMAIL,
handle: 'test@example.com',
isSyncEnabled: true,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
};
mockMessageChannelRepository.save.mockResolvedValue(
@@ -374,13 +350,7 @@ describe('ImapSmtpCalDavAPIService', () => {
expect(mockMessageChannelRepository.save).toHaveBeenCalled();
expect(mockCalendarChannelRepository.save).not.toHaveBeenCalled();
expect(mockMessageQueueService.add).toHaveBeenCalledWith(
'MessagingMessageListFetchJob',
{
workspaceId: 'workspace-id',
messageChannelId: 'mocked-uuid',
},
);
expect(mockMessageQueueService.add).not.toHaveBeenCalled();
expect(mockCalendarQueueService.add).not.toHaveBeenCalled();
});
@@ -421,8 +391,8 @@ describe('ImapSmtpCalDavAPIService', () => {
type: MessageChannelType.EMAIL,
handle: 'test@example.com',
isSyncEnabled: true,
syncStatus: MessageChannelSyncStatus.ONGOING,
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
};
const expectedCalendarChannel = {
@@ -442,20 +412,8 @@ describe('ImapSmtpCalDavAPIService', () => {
expect(mockMessageChannelRepository.save).toHaveBeenCalled();
expect(mockCalendarChannelRepository.save).toHaveBeenCalled();
expect(mockMessageQueueService.add).toHaveBeenCalledWith(
'MessagingMessageListFetchJob',
{
workspaceId: 'workspace-id',
messageChannelId: 'mocked-uuid',
},
);
expect(mockCalendarQueueService.add).toHaveBeenCalledWith(
'CalendarEventListFetchJob',
{
workspaceId: 'workspace-id',
calendarChannelId: 'mocked-uuid',
},
);
expect(mockMessageQueueService.add).not.toHaveBeenCalled();
expect(mockCalendarQueueService.add).not.toHaveBeenCalled();
});
it('should handle account found by handle when connectedAccountId is not provided', async () => {
@@ -4,15 +4,8 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
import { v4 } from 'uuid';
import { type EmailAccountConnectionParameters } from 'src/engine/core-modules/imap-smtp-caldav-connection/dtos/imap-smtp-caldav-connection.dto';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import {
CalendarEventListFetchJob,
type CalendarEventListFetchJobData,
} from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
import {
CalendarChannelSyncStage,
CalendarChannelSyncStatus,
@@ -25,19 +18,11 @@ import {
MessageChannelType,
type MessageChannelWorkspaceEntity,
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import {
MessagingMessageListFetchJob,
type MessagingMessageListFetchJobData,
} from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job';
@Injectable()
export class ImapSmtpCalDavAPIService {
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectMessageQueue(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
@InjectMessageQueue(MessageQueue.calendarQueue)
private readonly calendarQueueService: MessageQueueService,
) {}
async processAccount(input: {
@@ -119,13 +104,6 @@ export class ImapSmtpCalDavAPIService {
}
});
await this.enqueueSyncJobs(
input,
workspaceId,
messageChannel,
calendarChannel,
);
return accountId;
}
@@ -168,12 +146,8 @@ export class ImapSmtpCalDavAPIService {
type: MessageChannelType.EMAIL,
handle: input.handle,
isSyncEnabled: shouldEnableSync,
syncStatus: shouldEnableSync
? MessageChannelSyncStatus.ONGOING
: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: shouldEnableSync
? MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING
: undefined,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
syncCursor: '',
syncStageStartedAt: null,
},
@@ -192,17 +166,19 @@ export class ImapSmtpCalDavAPIService {
accountId: string,
calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
): Promise<CalendarChannelWorkspaceEntity | null> {
const shouldEnableSync = Boolean(input.connectionParameters.CALDAV);
const shouldCreateCalendarChannel = Boolean(
input.connectionParameters.CALDAV,
);
if (shouldEnableSync) {
if (shouldCreateCalendarChannel) {
const newCalendarChannel = await calendarChannelRepository.save(
{
id: v4(),
connectedAccountId: accountId,
handle: input.handle,
isSyncEnabled: shouldEnableSync,
syncStatus: CalendarChannelSyncStatus.ONGOING,
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING,
isSyncEnabled: shouldCreateCalendarChannel,
syncStatus: CalendarChannelSyncStatus.NOT_SYNCED,
syncStage: CalendarChannelSyncStage.PENDING_CONFIGURATION,
syncCursor: '',
syncStageStartedAt: null,
},
@@ -214,33 +190,4 @@ export class ImapSmtpCalDavAPIService {
return null;
}
private async enqueueSyncJobs(
input: {
connectionParameters: EmailAccountConnectionParameters;
},
workspaceId: string,
messageChannel: MessageChannelWorkspaceEntity | null,
calendarChannel: CalendarChannelWorkspaceEntity | null,
) {
if (input.connectionParameters.IMAP && messageChannel) {
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
MessagingMessageListFetchJob.name,
{
workspaceId,
messageChannelId: messageChannel.id,
},
);
}
if (input.connectionParameters.CALDAV && calendarChannel) {
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
CalendarEventListFetchJob.name,
{
workspaceId,
calendarChannelId: calendarChannel.id,
},
);
}
}
}