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:
Paul Rastoin
2025-07-09 16:58:17 +02:00
committed by GitHub
parent 867619247f
commit 18792f9f74
30 changed files with 1086 additions and 371 deletions

View File

@ -0,0 +1,7 @@
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
export type FlatFieldMetadata = Partial<
Omit<FieldMetadataEntity, 'object' | 'indexFieldMetadatas'>
> & {
uniqueIdentifier: string;
};

View File

@ -0,0 +1,15 @@
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
const indexFieldMetadataEntityRelationProperties = [
'indexMetadata',
'fieldMetadata',
] as const satisfies (keyof IndexFieldMetadataEntity)[];
type IndexFieldMetadataRelationProperties =
(typeof indexFieldMetadataEntityRelationProperties)[number];
export type FlatIndexFieldMetadata = Partial<
Omit<IndexFieldMetadataEntity, IndexFieldMetadataRelationProperties>
> & {
uniqueIdentifier: string;
};

View File

@ -0,0 +1,9 @@
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
export type FlatIndexMetadata = Partial<
Omit<IndexMetadataEntity, 'indexFieldMetadatas' | 'objectMetadata'> // Might have an issue as ObjectMetadataId != uniqueIdentifier
> & {
flatIndexFieldMetadatas: FlatIndexFieldMetadata[];
uniqueIdentifier: string;
};

View File

@ -0,0 +1,17 @@
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
export type FlatObjectMetadata = Partial<
Omit<ObjectMetadataEntity, 'fields' | 'indexMetadatas'>
> & {
uniqueIdentifier: string;
flatIndexMetadatas: FlatIndexMetadata[];
flatFieldMetadatas: FlatFieldMetadata[];
};
// Could be renamed
export type FlatObjectMetadataWithoutFields = Omit<
FlatObjectMetadata,
'flatFieldMetadatas' | 'flatIndexMetadatas'
>;

View File

@ -1,12 +0,0 @@
import { WorkspaceMigrationFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-action-v2';
import { WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-index-action-v2';
import { WorkspaceMigrationV2ObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-action-v2';
import { WorkspaceMigrationUniquenessActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-uniqueness-action-v2';
export type WorkspaceMigrationActionV2 =
| WorkspaceMigrationV2ObjectAction
| WorkspaceMigrationFieldActionV2
| WorkspaceMigrationUniquenessActionV2
| WorkspaceMigrationIndexActionV2;
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];

View File

@ -1,35 +0,0 @@
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
export const fieldMetadataEntityEditableProperties = [
'defaultValue',
'description',
'icon',
'isActive',
'isLabelSyncedWithName',
'isUnique', // unsure
'label',
'name',
'options',
// TODO update once we reactivate the relation edition
// 'relationTargetFieldMetadata',
// 'relationTargetFieldMetadataId',
// 'relationTargetObjectMetadata',
// 'relationTargetObjectMetadataId',
// 'settings',
///
'standardOverrides',
] as const satisfies (keyof FieldMetadataEntity)[];
export type FieldMetadataEntityEditableProperties =
(typeof fieldMetadataEntityEditableProperties)[number];
// TODO could describe required minimum keys
export type WorkspaceMigrationFieldInput = Partial<
Omit<FieldMetadataEntity, 'object'>
> & {
uniqueIdentifier: string;
};
export const fieldMetadataPropertiesToStringify = [
'defaultValue',
'standardOverrides',
] as const satisfies FieldMetadataEntityEditableProperties[];

View File

@ -1,28 +0,0 @@
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceMigrationFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input';
export const objectMetadataEntityEditableProperties = [
'description',
'icon',
'isActive',
'isLabelSyncedWithName',
'labelPlural',
'labelSingular',
'namePlural',
'nameSingular',
'standardOverrides', // Only if standard
] as const satisfies (keyof ObjectMetadataEntity)[];
export type ObjectMetadataEntityEditableProperties =
(typeof objectMetadataEntityEditableProperties)[number];
export type WorkspaceMigrationObjectInput = Partial<
Omit<ObjectMetadataEntity, 'fields'>
> & {
uniqueIdentifier: string;
fieldInputs: WorkspaceMigrationFieldInput[];
};
export type WorkspaceMigrationObjectWithoutFields = Omit<
WorkspaceMigrationObjectInput,
'fieldInputs'
>;

View File

@ -1,11 +0,0 @@
export type AddUniquenessConstraintAction = {
type: 'add_uniqueness_constraint';
};
export type RemoveUniquenessConstraintAction = {
type: 'remove_uniqueness_constraint';
};
export type WorkspaceMigrationUniquenessActionV2 =
| RemoveUniquenessConstraintAction
| AddUniquenessConstraintAction;

View File

@ -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 [];
}
}
});
};

View File

@ -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 [];
}
},
);
};

View File

@ -2,21 +2,30 @@ import omit from 'lodash.omit';
import diff from 'microdiff'; import diff from 'microdiff';
import { assertUnreachable } from 'twenty-shared/utils'; 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 { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
import { UpdateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-action-v2'; import { UpdateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
import {
ObjectMetadataEntityEditableProperties,
WorkspaceMigrationObjectInput,
objectMetadataEntityEditableProperties,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util'; import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
type ObjectWorkspaceMigrationUpdate = FromTo<WorkspaceMigrationObjectInput>; const flatObjectMetadataPropertiesToCompare = [
'description',
'icon',
'isActive',
'isLabelSyncedWithName',
'labelPlural',
'labelSingular',
'namePlural',
'nameSingular',
'standardOverrides', // Only if standard
] as const satisfies (keyof FlatObjectMetadata)[];
export const compareTwoWorkspaceMigrationObjectInput = ({ export type FlatObjectMetadataPropertiesToCompare =
(typeof flatObjectMetadataPropertiesToCompare)[number];
export const compareTwoFlatObjectMetadata = ({
from, from,
to, to,
}: ObjectWorkspaceMigrationUpdate) => { }: FromTo<FlatObjectMetadata>) => {
const fromCompare = transformMetadataForComparison(from, {}); const fromCompare = transformMetadataForComparison(from, {});
const toCompare = transformMetadataForComparison(to, {}); const toCompare = transformMetadataForComparison(to, {});
const objectMetadataDifference = diff(fromCompare, omit(toCompare, 'fields')); const objectMetadataDifference = diff(fromCompare, omit(toCompare, 'fields'));
@ -41,15 +50,15 @@ export const compareTwoWorkspaceMigrationObjectInput = ({
// Could be handled directly from the diff we do above // Could be handled directly from the diff we do above
if ( if (
!objectMetadataEntityEditableProperties.includes( !flatObjectMetadataPropertiesToCompare.includes(
property as ObjectMetadataEntityEditableProperties, property as FlatObjectMetadataPropertiesToCompare,
) )
) { ) {
return []; return [];
} }
return { return {
property: property as ObjectMetadataEntityEditableProperties, property: property as FlatObjectMetadataPropertiesToCompare,
from: difference.oldValue, from: difference.oldValue,
to: difference.value, to: difference.value,
}; };

View File

@ -1,19 +1,20 @@
import { FieldMetadataType } from 'twenty-shared/types'; import { FieldMetadataType } from 'twenty-shared/types';
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input'; import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
describe('WorkspaceMigrationBuilderV2Service', () => { describe('WorkspaceMigrationBuilderV2Service', () => {
let service: WorkspaceMigrationBuilderV2Service; let service: WorkspaceMigrationBuilderV2Service;
const baseObject: WorkspaceMigrationObjectInput = { const baseObject: FlatObjectMetadata = {
uniqueIdentifier: '20202020-e89b-12d3-a456-426614175000', uniqueIdentifier: '20202020-e89b-12d3-a456-426614175000',
nameSingular: 'Contact', nameSingular: 'Contact',
namePlural: 'Contacts', namePlural: 'Contacts',
labelSingular: 'Contact', labelSingular: 'Contact',
flatIndexMetadatas: [],
labelPlural: 'Contacts', labelPlural: 'Contacts',
description: 'A contact', description: 'A contact',
fieldInputs: [ flatFieldMetadatas: [
{ {
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174000', uniqueIdentifier: '20202020-e89b-12d3-a456-426614174000',
name: 'firstName', name: 'firstName',
@ -30,8 +31,8 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
}); });
it('should return a migration when nameSingular changes', () => { it('should return a migration when nameSingular changes', () => {
const from: WorkspaceMigrationObjectInput = baseObject; const from: FlatObjectMetadata = baseObject;
const to: WorkspaceMigrationObjectInput = { const to: FlatObjectMetadata = {
...from, ...from,
nameSingular: 'Person', nameSingular: 'Person',
}; };
@ -41,9 +42,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
{ {
"actions": [ "actions": [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A contact", "description": "A contact",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -53,6 +54,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000", "uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Contacts", "labelPlural": "Contacts",
"labelSingular": "Contact", "labelSingular": "Contact",
"namePlural": "Contacts", "namePlural": "Contacts",
@ -74,14 +76,15 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
}); });
it('should return a migration when creating a new object', () => { it('should return a migration when creating a new object', () => {
const newObject: WorkspaceMigrationObjectInput = { const newObject: FlatObjectMetadata = {
uniqueIdentifier: '20202020-e89b-12d3-a456-426614175001', uniqueIdentifier: '20202020-e89b-12d3-a456-426614175001',
nameSingular: 'Company', nameSingular: 'Company',
namePlural: 'Companies', namePlural: 'Companies',
flatIndexMetadatas: [],
labelSingular: 'Company', labelSingular: 'Company',
labelPlural: 'Companies', labelPlural: 'Companies',
description: 'A company', description: 'A company',
fieldInputs: [ flatFieldMetadatas: [
{ {
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174001', uniqueIdentifier: '20202020-e89b-12d3-a456-426614174001',
name: 'name', name: 'name',
@ -99,9 +102,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
{ {
"actions": [ "actions": [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A company", "description": "A company",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -111,6 +114,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001", "uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Companies", "labelPlural": "Companies",
"labelSingular": "Company", "labelSingular": "Company",
"namePlural": "Companies", "namePlural": "Companies",
@ -120,7 +124,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "create_object", "type": "create_object",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
"label": "Name", "label": "Name",
@ -128,9 +132,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "ADDRESS", "type": "ADDRESS",
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001", "uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A company", "description": "A company",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -140,6 +144,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001", "uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Companies", "labelPlural": "Companies",
"labelSingular": "Company", "labelSingular": "Company",
"namePlural": "Companies", "namePlural": "Companies",
@ -160,9 +165,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
{ {
"actions": [ "actions": [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A contact", "description": "A contact",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -172,6 +177,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000", "uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Contacts", "labelPlural": "Contacts",
"labelSingular": "Contact", "labelSingular": "Contact",
"namePlural": "Contacts", "namePlural": "Contacts",
@ -186,11 +192,11 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
}); });
it('should handle multiple operations in a single migration', () => { it('should handle multiple operations in a single migration', () => {
const objectToUpdate: WorkspaceMigrationObjectInput = { const objectToUpdate: FlatObjectMetadata = {
...baseObject, ...baseObject,
nameSingular: 'Person', nameSingular: 'Person',
fieldInputs: [ flatFieldMetadatas: [
...baseObject.fieldInputs, ...baseObject.flatFieldMetadatas,
{ {
defaultValue: '', defaultValue: '',
label: 'New field', label: 'New field',
@ -205,14 +211,15 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
...baseObject, ...baseObject,
uniqueIdentifier: '20202020-59ef-4a14-a509-0a02acb248d5', uniqueIdentifier: '20202020-59ef-4a14-a509-0a02acb248d5',
}; };
const objectToCreate: WorkspaceMigrationObjectInput = { const objectToCreate: FlatObjectMetadata = {
uniqueIdentifier: '20202020-1218-4fc0-b32d-fc4f005c4bab', uniqueIdentifier: '20202020-1218-4fc0-b32d-fc4f005c4bab',
nameSingular: 'Company', nameSingular: 'Company',
namePlural: 'Companies', namePlural: 'Companies',
flatIndexMetadatas: [],
labelSingular: 'Company', labelSingular: 'Company',
labelPlural: 'Companies', labelPlural: 'Companies',
description: 'A company', description: 'A company',
fieldInputs: [ flatFieldMetadatas: [
{ {
uniqueIdentifier: '20202020-1016-4f09-bad6-e75681f385f4', uniqueIdentifier: '20202020-1016-4f09-bad6-e75681f385f4',
name: 'name', name: 'name',
@ -233,9 +240,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
{ {
"actions": [ "actions": [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A company", "description": "A company",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -245,6 +252,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4", "uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Companies", "labelPlural": "Companies",
"labelSingular": "Company", "labelSingular": "Company",
"namePlural": "Companies", "namePlural": "Companies",
@ -254,9 +262,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "create_object", "type": "create_object",
}, },
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A contact", "description": "A contact",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -266,6 +274,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000", "uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Contacts", "labelPlural": "Contacts",
"labelSingular": "Contact", "labelSingular": "Contact",
"namePlural": "Contacts", "namePlural": "Contacts",
@ -275,9 +284,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "delete_object", "type": "delete_object",
}, },
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A contact", "description": "A contact",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -295,6 +304,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3", "uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Contacts", "labelPlural": "Contacts",
"labelSingular": "Contact", "labelSingular": "Contact",
"namePlural": "Contacts", "namePlural": "Contacts",
@ -311,7 +321,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
], ],
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
"label": "Name", "label": "Name",
@ -319,9 +329,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "ADDRESS", "type": "ADDRESS",
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4", "uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A company", "description": "A company",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -331,6 +341,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4", "uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Companies", "labelPlural": "Companies",
"labelSingular": "Company", "labelSingular": "Company",
"namePlural": "Companies", "namePlural": "Companies",
@ -340,7 +351,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "create_field", "type": "create_field",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"defaultValue": "", "defaultValue": "",
"description": "new field description", "description": "new field description",
"label": "New field", "label": "New field",
@ -348,9 +359,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "NUMBER", "type": "NUMBER",
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3", "uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"description": "A contact", "description": "A contact",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -368,6 +379,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3", "uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Contacts", "labelPlural": "Contacts",
"labelSingular": "Contact", "labelSingular": "Contact",
"namePlural": "Contacts", "namePlural": "Contacts",
@ -382,14 +394,15 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
}); });
it('should treat objects with the same name but different IDs as distinct', () => { it('should treat objects with the same name but different IDs as distinct', () => {
const objectA: WorkspaceMigrationObjectInput = { const objectA: FlatObjectMetadata = {
uniqueIdentifier: 'id-1', uniqueIdentifier: 'id-1',
flatIndexMetadatas: [],
nameSingular: 'Duplicate', nameSingular: 'Duplicate',
namePlural: 'Duplicates', namePlural: 'Duplicates',
labelSingular: 'Duplicate', labelSingular: 'Duplicate',
labelPlural: 'Duplicates', labelPlural: 'Duplicates',
description: 'First object', description: 'First object',
fieldInputs: [ flatFieldMetadatas: [
{ {
uniqueIdentifier: 'field-1', uniqueIdentifier: 'field-1',
name: 'fieldA', name: 'fieldA',
@ -400,14 +413,15 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
}, },
], ],
}; };
const objectB: WorkspaceMigrationObjectInput = { const objectB: FlatObjectMetadata = {
uniqueIdentifier: 'id-2', uniqueIdentifier: 'id-2',
nameSingular: 'Duplicate', nameSingular: 'Duplicate',
namePlural: 'Duplicates', namePlural: 'Duplicates',
labelSingular: 'Duplicate', labelSingular: 'Duplicate',
labelPlural: 'Duplicates', labelPlural: 'Duplicates',
flatIndexMetadatas: [],
description: 'Second object', description: 'Second object',
fieldInputs: [ flatFieldMetadatas: [
{ {
uniqueIdentifier: 'field-2', uniqueIdentifier: 'field-2',
name: 'fieldB', name: 'fieldB',
@ -424,9 +438,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
expect(result.actions).toMatchInlineSnapshot(` expect(result.actions).toMatchInlineSnapshot(`
[ [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "First object", "description": "First object",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -436,6 +450,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "field-1", "uniqueIdentifier": "field-1",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Duplicates", "labelPlural": "Duplicates",
"labelSingular": "Duplicate", "labelSingular": "Duplicate",
"namePlural": "Duplicates", "namePlural": "Duplicates",
@ -445,9 +460,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "create_object", "type": "create_object",
}, },
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "Second object", "description": "Second object",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -457,6 +472,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "field-2", "uniqueIdentifier": "field-2",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Duplicates", "labelPlural": "Duplicates",
"labelSingular": "Duplicate", "labelSingular": "Duplicate",
"namePlural": "Duplicates", "namePlural": "Duplicates",
@ -466,7 +482,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "create_object", "type": "create_object",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
"label": "Field A", "label": "Field A",
@ -474,9 +490,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "FULL_NAME", "type": "FULL_NAME",
"uniqueIdentifier": "field-1", "uniqueIdentifier": "field-1",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"description": "First object", "description": "First object",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -486,6 +502,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "field-1", "uniqueIdentifier": "field-1",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Duplicates", "labelPlural": "Duplicates",
"labelSingular": "Duplicate", "labelSingular": "Duplicate",
"namePlural": "Duplicates", "namePlural": "Duplicates",
@ -495,7 +512,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "create_field", "type": "create_field",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
"label": "Field B", "label": "Field B",
@ -503,9 +520,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "ADDRESS", "type": "ADDRESS",
"uniqueIdentifier": "field-2", "uniqueIdentifier": "field-2",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"description": "Second object", "description": "Second object",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -515,6 +532,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "field-2", "uniqueIdentifier": "field-2",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Duplicates", "labelPlural": "Duplicates",
"labelSingular": "Duplicate", "labelSingular": "Duplicate",
"namePlural": "Duplicates", "namePlural": "Duplicates",
@ -531,9 +549,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
expect(deleteResult.actions).toMatchInlineSnapshot(` expect(deleteResult.actions).toMatchInlineSnapshot(`
[ [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "First object", "description": "First object",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -543,6 +561,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "field-1", "uniqueIdentifier": "field-1",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Duplicates", "labelPlural": "Duplicates",
"labelSingular": "Duplicate", "labelSingular": "Duplicate",
"namePlural": "Duplicates", "namePlural": "Duplicates",
@ -552,9 +571,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"type": "delete_object", "type": "delete_object",
}, },
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"description": "Second object", "description": "Second object",
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"defaultValue": "", "defaultValue": "",
"description": "", "description": "",
@ -564,6 +583,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
"uniqueIdentifier": "field-2", "uniqueIdentifier": "field-2",
}, },
], ],
"flatIndexMetadatas": [],
"labelPlural": "Duplicates", "labelPlural": "Duplicates",
"labelSingular": "Duplicate", "labelSingular": "Duplicate",
"namePlural": "Duplicates", "namePlural": "Duplicates",
@ -577,7 +597,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
}); });
it('should emit no actions when from and to are deeply equal', () => { it('should emit no actions when from and to are deeply equal', () => {
const obj: WorkspaceMigrationObjectInput = { ...baseObject }; const obj: FlatObjectMetadata = { ...baseObject };
const result = service.build({ from: [obj], to: [obj] }); const result = service.build({ from: [obj], to: [obj] });
expect(result.actions).toEqual([]); expect(result.actions).toEqual([]);

View File

@ -0,0 +1,522 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
describe('Workspace migration builder indexes tests suite', () => {
let service: WorkspaceMigrationBuilderV2Service;
beforeEach(() => {
service = new WorkspaceMigrationBuilderV2Service();
});
const createMockObject = (
identifier: string,
fields: Partial<FlatFieldMetadata>[] = [],
indexes: FlatIndexMetadata[] = [],
): FlatObjectMetadata => ({
uniqueIdentifier: identifier,
flatIndexMetadatas: indexes,
flatFieldMetadatas: fields.map((field) => ({
type: FieldMetadataType.TEXT,
name: 'defaultName',
label: 'Default Label',
isCustom: true,
isActive: true,
isNullable: true,
uniqueIdentifier: 'default-id',
...field,
})),
});
const createMockIndex = (
name: string,
fields: string[],
isUnique = false,
): FlatIndexMetadata => ({
name,
isUnique,
indexType: IndexType.BTREE,
indexWhereClause: null,
uniqueIdentifier: name,
flatIndexFieldMetadatas: fields.map((field, index) => ({
uniqueIdentifier: `${name}-field-${index}`,
order: index,
})),
});
describe('buildWorkspaceMigrationV2IndexActions', () => {
it('should create index actions for created indexes', () => {
const fromObjects: FlatObjectMetadata[] = [];
const toObjects: FlatObjectMetadata[] = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name', ['name'], true)],
),
];
const result = service.build({ from: fromObjects, to: toObjects });
expect(result).toMatchInlineSnapshot(`
{
"actions": [
{
"flatObjectMetadata": {
"flatFieldMetadatas": [
{
"isActive": true,
"isCustom": true,
"isNullable": true,
"label": "Name",
"name": "name",
"type": "TEXT",
"uniqueIdentifier": "name",
},
],
"flatIndexMetadatas": [
{
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name",
"uniqueIdentifier": "idx_company_name",
},
],
"uniqueIdentifier": "company",
},
"type": "create_object",
},
{
"flatFieldMetadata": {
"isActive": true,
"isCustom": true,
"isNullable": true,
"label": "Name",
"name": "name",
"type": "TEXT",
"uniqueIdentifier": "name",
},
"flatObjectMetadata": {
"flatFieldMetadatas": [
{
"isActive": true,
"isCustom": true,
"isNullable": true,
"label": "Name",
"name": "name",
"type": "TEXT",
"uniqueIdentifier": "name",
},
],
"flatIndexMetadatas": [
{
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name",
"uniqueIdentifier": "idx_company_name",
},
],
"uniqueIdentifier": "company",
},
"type": "create_field",
},
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name",
"uniqueIdentifier": "idx_company_name",
},
"type": "create_index",
},
],
}
`);
});
it('should create delete actions for deleted indexes', () => {
const fromObjects: FlatObjectMetadata[] = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name', ['name'], true)],
),
];
const toObjects: FlatObjectMetadata[] = [
createMockObject('company', [
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
]),
];
const result = service.build({ from: fromObjects, to: toObjects });
expect(result).toMatchInlineSnapshot(`
{
"actions": [
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name",
"uniqueIdentifier": "idx_company_name",
},
"type": "delete_index",
},
],
}
`);
});
it('should handle multiple index changes across different objects', () => {
const fromObjects: FlatObjectMetadata[] = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name_old', ['name'], true)],
),
];
const toObjects: FlatObjectMetadata[] = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name_new', ['name'], true)],
),
createMockObject(
'person',
[
{
type: FieldMetadataType.TEXT,
name: 'email',
label: 'Email',
uniqueIdentifier: 'email',
},
],
[createMockIndex('idx_person_email', ['email'], true)],
),
];
const result = service.build({ from: fromObjects, to: toObjects });
expect(result).toMatchInlineSnapshot(`
{
"actions": [
{
"flatObjectMetadata": {
"flatFieldMetadatas": [
{
"isActive": true,
"isCustom": true,
"isNullable": true,
"label": "Email",
"name": "email",
"type": "TEXT",
"uniqueIdentifier": "email",
},
],
"flatIndexMetadatas": [
{
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_person_email-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_person_email",
"uniqueIdentifier": "idx_person_email",
},
],
"uniqueIdentifier": "person",
},
"type": "create_object",
},
{
"flatFieldMetadata": {
"isActive": true,
"isCustom": true,
"isNullable": true,
"label": "Email",
"name": "email",
"type": "TEXT",
"uniqueIdentifier": "email",
},
"flatObjectMetadata": {
"flatFieldMetadatas": [
{
"isActive": true,
"isCustom": true,
"isNullable": true,
"label": "Email",
"name": "email",
"type": "TEXT",
"uniqueIdentifier": "email",
},
],
"flatIndexMetadatas": [
{
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_person_email-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_person_email",
"uniqueIdentifier": "idx_person_email",
},
],
"uniqueIdentifier": "person",
},
"type": "create_field",
},
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_person_email-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_person_email",
"uniqueIdentifier": "idx_person_email",
},
"type": "create_index",
},
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name_new-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name_new",
"uniqueIdentifier": "idx_company_name_new",
},
"type": "create_index",
},
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name_old-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name_old",
"uniqueIdentifier": "idx_company_name_old",
},
"type": "delete_index",
},
],
}
`);
});
it('should handle empty objects', () => {
const result = service.build({ from: [], to: [] });
expect(result).toMatchInlineSnapshot(`
{
"actions": [],
}
`);
});
it('should handle objects with no index changes', () => {
const objects = [
createMockObject('company', [
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
]),
];
const result = service.build({ from: objects, to: objects });
expect(result).toMatchInlineSnapshot(`
{
"actions": [],
}
`);
});
it('should handle index updates', () => {
const fromObjects: FlatObjectMetadata[] = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name', ['name'], true)],
),
];
const toObjects: FlatObjectMetadata[] = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name', ['name'], false)],
),
];
const result = service.build({ from: fromObjects, to: toObjects });
expect(result).toMatchInlineSnapshot(`
{
"actions": [
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": true,
"name": "idx_company_name",
"uniqueIdentifier": "idx_company_name",
},
"type": "delete_index",
},
{
"flatIndexMetadata": {
"flatIndexFieldMetadatas": [
{
"order": 0,
"uniqueIdentifier": "idx_company_name-field-0",
},
],
"indexType": "BTREE",
"indexWhereClause": null,
"isUnique": false,
"name": "idx_company_name",
"uniqueIdentifier": "idx_company_name",
},
"type": "create_index",
},
],
}
`);
});
it('should not generate any actions when indexes are identical', () => {
const objects = [
createMockObject(
'company',
[
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
uniqueIdentifier: 'name',
},
],
[createMockIndex('idx_company_name', ['name'], true)],
),
];
const result = service.build({ from: objects, to: objects });
expect(result).toMatchInlineSnapshot(`
{
"actions": [],
}
`);
});
});
});

View File

@ -4,8 +4,8 @@ import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadat
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface'; import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { WorkspaceMigrationFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input'; import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input'; import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
describe('Workspace migration builder relations tests suite', () => { describe('Workspace migration builder relations tests suite', () => {
@ -17,10 +17,11 @@ describe('Workspace migration builder relations tests suite', () => {
const createMockObject = ( const createMockObject = (
identifier: string, identifier: string,
fields: Partial<WorkspaceMigrationFieldInput>[] = [], fields: Partial<FlatFieldMetadata>[] = [],
): WorkspaceMigrationObjectInput => ({ ): FlatObjectMetadata => ({
uniqueIdentifier: identifier, uniqueIdentifier: identifier,
fieldInputs: fields.map((field) => ({ flatIndexMetadatas: [],
flatFieldMetadatas: fields.map((field) => ({
type: FieldMetadataType.TEXT, type: FieldMetadataType.TEXT,
name: 'defaultName', name: 'defaultName',
label: 'Default Label', label: 'Default Label',
@ -34,8 +35,8 @@ describe('Workspace migration builder relations tests suite', () => {
describe('buildWorkspaceMigrationV2RelationActions', () => { describe('buildWorkspaceMigrationV2RelationActions', () => {
it('should create relation actions for created fields', () => { it('should create relation actions for created fields', () => {
const fromObjects: WorkspaceMigrationObjectInput[] = []; const fromObjects: FlatObjectMetadata[] = [];
const toObjects: WorkspaceMigrationObjectInput[] = [ const toObjects: FlatObjectMetadata[] = [
createMockObject('company', [ createMockObject('company', [
{ {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
@ -54,8 +55,8 @@ describe('Workspace migration builder relations tests suite', () => {
{ {
"actions": [ "actions": [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -68,12 +69,13 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "employees", "uniqueIdentifier": "employees",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "company", "uniqueIdentifier": "company",
}, },
"type": "create_object", "type": "create_object",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
"isNullable": true, "isNullable": true,
@ -84,8 +86,8 @@ describe('Workspace migration builder relations tests suite', () => {
"type": "RELATION", "type": "RELATION",
"uniqueIdentifier": "employees", "uniqueIdentifier": "employees",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -98,6 +100,7 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "employees", "uniqueIdentifier": "employees",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "company", "uniqueIdentifier": "company",
}, },
"type": "create_field", "type": "create_field",
@ -108,7 +111,7 @@ describe('Workspace migration builder relations tests suite', () => {
}); });
it('should create delete actions for deleted fields', () => { it('should create delete actions for deleted fields', () => {
const fromObjects: WorkspaceMigrationObjectInput[] = [ const fromObjects: FlatObjectMetadata[] = [
createMockObject('company', [ createMockObject('company', [
{ {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
@ -120,9 +123,7 @@ describe('Workspace migration builder relations tests suite', () => {
}, },
]), ]),
]; ];
const toObjects: WorkspaceMigrationObjectInput[] = [ const toObjects: FlatObjectMetadata[] = [createMockObject('company')];
createMockObject('company'),
];
const result = service.build({ from: fromObjects, to: toObjects }); const result = service.build({ from: fromObjects, to: toObjects });
@ -130,7 +131,7 @@ describe('Workspace migration builder relations tests suite', () => {
{ {
"actions": [ "actions": [
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
"isNullable": true, "isNullable": true,
@ -141,8 +142,9 @@ describe('Workspace migration builder relations tests suite', () => {
"type": "RELATION", "type": "RELATION",
"uniqueIdentifier": "employees", "uniqueIdentifier": "employees",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [], "flatFieldMetadatas": [],
"flatIndexMetadatas": [],
"uniqueIdentifier": "company", "uniqueIdentifier": "company",
}, },
"type": "delete_field", "type": "delete_field",
@ -153,7 +155,7 @@ describe('Workspace migration builder relations tests suite', () => {
}); });
it('should handle multiple relation changes across different objects', () => { it('should handle multiple relation changes across different objects', () => {
const fromObjects: WorkspaceMigrationObjectInput[] = [ const fromObjects: FlatObjectMetadata[] = [
createMockObject('company', [ createMockObject('company', [
{ {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
@ -165,7 +167,7 @@ describe('Workspace migration builder relations tests suite', () => {
}, },
]), ]),
]; ];
const toObjects: WorkspaceMigrationObjectInput[] = [ const toObjects: FlatObjectMetadata[] = [
createMockObject('company', [ createMockObject('company', [
{ {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
@ -194,8 +196,8 @@ describe('Workspace migration builder relations tests suite', () => {
{ {
"actions": [ "actions": [
{ {
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -208,12 +210,13 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "manager", "uniqueIdentifier": "manager",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "person", "uniqueIdentifier": "person",
}, },
"type": "create_object", "type": "create_object",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
"isNullable": true, "isNullable": true,
@ -224,8 +227,8 @@ describe('Workspace migration builder relations tests suite', () => {
"type": "RELATION", "type": "RELATION",
"uniqueIdentifier": "manager", "uniqueIdentifier": "manager",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -238,12 +241,13 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "manager", "uniqueIdentifier": "manager",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "person", "uniqueIdentifier": "person",
}, },
"type": "create_field", "type": "create_field",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
"isNullable": true, "isNullable": true,
@ -254,8 +258,8 @@ describe('Workspace migration builder relations tests suite', () => {
"type": "RELATION", "type": "RELATION",
"uniqueIdentifier": "new-relation", "uniqueIdentifier": "new-relation",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -268,12 +272,13 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "new-relation", "uniqueIdentifier": "new-relation",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "company", "uniqueIdentifier": "company",
}, },
"type": "create_field", "type": "create_field",
}, },
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
"isNullable": true, "isNullable": true,
@ -284,8 +289,8 @@ describe('Workspace migration builder relations tests suite', () => {
"type": "RELATION", "type": "RELATION",
"uniqueIdentifier": "old-relation", "uniqueIdentifier": "old-relation",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -298,6 +303,7 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "new-relation", "uniqueIdentifier": "new-relation",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "company", "uniqueIdentifier": "company",
}, },
"type": "delete_field", "type": "delete_field",
@ -350,7 +356,7 @@ describe('Workspace migration builder relations tests suite', () => {
description: 'Company employees', description: 'Company employees',
}; };
const fromObjects: WorkspaceMigrationObjectInput[] = [ const fromObjects: FlatObjectMetadata[] = [
createMockObject('company', [ createMockObject('company', [
{ {
...baseField, ...baseField,
@ -364,10 +370,10 @@ describe('Workspace migration builder relations tests suite', () => {
]), ]),
]; ];
const toObjects: WorkspaceMigrationObjectInput[] = [ const toObjects: FlatObjectMetadata[] = [
{ {
...fromObjects[0], ...fromObjects[0],
fieldInputs: [ flatFieldMetadatas: [
{ {
...baseField, ...baseField,
name: 'updatedName', name: 'updatedName',
@ -382,7 +388,7 @@ describe('Workspace migration builder relations tests suite', () => {
{ {
"actions": [ "actions": [
{ {
"fieldMetadataInput": { "flatFieldMetadata": {
"description": "Company employees", "description": "Company employees",
"isActive": true, "isActive": true,
"isCustom": true, "isCustom": true,
@ -392,8 +398,8 @@ describe('Workspace migration builder relations tests suite', () => {
"type": "RELATION", "type": "RELATION",
"uniqueIdentifier": "employees", "uniqueIdentifier": "employees",
}, },
"objectMetadataInput": { "flatObjectMetadata": {
"fieldInputs": [ "flatFieldMetadatas": [
{ {
"description": "Company employees", "description": "Company employees",
"isActive": true, "isActive": true,
@ -405,6 +411,7 @@ describe('Workspace migration builder relations tests suite', () => {
"uniqueIdentifier": "employees", "uniqueIdentifier": "employees",
}, },
], ],
"flatIndexMetadatas": [],
"uniqueIdentifier": "company", "uniqueIdentifier": "company",
}, },
"type": "update_field", "type": "update_field",

View File

@ -0,0 +1,10 @@
import { WorkspaceMigrationFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
import { WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
import { WorkspaceMigrationObjectActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
export type WorkspaceMigrationActionV2 =
| WorkspaceMigrationObjectActionV2
| WorkspaceMigrationFieldActionV2
| WorkspaceMigrationIndexActionV2;
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];

View File

@ -1,14 +1,12 @@
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
import { FlatObjectMetadataWithoutFields } 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 { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
import { import { FlatFieldMetadataPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-field-metadata-comparator.util';
FieldMetadataEntityEditableProperties,
WorkspaceMigrationFieldInput,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input';
import { WorkspaceMigrationObjectWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
export type FieldAndObjectMetadataWorkspaceMigrationInput = { export type FieldAndObjectMetadataWorkspaceMigrationInput = {
fieldMetadataInput: WorkspaceMigrationFieldInput; flatFieldMetadata: FlatFieldMetadata;
objectMetadataInput: WorkspaceMigrationObjectWithoutFields; flatObjectMetadata: FlatObjectMetadataWithoutFields;
}; };
export type CreateFieldAction = { export type CreateFieldAction = {
type: 'create_field'; type: 'create_field';
@ -18,10 +16,10 @@ export type UpdateFieldAction = {
type: 'update_field'; type: 'update_field';
updates: Partial< updates: Partial<
{ {
[P in FieldMetadataEntityEditableProperties]: { [P in FlatFieldMetadataPropertiesToCompare]: {
property: P; property: P;
} & FromTo<FieldMetadataEntity[P]>; } & FromTo<FieldMetadataEntity[P]>;
}[FieldMetadataEntityEditableProperties] }[FlatFieldMetadataPropertiesToCompare]
>[]; >[];
} & FieldAndObjectMetadataWorkspaceMigrationInput; } & FieldAndObjectMetadataWorkspaceMigrationInput;

View File

@ -1,9 +1,13 @@
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
export type CreateIndexAction = { export type CreateIndexAction = {
type: 'create_index'; type: 'create_index';
flatIndexMetadata: FlatIndexMetadata;
}; };
export type DeleteIndexAction = { export type DeleteIndexAction = {
type: 'delete_index'; type: 'delete_index';
flatIndexMetadata: FlatIndexMetadata;
}; };
export type WorkspaceMigrationIndexActionV2 = export type WorkspaceMigrationIndexActionV2 =

View File

@ -1,12 +1,10 @@
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { FlatObjectMetadataWithoutFields } 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 { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
import { import { FlatObjectMetadataPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-object-metadata-comparator.util';
ObjectMetadataEntityEditableProperties,
WorkspaceMigrationObjectWithoutFields,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
type ObjectActionCommon = { type ObjectActionCommon = {
objectMetadataInput: WorkspaceMigrationObjectWithoutFields; flatObjectMetadata: FlatObjectMetadataWithoutFields;
}; };
export type CreateObjectAction = { export type CreateObjectAction = {
type: 'create_object'; type: 'create_object';
@ -16,10 +14,10 @@ export type UpdateObjectAction = {
type: 'update_object'; type: 'update_object';
updates: Partial< updates: Partial<
{ {
[P in ObjectMetadataEntityEditableProperties]: { [P in FlatObjectMetadataPropertiesToCompare]: {
property: P; property: P;
} & FromTo<ObjectMetadataEntity[P]>; } & FromTo<ObjectMetadataEntity[P]>;
}[ObjectMetadataEntityEditableProperties] }[FlatObjectMetadataPropertiesToCompare]
>[]; >[];
} & ObjectActionCommon; } & ObjectActionCommon;
@ -27,7 +25,7 @@ export type DeleteObjectAction = {
type: 'delete_object'; type: 'delete_object';
} & ObjectActionCommon; } & ObjectActionCommon;
export type WorkspaceMigrationV2ObjectAction = export type WorkspaceMigrationObjectActionV2 =
| CreateObjectAction | CreateObjectAction
| UpdateObjectAction | UpdateObjectAction
| DeleteObjectAction; | DeleteObjectAction;

View File

@ -1,4 +1,4 @@
import { WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-common-v2'; import { WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
export type WorkspaceMigrationV2< export type WorkspaceMigrationV2<
TActions extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2, TActions extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2,

View File

@ -1,35 +1,32 @@
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type'; import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
import { WorkspaceMigrationFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input';
import { import {
WorkspaceMigrationObjectInput, FlatObjectMetadata,
WorkspaceMigrationObjectWithoutFields, FlatObjectMetadataWithoutFields,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input'; } 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 { import {
CustomDeletedCreatedUpdatedMatrix, CustomDeletedCreatedUpdatedMatrix,
deletedCreatedUpdatedMatrixDispatcher, deletedCreatedUpdatedMatrixDispatcher,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/deleted-created-updated-matrix-dispatcher.util'; } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
export type UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = { export type UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = {
objectMetadataInput: WorkspaceMigrationObjectWithoutFields; flatObjectMetadata: FlatObjectMetadataWithoutFields;
} & CustomDeletedCreatedUpdatedMatrix< } & CustomDeletedCreatedUpdatedMatrix<'fieldMetadata', FlatFieldMetadata>;
'fieldMetadata',
WorkspaceMigrationFieldInput
>;
export const computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = ( export const computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = (
updatedObjectMetadata: FromTo<WorkspaceMigrationObjectInput>[], updatedObjectMetadata: FromTo<FlatObjectMetadata>[],
): UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] => { ): UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] => {
const matrixAccumulator: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] = const matrixAccumulator: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] =
[]; [];
for (const { from, to } of updatedObjectMetadata) { for (const { from, to } of updatedObjectMetadata) {
const fieldMetadataMatrix = deletedCreatedUpdatedMatrixDispatcher({ const fieldMetadataMatrix = deletedCreatedUpdatedMatrixDispatcher({
from: from.fieldInputs, from: from.flatFieldMetadatas,
to: to.fieldInputs, to: to.flatFieldMetadatas,
}); });
matrixAccumulator.push({ matrixAccumulator.push({
objectMetadataInput: to, flatObjectMetadata: to,
createdFieldMetadata: fieldMetadataMatrix.created, createdFieldMetadata: fieldMetadataMatrix.created,
deletedFieldMetadata: fieldMetadataMatrix.deleted, deletedFieldMetadata: fieldMetadataMatrix.deleted,
updatedFieldMetadata: fieldMetadataMatrix.updated, updatedFieldMetadata: fieldMetadataMatrix.updated,

View File

@ -0,0 +1,37 @@
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
import {
FlatObjectMetadata,
FlatObjectMetadataWithoutFields,
} 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 {
CustomDeletedCreatedUpdatedMatrix,
deletedCreatedUpdatedMatrixDispatcher,
} from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
export type UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix = {
flatObjectMetadata: FlatObjectMetadataWithoutFields;
} & CustomDeletedCreatedUpdatedMatrix<'indexMetadata', FlatIndexMetadata>;
export const computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix = (
updatedObjectMetadata: FromTo<FlatObjectMetadata>[],
): UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix[] => {
const matrixAccumulator: UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix[] =
[];
for (const { from, to } of updatedObjectMetadata) {
const indexMetadataMatrix = deletedCreatedUpdatedMatrixDispatcher({
from: from.flatIndexMetadatas,
to: to.flatIndexMetadatas,
});
matrixAccumulator.push({
flatObjectMetadata: to,
createdIndexMetadata: indexMetadataMatrix.created,
deletedIndexMetadata: indexMetadataMatrix.deleted,
updatedIndexMetadata: indexMetadataMatrix.updated,
});
}
return matrixAccumulator;
};

View File

@ -2,22 +2,22 @@ import {
CreateFieldAction, CreateFieldAction,
DeleteFieldAction, DeleteFieldAction,
FieldAndObjectMetadataWorkspaceMigrationInput, FieldAndObjectMetadataWorkspaceMigrationInput,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-action-v2'; } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
export const getWorkspaceMigrationV2FieldCreateAction = ({ export const getWorkspaceMigrationV2FieldCreateAction = ({
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}: FieldAndObjectMetadataWorkspaceMigrationInput): CreateFieldAction => ({ }: FieldAndObjectMetadataWorkspaceMigrationInput): CreateFieldAction => ({
type: 'create_field', type: 'create_field',
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}); });
export const getWorkspaceMigrationV2FieldDeleteAction = ({ export const getWorkspaceMigrationV2FieldDeleteAction = ({
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}: FieldAndObjectMetadataWorkspaceMigrationInput): DeleteFieldAction => ({ }: FieldAndObjectMetadataWorkspaceMigrationInput): DeleteFieldAction => ({
type: 'delete_field', type: 'delete_field',
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}); });

View File

@ -0,0 +1,19 @@
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
import {
CreateIndexAction,
DeleteIndexAction,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
export const getWorkspaceMigrationV2CreateIndexAction = (
flatIndexMetadata: FlatIndexMetadata,
): CreateIndexAction => ({
type: 'create_index',
flatIndexMetadata,
});
export const getWorkspaceMigrationV2DeleteIndexAction = (
flatIndexMetadata: FlatIndexMetadata,
): DeleteIndexAction => ({
type: 'delete_index',
flatIndexMetadata,
});

View File

@ -1,19 +1,19 @@
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
import { import {
CreateObjectAction, CreateObjectAction,
DeleteObjectAction, DeleteObjectAction,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-action-v2'; } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
export const getWorkspaceMigrationV2ObjectCreateAction = ( export const getWorkspaceMigrationV2ObjectCreateAction = (
objectMetadataInput: WorkspaceMigrationObjectInput, flatObjectMetadata: FlatObjectMetadata,
): CreateObjectAction => ({ ): CreateObjectAction => ({
type: 'create_object', type: 'create_object',
objectMetadataInput, flatObjectMetadata,
}); });
export const getWorkspaceMigrationV2ObjectDeleteAction = ( export const getWorkspaceMigrationV2ObjectDeleteAction = (
objectMetadataInput: WorkspaceMigrationObjectInput, flatObjectMetadata: FlatObjectMetadata,
): DeleteObjectAction => ({ ): DeleteObjectAction => ({
type: 'delete_object', type: 'delete_object',
objectMetadataInput, flatObjectMetadata,
}); });

View File

@ -1,101 +0,0 @@
import diff from 'microdiff';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
import { UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-action-v2';
import {
FieldMetadataEntityEditableProperties,
WorkspaceMigrationFieldInput,
fieldMetadataEntityEditableProperties,
fieldMetadataPropertiesToStringify,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input';
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
return [
FieldMetadataType.BOOLEAN,
FieldMetadataType.SELECT,
FieldMetadataType.MULTI_SELECT,
FieldMetadataType.CURRENCY,
FieldMetadataType.PHONES,
FieldMetadataType.ADDRESS,
].includes(type);
};
const compareTwoWorkspaceMigrationFieldInput = ({
from,
to,
}: FromTo<WorkspaceMigrationFieldInput>) => {
const compareFieldMetadataOptions = {
shouldIgnoreProperty: (
property: string,
fieldMetadata: WorkspaceMigrationFieldInput,
) => {
if (
!fieldMetadataEntityEditableProperties.includes(
property as FieldMetadataEntityEditableProperties,
)
) {
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 fieldMetadataDifference = diff(fromCompare, toCompare);
return fieldMetadataDifference;
};
type GetWorkspaceMigrationUpdateFieldActionArgs =
FromTo<WorkspaceMigrationFieldInput>;
export const compareFieldMetadataInputAndGetUpdateFieldActions = ({
from,
to,
}: GetWorkspaceMigrationUpdateFieldActionArgs) => {
const fieldMetadataDifferences = compareTwoWorkspaceMigrationFieldInput({
from,
to,
});
return fieldMetadataDifferences.flatMap<UpdateFieldAction['updates'][number]>(
(difference) => {
switch (difference.type) {
case 'CHANGE': {
const { oldValue, path, value } = difference;
return {
from: oldValue,
to: value,
property: path[0] as FieldMetadataEntityEditableProperties,
};
}
case 'CREATE':
case 'REMOVE':
default: {
// Should never occurs, we should only provide null never undefined and so on
return [];
}
}
},
);
};

View File

@ -1,19 +1,22 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
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 { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input'; import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-v2'; import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
import { computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util'; import { computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util';
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/deleted-created-updated-matrix-dispatcher.util'; import { computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-index-matrix.util';
import { getWorkspaceMigrationV2FieldCreateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions'; import { getWorkspaceMigrationV2FieldCreateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions';
import { getWorkspaceMigrationV2CreateIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-index-actions';
import { buildWorkspaceMigrationV2FieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-field-actions-builder'; import { buildWorkspaceMigrationV2FieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-field-actions-builder';
import { buildWorkspaceMigrationIndexActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-index-actions-builder';
import { buildWorkspaceMigrationV2ObjectActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder'; import { buildWorkspaceMigrationV2ObjectActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder';
@Injectable() @Injectable()
export class WorkspaceMigrationBuilderV2Service { export class WorkspaceMigrationBuilderV2Service {
constructor() {} constructor() {}
build( build(
objectMetadataFromToInputs: FromTo<WorkspaceMigrationObjectInput[]>, objectMetadataFromToInputs: FromTo<FlatObjectMetadata[]>,
): WorkspaceMigrationV2 { ): WorkspaceMigrationV2 {
const { const {
created: createdObjectMetadata, created: createdObjectMetadata,
@ -29,30 +32,47 @@ export class WorkspaceMigrationBuilderV2Service {
}); });
const createdObjectWorkspaceMigrationCreateFieldActions = const createdObjectWorkspaceMigrationCreateFieldActions =
createdObjectMetadata.flatMap((objectMetadataInput) => createdObjectMetadata.flatMap((flatObjectMetadata) =>
objectMetadataInput.fieldInputs.map((fieldMetadataInput) => flatObjectMetadata.flatFieldMetadatas.map((flatFieldMetadata) =>
getWorkspaceMigrationV2FieldCreateAction({ getWorkspaceMigrationV2FieldCreateAction({
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}), }),
), ),
); );
const updatedObjectMetadataFieldAndRelationDeletedCreatedUpdatedMatrix = const createdObjectMetadataCreateIndexActions =
createdObjectMetadata.flatMap((objectMetadata) =>
objectMetadata.flatIndexMetadatas.map(
getWorkspaceMigrationV2CreateIndexAction,
),
);
const updatedObjectMetadataDeletedCreatedUpdatedFieldMatrix =
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix( computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
updatedObjectMetadata, updatedObjectMetadata,
); );
const fieldWorkspaceMigrationActions = const fieldWorkspaceMigrationActions =
buildWorkspaceMigrationV2FieldActions( buildWorkspaceMigrationV2FieldActions(
updatedObjectMetadataFieldAndRelationDeletedCreatedUpdatedMatrix, updatedObjectMetadataDeletedCreatedUpdatedFieldMatrix,
); );
const updatedObjectMetadataIndexDeletedCreatedUpdatedMatrix =
computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix(
updatedObjectMetadata,
);
const indexWorkspaceMigrationActions = buildWorkspaceMigrationIndexActions(
updatedObjectMetadataIndexDeletedCreatedUpdatedMatrix,
);
return { return {
actions: [ actions: [
...objectWorkspaceMigrationActions, ...objectWorkspaceMigrationActions,
...createdObjectWorkspaceMigrationCreateFieldActions, ...createdObjectWorkspaceMigrationCreateFieldActions,
...createdObjectMetadataCreateIndexActions,
...fieldWorkspaceMigrationActions, ...fieldWorkspaceMigrationActions,
...indexWorkspaceMigrationActions,
], ],
}; };
} }

View File

@ -1,13 +1,13 @@
import { compareTwoFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-field-metadata-comparator.util';
import { import {
UpdateFieldAction, UpdateFieldAction,
WorkspaceMigrationFieldActionV2, WorkspaceMigrationFieldActionV2,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-action-v2'; } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
import { UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util'; import { UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util';
import { import {
getWorkspaceMigrationV2FieldCreateAction, getWorkspaceMigrationV2FieldCreateAction,
getWorkspaceMigrationV2FieldDeleteAction, getWorkspaceMigrationV2FieldDeleteAction,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions'; } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions';
import { compareFieldMetadataInputAndGetUpdateFieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/workspace-migration-field-metadata-input-comparator.util';
export const buildWorkspaceMigrationV2FieldActions = ( export const buildWorkspaceMigrationV2FieldActions = (
objectMetadataDeletedCreatedUpdatedFields: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[], objectMetadataDeletedCreatedUpdatedFields: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[],
@ -19,11 +19,11 @@ export const buildWorkspaceMigrationV2FieldActions = (
createdFieldMetadata, createdFieldMetadata,
deletedFieldMetadata, deletedFieldMetadata,
updatedFieldMetadata, updatedFieldMetadata,
objectMetadataInput, flatObjectMetadata,
} of objectMetadataDeletedCreatedUpdatedFields) { } of objectMetadataDeletedCreatedUpdatedFields) {
const updateFieldActions = updatedFieldMetadata.flatMap<UpdateFieldAction>( const updateFieldActions = updatedFieldMetadata.flatMap<UpdateFieldAction>(
({ from, to }) => { ({ from, to }) => {
const updates = compareFieldMetadataInputAndGetUpdateFieldActions({ const updates = compareTwoFlatFieldMetadata({
from, from,
to, to,
}); });
@ -34,24 +34,24 @@ export const buildWorkspaceMigrationV2FieldActions = (
return { return {
type: 'update_field', type: 'update_field',
fieldMetadataInput: to, flatFieldMetadata: to,
objectMetadataInput, flatObjectMetadata,
updates, updates,
}; };
}, },
); );
const createFieldAction = createdFieldMetadata.map((fieldMetadataInput) => const createFieldAction = createdFieldMetadata.map((flatFieldMetadata) =>
getWorkspaceMigrationV2FieldCreateAction({ getWorkspaceMigrationV2FieldCreateAction({
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}), }),
); );
const deleteFieldAction = deletedFieldMetadata.map((fieldMetadataInput) => const deleteFieldAction = deletedFieldMetadata.map((flatFieldMetadata) =>
getWorkspaceMigrationV2FieldDeleteAction({ getWorkspaceMigrationV2FieldDeleteAction({
fieldMetadataInput, flatFieldMetadata,
objectMetadataInput, flatObjectMetadata,
}), }),
); );

View File

@ -0,0 +1,52 @@
import { compareTwoFlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-index-metadata-comparator.util';
import { WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
import { UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-index-matrix.util';
import {
getWorkspaceMigrationV2CreateIndexAction,
getWorkspaceMigrationV2DeleteIndexAction,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-index-actions';
export const buildWorkspaceMigrationIndexActions = (
objectMetadataDeletedCreatedUpdatedIndex: UpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix[],
): WorkspaceMigrationIndexActionV2[] => {
let allUpdatedObjectMetadataIndexActions: WorkspaceMigrationIndexActionV2[] =
[];
for (const {
createdIndexMetadata,
deletedIndexMetadata,
updatedIndexMetadata,
} of objectMetadataDeletedCreatedUpdatedIndex) {
const updatedDeleteAndCreateIndexActions =
updatedIndexMetadata.flatMap<WorkspaceMigrationIndexActionV2>(
({ to, from }) => {
const updates = compareTwoFlatIndexMetadata({ from, to });
if (updates.length === 0) {
return [];
}
return [
getWorkspaceMigrationV2DeleteIndexAction(from),
getWorkspaceMigrationV2CreateIndexAction(to),
];
},
);
const createIndexActions = createdIndexMetadata.map(
getWorkspaceMigrationV2CreateIndexAction,
);
const deleteIndexActions = deletedIndexMetadata.map(
getWorkspaceMigrationV2DeleteIndexAction,
);
allUpdatedObjectMetadataIndexActions =
allUpdatedObjectMetadataIndexActions.concat([
...createIndexActions,
...deleteIndexActions,
...updatedDeleteAndCreateIndexActions,
]);
}
return allUpdatedObjectMetadataIndexActions;
};

View File

@ -1,25 +1,22 @@
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
import { CustomDeletedCreatedUpdatedMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
import { compareTwoFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-object-metadata-comparator.util';
import { import {
UpdateObjectAction, UpdateObjectAction,
WorkspaceMigrationV2ObjectAction, WorkspaceMigrationObjectActionV2,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-action-v2'; } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
import { CustomDeletedCreatedUpdatedMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/deleted-created-updated-matrix-dispatcher.util';
import { import {
getWorkspaceMigrationV2ObjectCreateAction, getWorkspaceMigrationV2ObjectCreateAction,
getWorkspaceMigrationV2ObjectDeleteAction, getWorkspaceMigrationV2ObjectDeleteAction,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-object-actions'; } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-object-actions';
import { compareTwoWorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/workspace-migration-object-metadata-input-comparator.util';
export type CreatedDeletedUpdatedObjectMetadataInputMatrix = export type CreatedDeletedUpdatedObjectMetadataInputMatrix =
CustomDeletedCreatedUpdatedMatrix< CustomDeletedCreatedUpdatedMatrix<'objectMetadata', FlatObjectMetadata>;
'objectMetadata',
WorkspaceMigrationObjectInput
>;
export const buildWorkspaceMigrationV2ObjectActions = ({ export const buildWorkspaceMigrationV2ObjectActions = ({
createdObjectMetadata, createdObjectMetadata,
deletedObjectMetadata, deletedObjectMetadata,
updatedObjectMetadata, updatedObjectMetadata,
}: CreatedDeletedUpdatedObjectMetadataInputMatrix): WorkspaceMigrationV2ObjectAction[] => { }: CreatedDeletedUpdatedObjectMetadataInputMatrix): WorkspaceMigrationObjectActionV2[] => {
const createdObjectActions = createdObjectMetadata.map( const createdObjectActions = createdObjectMetadata.map(
getWorkspaceMigrationV2ObjectCreateAction, getWorkspaceMigrationV2ObjectCreateAction,
); );
@ -30,7 +27,7 @@ export const buildWorkspaceMigrationV2ObjectActions = ({
const updatedObjectActions = const updatedObjectActions =
updatedObjectMetadata.flatMap<UpdateObjectAction>(({ from, to }) => { updatedObjectMetadata.flatMap<UpdateObjectAction>(({ from, to }) => {
const objectUpdatedProperties = compareTwoWorkspaceMigrationObjectInput({ const objectUpdatedProperties = compareTwoFlatObjectMetadata({
from, from,
to, to,
}); });
@ -41,7 +38,7 @@ export const buildWorkspaceMigrationV2ObjectActions = ({
return { return {
type: 'update_object', type: 'update_object',
objectMetadataInput: to, flatObjectMetadata: to,
updates: objectUpdatedProperties, updates: objectUpdatedProperties,
}; };
}); });