7417 workflows i can send emails using the email account (#7431)
- update `send-email.workflow-action.ts` so it send email via the google sdk - remove useless `workflow-action.email.ts` - add `send` authorization to google api scopes - update the front workflow email step form to provide a `connectedAccountId` from the available connected accounts - update the permissions of connected accounts: ask users to reconnect when selecting missing send permission 
This commit is contained in:
@ -70,6 +70,11 @@ export const seedFeatureFlags = async (
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IsGmailSendEmailScopeEnabled,
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
};
|
||||
|
||||
@ -27,6 +27,7 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
||||
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
|
||||
import { AuthResolver } from './auth.resolver';
|
||||
|
||||
@ -52,6 +53,7 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
|
||||
OnboardingModule,
|
||||
WorkspaceDataSourceModule,
|
||||
ConnectedAccountModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
controllers: [
|
||||
GoogleAuthController,
|
||||
|
||||
@ -8,18 +8,33 @@ import {
|
||||
import { GoogleAPIsOauthExchangeCodeForTokenStrategy } from 'src/engine/core-modules/auth/strategies/google-apis-oauth-exchange-code-for-token.auth.strategy';
|
||||
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { TokenService } from 'src/engine/core-modules/auth/token/services/token.service';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
|
||||
'google-apis',
|
||||
) {
|
||||
constructor(private readonly environmentService: EnvironmentService) {
|
||||
constructor(
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly tokenService: TokenService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async canActivate(context: ExecutionContext) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const state = JSON.parse(request.query.state);
|
||||
const { workspaceId } = await this.tokenService.verifyTransientToken(
|
||||
state.transientToken,
|
||||
);
|
||||
const isGmailSendEmailScopeEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsGmailSendEmailScopeEnabled,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (
|
||||
!this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') &&
|
||||
@ -34,6 +49,7 @@ export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
|
||||
new GoogleAPIsOauthExchangeCodeForTokenStrategy(
|
||||
this.environmentService,
|
||||
{},
|
||||
isGmailSendEmailScopeEnabled,
|
||||
);
|
||||
|
||||
setRequestExtraParams(request, {
|
||||
|
||||
@ -8,10 +8,17 @@ import {
|
||||
import { GoogleAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/auth/strategies/google-apis-oauth-request-code.auth.strategy';
|
||||
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { TokenService } from 'src/engine/core-modules/auth/token/services/token.service';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
|
||||
constructor(private readonly environmentService: EnvironmentService) {
|
||||
constructor(
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly tokenService: TokenService,
|
||||
) {
|
||||
super({
|
||||
prompt: 'select_account',
|
||||
});
|
||||
@ -20,6 +27,15 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
|
||||
async canActivate(context: ExecutionContext) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
const { workspaceId } = await this.tokenService.verifyTransientToken(
|
||||
request.query.transientToken,
|
||||
);
|
||||
const isGmailSendEmailScopeEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsGmailSendEmailScopeEnabled,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (
|
||||
!this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') &&
|
||||
!this.environmentService.get('CALENDAR_PROVIDER_GOOGLE_ENABLED')
|
||||
@ -30,12 +46,17 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
|
||||
);
|
||||
}
|
||||
|
||||
new GoogleAPIsOauthRequestCodeStrategy(this.environmentService, {});
|
||||
new GoogleAPIsOauthRequestCodeStrategy(
|
||||
this.environmentService,
|
||||
{},
|
||||
isGmailSendEmailScopeEnabled,
|
||||
);
|
||||
setRequestExtraParams(request, {
|
||||
transientToken: request.query.transientToken,
|
||||
redirectLocation: request.query.redirectLocation,
|
||||
calendarVisibility: request.query.calendarVisibility,
|
||||
messageVisibility: request.query.messageVisibility,
|
||||
loginHint: request.query.loginHint,
|
||||
});
|
||||
|
||||
const activate = (await super.canActivate(context)) as boolean;
|
||||
|
||||
@ -33,6 +33,9 @@ import {
|
||||
MessagingMessageListFetchJobData,
|
||||
} from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { getGoogleApisOauthScopes } from 'src/engine/core-modules/auth/utils/get-google-apis-oauth-scopes';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleAPIsService {
|
||||
@ -44,6 +47,7 @@ export class GoogleAPIsService {
|
||||
private readonly calendarQueueService: MessageQueueService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly accountsToReconnectService: AccountsToReconnectService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
async refreshGoogleRefreshToken(input: {
|
||||
@ -95,6 +99,13 @@ export class GoogleAPIsService {
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(workspaceId);
|
||||
|
||||
const isGmailSendEmailScopeEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsGmailSendEmailScopeEnabled,
|
||||
workspaceId,
|
||||
);
|
||||
const scopes = getGoogleApisOauthScopes(isGmailSendEmailScopeEnabled);
|
||||
|
||||
await workspaceDataSource.transaction(async (manager: EntityManager) => {
|
||||
if (!existingAccountId) {
|
||||
await connectedAccountRepository.save(
|
||||
@ -105,6 +116,7 @@ export class GoogleAPIsService {
|
||||
accessToken: input.accessToken,
|
||||
refreshToken: input.refreshToken,
|
||||
accountOwnerId: workspaceMemberId,
|
||||
scopes,
|
||||
},
|
||||
{},
|
||||
manager,
|
||||
@ -146,6 +158,7 @@ export class GoogleAPIsService {
|
||||
{
|
||||
accessToken: input.accessToken,
|
||||
refreshToken: input.refreshToken,
|
||||
scopes,
|
||||
},
|
||||
manager,
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@ import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy } from 'passport-google-oauth20';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { getGoogleApisOauthScopes } from 'src/engine/core-modules/auth/utils/get-google-apis-oauth-scopes';
|
||||
|
||||
export type GoogleAPIScopeConfig = {
|
||||
isCalendarEnabled?: boolean;
|
||||
@ -18,14 +19,9 @@ export class GoogleAPIsOauthCommonStrategy extends PassportStrategy(
|
||||
constructor(
|
||||
environmentService: EnvironmentService,
|
||||
scopeConfig: GoogleAPIScopeConfig,
|
||||
isGmailSendEmailScopeEnabled = false,
|
||||
) {
|
||||
const scopes = [
|
||||
'email',
|
||||
'profile',
|
||||
'https://www.googleapis.com/auth/gmail.readonly',
|
||||
'https://www.googleapis.com/auth/calendar.events',
|
||||
'https://www.googleapis.com/auth/profile.emails.read',
|
||||
];
|
||||
const scopes = getGoogleApisOauthScopes(isGmailSendEmailScopeEnabled);
|
||||
|
||||
super({
|
||||
clientID: environmentService.get('AUTH_GOOGLE_CLIENT_ID'),
|
||||
|
||||
@ -15,8 +15,9 @@ export class GoogleAPIsOauthExchangeCodeForTokenStrategy extends GoogleAPIsOauth
|
||||
constructor(
|
||||
environmentService: EnvironmentService,
|
||||
scopeConfig: GoogleAPIScopeConfig,
|
||||
isGmailSendEmailScopeEnabled = false,
|
||||
) {
|
||||
super(environmentService, scopeConfig);
|
||||
super(environmentService, scopeConfig, isGmailSendEmailScopeEnabled);
|
||||
}
|
||||
|
||||
async validate(
|
||||
|
||||
@ -13,8 +13,9 @@ export class GoogleAPIsOauthRequestCodeStrategy extends GoogleAPIsOauthCommonStr
|
||||
constructor(
|
||||
environmentService: EnvironmentService,
|
||||
scopeConfig: GoogleAPIScopeConfig,
|
||||
isGmailSendEmailScopeEnabled = false,
|
||||
) {
|
||||
super(environmentService, scopeConfig);
|
||||
super(environmentService, scopeConfig, isGmailSendEmailScopeEnabled);
|
||||
}
|
||||
|
||||
authenticate(req: any, options: any) {
|
||||
@ -22,6 +23,7 @@ export class GoogleAPIsOauthRequestCodeStrategy extends GoogleAPIsOauthCommonStr
|
||||
...options,
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
loginHint: req.params.loginHint,
|
||||
state: JSON.stringify({
|
||||
transientToken: req.params.transientToken,
|
||||
redirectLocation: req.params.redirectLocation,
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
export const getGoogleApisOauthScopes = (
|
||||
isGmailSendEmailScopeEnabled = false,
|
||||
) => {
|
||||
const scopes = [
|
||||
'email',
|
||||
'profile',
|
||||
'https://www.googleapis.com/auth/gmail.readonly',
|
||||
'https://www.googleapis.com/auth/calendar.events',
|
||||
'https://www.googleapis.com/auth/profile.emails.read',
|
||||
];
|
||||
|
||||
if (isGmailSendEmailScopeEnabled) {
|
||||
scopes.push('https://www.googleapis.com/auth/gmail.send');
|
||||
}
|
||||
|
||||
return scopes;
|
||||
};
|
||||
@ -9,6 +9,7 @@ type GoogleAPIsRequestExtraParams = {
|
||||
redirectLocation?: string;
|
||||
calendarVisibility?: string;
|
||||
messageVisibility?: string;
|
||||
loginHint?: string;
|
||||
};
|
||||
|
||||
export const setRequestExtraParams = (
|
||||
@ -20,6 +21,7 @@ export const setRequestExtraParams = (
|
||||
redirectLocation,
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
loginHint,
|
||||
} = params;
|
||||
|
||||
if (!transientToken) {
|
||||
@ -42,4 +44,7 @@ export const setRequestExtraParams = (
|
||||
if (messageVisibility) {
|
||||
request.params.messageVisibility = messageVisibility;
|
||||
}
|
||||
if (loginHint) {
|
||||
request.params.loginHint = loginHint;
|
||||
}
|
||||
};
|
||||
|
||||
@ -12,4 +12,5 @@ export enum FeatureFlagKey {
|
||||
IsWorkspaceFavoriteEnabled = 'IS_WORKSPACE_FAVORITE_ENABLED',
|
||||
IsSearchEnabled = 'IS_SEARCH_ENABLED',
|
||||
IsWorkspaceMigratedForSearch = 'IS_WORKSPACE_MIGRATED_FOR_SEARCH',
|
||||
IsGmailSendEmailScopeEnabled = 'IS_GMAIL_SEND_EMAIL_SCOPE_ENABLED',
|
||||
}
|
||||
|
||||
@ -150,6 +150,7 @@ export const CONNECTED_ACCOUNT_STANDARD_FIELD_IDS = {
|
||||
messageChannels: '20202020-24f7-4362-8468-042204d1e445',
|
||||
calendarChannels: '20202020-af4a-47bb-99ec-51911c1d3977',
|
||||
handleAliases: '20202020-8a3d-46be-814f-6228af16c47b',
|
||||
scopes: '20202020-8a3d-46be-814f-6228af16c47c',
|
||||
};
|
||||
|
||||
export const EVENT_STANDARD_FIELD_IDS = {
|
||||
|
||||
@ -99,6 +99,16 @@ export class ConnectedAccountWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
handleAliases: string;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS.scopes,
|
||||
type: FieldMetadataType.ARRAY,
|
||||
label: 'Scopes',
|
||||
description: 'Scopes',
|
||||
icon: 'IconSettings',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
scopes: string[] | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS.accountOwner,
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class MailSenderException extends CustomException {
|
||||
code: MailSenderExceptionCode;
|
||||
constructor(message: string, code: MailSenderExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
}
|
||||
|
||||
export enum MailSenderExceptionCode {
|
||||
PROVIDER_NOT_SUPPORTED = 'PROVIDER_NOT_SUPPORTED',
|
||||
CONNECTED_ACCOUNT_NOT_FOUND = 'CONNECTED_ACCOUNT_NOT_FOUND',
|
||||
}
|
||||
@ -4,13 +4,24 @@ import { z } from 'zod';
|
||||
import Handlebars from 'handlebars';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { WorkflowActionEmail } from 'twenty-emails';
|
||||
import { render } from '@react-email/components';
|
||||
|
||||
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type';
|
||||
import { WorkflowSendEmailStep } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { EmailService } from 'src/engine/core-modules/email/email.service';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import {
|
||||
MailSenderException,
|
||||
MailSenderExceptionCode,
|
||||
} from 'src/modules/mail-sender/exceptions/mail-sender.exception';
|
||||
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
@Injectable()
|
||||
export class SendEmailWorkflowAction {
|
||||
@ -18,8 +29,48 @@ export class SendEmailWorkflowAction {
|
||||
constructor(
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly emailService: EmailService,
|
||||
private readonly gmailClientProvider: GmailClientProvider,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
private async getEmailClient(step: WorkflowSendEmailStep) {
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Scoped workspace not found',
|
||||
WorkflowStepExecutorExceptionCode.SCOPED_WORKSPACE_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'connectedAccount',
|
||||
);
|
||||
const connectedAccount = await connectedAccountRepository.findOneBy({
|
||||
id: step.settings.connectedAccountId,
|
||||
});
|
||||
|
||||
if (!isDefined(connectedAccount)) {
|
||||
throw new MailSenderException(
|
||||
`Connected Account '${step.settings.connectedAccountId}' not found`,
|
||||
MailSenderExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
switch (connectedAccount.provider) {
|
||||
case 'google':
|
||||
return await this.gmailClientProvider.getGmailClient(connectedAccount);
|
||||
default:
|
||||
throw new MailSenderException(
|
||||
`Provider ${connectedAccount.provider} is not supported`,
|
||||
MailSenderExceptionCode.PROVIDER_NOT_SUPPORTED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async execute({
|
||||
step,
|
||||
payload,
|
||||
@ -30,6 +81,8 @@ export class SendEmailWorkflowAction {
|
||||
[key: string]: string;
|
||||
};
|
||||
}): Promise<WorkflowActionResult> {
|
||||
const emailProvider = await this.getEmailClient(step);
|
||||
|
||||
try {
|
||||
const emailSchema = z.string().trim().email('Invalid email');
|
||||
|
||||
@ -41,33 +94,33 @@ export class SendEmailWorkflowAction {
|
||||
return { result: { success: false } };
|
||||
}
|
||||
|
||||
const mainText = Handlebars.compile(step.settings.template)(payload);
|
||||
const body = Handlebars.compile(step.settings.body)(payload);
|
||||
const subject = Handlebars.compile(step.settings.subject)(payload);
|
||||
|
||||
const window = new JSDOM('').window;
|
||||
const purify = DOMPurify(window);
|
||||
const safeHTML = purify.sanitize(mainText || '');
|
||||
const safeBody = purify.sanitize(body || '');
|
||||
const safeSubject = purify.sanitize(subject || '');
|
||||
|
||||
const email = WorkflowActionEmail({
|
||||
dangerousHTML: safeHTML,
|
||||
title: step.settings.title,
|
||||
callToAction: step.settings.callToAction,
|
||||
});
|
||||
const html = render(email, {
|
||||
pretty: true,
|
||||
});
|
||||
const text = render(email, {
|
||||
plainText: true,
|
||||
const message = [
|
||||
`To: ${payload.email}`,
|
||||
`Subject: ${safeSubject || ''}`,
|
||||
'MIME-Version: 1.0',
|
||||
'Content-Type: text/plain; charset="UTF-8"',
|
||||
'',
|
||||
safeBody,
|
||||
].join('\n');
|
||||
|
||||
const encodedMessage = Buffer.from(message).toString('base64');
|
||||
|
||||
await emailProvider.users.messages.send({
|
||||
userId: 'me',
|
||||
requestBody: {
|
||||
raw: encodedMessage,
|
||||
},
|
||||
});
|
||||
|
||||
await this.emailService.send({
|
||||
from: `${this.environmentService.get(
|
||||
'EMAIL_FROM_NAME',
|
||||
)} <${this.environmentService.get('EMAIL_FROM_ADDRESS')}>`,
|
||||
to: payload.email,
|
||||
subject: step.settings.subject || '',
|
||||
text,
|
||||
html,
|
||||
});
|
||||
this.logger.log(`Email sent successfully`);
|
||||
|
||||
return { result: { success: true } };
|
||||
} catch (error) {
|
||||
|
||||
@ -42,6 +42,10 @@ import { MessageParticipantManagerModule } from 'src/modules/messaging/message-p
|
||||
GmailGetMessageListService,
|
||||
GmailHandleErrorService,
|
||||
],
|
||||
exports: [GmailGetMessagesService, GmailGetMessageListService],
|
||||
exports: [
|
||||
GmailGetMessagesService,
|
||||
GmailGetMessageListService,
|
||||
GmailClientProvider,
|
||||
],
|
||||
})
|
||||
export class MessagingGmailDriverModule {}
|
||||
|
||||
@ -14,11 +14,7 @@ export type WorkflowCodeStepSettings = BaseWorkflowStepSettings & {
|
||||
};
|
||||
|
||||
export type WorkflowSendEmailStepSettings = BaseWorkflowStepSettings & {
|
||||
connectedAccountId: string;
|
||||
subject?: string;
|
||||
template?: string;
|
||||
title?: string;
|
||||
callToAction?: {
|
||||
value: string;
|
||||
href: string;
|
||||
};
|
||||
body?: string;
|
||||
};
|
||||
|
||||
@ -7,9 +7,14 @@ import { CodeWorkflowAction } from 'src/modules/serverless/workflow-actions/code
|
||||
import { SendEmailWorkflowAction } from 'src/modules/mail-sender/workflow-actions/send-email.workflow-action';
|
||||
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { MessagingGmailDriverModule } from 'src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module';
|
||||
|
||||
@Module({
|
||||
imports: [WorkflowCommonModule, ServerlessFunctionModule],
|
||||
imports: [
|
||||
WorkflowCommonModule,
|
||||
ServerlessFunctionModule,
|
||||
MessagingGmailDriverModule,
|
||||
],
|
||||
providers: [
|
||||
WorkflowExecutorWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
|
||||
Reference in New Issue
Block a user