Child folders followup (#15526)

This commit is contained in:
neo773
2025-11-04 19:34:03 +05:30
committed by GitHub
parent 15a6ca64da
commit e3076328dd
22 changed files with 736 additions and 121 deletions
@@ -5,6 +5,11 @@ import { ListResponse, type ImapFlow } from 'imapflow';
import { getImapSentFolderCandidatesByRegex } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-sent-folder-candidates-by-regex.util';
type SentFolderResult = {
name: string;
path: string;
} | null;
/**
* Service to find sent folder using IMAP special-use flags
*
@@ -19,7 +24,7 @@ import { getImapSentFolderCandidatesByRegex } from 'src/modules/messaging/messag
export class ImapFindSentFolderService {
private readonly logger = new Logger(ImapFindSentFolderService.name);
public async findSentFolder(client: ImapFlow): Promise<string | null> {
public async findSentFolder(client: ImapFlow): Promise<SentFolderResult> {
try {
const list = await client.list();
@@ -60,7 +65,7 @@ export class ImapFindSentFolderService {
private async findSentFolderBySpecialUse(
client: ImapFlow,
list: ListResponse[],
): Promise<string | null> {
): Promise<SentFolderResult> {
for (const folder of list) {
if (folder.specialUse && folder.specialUse.includes('\\Sent')) {
this.logger.log(
@@ -73,7 +78,10 @@ export class ImapFindSentFolderService {
);
if (messageCount > 0) {
return folder.path;
return {
name: folder.name,
path: folder.path,
};
}
this.logger.warn(
@@ -90,25 +98,38 @@ export class ImapFindSentFolderService {
private async findSentFolderByRegexCandidates(
client: ImapFlow,
list: ListResponse[],
): Promise<string | null> {
): Promise<SentFolderResult> {
const regexCandidateFolders = getImapSentFolderCandidatesByRegex(list);
for (const folder of regexCandidateFolders) {
const messageCount = await this.getFolderMessageCount(client, folder);
const messageCount = await this.getFolderMessageCount(
client,
folder.path,
);
if (messageCount > 0) {
this.logger.log(`Selected sent folder via pattern match: ${folder}`);
this.logger.log(
`Selected sent folder via pattern match: ${folder.path}`,
);
return folder;
return {
name: folder.name,
path: folder.path,
};
}
}
if (regexCandidateFolders.length > 0) {
this.logger.log(
`Using first regex candidate sent folder: ${regexCandidateFolders[0]} (no messages found in any regex candidate)`,
`Using first regex candidate sent folder: ${regexCandidateFolders[0].path} (no messages found in any regex candidate)`,
);
return regexCandidateFolders[0];
const folder = regexCandidateFolders[0];
return {
name: folder.name,
path: folder.path,
};
}
return null;
@@ -41,10 +41,17 @@ export class ImapGetMessageListService {
for (const folder of messageFolders) {
this.logger.log(`Processing folder: ${folder.name}`);
const folderPath = folder.externalId?.split(':')[0];
if (!folderPath) {
this.logger.warn(`Folder ${folder.name} has no path. Skipping.`);
continue;
}
try {
const response = await this.getMessageList(
client,
folder.name,
folderPath,
folder,
);
@@ -3,7 +3,7 @@ import { type ListResponse } from 'imapflow';
import { getImapSentFolderCandidatesByRegex } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-sent-folder-candidates-by-regex.util';
function makeList(paths: string[]): ListResponse[] {
return paths.map((p) => ({ path: p }) as ListResponse);
return paths.map((p) => ({ path: p, name: p }) as ListResponse);
}
describe('getSentFolderCandidatesByRegex', () => {
@@ -18,7 +18,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(englishVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(englishVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(englishVariants),
);
});
it('matches French variants', () => {
@@ -26,7 +28,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(frenchVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(frenchVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(frenchVariants),
);
});
it('matches German variants', () => {
@@ -34,7 +38,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(germanVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(germanVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(germanVariants),
);
});
it('matches Spanish variants', () => {
@@ -42,7 +48,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(spanishVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(spanishVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(spanishVariants),
);
});
it('matches Portuguese variants', () => {
@@ -50,7 +58,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(portugueseVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(portugueseVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(portugueseVariants),
);
});
it('matches Italian variants', () => {
@@ -58,7 +68,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(italianVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(italianVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(italianVariants),
);
});
it('matches Korean variant', () => {
@@ -66,7 +78,7 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(koreanVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(koreanVariants);
expect(result.map((r) => r.path)).toEqual(koreanVariants);
});
it('matches Japanese variants', () => {
@@ -74,7 +86,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(japaneseVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(japaneseVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(japaneseVariants),
);
});
it('matches Polish variants', () => {
@@ -82,7 +96,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(polishVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(polishVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(polishVariants),
);
});
it('matches Russian variants', () => {
@@ -95,7 +111,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(russianVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(russianVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(russianVariants),
);
});
it('matches Gmail special folder', () => {
@@ -103,7 +121,9 @@ describe('getSentFolderCandidatesByRegex', () => {
const input = makeList(gmailVariants);
const result = getImapSentFolderCandidatesByRegex(input);
expect(result).toEqual(expect.arrayContaining(gmailVariants));
expect(result.map((r) => r.path)).toEqual(
expect.arrayContaining(gmailVariants),
);
});
it('does not match unrelated folders', () => {
@@ -5,7 +5,7 @@ import { getStandardFolderByRegex } from 'src/modules/messaging/message-import-m
export function getImapSentFolderCandidatesByRegex(
list: ListResponse[],
): string[] {
): { name: string; path: string }[] {
const regexCandidateFolders: string[] = [];
for (const folder of list) {
@@ -16,5 +16,12 @@ export function getImapSentFolderCandidatesByRegex(
}
}
return regexCandidateFolders;
return regexCandidateFolders.map((folderPath) => {
const folder = list.find((folder) => folder.path === folderPath);
return {
name: folder?.name ?? folderPath,
path: folderPath,
};
});
}