Send Email Tool: Don't persist message on SMTP only connections (#19756)
Previously this blocked users who only had SMTP configured to send outbound emails, this fixes it by making messageChannel and persist layer conditional
This commit is contained in:
+50
-19
@@ -5,7 +5,11 @@ import { render, toPlainText } from '@react-email/render';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { reactMarkupFromJSON } from 'twenty-emails';
|
||||
import { MAX_EMAIL_RECIPIENTS } from 'twenty-shared/constants';
|
||||
import { type EmailAttachment, FileFolder } from 'twenty-shared/types';
|
||||
import {
|
||||
ConnectedAccountProvider,
|
||||
type EmailAttachment,
|
||||
FileFolder,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||
import { In, type Repository } from 'typeorm';
|
||||
import { z } from 'zod';
|
||||
@@ -274,6 +278,27 @@ export class EmailComposerService {
|
||||
);
|
||||
}
|
||||
|
||||
private async refreshConnectedAccountTokens(
|
||||
connectedAccount: ConnectedAccountEntity,
|
||||
workspaceId: string,
|
||||
messageChannelId: string,
|
||||
): Promise<ConnectedAccountEntity> {
|
||||
const { accessToken, refreshToken } =
|
||||
await this.messagingAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication(
|
||||
{
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
messageChannelId,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
...connectedAccount,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
};
|
||||
}
|
||||
|
||||
async composeEmail(
|
||||
parameters: ComposeEmailParams,
|
||||
context: ToolExecutionContext,
|
||||
@@ -331,29 +356,34 @@ export class EmailComposerService {
|
||||
(channel) => channel.handle === connectedAccount.handle,
|
||||
);
|
||||
|
||||
if (!isDefined(messageChannel)) {
|
||||
const isSmtpOnlyAccount =
|
||||
connectedAccount.provider === ConnectedAccountProvider.IMAP_SMTP_CALDAV &&
|
||||
!isDefined(connectedAccount.connectionParameters?.IMAP);
|
||||
|
||||
if (
|
||||
isSmtpOnlyAccount &&
|
||||
!isDefined(connectedAccount.connectionParameters?.SMTP)
|
||||
) {
|
||||
throw new EmailToolException(
|
||||
`SMTP is not configured for connected account '${connectedAccountId}'`,
|
||||
EmailToolExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isSmtpOnlyAccount && !isDefined(messageChannel)) {
|
||||
throw new EmailToolException(
|
||||
`No message channel found for connected account '${connectedAccountId}'`,
|
||||
EmailToolExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const connectedAccountAsWorkspaceEntity = connectedAccount;
|
||||
|
||||
const { accessToken, refreshToken } =
|
||||
await this.messagingAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication(
|
||||
{
|
||||
connectedAccount: connectedAccountAsWorkspaceEntity,
|
||||
const connectedAccountWithFreshTokens = isDefined(messageChannel)
|
||||
? await this.refreshConnectedAccountTokens(
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
messageChannelId: messageChannel.id,
|
||||
},
|
||||
);
|
||||
|
||||
const connectedAccountWithFreshTokens = {
|
||||
...connectedAccountAsWorkspaceEntity,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
};
|
||||
messageChannel.id,
|
||||
)
|
||||
: connectedAccount;
|
||||
|
||||
const attachments = await this.getAttachments(
|
||||
files || [],
|
||||
@@ -374,7 +404,7 @@ export class EmailComposerService {
|
||||
|
||||
let threadExternalId: string | undefined;
|
||||
|
||||
if (inReplyTo) {
|
||||
if (inReplyTo && isDefined(messageChannel)) {
|
||||
threadExternalId = await this.getThreadExternalId(
|
||||
workspaceId,
|
||||
inReplyTo,
|
||||
@@ -392,7 +422,8 @@ export class EmailComposerService {
|
||||
sanitizedHtmlBody,
|
||||
attachments,
|
||||
connectedAccount: connectedAccountWithFreshTokens,
|
||||
messageChannelId: messageChannel.id,
|
||||
messageChannelId: messageChannel?.id,
|
||||
shouldPersistMessage: isDefined(messageChannel),
|
||||
inReplyTo,
|
||||
threadExternalId,
|
||||
},
|
||||
|
||||
+7
-5
@@ -44,11 +44,13 @@ export class SendEmailTool implements Tool {
|
||||
|
||||
const sendResult = await this.sendEmailService.sendComposedEmail(data);
|
||||
|
||||
await this.sendEmailService.persistSentMessage(
|
||||
sendResult,
|
||||
data,
|
||||
context.workspaceId,
|
||||
);
|
||||
if (data.shouldPersistMessage) {
|
||||
await this.sendEmailService.persistSentMessage(
|
||||
sendResult,
|
||||
data,
|
||||
context.workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Email sent successfully to ${data.toRecipientsDisplay}${data.attachments.length > 0 ? ` with ${data.attachments.length} attachments` : ''}`,
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@ export type ComposedEmail = {
|
||||
sanitizedHtmlBody: string;
|
||||
attachments: MessageAttachment[];
|
||||
connectedAccount: ConnectedAccountEntity;
|
||||
messageChannelId: string;
|
||||
messageChannelId?: string;
|
||||
shouldPersistMessage: boolean;
|
||||
inReplyTo?: string;
|
||||
threadExternalId?: string;
|
||||
};
|
||||
|
||||
+7
-5
@@ -79,11 +79,13 @@ export class SendEmailResolver {
|
||||
|
||||
const sendResult = await this.sendEmailService.sendComposedEmail(data);
|
||||
|
||||
await this.sendEmailService.persistSentMessage(
|
||||
sendResult,
|
||||
data,
|
||||
workspace.id,
|
||||
);
|
||||
if (data.shouldPersistMessage) {
|
||||
await this.sendEmailService.persistSentMessage(
|
||||
sendResult,
|
||||
data,
|
||||
workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
const attachmentFileIds = (input.files ?? []).map((file) => file.id);
|
||||
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ export class SendEmailService {
|
||||
body: data.plainTextBody,
|
||||
recipients: data.recipients,
|
||||
connectedAccount: data.connectedAccount,
|
||||
messageChannelId: data.messageChannelId,
|
||||
messageChannelId: data.messageChannelId!,
|
||||
inReplyTo: data.inReplyTo,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user