fix email alias guard with message channel availibility (#22521)

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22521?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
neo773
2026-07-03 18:23:01 +05:30
committed by GitHub
parent 0992d0b748
commit 3c3a8078fe
3 changed files with 77 additions and 27 deletions
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { 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 { GmailEmailAliasErrorHandlerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-error-handler.service';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/services/microsoft-email-alias-manager.service';
@@ -11,7 +12,7 @@ import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-
@Module({
imports: [
OAuth2ClientManagerModule,
TypeOrmModule.forFeature([ConnectedAccountEntity]),
TypeOrmModule.forFeature([ConnectedAccountEntity, MessageChannelEntity]),
],
providers: [
EmailAliasManagerService,
@@ -4,6 +4,7 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { 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 { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
import { microsoftGraphMeResponseWithProxyAddresses } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/mocks/microsoft-api-examples';
@@ -19,6 +20,13 @@ describe('Email Alias Manager Service', () => {
// @ts-expect-error legacy noImplicitAny
update: jest.fn().mockResolvedValue((arg) => arg),
};
const mockMessageChannelRepository = {
exists: jest.fn().mockResolvedValue(true),
};
afterEach(() => {
jest.clearAllMocks();
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
@@ -36,6 +44,10 @@ describe('Email Alias Manager Service', () => {
provide: getRepositoryToken(ConnectedAccountEntity),
useValue: mockConnectedAccountRepository,
},
{
provide: getRepositoryToken(MessageChannelEntity),
useValue: mockMessageChannelRepository,
},
{
provide: GoogleEmailAliasManagerService,
useValue: {},
@@ -102,4 +114,30 @@ describe('Email Alias Manager Service', () => {
);
});
});
describe('Refresh handle aliases without a mailbox', () => {
it('Should preserve existing aliases and skip the update when the account has no message channel', async () => {
mockMessageChannelRepository.exists.mockResolvedValueOnce(false);
const existingAliases = ['existing@domain.com'];
const mockConnectedAccount: Partial<ConnectedAccountEntity> = {
id: 'test-id',
provider: ConnectedAccountProvider.GOOGLE,
handleAliases: existingAliases,
};
jest.spyOn(microsoftEmailAliasManagerService, 'getHandleAliases');
const result = await emailAliasManagerService.refreshHandleAliases(
mockConnectedAccount as ConnectedAccountEntity,
'test-workspace-id',
);
expect(result).toEqual(existingAliases);
expect(
microsoftEmailAliasManagerService.getHandleAliases,
).not.toHaveBeenCalled();
expect(mockConnectedAccountRepository.update).not.toHaveBeenCalled();
});
});
});
@@ -6,6 +6,7 @@ import { assertUnreachable } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { 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 { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
@@ -19,41 +20,25 @@ export class EmailAliasManagerService {
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
@InjectRepository(ConnectedAccountEntity)
private readonly connectedAccountRepository: Repository<ConnectedAccountEntity>,
@InjectRepository(MessageChannelEntity)
private readonly messageChannelRepository: Repository<MessageChannelEntity>,
) {}
public async refreshHandleAliases(
connectedAccount: ConnectedAccountEntity,
workspaceId: string,
): Promise<string[]> {
let handleAliases: string[];
const accountHasMailbox = await this.messageChannelRepository.exists({
where: { connectedAccountId: connectedAccount.id, workspaceId },
});
switch (connectedAccount.provider) {
case ConnectedAccountProvider.MICROSOFT:
handleAliases =
await this.microsoftEmailAliasManagerService.getHandleAliases(
connectedAccount,
);
break;
case ConnectedAccountProvider.GOOGLE:
handleAliases =
await this.googleEmailAliasManagerService.getHandleAliases(
connectedAccount,
);
break;
case ConnectedAccountProvider.IMAP_SMTP_CALDAV:
case ConnectedAccountProvider.OIDC:
case ConnectedAccountProvider.SAML:
case ConnectedAccountProvider.EMAIL_GROUP:
case ConnectedAccountProvider.APP:
handleAliases = [];
break;
default:
assertUnreachable(
connectedAccount.provider,
`Email alias manager for provider ${connectedAccount.provider} is not implemented`,
);
if (!accountHasMailbox) {
return connectedAccount.handleAliases ?? [];
}
const handleAliases =
await this.getHandleAliasesFromProvider(connectedAccount);
const authContext = buildSystemAuthContext(workspaceId);
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => {
@@ -67,4 +52,30 @@ export class EmailAliasManagerService {
return handleAliases;
}
private async getHandleAliasesFromProvider(
connectedAccount: ConnectedAccountEntity,
): Promise<string[]> {
switch (connectedAccount.provider) {
case ConnectedAccountProvider.MICROSOFT:
return this.microsoftEmailAliasManagerService.getHandleAliases(
connectedAccount,
);
case ConnectedAccountProvider.GOOGLE:
return this.googleEmailAliasManagerService.getHandleAliases(
connectedAccount,
);
case ConnectedAccountProvider.IMAP_SMTP_CALDAV:
case ConnectedAccountProvider.OIDC:
case ConnectedAccountProvider.SAML:
case ConnectedAccountProvider.EMAIL_GROUP:
case ConnectedAccountProvider.APP:
return [];
default:
return assertUnreachable(
connectedAccount.provider,
`Email alias manager for provider ${connectedAccount.provider} is not implemented`,
);
}
}
}