feat(messaging): add workspace toggle to sync internal emails (#20457)

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
neo773
2026-05-12 01:12:51 +05:30
committed by GitHub
parent 75c22a2119
commit b03f044d0f
27 changed files with 235 additions and 12 deletions
@@ -26,6 +26,7 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import { MessagingMonitoringService } from 'src/modules/messaging/monitoring/services/messaging-monitoring.service';
import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
describe('MessagingMessagesImportService', () => {
let service: MessagingMessagesImportService;
@@ -183,6 +184,14 @@ describe('MessagingMessagesImportService', () => {
findOne: jest.fn().mockResolvedValue({ userId: 'user-id' }),
},
},
{
provide: getRepositoryToken(WorkspaceEntity),
useValue: {
findOne: jest
.fn()
.mockResolvedValue({ isInternalMessagesImportEnabled: false }),
},
},
{
provide: TwentyConfigService,
useValue: {
@@ -9,6 +9,7 @@ import { CacheStorageService } from 'src/engine/core-modules/cache-storage/servi
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { type ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
@@ -55,6 +56,8 @@ export class MessagingMessagesImportService {
private readonly messagingAccountAuthenticationService: MessagingAccountAuthenticationService,
@InjectRepository(UserWorkspaceEntity)
private readonly userWorkspaceRepository: Repository<UserWorkspaceEntity>,
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
private readonly twentyConfigService: TwentyConfigService,
) {}
@@ -197,6 +200,11 @@ export class MessagingMessagesImportService {
);
}
const workspace = await this.workspaceRepository.findOne({
where: { id: workspaceId },
select: ['id', 'isInternalMessagesImportEnabled'],
});
const messagesToSave = filterEmails(
messageChannel.handle,
[...connectedAccountWithFreshTokens.handleAliases],
@@ -205,6 +213,7 @@ export class MessagingMessagesImportService {
.map((blocklistItem) => blocklistItem.handle)
.filter(isDefined),
messageChannel.excludeGroupEmails,
workspace?.isInternalMessagesImportEnabled ?? false,
);
if (messagesToSave.length > 0) {
@@ -37,6 +37,27 @@ describe('filterEmails', () => {
expect(filteredMessages).toEqual([]);
});
it('Should keep same-domain emails when isInternalMessagesImportEnabled is true', () => {
// Workspace opted into syncing internal emails (e.g. university or
// shared-domain institution). Same-domain participants must not be
// dropped — the toggle bypasses filterOutInternals.
const primaryHandle = 'guillim@acme.com';
const messages = messagingGetMessagesServiceGetMessages.filter(
(message) => message.externalId === 'AA-work-emails-internal',
);
const filteredMessages = filterEmails(
primaryHandle,
[],
messages,
[],
true,
true,
);
expect(filteredMessages).toEqual(messages);
});
it('Should filter messages with participant from the blocklist', () => {
const primaryHandle = 'guillim@acme.com';
const messages = messagingGetMessagesServiceGetMessages.filter(
@@ -15,6 +15,7 @@ export const filterEmails = (
messages: MessageWithParticipants[],
blocklist: string[],
excludeGroupEmails: boolean = true,
isInternalMessagesImportEnabled: boolean = false,
) => {
const messagesWithoutIcsAttachments = filterOutIcsAttachments(messages);
@@ -24,7 +25,10 @@ export const filterEmails = (
blocklist,
);
const messagesWithoutInternals = isWorkEmail(primaryHandle)
const shouldFilterOutInternals =
isWorkEmail(primaryHandle) && !isInternalMessagesImportEnabled;
const messagesWithoutInternals = shouldFilterOutInternals
? filterOutInternals(primaryHandle, messagesWithoutBlocklisted)
: messagesWithoutBlocklisted;