Workspace migration v2 builder INDEX (#13100)
# Introduction - Added `INDEX` action generation - Refactored the naming, mainly `WorkspaceMigrationV2ObjectInput` -> `FlattenObjectMetadata` and same for field. The transpilation will be done above, agnostically of the workspace migration Still need to: - Create testing toolbox and follow each testing pattern for each actions and make a complex one ( remove static current tests ) - Handle standard and custom edges cases Notes: `workspace-migration-v2/types` and `workspace-migration-v2/utils` could be located outside of this folder, my hunch is that we will move them once we work on flatten tranpilers
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||
|
||||
export type DeletedCreatedUpdatedMatrix<T> = {
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
updated: FromTo<T>[];
|
||||
};
|
||||
|
||||
export type CustomDeletedCreatedUpdatedMatrix<TLabel extends string, TInput> = {
|
||||
[P in keyof DeletedCreatedUpdatedMatrix<TInput> as `${P}${Capitalize<TLabel>}`]: DeletedCreatedUpdatedMatrix<TInput>[P];
|
||||
};
|
||||
|
||||
export type UniqueIdentifierItem = {
|
||||
uniqueIdentifier: string;
|
||||
};
|
||||
|
||||
export const deletedCreatedUpdatedMatrixDispatcher = <
|
||||
T extends UniqueIdentifierItem,
|
||||
>({
|
||||
from,
|
||||
to,
|
||||
}: FromTo<T[]>): DeletedCreatedUpdatedMatrix<T> => {
|
||||
const initialDispatcher: DeletedCreatedUpdatedMatrix<T> = {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
};
|
||||
|
||||
const fromMap = new Map(from.map((obj) => [obj.uniqueIdentifier, obj]));
|
||||
const toMap = new Map(to.map((obj) => [obj.uniqueIdentifier, obj]));
|
||||
|
||||
for (const [identifier, fromObj] of fromMap) {
|
||||
if (!toMap.has(identifier)) {
|
||||
initialDispatcher.deleted.push(fromObj);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [identifier, toObj] of toMap) {
|
||||
if (!fromMap.has(identifier)) {
|
||||
initialDispatcher.created.push(toObj);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [identifier, fromObj] of fromMap) {
|
||||
const toObj = toMap.get(identifier);
|
||||
|
||||
if (toObj) {
|
||||
initialDispatcher.updated.push({
|
||||
from: fromObj,
|
||||
to: toObj,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return initialDispatcher;
|
||||
};
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import diff from 'microdiff';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||
import { UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
const flatFieldMetadataPropertiesToCompare = [
|
||||
'defaultValue',
|
||||
'description',
|
||||
'icon',
|
||||
'isActive',
|
||||
'isLabelSyncedWithName',
|
||||
'isUnique',
|
||||
'label',
|
||||
'name',
|
||||
'options',
|
||||
'standardOverrides',
|
||||
] as const satisfies (keyof FlatFieldMetadata)[];
|
||||
|
||||
export type FlatFieldMetadataPropertiesToCompare =
|
||||
(typeof flatFieldMetadataPropertiesToCompare)[number];
|
||||
|
||||
const fieldMetadataPropertiesToStringify = [
|
||||
'defaultValue',
|
||||
'standardOverrides',
|
||||
] as const satisfies FlatFieldMetadataPropertiesToCompare[];
|
||||
|
||||
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
|
||||
return [
|
||||
FieldMetadataType.BOOLEAN,
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
FieldMetadataType.CURRENCY,
|
||||
FieldMetadataType.PHONES,
|
||||
FieldMetadataType.ADDRESS,
|
||||
].includes(type);
|
||||
};
|
||||
|
||||
type GetWorkspaceMigrationUpdateFieldActionArgs = FromTo<FlatFieldMetadata>;
|
||||
export const compareTwoFlatFieldMetadata = ({
|
||||
from,
|
||||
to,
|
||||
}: GetWorkspaceMigrationUpdateFieldActionArgs) => {
|
||||
const compareFieldMetadataOptions = {
|
||||
shouldIgnoreProperty: (
|
||||
property: string,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
) => {
|
||||
if (
|
||||
!flatFieldMetadataPropertiesToCompare.includes(
|
||||
property as FlatFieldMetadataPropertiesToCompare,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
property === 'defaultValue' &&
|
||||
isDefined(fieldMetadata.type) &&
|
||||
shouldNotOverrideDefaultValue(fieldMetadata.type)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
propertiesToStringify: fieldMetadataPropertiesToStringify,
|
||||
};
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
from,
|
||||
compareFieldMetadataOptions,
|
||||
);
|
||||
const toCompare = transformMetadataForComparison(
|
||||
to,
|
||||
compareFieldMetadataOptions,
|
||||
);
|
||||
|
||||
const flatFieldMetadataDifferences = diff(fromCompare, toCompare);
|
||||
|
||||
return flatFieldMetadataDifferences.flatMap<
|
||||
UpdateFieldAction['updates'][number]
|
||||
>((difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property: path[0] as FlatFieldMetadataPropertiesToCompare,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default: {
|
||||
// Should never occurs, we should only provide null never undefined and so on
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import diff from 'microdiff';
|
||||
|
||||
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
const flatIndexMetadataPropertiesToCompare = [
|
||||
'flatIndexFieldMetadatas', // Comparing this as whole ? should iterate on each keys ? => TBD should only map over cols as before ?
|
||||
'indexType',
|
||||
'indexWhereClause',
|
||||
'isUnique',
|
||||
'name',
|
||||
] as const satisfies (keyof FlatIndexMetadata)[];
|
||||
|
||||
type FlatIndexMetadataPropertiesToCompare =
|
||||
(typeof flatIndexMetadataPropertiesToCompare)[number];
|
||||
|
||||
// Should also handle indexFieldMetadata comparison ?
|
||||
export const compareTwoFlatIndexMetadata = ({
|
||||
from,
|
||||
to,
|
||||
}: FromTo<FlatIndexMetadata>) => {
|
||||
const transformOptions = {
|
||||
shouldIgnoreProperty: (property: string) =>
|
||||
!flatIndexMetadataPropertiesToCompare.includes(
|
||||
property as FlatIndexMetadataPropertiesToCompare,
|
||||
),
|
||||
};
|
||||
|
||||
const fromCompare = transformMetadataForComparison(from, transformOptions);
|
||||
const toCompare = transformMetadataForComparison(to, transformOptions);
|
||||
|
||||
const flatIndexeDifferences = diff(fromCompare, toCompare);
|
||||
|
||||
return flatIndexeDifferences.flatMap<{ property: string } & FromTo<unknown>>(
|
||||
(difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
|
||||
const property = path[0];
|
||||
|
||||
if (typeof property === 'number') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
property,
|
||||
to: value,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE':
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import omit from 'lodash.omit';
|
||||
import diff from 'microdiff';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||
import { UpdateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
const flatObjectMetadataPropertiesToCompare = [
|
||||
'description',
|
||||
'icon',
|
||||
'isActive',
|
||||
'isLabelSyncedWithName',
|
||||
'labelPlural',
|
||||
'labelSingular',
|
||||
'namePlural',
|
||||
'nameSingular',
|
||||
'standardOverrides', // Only if standard
|
||||
] as const satisfies (keyof FlatObjectMetadata)[];
|
||||
|
||||
export type FlatObjectMetadataPropertiesToCompare =
|
||||
(typeof flatObjectMetadataPropertiesToCompare)[number];
|
||||
|
||||
export const compareTwoFlatObjectMetadata = ({
|
||||
from,
|
||||
to,
|
||||
}: FromTo<FlatObjectMetadata>) => {
|
||||
const fromCompare = transformMetadataForComparison(from, {});
|
||||
const toCompare = transformMetadataForComparison(to, {});
|
||||
const objectMetadataDifference = diff(fromCompare, omit(toCompare, 'fields'));
|
||||
|
||||
return objectMetadataDifference.flatMap<
|
||||
UpdateObjectAction['updates'][number]
|
||||
>((difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
if (
|
||||
difference.oldValue === null &&
|
||||
(difference.value === null || difference.value === undefined)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
const property = difference.path[0];
|
||||
|
||||
// TODO investigate why it would be a number, in case of array I guess ?
|
||||
if (typeof property === 'number') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Could be handled directly from the diff we do above
|
||||
if (
|
||||
!flatObjectMetadataPropertiesToCompare.includes(
|
||||
property as FlatObjectMetadataPropertiesToCompare,
|
||||
)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
property: property as FlatObjectMetadataPropertiesToCompare,
|
||||
from: difference.oldValue,
|
||||
to: difference.value,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE': {
|
||||
// Should never occurs ? should throw ?
|
||||
return [];
|
||||
}
|
||||
default: {
|
||||
assertUnreachable(
|
||||
difference,
|
||||
`Unexpected difference type: ${difference['type']}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user