feat: message folders control (#14144)

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
neo773
2025-08-30 21:09:10 +05:30
committed by GitHub
parent a0884cede9
commit 8ab71fef1f
49 changed files with 1786 additions and 301 deletions
@@ -27,6 +27,7 @@ import { MicrosoftGetMessageListService } from './services/microsoft-get-message
MicrosoftClientProvider,
MicrosoftGetMessageListService,
MicrosoftGetMessagesService,
MicrosoftFetchByBatchService,
MicrosoftHandleErrorService,
MicrosoftOAuth2ClientManagerService,
@@ -35,6 +36,7 @@ import { MicrosoftGetMessageListService } from './services/microsoft-get-message
MicrosoftGetMessageListService,
MicrosoftClientProvider,
MicrosoftGetMessagesService,
MicrosoftHandleErrorService,
],
})
export class MessagingMicrosoftDriverModule {}
@@ -66,6 +66,9 @@ xdescribe('Microsoft dev tests : get message list service', () => {
id: 'inbox-folder-id',
name: MessageFolderName.INBOX,
syncCursor: 'inbox-sync-cursor',
isSynced: false,
isSentFolder: false,
externalId: null,
},
],
});
@@ -91,6 +94,9 @@ xdescribe('Microsoft dev tests : get message list service', () => {
id: 'inbox-folder-id',
name: MessageFolderName.INBOX,
syncCursor: 'inbox-sync-cursor',
isSynced: false,
isSentFolder: false,
externalId: null,
},
],
}),
@@ -107,6 +113,9 @@ xdescribe('Microsoft dev tests : get message list service', () => {
id: 'inbox-folder-id',
name: MessageFolderName.INBOX,
syncCursor: syncCursor,
isSynced: false,
isSentFolder: false,
externalId: null,
},
],
});
@@ -127,6 +136,9 @@ xdescribe('Microsoft dev tests : get message list service', () => {
id: 'inbox-folder-id',
name: MessageFolderName.INBOX,
syncCursor: 'invalid-syncCursor',
isSynced: false,
isSentFolder: false,
externalId: null,
},
],
}),
@@ -6,9 +6,7 @@ import {
type PageIteratorCallback,
} from '@microsoft/microsoft-graph-client';
import { isNonEmptyString } from '@sniptt/guards';
import { v4 } from 'uuid';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { type MessageFolderWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity';
import {
@@ -17,7 +15,6 @@ import {
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
import { MicrosoftClientProvider } from 'src/modules/messaging/message-import-manager/drivers/microsoft/providers/microsoft-client.provider';
import { MicrosoftHandleErrorService } from 'src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-handle-error.service';
import { MessageFolderName } from 'src/modules/messaging/message-import-manager/drivers/microsoft/types/folders';
import { isAccessTokenRefreshingError } from 'src/modules/messaging/message-import-manager/drivers/microsoft/utils/is-access-token-refreshing-error.utils';
import { type GetMessageListsArgs } from 'src/modules/messaging/message-import-manager/types/get-message-lists-args.type';
import {
@@ -34,7 +31,6 @@ export class MicrosoftGetMessageListService {
constructor(
private readonly microsoftClientProvider: MicrosoftClientProvider,
private readonly microsoftHandleErrorService: MicrosoftHandleErrorService,
private readonly twentyORMManager: TwentyORMManager,
) {}
public async getMessageLists({
@@ -45,46 +41,10 @@ export class MicrosoftGetMessageListService {
const result: GetMessageListsResponse = [];
if (messageFolders.length === 0) {
// permanent solution:
// throw new MessageImportDriverException(
// `Message channel ${messageChannel.id} has no message folders`,
// MessageImportDriverExceptionCode.NOT_FOUND,
// );
// temporary solution: TODO: remove this once we have a permanent solution
// if no folders exist, most probably a first time sync for microsoft
// so we create the folders INBOX and SENTITEMS
// and fill the INBOX with the previous sync cursor
// and for sentitms, we do the full message list fetch
// console.warn(
// `Message channel ${messageChannel.id} has no message folders, most probably a first time`,
// );
const messageFolderRepository =
await this.twentyORMManager.getRepository<MessageFolderWorkspaceEntity>(
'messageFolder',
);
const newFolder = await messageFolderRepository.save({
id: v4(),
messageChannelId: messageChannel.id,
name: MessageFolderName.INBOX,
syncCursor: messageChannel.syncCursor,
});
const response = await this.getMessageList(connectedAccount, {
name: MessageFolderName.INBOX,
syncCursor: messageChannel.syncCursor,
});
result.push({
...response,
folderId: newFolder.id,
});
// we are ok with not synchronizing the legacy connected microsoft accounts.
// so we return an empty array.
return result;
throw new MessageImportDriverException(
`Message channel ${messageChannel.id} has no message folders`,
MessageImportDriverExceptionCode.NOT_FOUND,
);
}
for (const folder of messageFolders) {
@@ -104,7 +64,10 @@ export class MicrosoftGetMessageListService {
ConnectedAccountWorkspaceEntity,
'provider' | 'refreshToken' | 'id'
>,
messageFolder: Pick<MessageFolderWorkspaceEntity, 'name' | 'syncCursor'>,
messageFolder: Pick<
MessageFolderWorkspaceEntity,
'name' | 'syncCursor' | 'externalId'
>,
): Promise<GetOneMessageListResponse> {
const messageExternalIds: string[] = [];
const messageExternalIdsToDelete: string[] = [];
@@ -112,9 +75,10 @@ export class MicrosoftGetMessageListService {
const microsoftClient =
await this.microsoftClientProvider.getMicrosoftClient(connectedAccount);
const folderId = messageFolder.externalId || messageFolder.name;
const apiUrl = isNonEmptyString(messageFolder.syncCursor)
? messageFolder.syncCursor
: `/me/mailfolders/${messageFolder.name}/messages/delta?$select=id`;
: `/me/mailfolders/${folderId}/messages/delta?$select=id`;
const response: PageCollection = await microsoftClient
.api(apiUrl)