Refactor workspace migration update action (#17701)
# Introduction
Removing:
- `from` property from actions definition, as it's a legitimate source
of truth. The stored comparison might have been compromised since action
generation. If from is needed it should be computed from the optimistic
cache at runner lvl
- Removed the `FlatEntityPropertyUpdates` Array complexity in favor of
From
```ts
export type PropertyUpdate<T, P extends keyof T> = {
property: P;
} & FromTo<T[P]>;
```
To
```ts
export type FlatEntityUpdate<T extends AllMetadataName> = Partial<
Pick<
MetadataFlatEntity<T>,
Extract<FlatEntityPropertiesToCompare<T>, keyof MetadataFlatEntity<T>>
>
>;
```
## New interactions
From
```ts
const positionUpdate = findFlatEntityPropertyUpdate({
flatEntityUpdates,
property: 'position',
});
if (
isDefined(positionUpdate) &&
(!Number.isInteger(positionUpdate.to) || positionUpdate.to < 0)
) {
const toFlatNavigationMenuItem = {
...fromFlatNavigationMenuItem,
...fromFlatEntityPropertiesUpdatesToPartialFlatEntity({
updates: flatEntityUpdates,
}),
};
```
To
```ts
const positionUpdate = flatEntityUpdate.position;
if (
isDefined(positionUpdate) &&
(!Number.isInteger(positionUpdate) || positionUpdate < 0)
) {
const toFlatNavigationMenuItem = {
...fromFlatNavigationMenuItem,
...flatEntityUpdate,
};
```
## `SanitizeFlatEntityUpdate`
Enforcing the `flatEntityUpdate` to only contains comparable properties
per flat entity by striping out all unexpected keys
In the future we will also move the whole validation at runner lvl at
some point
```ts
export const sanitizeFlatEntityUpdate = <T extends AllMetadataName>({
flatEntityUpdate,
metadataName,
}: {
flatEntityUpdate: FlatEntityUpdate<T>;
metadataName: T;
}): FlatEntityUpdate<T> => {
const { propertiesToCompare } =
ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY[metadataName];
const initialAccumulator: FlatEntityUpdate<T> = {};
return propertiesToCompare.reduce((accumulator, property) => {
const updatedValue =
flatEntityUpdate[property as MetadataFlatEntityComparableProperties<T>];
if (updatedValue === undefined) {
return accumulator;
}
return {
...accumulator,
[property]: updatedValue,
};
}, initialAccumulator);
};
```
This commit is contained in:
+6
-14
@@ -1,21 +1,13 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`compareTwoFlatEntity It should detect flat field metadata isActive diff from true to false 1`] = `
|
||||
[
|
||||
{
|
||||
"from": true,
|
||||
"property": "isActive",
|
||||
"to": false,
|
||||
},
|
||||
]
|
||||
{
|
||||
"isActive": false,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`compareTwoFlatEntity It should detect flat field metadata isActive diff from true to false 2`] = `
|
||||
[
|
||||
{
|
||||
"from": false,
|
||||
"property": "isActive",
|
||||
"to": true,
|
||||
},
|
||||
]
|
||||
{
|
||||
"isActive": true,
|
||||
}
|
||||
`;
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ describe('compareTwoFlatEntity', () => {
|
||||
});
|
||||
|
||||
expect(result).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(result),
|
||||
extractRecordIdsAndDatesAsExpectAny({ ...result }),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
+32
-30
@@ -1,10 +1,10 @@
|
||||
import diff from 'microdiff';
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { parseJson } from 'twenty-shared/utils';
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
|
||||
import { type FlatEntityPropertiesToCompare } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-to-compare.type';
|
||||
import { type FlatEntityPropertiesUpdates } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-updates.type';
|
||||
import { type FlatEntityUpdate } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-updates.type';
|
||||
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
|
||||
import { transformFlatEntityForComparison } from 'src/engine/metadata-modules/flat-entity/utils/transform-flat-entity-for-comparison.util';
|
||||
|
||||
@@ -23,7 +23,7 @@ export const compareTwoFlatEntity = <
|
||||
}: FromTo<MetadataFlatEntity<T>, 'flatEntity'> & {
|
||||
propertiesToCompare: readonly PToCompare[];
|
||||
propertiesToStringify: readonly PJsonB[];
|
||||
}): FlatEntityPropertiesUpdates<T> => {
|
||||
}): FlatEntityUpdate<T> | undefined => {
|
||||
const [transformedFromFlatEntity, transformedToFlatEntity] = [
|
||||
fromFlatEntity,
|
||||
toFlatEntity,
|
||||
@@ -40,37 +40,39 @@ export const compareTwoFlatEntity = <
|
||||
transformedToFlatEntity,
|
||||
);
|
||||
|
||||
return flatEntityDifferences.flatMap<FlatEntityPropertiesUpdates<T>[number]>(
|
||||
(difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as PToCompare;
|
||||
const isJsonb = propertiesToStringify.includes(
|
||||
property as unknown as PJsonB,
|
||||
);
|
||||
if (flatEntityDifferences.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isJsonb) {
|
||||
return {
|
||||
from: parseJson(oldValue),
|
||||
to: parseJson(value),
|
||||
property,
|
||||
};
|
||||
}
|
||||
const initialAccumulator: FlatEntityUpdate<T> = {};
|
||||
|
||||
return flatEntityDifferences.reduce((accumulator, difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { path, value } = difference;
|
||||
const property = path[0] as PToCompare;
|
||||
const isJsonb = propertiesToStringify.includes(
|
||||
property as unknown as PJsonB,
|
||||
);
|
||||
|
||||
if (isJsonb) {
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
...accumulator,
|
||||
[property]: parseJson(value),
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occur, we should only provide null never undefined and so on
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
...accumulator,
|
||||
[property]: value,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occur, we should only provide null never undefined and so on
|
||||
return accumulator;
|
||||
}
|
||||
}
|
||||
}, initialAccumulator);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user