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
This commit is contained in:
Paul Rastoin
2026-01-16 13:44:23 +01:00
committed by GitHub
parent 9750bcba81
commit 964f1c6227
3 changed files with 0 additions and 327 deletions
@@ -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<string, number>;
expectDeleteObjectActionPerObjectMetadataId: Record<string, number>;
};
}>;
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<string, number>,
);
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<string, number>,
);
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);
},
);
});
@@ -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<string, DeleteFieldAction>;
};
export const aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions =
({
orchestratorActionsReport,
}: AggregateOrchestratorActionsReportArgs): OrchestratorActionsReport => {
const deleteObjectActionByObjectMetadataId = (
orchestratorActionsReport.objectMetadata.delete as DeleteObjectAction[]
).reduce<Record<string, DeleteObjectAction>>(
(acc, deleteObjectAction) => ({
...acc,
[deleteObjectAction.entityId]: deleteObjectAction,
}),
{},
);
const initialAccumulator: AggregatedActions = {
deleteFieldActionByFieldMetadataId: {},
};
const { deleteFieldActionByFieldMetadataId } = (
orchestratorActionsReport.fieldMetadata.delete as DeleteFieldAction[]
).reduce<AggregatedActions>(
({ 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),
},
};
};
@@ -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) =>