From 964f1c62275b5a58e8c1216dcbbf3dd71cbcb316 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:44:23 +0100 Subject: [PATCH] Remove `delete-field` and `delete-object` aggregators (#17183) # Introduction ~~Testing first this might break some constraints can't remember the initial motivation~~ -> it does not The goal here is to avoid relying on pg cascading which will lock the schema longer than if done in n operations --- ...ject-and-delete-field-actions.util.spec.ts | 265 ------------------ ...ort-delete-object-and-delete-field.util.ts | 60 ---- ...regate-orchestrator-actions-report.util.ts | 2 - 3 files changed, 327 deletions(-) delete mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/__tests__/aggregate-orchestrator-actions-report-delete-object-and-delete-field-actions.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util.ts diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/__tests__/aggregate-orchestrator-actions-report-delete-object-and-delete-field-actions.util.spec.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/__tests__/aggregate-orchestrator-actions-report-delete-object-and-delete-field-actions.util.spec.ts deleted file mode 100644 index b7848a1b3a..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/__tests__/aggregate-orchestrator-actions-report-delete-object-and-delete-field-actions.util.spec.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { - eachTestingContextFilter, - type EachTestingContext, -} from 'twenty-shared/testing'; - -import { createEmptyOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/constant/empty-orchestrator-actions-report.constant'; -import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type'; -import { aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions } from 'src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util'; -import { type DeleteFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action'; -import { type DeleteObjectAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/object/types/workspace-migration-object-action'; - -type DeleteAggregationTestCase = EachTestingContext<{ - input: OrchestratorActionsReport; - expected: { - expectDeleteFieldActionPerObjectMetadataId: Record; - expectDeleteObjectActionPerObjectMetadataId: Record; - }; -}>; - -describe('aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions', () => { - const testCases: DeleteAggregationTestCase[] = [ - { - title: 'should remove field actions when parent object is being deleted', - context: { - input: { - ...createEmptyOrchestratorActionsReport(), - objectMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'objectMetadata', - entityId: 'object-1', - } satisfies DeleteObjectAction, - ], - }, - fieldMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-1', - objectMetadataId: 'object-1', - } satisfies DeleteFieldAction, - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-2', - objectMetadataId: 'object-1', - } satisfies DeleteFieldAction, - ], - }, - } satisfies OrchestratorActionsReport, - expected: { - expectDeleteFieldActionPerObjectMetadataId: {}, - expectDeleteObjectActionPerObjectMetadataId: { - 'object-1': 1, - }, - }, - }, - }, - { - title: 'should keep field actions when no parent object is being deleted', - context: { - input: { - ...createEmptyOrchestratorActionsReport(), - objectMetadata: { - create: [], - update: [], - delete: [], - }, - fieldMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-1', - objectMetadataId: 'object-1', - } satisfies DeleteFieldAction, - ], - }, - } satisfies OrchestratorActionsReport, - expected: { - expectDeleteFieldActionPerObjectMetadataId: { - 'object-1': 1, - }, - expectDeleteObjectActionPerObjectMetadataId: {}, - }, - }, - }, - { - title: - 'should handle mixed scenario with some fields removed and some kept', - context: { - input: { - ...createEmptyOrchestratorActionsReport(), - objectMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'objectMetadata', - entityId: 'object-1', - } satisfies DeleteObjectAction, - ], - }, - fieldMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-1', - objectMetadataId: 'object-1', - } satisfies DeleteFieldAction, - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-2', - objectMetadataId: 'object-2', - } satisfies DeleteFieldAction, - ], - }, - } satisfies OrchestratorActionsReport, - expected: { - expectDeleteFieldActionPerObjectMetadataId: { - 'object-2': 1, - }, - expectDeleteObjectActionPerObjectMetadataId: { - 'object-1': 1, - }, - }, - }, - }, - { - title: 'should handle multiple objects with mixed field deletions', - context: { - input: { - ...createEmptyOrchestratorActionsReport(), - objectMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'objectMetadata', - entityId: 'object-1', - } satisfies DeleteObjectAction, - { - type: 'delete', - metadataName: 'objectMetadata', - entityId: 'object-2', - } satisfies DeleteObjectAction, - ], - }, - fieldMetadata: { - create: [], - update: [], - delete: [ - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-1', - objectMetadataId: 'object-1', - } satisfies DeleteFieldAction, - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-2', - objectMetadataId: 'object-2', - } satisfies DeleteFieldAction, - { - type: 'delete', - metadataName: 'fieldMetadata', - entityId: 'field-3', - objectMetadataId: 'object-3', - } satisfies DeleteFieldAction, - ], - }, - } satisfies OrchestratorActionsReport, - expected: { - expectDeleteFieldActionPerObjectMetadataId: { - 'object-3': 1, - }, - expectDeleteObjectActionPerObjectMetadataId: { - 'object-1': 1, - 'object-2': 1, - }, - }, - }, - }, - { - title: 'should handle empty actions report', - context: { - input: createEmptyOrchestratorActionsReport(), - expected: { - expectDeleteFieldActionPerObjectMetadataId: {}, - expectDeleteObjectActionPerObjectMetadataId: {}, - }, - }, - }, - ]; - - test.each(eachTestingContextFilter(testCases))( - '$title', - ({ context: { input, expected } }) => { - const result = - aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions({ - orchestratorActionsReport: input, - }); - - const fieldActions = result.fieldMetadata.delete as DeleteFieldAction[]; - const fieldActionCounts = fieldActions.reduce( - (acc, action) => { - acc[action.objectMetadataId] = - (acc[action.objectMetadataId] || 0) + 1; - - return acc; - }, - {} as Record, - ); - - const objectActions = result.objectMetadata - .delete as DeleteObjectAction[]; - const objectActionCounts = objectActions.reduce( - (acc, action) => { - acc[action.entityId] = (acc[action.entityId] || 0) + 1; - - return acc; - }, - {} as Record, - ); - - Object.entries( - expected.expectDeleteFieldActionPerObjectMetadataId, - ).forEach(([objectId, expectedCount]) => { - expect(fieldActionCounts[objectId]).toBe(expectedCount); - }); - - Object.entries( - expected.expectDeleteObjectActionPerObjectMetadataId, - ).forEach(([objectId, expectedCount]) => { - expect(objectActionCounts[objectId]).toBe(expectedCount); - }); - - // Check total counts - const expectedTotalFieldActions = Object.values( - expected.expectDeleteFieldActionPerObjectMetadataId, - ).reduce((sum, count) => sum + count, 0); - const expectedTotalObjectActions = Object.values( - expected.expectDeleteObjectActionPerObjectMetadataId, - ).reduce((sum, count) => sum + count, 0); - - expect(fieldActions).toHaveLength(expectedTotalFieldActions); - expect(objectActions).toHaveLength(expectedTotalObjectActions); - }, - ); -}); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util.ts deleted file mode 100644 index f056ea8de9..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { type AggregateOrchestratorActionsReportArgs } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-aggregate-orchestrator-actions-report-args.type'; -import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type'; -import { type DeleteFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action'; -import { type DeleteObjectAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/object/types/workspace-migration-object-action'; - -type AggregatedActions = { - deleteFieldActionByFieldMetadataId: Record; -}; -export const aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions = - ({ - orchestratorActionsReport, - }: AggregateOrchestratorActionsReportArgs): OrchestratorActionsReport => { - const deleteObjectActionByObjectMetadataId = ( - orchestratorActionsReport.objectMetadata.delete as DeleteObjectAction[] - ).reduce>( - (acc, deleteObjectAction) => ({ - ...acc, - [deleteObjectAction.entityId]: deleteObjectAction, - }), - {}, - ); - const initialAccumulator: AggregatedActions = { - deleteFieldActionByFieldMetadataId: {}, - }; - - const { deleteFieldActionByFieldMetadataId } = ( - orchestratorActionsReport.fieldMetadata.delete as DeleteFieldAction[] - ).reduce( - ({ deleteFieldActionByFieldMetadataId }, deleteFieldAction) => { - const fieldParentObjectDeleteObjectAction = - deleteObjectActionByObjectMetadataId[ - deleteFieldAction.objectMetadataId - ]; - - if (isDefined(fieldParentObjectDeleteObjectAction)) { - return { - deleteFieldActionByFieldMetadataId, - }; - } - - return { - deleteFieldActionByFieldMetadataId: { - ...deleteFieldActionByFieldMetadataId, - [deleteFieldAction.entityId]: deleteFieldAction, - }, - }; - }, - initialAccumulator, - ); - - return { - ...orchestratorActionsReport, - fieldMetadata: { - ...orchestratorActionsReport.fieldMetadata, - delete: Object.values(deleteFieldActionByFieldMetadataId), - }, - }; - }; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report.util.ts index 0382d69e9b..a04f77b4a0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report.util.ts @@ -1,6 +1,5 @@ import { type AggregateOrchestratorActionsReportArgs } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-aggregate-orchestrator-actions-report-args.type'; import { aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions } from 'src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-create-object-and-create-field-actions.util'; -import { aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions } from 'src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util'; import { aggregateOrchestratorActionsReportDeprioritizeSearchVectorUpdateFieldActions } from 'src/engine/workspace-manager/workspace-migration/utils/aggregate-orchestrator-actions-report-deprioritize-search-vector-update-field-actions.util'; export const aggregateOrchestratorActionsReport = ({ @@ -9,7 +8,6 @@ export const aggregateOrchestratorActionsReport = ({ }: AggregateOrchestratorActionsReportArgs) => { const aggregatedOrchestratorActionsReport = [ aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions, - aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions, aggregateOrchestratorActionsReportDeprioritizeSearchVectorUpdateFieldActions, ].reduce( (currentOrchestratorActionsReport, aggregator) =>