Files
twenty/packages/twenty-shared/src/utils/compute-diff-between-objects.ts
T
Weiko c1e4756f9c Add is active to overridable entities and deactivation logic for page layouts (#19200)
- Replace soft-deletion (deletedAt) with isActive boolean for
overridable entities (tabs, widgets, viewFieldGroups, viewFields).
Standard entities are deactivated (isActive: false) when removed from
update payloads, while custom entities are hard-deleted.
- When a viewFieldGroup is deactivated/deleted, its viewFields are
reassigned to the next section by position (or null if none remain).
- Add isActive: true filters to viewFieldGroup and viewField API queries
so deactivated entities are excluded from responses.

Next: 
- fields-widget-upsert.service.ts should be refactored a bit
- Add restore logic
2026-04-02 14:50:50 +00:00

91 lines
2.2 KiB
TypeScript

import { isDefined } from '@/utils/validation';
import deepEqual from 'deep-equal';
type Diff<T extends { id: string }> = {
toCreate: T[];
toUpdate: T[];
toRestoreAndUpdate: T[];
idsToRemove: string[];
};
const extractProperties = <T extends { id: string }>(
object: T,
properties: (keyof T)[],
) => {
return properties.reduce((acc, property) => {
return {
...acc,
[property]: object[property],
};
}, {});
};
type ComputeDiffBetweenObjectsParams<
T extends { id: string },
K extends { id: string },
> = {
existingObjects: T[];
receivedObjects: K[];
propertiesToCompare: (keyof K & keyof T)[];
isEntityIncluded: (entity: NoInfer<T>) => boolean;
};
export const computeDiffBetweenObjects = <
T extends { id: string },
K extends { id: string },
>({
existingObjects,
receivedObjects,
propertiesToCompare,
isEntityIncluded,
}: ComputeDiffBetweenObjectsParams<T, K>): Diff<K> => {
const toCreate: K[] = [];
const toUpdate: K[] = [];
const toRestoreAndUpdate: K[] = [];
const existingEntitiesMap = new Map(
existingObjects.map((entity) => [entity.id, entity]),
);
const receivedEntitiesMap = new Map(
receivedObjects.map((entity) => [entity.id, entity]),
);
for (const receivedObject of receivedObjects) {
const existingEntity = existingEntitiesMap.get(receivedObject.id);
if (isDefined(existingEntity)) {
if (!isEntityIncluded(existingEntity)) {
toRestoreAndUpdate.push(receivedObject);
} else {
const comparableExistingEntity = extractProperties(
existingEntity,
propertiesToCompare,
);
const comparableReceivedEntity = extractProperties(
receivedObject,
propertiesToCompare,
);
if (!deepEqual(comparableExistingEntity, comparableReceivedEntity)) {
toUpdate.push(receivedObject);
}
}
} else {
toCreate.push(receivedObject);
}
}
const idsToRemove = existingObjects
.filter((existingEntity) => isEntityIncluded(existingEntity))
.filter((existingEntity) => !receivedEntitiesMap.has(existingEntity.id))
.map((entity) => entity.id);
return {
toCreate,
toUpdate,
toRestoreAndUpdate,
idsToRemove,
};
};