Skip to content

Commit 79e0207

Browse files
authored
Filter valid fields in record steps (#17145)
Fixes #16775 When a field is deleted from the model, the workflow action step still stores the deleted field name in `step.settings.input.objectRecord`. We need to filter out the fields that are not valid anymore. This logic existed before but had been removed with the migration to tool services.
1 parent 93aef30 commit 79e0207

4 files changed

Lines changed: 89 additions & 5 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { isDefined } from 'twenty-shared/utils';
2+
3+
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
4+
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
5+
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
6+
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
7+
8+
export const filterValidFieldsInRecord = (
9+
record: Record<string, unknown>,
10+
flatObjectMetadata: FlatObjectMetadata,
11+
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
12+
): Record<string, unknown> => {
13+
const { fieldIdByName, fieldIdByJoinColumnName } =
14+
buildFieldMapsFromFlatObjectMetadata(
15+
flatFieldMetadataMaps,
16+
flatObjectMetadata,
17+
);
18+
19+
const filteredRecord: Record<string, unknown> = {};
20+
21+
for (const [key, value] of Object.entries(record)) {
22+
const fieldMetadataId = fieldIdByName[key] || fieldIdByJoinColumnName[key];
23+
24+
if (isDefined(fieldMetadataId)) {
25+
filteredRecord[key] = value;
26+
}
27+
}
28+
29+
return filteredRecord;
30+
};

packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-e
1414
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
1515
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
1616
import { buildWorkflowActorMetadata } from 'src/modules/workflow/workflow-executor/utils/build-workflow-actor-metadata.util';
17+
import { filterValidFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/filter-valid-fields-in-record.util';
1718
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
1819
import { resolveRichTextFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/resolve-rich-text-fields-in-record.util';
1920
import { type WorkflowCreateRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
@@ -61,14 +62,20 @@ export class CreateRecordWorkflowAction implements WorkflowAction {
6162
context,
6263
) as WorkflowCreateRecordActionInput;
6364

65+
const filteredObjectRecord = filterValidFieldsInRecord(
66+
workflowActionInput.objectRecord,
67+
objectMetadataInfo.flatObjectMetadata,
68+
objectMetadataInfo.flatFieldMetadataMaps,
69+
);
70+
6471
const executionContext =
6572
await this.workflowExecutionContextService.getExecutionContext(runInfo);
6673

6774
const createdBy = buildWorkflowActorMetadata(executionContext);
6875

6976
const toolOutput = await this.createRecordService.execute({
7077
objectName: workflowActionInput.objectName,
71-
objectRecord: workflowActionInput.objectRecord,
78+
objectRecord: filteredObjectRecord,
7279
authContext: executionContext.authContext,
7380
createdBy,
7481
rolePermissionConfig: executionContext.rolePermissionConfig,

packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-e
1818
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
1919
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
2020
import { buildWorkflowActorMetadata } from 'src/modules/workflow/workflow-executor/utils/build-workflow-actor-metadata.util';
21+
import { filterValidFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/filter-valid-fields-in-record.util';
2122
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
2223
import { resolveRichTextFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/resolve-rich-text-fields-in-record.util';
2324
import { isWorkflowUpdateRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-update-record-action.guard';
@@ -84,6 +85,23 @@ export class UpdateRecordWorkflowAction implements WorkflowAction {
8485
);
8586
}
8687

88+
const filteredObjectRecord = filterValidFieldsInRecord(
89+
workflowActionInput.objectRecord,
90+
objectMetadataInfo.flatObjectMetadata,
91+
objectMetadataInfo.flatFieldMetadataMaps,
92+
);
93+
94+
const filteredFieldsToUpdate = workflowActionInput.fieldsToUpdate?.filter(
95+
(fieldName) => fieldName in filteredObjectRecord,
96+
);
97+
98+
if (filteredFieldsToUpdate?.length === 0) {
99+
throw new RecordCrudException(
100+
'Failed to update: No fields to update',
101+
RecordCrudExceptionCode.INVALID_REQUEST,
102+
);
103+
}
104+
87105
const executionContext =
88106
await this.workflowExecutionContextService.getExecutionContext(runInfo);
89107

@@ -92,8 +110,8 @@ export class UpdateRecordWorkflowAction implements WorkflowAction {
92110
const toolOutput = await this.updateRecordService.execute({
93111
objectName: workflowActionInput.objectName,
94112
objectRecordId: workflowActionInput.objectRecordId,
95-
objectRecord: workflowActionInput.objectRecord,
96-
fieldsToUpdate: workflowActionInput.fieldsToUpdate,
113+
objectRecord: filteredObjectRecord,
114+
fieldsToUpdate: filteredFieldsToUpdate,
97115
authContext: executionContext.authContext,
98116
updatedBy,
99117
rolePermissionConfig: executionContext.rolePermissionConfig,

packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ import {
99
RecordCrudExceptionCode,
1010
} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception';
1111
import { UpsertRecordService } from 'src/engine/core-modules/record-crud/services/upsert-record.service';
12+
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
1213
import {
1314
WorkflowStepExecutorException,
1415
WorkflowStepExecutorExceptionCode,
1516
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
1617
import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-executor/services/workflow-execution-context.service';
1718
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
1819
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
20+
import { filterValidFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/filter-valid-fields-in-record.util';
1921
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
22+
import { resolveRichTextFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/resolve-rich-text-fields-in-record.util';
2023
import { isWorkflowUpsertRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-upsert-record-action.guard';
2124
import { type WorkflowUpsertRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
2225

@@ -25,6 +28,7 @@ export class UpsertRecordWorkflowAction implements WorkflowAction {
2528
constructor(
2629
private readonly upsertRecordService: UpsertRecordService,
2730
private readonly workflowExecutionContextService: WorkflowExecutionContextService,
31+
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
2832
) {}
2933

3034
async execute({
@@ -45,8 +49,27 @@ export class UpsertRecordWorkflowAction implements WorkflowAction {
4549
);
4650
}
4751

52+
const { workspaceId } = runInfo;
53+
54+
const rawInput = step.settings.input as WorkflowUpsertRecordActionInput;
55+
56+
const objectMetadataInfo =
57+
await this.workflowCommonWorkspaceService.getObjectMetadataInfo(
58+
rawInput.objectName,
59+
workspaceId,
60+
);
61+
62+
const inputWithResolvedRichText = {
63+
...rawInput,
64+
objectRecord: resolveRichTextFieldsInRecord(
65+
rawInput.objectRecord,
66+
objectMetadataInfo,
67+
context,
68+
),
69+
};
70+
4871
const workflowActionInput = resolveInput(
49-
step.settings.input,
72+
inputWithResolvedRichText,
5073
context,
5174
) as WorkflowUpsertRecordActionInput;
5275

@@ -57,12 +80,18 @@ export class UpsertRecordWorkflowAction implements WorkflowAction {
5780
);
5881
}
5982

83+
const filteredObjectRecord = filterValidFieldsInRecord(
84+
workflowActionInput.objectRecord,
85+
objectMetadataInfo.flatObjectMetadata,
86+
objectMetadataInfo.flatFieldMetadataMaps,
87+
);
88+
6089
const executionContext =
6190
await this.workflowExecutionContextService.getExecutionContext(runInfo);
6291

6392
const toolOutput = await this.upsertRecordService.execute({
6493
objectName: workflowActionInput.objectName,
65-
objectRecord: workflowActionInput.objectRecord,
94+
objectRecord: filteredObjectRecord,
6695
authContext: executionContext.authContext,
6796
rolePermissionConfig: executionContext.rolePermissionConfig,
6897
});

0 commit comments

Comments
 (0)