Skip to content

Commit a3f2faf

Browse files
authored
fix smtp outbound persist message (#20276)
`APPEND` used display name `Sent` instead of `INBOX.Sent` Fix is to use mailbox path, extreacted this as a utility, all services are consistent now. /closes #20267
1 parent ff65b50 commit a3f2faf

5 files changed

Lines changed: 60 additions & 14 deletions

File tree

packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/imap/services/imap-get-all-folders.service.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { shouldCreateFolderByDefault } from 'src/modules/messaging/message-folde
1414
import { shouldSyncFolderByDefault } from 'src/modules/messaging/message-folder-manager/utils/should-sync-folder-by-default.util';
1515
import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider';
1616
import { ImapFindSentFolderService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-find-sent-folder.service';
17+
import { getImapFolderPath } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-imap-folder-path.util';
1718
import { getStandardFolderByRegex } from 'src/modules/messaging/message-import-manager/drivers/utils/get-standard-folder-by-regex';
1819

1920
@Injectable()
@@ -163,15 +164,9 @@ export class ImapGetAllFoldersService implements MessageFolderDriver {
163164
return false;
164165
}
165166

166-
const isDuplicate = existingFolders.some((folder) => {
167-
const folderPath = folder?.externalId?.split(':')[0];
168-
169-
if (!isDefined(folderPath)) {
170-
return false;
171-
}
172-
173-
return folderPath === mailbox.path;
174-
});
167+
const isDuplicate = existingFolders.some(
168+
(folder) => getImapFolderPath(folder?.externalId) === mailbox.path,
169+
);
175170

176171
return !isDuplicate;
177172
}

packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-message-list.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { ImapMessageListFetchErrorHandler } from 'src/modules/messaging/message-
1515
import { ImapSyncService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-sync.service';
1616
import { createSyncCursor } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/create-sync-cursor.util';
1717
import { extractMailboxState } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/extract-mailbox-state.util';
18-
import { parseMessageId } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-message-id.util';
18+
import { getImapFolderPath } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-imap-folder-path.util';
1919
import { parseSyncCursor } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-sync-cursor.util';
2020
import { type GetMessageListsArgs } from 'src/modules/messaging/message-import-manager/types/get-message-lists-args.type';
2121
import {
@@ -79,7 +79,7 @@ export class ImapGetMessageListService {
7979
client: ImapFlow,
8080
folder: MessageFolder,
8181
): Promise<GetOneMessageListResponse> {
82-
const folderPath = parseMessageId(folder.externalId ?? '')?.folder;
82+
const folderPath = getImapFolderPath(folder.externalId);
8383

8484
if (!isDefined(folderPath)) {
8585
throw new MessageImportDriverException(
@@ -157,7 +157,7 @@ export class ImapGetMessageListService {
157157
client: ImapFlow,
158158
folder: MessageFolder,
159159
): Promise<boolean> {
160-
const folderPath = parseMessageId(folder.externalId ?? '')?.folder;
160+
const folderPath = getImapFolderPath(folder.externalId);
161161
const previousCursor = parseSyncCursor(folder.syncCursor);
162162

163163
if (!isDefined(folderPath) || !isDefined(previousCursor)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { getImapFolderPath } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-imap-folder-path.util';
2+
3+
describe('getImapFolderPath', () => {
4+
it('extracts the path from a `path:uidValidity` externalId', () => {
5+
expect(getImapFolderPath('INBOX.Sent:1768984533')).toBe('INBOX.Sent');
6+
});
7+
8+
it('returns the externalId unchanged when it has no uidValidity suffix', () => {
9+
expect(getImapFolderPath('INBOX')).toBe('INBOX');
10+
});
11+
12+
it('preserves colons inside the path and only strips the trailing uidValidity', () => {
13+
expect(getImapFolderPath('Foo:Bar:42')).toBe('Foo:Bar');
14+
});
15+
16+
it('returns the externalId unchanged when the trailing segment is non-numeric', () => {
17+
expect(getImapFolderPath('Project: Updates')).toBe('Project: Updates');
18+
});
19+
20+
it('returns null for empty, null, or undefined input', () => {
21+
expect(getImapFolderPath('')).toBeNull();
22+
expect(getImapFolderPath(null)).toBeNull();
23+
expect(getImapFolderPath(undefined)).toBeNull();
24+
});
25+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { isNonEmptyString } from '@sniptt/guards';
2+
3+
export const getImapFolderPath = (
4+
externalId: string | null | undefined,
5+
): string | null => {
6+
if (!isNonEmptyString(externalId)) {
7+
return null;
8+
}
9+
10+
const lastColonIndex = externalId.lastIndexOf(':');
11+
12+
if (lastColonIndex === -1) {
13+
return externalId;
14+
}
15+
16+
const trailingSegment = externalId.slice(lastColonIndex + 1);
17+
18+
if (!/^\d+$/.test(trailingSegment)) {
19+
return externalId;
20+
}
21+
22+
return externalId.slice(0, lastColonIndex);
23+
};

packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/imap/services/imap-smtp-message-outbound.service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { MessageFolderEntity } from 'src/engine/metadata-modules/message-folder/
1212
import { type ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
1313
import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider';
1414
import { ImapFindDraftsFolderService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-find-drafts-folder.service';
15+
import { getImapFolderPath } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-imap-folder-path.util';
1516
import { SmtpClientProvider } from 'src/modules/messaging/message-import-manager/drivers/smtp/providers/smtp-client.provider';
1617
import { type SendMessageInput } from 'src/modules/messaging/message-outbound-manager/types/send-message-input.type';
1718
import { type SendMessageResult } from 'src/modules/messaging/message-outbound-manager/types/send-message-result.type';
@@ -76,8 +77,10 @@ export class ImapSmtpMessageOutboundService implements MessageOutboundDriver {
7677
});
7778
}
7879

79-
if (isDefined(sentFolder) && isDefined(sentFolder.name)) {
80-
await imapClient.append(sentFolder.name, messageBuffer);
80+
const sentFolderPath = getImapFolderPath(sentFolder?.externalId);
81+
82+
if (isDefined(sentFolderPath)) {
83+
await imapClient.append(sentFolderPath, messageBuffer);
8184
}
8285

8386
await this.imapClientProvider.closeClient(imapClient);

0 commit comments

Comments
 (0)