Fix send email connected account (#12149)

Fixes https://github.com/twentyhq/twenty/issues/12144

Account owner id is not fetched anymore by default in find many
connected accounts.
This is coming from the relation migration, since account owner id is
not a basic field anymore.

Enforcing the fetch of account owner id + adding tests

<img width="251" alt="Capture d’écran 2025-05-20 à 18 23 44"
src="https://github.com/user-attachments/assets/3e3105a0-d3e8-4b5d-87b0-80d3e97ab034"
/>
This commit is contained in:
Thomas Trompette
2025-05-21 11:02:04 +02:00
committed by GitHub
parent ca78fc3f27
commit fe25557337
3 changed files with 178 additions and 0 deletions

View File

@ -171,6 +171,13 @@ export const WorkflowEditActionSendEmail = ({
const { records: accounts, loading } = useFindManyRecords<ConnectedAccount>({
objectNameSingular: 'connectedAccount',
filter,
recordGqlFields: {
id: true,
handle: true,
provider: true,
scopes: true,
accountOwnerId: true,
},
});
let emptyOption: SelectOption<string | null> = { label: 'None', value: null };

View File

@ -0,0 +1,143 @@
import { WorkflowSendEmailAction } from '@/workflow/types/Workflow';
import { Meta, StoryObj } from '@storybook/react';
import { expect, fn, within } from '@storybook/test';
import { graphql, HttpResponse } from 'msw';
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import {
getMockedConnectedAccount,
mockedConnectedAccounts,
} from '~/testing/mock-data/connected-accounts';
import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow';
import { WorkflowEditActionSendEmail } from '../WorkflowEditActionSendEmail';
const DEFAULT_ACTION: WorkflowSendEmailAction = {
id: getWorkflowNodeIdMock(),
name: 'Send Email',
type: 'SEND_EMAIL',
valid: false,
settings: {
input: {
connectedAccountId: '',
email: '',
subject: '',
body: '',
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
};
const CONFIGURED_ACTION: WorkflowSendEmailAction = {
id: getWorkflowNodeIdMock(),
name: 'Send Welcome Email',
type: 'SEND_EMAIL',
valid: true,
settings: {
input: {
connectedAccountId: mockedConnectedAccounts[0].accountOwnerId,
email: 'test@twenty.com',
subject: 'Welcome to Twenty!',
body: 'Dear Tim,\n\nWelcome to Twenty! We are excited to have you on board.\n\nBest regards,\nThe Team',
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: true,
},
continueOnFailure: {
value: false,
},
},
},
};
const meta: Meta<typeof WorkflowEditActionSendEmail> = {
title: 'Modules/Workflow/WorkflowEditActionSendEmail',
component: WorkflowEditActionSendEmail,
parameters: {
msw: {
handlers: [
...graphqlMocks.handlers,
graphql.query('FindManyConnectedAccounts', () => {
return HttpResponse.json({
data: {
connectedAccounts: getMockedConnectedAccount(),
},
});
}),
],
},
},
args: {
action: DEFAULT_ACTION,
},
decorators: [
WorkflowStepActionDrawerDecorator,
WorkflowStepDecorator,
ComponentDecorator,
ObjectMetadataItemsDecorator,
SnackBarDecorator,
RouterDecorator,
WorkspaceDecorator,
I18nFrontDecorator,
],
};
export default meta;
type Story = StoryObj<typeof WorkflowEditActionSendEmail>;
export const Default: Story = {
args: {
actionOptions: {
onActionUpdate: fn(),
},
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(await canvas.findByText('Send Email')).toBeVisible();
expect(await canvas.findByText('Account')).toBeVisible();
expect(await canvas.findByText('Subject')).toBeVisible();
expect(await canvas.findByText('Body')).toBeVisible();
},
};
export const Configured: Story = {
args: {
action: CONFIGURED_ACTION,
actionOptions: {
onActionUpdate: fn(),
},
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(await canvas.findByText('Send Welcome Email')).toBeVisible();
expect(await canvas.findByText('Account')).toBeVisible();
expect(await canvas.findByText('Subject')).toBeVisible();
expect(await canvas.findByText('Body')).toBeVisible();
const emailInput = await canvas.findByText('tim@twenty.com');
expect(emailInput).toBeVisible();
const subjectInput = await canvas.findByText('Welcome to Twenty!');
expect(subjectInput).toBeVisible();
},
};

View File

@ -0,0 +1,28 @@
export const mockedConnectedAccounts = [
{
id: '8619ace5-1814-4e56-8439-553eab32a5cc',
handle: 'tim@twenty.com',
provider: 'gmail',
scopes: ['https://www.googleapis.com/auth/gmail.readonly'],
accountOwnerId: '56561b12-cbad-49db-a6bc-00e6b153ec80',
},
];
export const getMockedConnectedAccount = () => {
return {
edges: [
{
node: {
...mockedConnectedAccounts[0],
},
cursor: null,
},
],
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: null,
endCursor: null,
},
};
};