Migration builder v2 handle RELATION through fields actions (#13076)
# Introduction In this PR we've mainly refactor the typing to be extending existing entities. Also handling relations through the field abstraction layer rather than a dedicated one. We reverted midway We then still need to: - Handle indexing - Uniqueness - Add strong coverage and avoid static inline snapshoting as right now + building a coherent testing set - Deprecate the `standardId` in favor of a `uniqueIdentifier` on each `objectMetadata` and `fieldMetadata` - Rename types `input` to `flattened` - Handle custom or non custom edit edge cases. ( e.g cannot delete a standard field or object ) ## Notes Right I preferred including too many information ( whole object and field input ) in the action context, we might wanna evict redundant information in the future when implementing the runners
This commit is contained in:
@ -0,0 +1,4 @@
|
|||||||
|
export type FromTo<T> = {
|
||||||
|
from: T;
|
||||||
|
to: T;
|
||||||
|
};
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
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'];
|
||||||
@ -1,106 +0,0 @@
|
|||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
|
||||||
|
|
||||||
type UniqueIdentifierRecord<TTarget extends string> = {
|
|
||||||
[P in `${TTarget}UniqueIdentifier`]: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ObjectMetadataUniqueIdentifier = UniqueIdentifierRecord<'objectMetadata'>;
|
|
||||||
|
|
||||||
type FieldMetadataUniqueIdentifier = UniqueIdentifierRecord<'fieldMetadata'>;
|
|
||||||
|
|
||||||
export type FromTo<T> = {
|
|
||||||
from: T;
|
|
||||||
to: T;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ObjectActionCommon = ObjectMetadataUniqueIdentifier;
|
|
||||||
export type CreateObjectAction = {
|
|
||||||
type: 'create_object';
|
|
||||||
object: ObjectMetadataEntity;
|
|
||||||
} & ObjectActionCommon;
|
|
||||||
|
|
||||||
export type UpdateObjectAction = {
|
|
||||||
type: 'update_object';
|
|
||||||
updates: (FromTo<Partial<ObjectMetadataEntity>> & { property: string })[];
|
|
||||||
} & ObjectActionCommon;
|
|
||||||
|
|
||||||
export type DeleteObjectAction = {
|
|
||||||
type: 'delete_object';
|
|
||||||
} & ObjectActionCommon;
|
|
||||||
|
|
||||||
export type WorkspaceMigrationV2ObjectAction =
|
|
||||||
| CreateObjectAction
|
|
||||||
| UpdateObjectAction
|
|
||||||
| DeleteObjectAction;
|
|
||||||
|
|
||||||
type FieldActionCommon = {
|
|
||||||
field: Partial<FieldMetadataEntity>;
|
|
||||||
} & ObjectMetadataUniqueIdentifier &
|
|
||||||
FieldMetadataUniqueIdentifier;
|
|
||||||
export type CreateFieldAction = {
|
|
||||||
type: 'create_field';
|
|
||||||
} & FieldActionCommon;
|
|
||||||
|
|
||||||
export type UpdateFieldAction = {
|
|
||||||
type: 'update_field';
|
|
||||||
} & FieldActionCommon;
|
|
||||||
|
|
||||||
export type DeleteFieldAction = {
|
|
||||||
type: 'delete_field';
|
|
||||||
} & Omit<FieldActionCommon, 'field'>;
|
|
||||||
|
|
||||||
export type WorkspaceMigrationFieldActionV2 =
|
|
||||||
| CreateFieldAction
|
|
||||||
| UpdateFieldAction
|
|
||||||
| DeleteFieldAction;
|
|
||||||
|
|
||||||
export interface CreateRelationAction {
|
|
||||||
type: 'create_relation';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateRelationAction {
|
|
||||||
type: 'update_relation';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteRelationAction {
|
|
||||||
type: 'delete_relation';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WorkspaceMigrationRelationActionV2 =
|
|
||||||
| CreateRelationAction
|
|
||||||
| UpdateRelationAction
|
|
||||||
| DeleteRelationAction;
|
|
||||||
|
|
||||||
export interface CreateIndexAction {
|
|
||||||
type: 'create_index';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteIndexAction {
|
|
||||||
type: 'delete_index';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WorkspaceMigrationIndexActionV2 =
|
|
||||||
| CreateIndexAction
|
|
||||||
| DeleteIndexAction;
|
|
||||||
|
|
||||||
export interface AddUniquenessConstraintAction {
|
|
||||||
type: 'add_uniqueness_constraint';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RemoveUniquenessConstraintAction {
|
|
||||||
type: 'remove_uniqueness_constraint';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WorkspaceMigrationUniquenessActionV2 =
|
|
||||||
| RemoveUniquenessConstraintAction
|
|
||||||
| AddUniquenessConstraintAction;
|
|
||||||
|
|
||||||
export type WorkspaceMigrationActionV2 =
|
|
||||||
| WorkspaceMigrationRelationActionV2
|
|
||||||
| WorkspaceMigrationV2ObjectAction
|
|
||||||
| WorkspaceMigrationFieldActionV2
|
|
||||||
| WorkspaceMigrationUniquenessActionV2
|
|
||||||
| WorkspaceMigrationIndexActionV2;
|
|
||||||
|
|
||||||
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||||
|
import {
|
||||||
|
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 = {
|
||||||
|
fieldMetadataInput: WorkspaceMigrationFieldInput;
|
||||||
|
objectMetadataInput: WorkspaceMigrationObjectWithoutFields;
|
||||||
|
};
|
||||||
|
export type CreateFieldAction = {
|
||||||
|
type: 'create_field';
|
||||||
|
} & FieldAndObjectMetadataWorkspaceMigrationInput;
|
||||||
|
|
||||||
|
export type UpdateFieldAction = {
|
||||||
|
type: 'update_field';
|
||||||
|
updates: Partial<
|
||||||
|
{
|
||||||
|
[P in FieldMetadataEntityEditableProperties]: {
|
||||||
|
property: P;
|
||||||
|
} & FromTo<FieldMetadataEntity[P]>;
|
||||||
|
}[FieldMetadataEntityEditableProperties]
|
||||||
|
>[];
|
||||||
|
} & FieldAndObjectMetadataWorkspaceMigrationInput;
|
||||||
|
|
||||||
|
export type DeleteFieldAction = {
|
||||||
|
type: 'delete_field';
|
||||||
|
} & FieldAndObjectMetadataWorkspaceMigrationInput;
|
||||||
|
|
||||||
|
export type WorkspaceMigrationFieldActionV2 =
|
||||||
|
| CreateFieldAction
|
||||||
|
| UpdateFieldAction
|
||||||
|
| DeleteFieldAction;
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
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[];
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
export type CreateIndexAction = {
|
||||||
|
type: 'create_index';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeleteIndexAction = {
|
||||||
|
type: 'delete_index';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkspaceMigrationIndexActionV2 =
|
||||||
|
| CreateIndexAction
|
||||||
|
| DeleteIndexAction;
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
|
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||||
|
import {
|
||||||
|
ObjectMetadataEntityEditableProperties,
|
||||||
|
WorkspaceMigrationObjectWithoutFields,
|
||||||
|
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
||||||
|
|
||||||
|
type ObjectActionCommon = {
|
||||||
|
objectMetadataInput: WorkspaceMigrationObjectWithoutFields;
|
||||||
|
};
|
||||||
|
export type CreateObjectAction = {
|
||||||
|
type: 'create_object';
|
||||||
|
} & ObjectActionCommon;
|
||||||
|
|
||||||
|
export type UpdateObjectAction = {
|
||||||
|
type: 'update_object';
|
||||||
|
updates: Partial<
|
||||||
|
{
|
||||||
|
[P in ObjectMetadataEntityEditableProperties]: {
|
||||||
|
property: P;
|
||||||
|
} & FromTo<ObjectMetadataEntity[P]>;
|
||||||
|
}[ObjectMetadataEntityEditableProperties]
|
||||||
|
>[];
|
||||||
|
} & ObjectActionCommon;
|
||||||
|
|
||||||
|
export type DeleteObjectAction = {
|
||||||
|
type: 'delete_object';
|
||||||
|
} & ObjectActionCommon;
|
||||||
|
|
||||||
|
export type WorkspaceMigrationV2ObjectAction =
|
||||||
|
| CreateObjectAction
|
||||||
|
| UpdateObjectAction
|
||||||
|
| DeleteObjectAction;
|
||||||
@ -1,22 +1,28 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
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 type WorkspaceMigrationObjectFieldInput = {
|
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;
|
uniqueIdentifier: string;
|
||||||
name: string;
|
fieldInputs: WorkspaceMigrationFieldInput[];
|
||||||
label: string;
|
|
||||||
defaultValue: unknown;
|
|
||||||
type: FieldMetadataType;
|
|
||||||
description?: string;
|
|
||||||
// TODO this should extend FieldMetadataEntity
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceMigrationObjectInput = {
|
export type WorkspaceMigrationObjectWithoutFields = Omit<
|
||||||
uniqueIdentifier: string;
|
WorkspaceMigrationObjectInput,
|
||||||
nameSingular: string;
|
'fieldInputs'
|
||||||
namePlural: string;
|
>;
|
||||||
labelSingular: string;
|
|
||||||
labelPlural: string;
|
|
||||||
description?: string;
|
|
||||||
fields: WorkspaceMigrationObjectFieldInput[];
|
|
||||||
// TODO this should extend ObjectMetadataEntity
|
|
||||||
};
|
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
export type AddUniquenessConstraintAction = {
|
||||||
|
type: 'add_uniqueness_constraint';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RemoveUniquenessConstraintAction = {
|
||||||
|
type: 'remove_uniqueness_constraint';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkspaceMigrationUniquenessActionV2 =
|
||||||
|
| RemoveUniquenessConstraintAction
|
||||||
|
| AddUniquenessConstraintAction;
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-v2';
|
import { WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-common-v2';
|
||||||
|
|
||||||
export interface WorkspaceMigrationV2<
|
export type WorkspaceMigrationV2<
|
||||||
TActions extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2,
|
TActions extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2,
|
||||||
> {
|
> = {
|
||||||
// formatVersion: 1;
|
// formatVersion: 1;
|
||||||
// createdAt: string;
|
// createdAt: string;
|
||||||
// name: string;
|
// name: string;
|
||||||
// description?: string;
|
// description?: string;
|
||||||
actions: TActions[];
|
actions: TActions[];
|
||||||
// objectActions: TActions[] // could be cool ?
|
// objectActions: TActions[] // could be cool ?
|
||||||
}
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
labelSingular: 'Contact',
|
labelSingular: 'Contact',
|
||||||
labelPlural: 'Contacts',
|
labelPlural: 'Contacts',
|
||||||
description: 'A contact',
|
description: 'A contact',
|
||||||
fields: [
|
fieldInputs: [
|
||||||
{
|
{
|
||||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174000',
|
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174000',
|
||||||
name: 'firstName',
|
name: 'firstName',
|
||||||
@ -41,7 +41,24 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
{
|
{
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"objectMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
"objectMetadataInput": {
|
||||||
|
"description": "A contact",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "First Name",
|
||||||
|
"name": "firstName",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Contacts",
|
||||||
|
"labelSingular": "Contact",
|
||||||
|
"namePlural": "Contacts",
|
||||||
|
"nameSingular": "Person",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||||
|
},
|
||||||
"type": "update_object",
|
"type": "update_object",
|
||||||
"updates": [
|
"updates": [
|
||||||
{
|
{
|
||||||
@ -64,7 +81,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
labelSingular: 'Company',
|
labelSingular: 'Company',
|
||||||
labelPlural: 'Companies',
|
labelPlural: 'Companies',
|
||||||
description: 'A company',
|
description: 'A company',
|
||||||
fields: [
|
fieldInputs: [
|
||||||
{
|
{
|
||||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174001',
|
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174001',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@ -82,9 +99,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
{
|
{
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"object": {
|
"objectMetadataInput": {
|
||||||
"description": "A company",
|
"description": "A company",
|
||||||
"fields": [
|
"fieldInputs": [
|
||||||
{
|
{
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
@ -100,11 +117,10 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
"nameSingular": "Company",
|
"nameSingular": "Company",
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
||||||
},
|
},
|
||||||
"objectMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
|
||||||
"type": "create_object",
|
"type": "create_object",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": {
|
"fieldMetadataInput": {
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
@ -112,8 +128,24 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
"type": "ADDRESS",
|
"type": "ADDRESS",
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
||||||
},
|
},
|
||||||
"fieldMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
"objectMetadataInput": {
|
||||||
"objectMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
"description": "A company",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Name",
|
||||||
|
"name": "name",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Companies",
|
||||||
|
"labelSingular": "Company",
|
||||||
|
"namePlural": "Companies",
|
||||||
|
"nameSingular": "Company",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
||||||
|
},
|
||||||
"type": "create_field",
|
"type": "create_field",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -128,7 +160,24 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
{
|
{
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"objectMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
"objectMetadataInput": {
|
||||||
|
"description": "A contact",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "First Name",
|
||||||
|
"name": "firstName",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Contacts",
|
||||||
|
"labelSingular": "Contact",
|
||||||
|
"namePlural": "Contacts",
|
||||||
|
"nameSingular": "Contact",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||||
|
},
|
||||||
"type": "delete_object",
|
"type": "delete_object",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -140,8 +189,8 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
const objectToUpdate: WorkspaceMigrationObjectInput = {
|
const objectToUpdate: WorkspaceMigrationObjectInput = {
|
||||||
...baseObject,
|
...baseObject,
|
||||||
nameSingular: 'Person',
|
nameSingular: 'Person',
|
||||||
fields: [
|
fieldInputs: [
|
||||||
...baseObject.fields,
|
...baseObject.fieldInputs,
|
||||||
{
|
{
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
label: 'New field',
|
label: 'New field',
|
||||||
@ -163,7 +212,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
labelSingular: 'Company',
|
labelSingular: 'Company',
|
||||||
labelPlural: 'Companies',
|
labelPlural: 'Companies',
|
||||||
description: 'A company',
|
description: 'A company',
|
||||||
fields: [
|
fieldInputs: [
|
||||||
{
|
{
|
||||||
uniqueIdentifier: '20202020-1016-4f09-bad6-e75681f385f4',
|
uniqueIdentifier: '20202020-1016-4f09-bad6-e75681f385f4',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@ -184,9 +233,9 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
{
|
{
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"object": {
|
"objectMetadataInput": {
|
||||||
"description": "A company",
|
"description": "A company",
|
||||||
"fields": [
|
"fieldInputs": [
|
||||||
{
|
{
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
@ -202,28 +251,56 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
"nameSingular": "Company",
|
"nameSingular": "Company",
|
||||||
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
||||||
},
|
},
|
||||||
"objectMetadataUniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
|
||||||
"type": "create_object",
|
"type": "create_object",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": {
|
"objectMetadataInput": {
|
||||||
"defaultValue": "",
|
"description": "A contact",
|
||||||
"description": "",
|
"fieldInputs": [
|
||||||
"label": "Name",
|
{
|
||||||
"name": "name",
|
"defaultValue": "",
|
||||||
"type": "ADDRESS",
|
"description": "",
|
||||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
"label": "First Name",
|
||||||
|
"name": "firstName",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Contacts",
|
||||||
|
"labelSingular": "Contact",
|
||||||
|
"namePlural": "Contacts",
|
||||||
|
"nameSingular": "Contact",
|
||||||
|
"uniqueIdentifier": "20202020-59ef-4a14-a509-0a02acb248d5",
|
||||||
},
|
},
|
||||||
"fieldMetadataUniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
|
||||||
"objectMetadataUniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"objectMetadataUniqueIdentifier": "20202020-59ef-4a14-a509-0a02acb248d5",
|
|
||||||
"type": "delete_object",
|
"type": "delete_object",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"objectMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
"objectMetadataInput": {
|
||||||
|
"description": "A contact",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "First Name",
|
||||||
|
"name": "firstName",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "new field description",
|
||||||
|
"label": "New field",
|
||||||
|
"name": "newField",
|
||||||
|
"type": "NUMBER",
|
||||||
|
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Contacts",
|
||||||
|
"labelSingular": "Contact",
|
||||||
|
"namePlural": "Contacts",
|
||||||
|
"nameSingular": "Person",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||||
|
},
|
||||||
"type": "update_object",
|
"type": "update_object",
|
||||||
"updates": [
|
"updates": [
|
||||||
{
|
{
|
||||||
@ -234,7 +311,36 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": {
|
"fieldMetadataInput": {
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Name",
|
||||||
|
"name": "name",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"description": "A company",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Name",
|
||||||
|
"name": "name",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Companies",
|
||||||
|
"labelSingular": "Company",
|
||||||
|
"namePlural": "Companies",
|
||||||
|
"nameSingular": "Company",
|
||||||
|
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
||||||
|
},
|
||||||
|
"type": "create_field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"description": "new field description",
|
"description": "new field description",
|
||||||
"label": "New field",
|
"label": "New field",
|
||||||
@ -242,8 +348,32 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
"type": "NUMBER",
|
"type": "NUMBER",
|
||||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
||||||
},
|
},
|
||||||
"fieldMetadataUniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
"objectMetadataInput": {
|
||||||
"objectMetadataUniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
"description": "A contact",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "First Name",
|
||||||
|
"name": "firstName",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "new field description",
|
||||||
|
"label": "New field",
|
||||||
|
"name": "newField",
|
||||||
|
"type": "NUMBER",
|
||||||
|
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Contacts",
|
||||||
|
"labelSingular": "Contact",
|
||||||
|
"namePlural": "Contacts",
|
||||||
|
"nameSingular": "Person",
|
||||||
|
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||||
|
},
|
||||||
"type": "create_field",
|
"type": "create_field",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -259,7 +389,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
labelSingular: 'Duplicate',
|
labelSingular: 'Duplicate',
|
||||||
labelPlural: 'Duplicates',
|
labelPlural: 'Duplicates',
|
||||||
description: 'First object',
|
description: 'First object',
|
||||||
fields: [
|
fieldInputs: [
|
||||||
{
|
{
|
||||||
uniqueIdentifier: 'field-1',
|
uniqueIdentifier: 'field-1',
|
||||||
name: 'fieldA',
|
name: 'fieldA',
|
||||||
@ -277,7 +407,7 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
labelSingular: 'Duplicate',
|
labelSingular: 'Duplicate',
|
||||||
labelPlural: 'Duplicates',
|
labelPlural: 'Duplicates',
|
||||||
description: 'Second object',
|
description: 'Second object',
|
||||||
fields: [
|
fieldInputs: [
|
||||||
{
|
{
|
||||||
uniqueIdentifier: 'field-2',
|
uniqueIdentifier: 'field-2',
|
||||||
name: 'fieldB',
|
name: 'fieldB',
|
||||||
@ -291,33 +421,159 @@ describe('WorkspaceMigrationBuilderV2Service', () => {
|
|||||||
|
|
||||||
const result = service.build({ from: [], to: [objectA, objectB] });
|
const result = service.build({ from: [], to: [objectA, objectB] });
|
||||||
|
|
||||||
expect(result.actions).toEqual(
|
expect(result.actions).toMatchInlineSnapshot(`
|
||||||
expect.arrayContaining([
|
[
|
||||||
expect.objectContaining({
|
{
|
||||||
type: 'create_object',
|
"objectMetadataInput": {
|
||||||
objectMetadataUniqueIdentifier: 'id-1',
|
"description": "First object",
|
||||||
}),
|
"fieldInputs": [
|
||||||
expect.objectContaining({
|
{
|
||||||
type: 'create_object',
|
"defaultValue": "",
|
||||||
objectMetadataUniqueIdentifier: 'id-2',
|
"description": "",
|
||||||
}),
|
"label": "Field A",
|
||||||
]),
|
"name": "fieldA",
|
||||||
);
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "field-1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Duplicates",
|
||||||
|
"labelSingular": "Duplicate",
|
||||||
|
"namePlural": "Duplicates",
|
||||||
|
"nameSingular": "Duplicate",
|
||||||
|
"uniqueIdentifier": "id-1",
|
||||||
|
},
|
||||||
|
"type": "create_object",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"description": "Second object",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Field B",
|
||||||
|
"name": "fieldB",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "field-2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Duplicates",
|
||||||
|
"labelSingular": "Duplicate",
|
||||||
|
"namePlural": "Duplicates",
|
||||||
|
"nameSingular": "Duplicate",
|
||||||
|
"uniqueIdentifier": "id-2",
|
||||||
|
},
|
||||||
|
"type": "create_object",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Field A",
|
||||||
|
"name": "fieldA",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "field-1",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"description": "First object",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Field A",
|
||||||
|
"name": "fieldA",
|
||||||
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "field-1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Duplicates",
|
||||||
|
"labelSingular": "Duplicate",
|
||||||
|
"namePlural": "Duplicates",
|
||||||
|
"nameSingular": "Duplicate",
|
||||||
|
"uniqueIdentifier": "id-1",
|
||||||
|
},
|
||||||
|
"type": "create_field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Field B",
|
||||||
|
"name": "fieldB",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "field-2",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"description": "Second object",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Field B",
|
||||||
|
"name": "fieldB",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "field-2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Duplicates",
|
||||||
|
"labelSingular": "Duplicate",
|
||||||
|
"namePlural": "Duplicates",
|
||||||
|
"nameSingular": "Duplicate",
|
||||||
|
"uniqueIdentifier": "id-2",
|
||||||
|
},
|
||||||
|
"type": "create_field",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
|
||||||
const deleteResult = service.build({ from: [objectA, objectB], to: [] });
|
const deleteResult = service.build({ from: [objectA, objectB], to: [] });
|
||||||
|
|
||||||
expect(deleteResult.actions).toEqual(
|
expect(deleteResult.actions).toMatchInlineSnapshot(`
|
||||||
expect.arrayContaining([
|
[
|
||||||
expect.objectContaining({
|
{
|
||||||
type: 'delete_object',
|
"objectMetadataInput": {
|
||||||
objectMetadataUniqueIdentifier: 'id-1',
|
"description": "First object",
|
||||||
}),
|
"fieldInputs": [
|
||||||
expect.objectContaining({
|
{
|
||||||
type: 'delete_object',
|
"defaultValue": "",
|
||||||
objectMetadataUniqueIdentifier: 'id-2',
|
"description": "",
|
||||||
}),
|
"label": "Field A",
|
||||||
]),
|
"name": "fieldA",
|
||||||
);
|
"type": "FULL_NAME",
|
||||||
|
"uniqueIdentifier": "field-1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Duplicates",
|
||||||
|
"labelSingular": "Duplicate",
|
||||||
|
"namePlural": "Duplicates",
|
||||||
|
"nameSingular": "Duplicate",
|
||||||
|
"uniqueIdentifier": "id-1",
|
||||||
|
},
|
||||||
|
"type": "delete_object",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"description": "Second object",
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "Field B",
|
||||||
|
"name": "fieldB",
|
||||||
|
"type": "ADDRESS",
|
||||||
|
"uniqueIdentifier": "field-2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"labelPlural": "Duplicates",
|
||||||
|
"labelSingular": "Duplicate",
|
||||||
|
"namePlural": "Duplicates",
|
||||||
|
"nameSingular": "Duplicate",
|
||||||
|
"uniqueIdentifier": "id-2",
|
||||||
|
},
|
||||||
|
"type": "delete_object",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit no actions when from and to are deeply equal', () => {
|
it('should emit no actions when from and to are deeply equal', () => {
|
||||||
|
|||||||
@ -0,0 +1,424 @@
|
|||||||
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
|
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.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 { WorkspaceMigrationFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input';
|
||||||
|
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
||||||
|
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', () => {
|
||||||
|
let service: WorkspaceMigrationBuilderV2Service;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new WorkspaceMigrationBuilderV2Service();
|
||||||
|
});
|
||||||
|
|
||||||
|
const createMockObject = (
|
||||||
|
identifier: string,
|
||||||
|
fields: Partial<WorkspaceMigrationFieldInput>[] = [],
|
||||||
|
): WorkspaceMigrationObjectInput => ({
|
||||||
|
uniqueIdentifier: identifier,
|
||||||
|
fieldInputs: fields.map((field) => ({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
name: 'defaultName',
|
||||||
|
label: 'Default Label',
|
||||||
|
isCustom: true,
|
||||||
|
isActive: true,
|
||||||
|
isNullable: true,
|
||||||
|
uniqueIdentifier: 'default-id',
|
||||||
|
...field,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buildWorkspaceMigrationV2RelationActions', () => {
|
||||||
|
it('should create relation actions for created fields', () => {
|
||||||
|
const fromObjects: WorkspaceMigrationObjectInput[] = [];
|
||||||
|
const toObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
createMockObject('company', [
|
||||||
|
{
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
name: 'employees',
|
||||||
|
label: 'Employees',
|
||||||
|
uniqueIdentifier: 'employees',
|
||||||
|
relationTargetFieldMetadataId: 'field-2',
|
||||||
|
relationTargetObjectMetadataId: 'obj-2',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = service.build({ from: fromObjects, to: toObjects });
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Employees",
|
||||||
|
"name": "employees",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "employees",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "company",
|
||||||
|
},
|
||||||
|
"type": "create_object",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Employees",
|
||||||
|
"name": "employees",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "employees",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Employees",
|
||||||
|
"name": "employees",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "employees",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "company",
|
||||||
|
},
|
||||||
|
"type": "create_field",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create delete actions for deleted fields', () => {
|
||||||
|
const fromObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
createMockObject('company', [
|
||||||
|
{
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
name: 'employees',
|
||||||
|
label: 'Employees',
|
||||||
|
uniqueIdentifier: 'employees',
|
||||||
|
relationTargetFieldMetadataId: 'field-2',
|
||||||
|
relationTargetObjectMetadataId: 'obj-2',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
const toObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
createMockObject('company'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = service.build({ from: fromObjects, to: toObjects });
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Employees",
|
||||||
|
"name": "employees",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "employees",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [],
|
||||||
|
"uniqueIdentifier": "company",
|
||||||
|
},
|
||||||
|
"type": "delete_field",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple relation changes across different objects', () => {
|
||||||
|
const fromObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
createMockObject('company', [
|
||||||
|
{
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
name: 'oldRelation',
|
||||||
|
label: 'Old Relation',
|
||||||
|
uniqueIdentifier: 'old-relation',
|
||||||
|
relationTargetFieldMetadataId: 'field-1',
|
||||||
|
relationTargetObjectMetadataId: 'obj-1',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
const toObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
createMockObject('company', [
|
||||||
|
{
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
name: 'newRelation',
|
||||||
|
label: 'New Relation',
|
||||||
|
uniqueIdentifier: 'new-relation',
|
||||||
|
relationTargetFieldMetadataId: 'field-2',
|
||||||
|
relationTargetObjectMetadataId: 'obj-2',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
createMockObject('person', [
|
||||||
|
{
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
name: 'manager',
|
||||||
|
label: 'Manager',
|
||||||
|
uniqueIdentifier: 'manager',
|
||||||
|
relationTargetFieldMetadataId: 'field-3',
|
||||||
|
relationTargetObjectMetadataId: 'obj-3',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = service.build({ from: fromObjects, to: toObjects });
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Manager",
|
||||||
|
"name": "manager",
|
||||||
|
"relationTargetFieldMetadataId": "field-3",
|
||||||
|
"relationTargetObjectMetadataId": "obj-3",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "manager",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "person",
|
||||||
|
},
|
||||||
|
"type": "create_object",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Manager",
|
||||||
|
"name": "manager",
|
||||||
|
"relationTargetFieldMetadataId": "field-3",
|
||||||
|
"relationTargetObjectMetadataId": "obj-3",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "manager",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Manager",
|
||||||
|
"name": "manager",
|
||||||
|
"relationTargetFieldMetadataId": "field-3",
|
||||||
|
"relationTargetObjectMetadataId": "obj-3",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "manager",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "person",
|
||||||
|
},
|
||||||
|
"type": "create_field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "New Relation",
|
||||||
|
"name": "newRelation",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "new-relation",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "New Relation",
|
||||||
|
"name": "newRelation",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "new-relation",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "company",
|
||||||
|
},
|
||||||
|
"type": "create_field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Old Relation",
|
||||||
|
"name": "oldRelation",
|
||||||
|
"relationTargetFieldMetadataId": "field-1",
|
||||||
|
"relationTargetObjectMetadataId": "obj-1",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "old-relation",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "New Relation",
|
||||||
|
"name": "newRelation",
|
||||||
|
"relationTargetFieldMetadataId": "field-2",
|
||||||
|
"relationTargetObjectMetadataId": "obj-2",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "new-relation",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "company",
|
||||||
|
},
|
||||||
|
"type": "delete_field",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty objects', () => {
|
||||||
|
const result = service.build({ from: [], to: [] });
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle objects with no relation 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 relation field updates', () => {
|
||||||
|
const baseField = {
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
name: 'employees',
|
||||||
|
label: 'Employees',
|
||||||
|
uniqueIdentifier: 'employees',
|
||||||
|
isCustom: true,
|
||||||
|
isActive: true,
|
||||||
|
isNullable: true,
|
||||||
|
description: 'Company employees',
|
||||||
|
};
|
||||||
|
|
||||||
|
const fromObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
createMockObject('company', [
|
||||||
|
{
|
||||||
|
...baseField,
|
||||||
|
relationTargetFieldMetadataId: 'field-1',
|
||||||
|
relationTargetObjectMetadataId: 'obj-1',
|
||||||
|
settings: {
|
||||||
|
relationType: RelationType.ONE_TO_MANY,
|
||||||
|
onDelete: RelationOnDeleteAction.CASCADE,
|
||||||
|
} as FieldMetadataSettings<FieldMetadataType.RELATION>,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
const toObjects: WorkspaceMigrationObjectInput[] = [
|
||||||
|
{
|
||||||
|
...fromObjects[0],
|
||||||
|
fieldInputs: [
|
||||||
|
{
|
||||||
|
...baseField,
|
||||||
|
name: 'updatedName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = service.build({ from: fromObjects, to: toObjects });
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"fieldMetadataInput": {
|
||||||
|
"description": "Company employees",
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Employees",
|
||||||
|
"name": "updatedName",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "employees",
|
||||||
|
},
|
||||||
|
"objectMetadataInput": {
|
||||||
|
"fieldInputs": [
|
||||||
|
{
|
||||||
|
"description": "Company employees",
|
||||||
|
"isActive": true,
|
||||||
|
"isCustom": true,
|
||||||
|
"isNullable": true,
|
||||||
|
"label": "Employees",
|
||||||
|
"name": "updatedName",
|
||||||
|
"type": "RELATION",
|
||||||
|
"uniqueIdentifier": "employees",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"uniqueIdentifier": "company",
|
||||||
|
},
|
||||||
|
"type": "update_field",
|
||||||
|
"updates": [
|
||||||
|
{
|
||||||
|
"from": "employees",
|
||||||
|
"property": "name",
|
||||||
|
"to": "updatedName",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||||
|
import { WorkspaceMigrationFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-input';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationObjectInput,
|
||||||
|
WorkspaceMigrationObjectWithoutFields,
|
||||||
|
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
||||||
|
import {
|
||||||
|
CustomDeletedCreatedUpdatedMatrix,
|
||||||
|
deletedCreatedUpdatedMatrixDispatcher,
|
||||||
|
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||||
|
|
||||||
|
export type UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = {
|
||||||
|
objectMetadataInput: WorkspaceMigrationObjectWithoutFields;
|
||||||
|
} & CustomDeletedCreatedUpdatedMatrix<
|
||||||
|
'fieldMetadata',
|
||||||
|
WorkspaceMigrationFieldInput
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = (
|
||||||
|
updatedObjectMetadata: FromTo<WorkspaceMigrationObjectInput>[],
|
||||||
|
): UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] => {
|
||||||
|
const matrixAccumulator: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] =
|
||||||
|
[];
|
||||||
|
|
||||||
|
for (const { from, to } of updatedObjectMetadata) {
|
||||||
|
const fieldMetadataMatrix = deletedCreatedUpdatedMatrixDispatcher({
|
||||||
|
from: from.fieldInputs,
|
||||||
|
to: to.fieldInputs,
|
||||||
|
});
|
||||||
|
|
||||||
|
matrixAccumulator.push({
|
||||||
|
objectMetadataInput: to,
|
||||||
|
createdFieldMetadata: fieldMetadataMatrix.created,
|
||||||
|
deletedFieldMetadata: fieldMetadataMatrix.deleted,
|
||||||
|
updatedFieldMetadata: fieldMetadataMatrix.updated,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return matrixAccumulator;
|
||||||
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-v2';
|
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||||
|
|
||||||
export type DeletedCreatedUpdatedMatrix<T> = {
|
export type DeletedCreatedUpdatedMatrix<T> = {
|
||||||
created: T[];
|
created: T[];
|
||||||
|
|||||||
@ -1,29 +1,23 @@
|
|||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
|
||||||
import {
|
import {
|
||||||
CreateFieldAction,
|
CreateFieldAction,
|
||||||
DeleteFieldAction,
|
DeleteFieldAction,
|
||||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-v2';
|
FieldAndObjectMetadataWorkspaceMigrationInput,
|
||||||
import { WorkspaceMigrationObjectFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-action-v2';
|
||||||
|
|
||||||
type FieldInputAndObjectUniqueIdentifier = {
|
|
||||||
field: WorkspaceMigrationObjectFieldInput;
|
|
||||||
objectMetadataUniqueIdentifier: string;
|
|
||||||
};
|
|
||||||
export const getWorkspaceMigrationV2FieldCreateAction = ({
|
export const getWorkspaceMigrationV2FieldCreateAction = ({
|
||||||
field,
|
fieldMetadataInput,
|
||||||
objectMetadataUniqueIdentifier,
|
objectMetadataInput,
|
||||||
}: FieldInputAndObjectUniqueIdentifier): CreateFieldAction => ({
|
}: FieldAndObjectMetadataWorkspaceMigrationInput): CreateFieldAction => ({
|
||||||
type: 'create_field',
|
type: 'create_field',
|
||||||
field: field as unknown as FieldMetadataEntity, // TODO prastoin
|
fieldMetadataInput,
|
||||||
fieldMetadataUniqueIdentifier: field.uniqueIdentifier,
|
objectMetadataInput,
|
||||||
objectMetadataUniqueIdentifier,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getWorkspaceMigrationV2FieldDeleteAction = ({
|
export const getWorkspaceMigrationV2FieldDeleteAction = ({
|
||||||
field,
|
fieldMetadataInput,
|
||||||
objectMetadataUniqueIdentifier,
|
objectMetadataInput,
|
||||||
}: FieldInputAndObjectUniqueIdentifier): DeleteFieldAction => ({
|
}: FieldAndObjectMetadataWorkspaceMigrationInput): DeleteFieldAction => ({
|
||||||
type: 'delete_field',
|
type: 'delete_field',
|
||||||
fieldMetadataUniqueIdentifier: field.uniqueIdentifier,
|
fieldMetadataInput,
|
||||||
objectMetadataUniqueIdentifier,
|
objectMetadataInput,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,21 +1,19 @@
|
|||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
|
||||||
import {
|
import {
|
||||||
CreateObjectAction,
|
CreateObjectAction,
|
||||||
DeleteObjectAction,
|
DeleteObjectAction,
|
||||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-v2';
|
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-action-v2';
|
||||||
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
||||||
|
|
||||||
export const getWorkspaceMigrationV2ObjectCreateAction = (
|
export const getWorkspaceMigrationV2ObjectCreateAction = (
|
||||||
input: WorkspaceMigrationObjectInput,
|
objectMetadataInput: WorkspaceMigrationObjectInput,
|
||||||
): CreateObjectAction => ({
|
): CreateObjectAction => ({
|
||||||
type: 'create_object',
|
type: 'create_object',
|
||||||
objectMetadataUniqueIdentifier: input.uniqueIdentifier,
|
objectMetadataInput,
|
||||||
object: input as unknown as ObjectMetadataEntity, // TODO prastoin
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getWorkspaceMigrationV2ObjectDeleteAction = (
|
export const getWorkspaceMigrationV2ObjectDeleteAction = (
|
||||||
input: WorkspaceMigrationObjectInput,
|
objectMetadataInput: WorkspaceMigrationObjectInput,
|
||||||
): DeleteObjectAction => ({
|
): DeleteObjectAction => ({
|
||||||
type: 'delete_object',
|
type: 'delete_object',
|
||||||
objectMetadataUniqueIdentifier: input.uniqueIdentifier,
|
objectMetadataInput,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,101 @@
|
|||||||
|
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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import omit from 'lodash.omit';
|
||||||
|
import diff from 'microdiff';
|
||||||
|
import { assertUnreachable } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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';
|
||||||
|
|
||||||
|
type ObjectWorkspaceMigrationUpdate = FromTo<WorkspaceMigrationObjectInput>;
|
||||||
|
|
||||||
|
export const compareTwoWorkspaceMigrationObjectInput = ({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
}: ObjectWorkspaceMigrationUpdate) => {
|
||||||
|
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 (
|
||||||
|
!objectMetadataEntityEditableProperties.includes(
|
||||||
|
property as ObjectMetadataEntityEditableProperties,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
property: property as ObjectMetadataEntityEditableProperties,
|
||||||
|
from: difference.oldValue,
|
||||||
|
to: difference.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'CREATE':
|
||||||
|
case 'REMOVE': {
|
||||||
|
// Should never occurs ? should throw ?
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
assertUnreachable(
|
||||||
|
difference,
|
||||||
|
`Unexpected difference type: ${difference['type']}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,28 +1,19 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
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 { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
||||||
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-v2';
|
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-v2';
|
||||||
import {
|
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';
|
||||||
DeletedCreatedUpdatedMatrix,
|
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||||
deletedCreatedUpdatedMatrixDispatcher,
|
import { getWorkspaceMigrationV2FieldCreateAction } 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/deleted-created-updated-matrix-dispatcher.util';
|
|
||||||
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 { 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';
|
||||||
|
|
||||||
type WorkspaceMigrationBuilderV2ServiceArgs = {
|
|
||||||
from: WorkspaceMigrationObjectInput[];
|
|
||||||
to: WorkspaceMigrationObjectInput[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UniqueIdentifierWorkspaceMigrationObjectInputMapDispatcher =
|
|
||||||
DeletedCreatedUpdatedMatrix<WorkspaceMigrationObjectInput>;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceMigrationBuilderV2Service {
|
export class WorkspaceMigrationBuilderV2Service {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
build(
|
build(
|
||||||
objectMetadataFromToInputs: WorkspaceMigrationBuilderV2ServiceArgs,
|
objectMetadataFromToInputs: FromTo<WorkspaceMigrationObjectInput[]>,
|
||||||
): WorkspaceMigrationV2 {
|
): WorkspaceMigrationV2 {
|
||||||
const {
|
const {
|
||||||
created: createdObjectMetadata,
|
created: createdObjectMetadata,
|
||||||
@ -37,12 +28,30 @@ export class WorkspaceMigrationBuilderV2Service {
|
|||||||
updatedObjectMetadata,
|
updatedObjectMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const createdObjectWorkspaceMigrationCreateFieldActions =
|
||||||
|
createdObjectMetadata.flatMap((objectMetadataInput) =>
|
||||||
|
objectMetadataInput.fieldInputs.map((fieldMetadataInput) =>
|
||||||
|
getWorkspaceMigrationV2FieldCreateAction({
|
||||||
|
fieldMetadataInput,
|
||||||
|
objectMetadataInput,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedObjectMetadataFieldAndRelationDeletedCreatedUpdatedMatrix =
|
||||||
|
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
|
||||||
|
updatedObjectMetadata,
|
||||||
|
);
|
||||||
|
|
||||||
const fieldWorkspaceMigrationActions =
|
const fieldWorkspaceMigrationActions =
|
||||||
buildWorkspaceMigrationV2FieldActions({ updatedObjectMetadata });
|
buildWorkspaceMigrationV2FieldActions(
|
||||||
|
updatedObjectMetadataFieldAndRelationDeletedCreatedUpdatedMatrix,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
actions: [
|
actions: [
|
||||||
...objectWorkspaceMigrationActions,
|
...objectWorkspaceMigrationActions,
|
||||||
|
...createdObjectWorkspaceMigrationCreateFieldActions,
|
||||||
...fieldWorkspaceMigrationActions,
|
...fieldWorkspaceMigrationActions,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,207 +1,57 @@
|
|||||||
import diff from 'microdiff';
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FromTo,
|
UpdateFieldAction,
|
||||||
WorkspaceMigrationFieldActionV2,
|
WorkspaceMigrationFieldActionV2,
|
||||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-v2';
|
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-field-action-v2';
|
||||||
import { WorkspaceMigrationObjectFieldInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
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 { UniqueIdentifierWorkspaceMigrationObjectInputMapDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
import { compareFieldMetadataInputAndGetUpdateFieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/workspace-migration-field-metadata-input-comparator.util';
|
||||||
import { CreatedDeletedUpdatedObjectMetadataInputMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder';
|
|
||||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CustomDeletedCreatedUpdatedMatrix,
|
|
||||||
deletedCreatedUpdatedMatrixDispatcher,
|
|
||||||
} from './utils/deleted-created-updated-matrix-dispatcher.util';
|
|
||||||
|
|
||||||
// Start TODO prastoin refactor and strictly type
|
|
||||||
const commonFieldPropertiesToIgnore = [
|
|
||||||
'id',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt',
|
|
||||||
'objectMetadataId',
|
|
||||||
'isActive',
|
|
||||||
'options',
|
|
||||||
'settings',
|
|
||||||
'joinColumn',
|
|
||||||
'gate',
|
|
||||||
'asExpression',
|
|
||||||
'generatedType',
|
|
||||||
'isLabelSyncedWithName',
|
|
||||||
// uniqueIdentifier ?
|
|
||||||
];
|
|
||||||
|
|
||||||
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
|
|
||||||
return [
|
|
||||||
FieldMetadataType.BOOLEAN,
|
|
||||||
FieldMetadataType.SELECT,
|
|
||||||
FieldMetadataType.MULTI_SELECT,
|
|
||||||
FieldMetadataType.CURRENCY,
|
|
||||||
FieldMetadataType.PHONES,
|
|
||||||
FieldMetadataType.ADDRESS,
|
|
||||||
].includes(type);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fieldPropertiesToStringify = ['defaultValue'] as const;
|
|
||||||
/// End
|
|
||||||
|
|
||||||
export const compareTwoWorkspaceMigrationFieldInput = ({
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
}: FromTo<WorkspaceMigrationObjectFieldInput>) => {
|
|
||||||
const compareFieldMetadataOptions = {
|
|
||||||
shouldIgnoreProperty: (
|
|
||||||
property: string,
|
|
||||||
fieldMetadata: WorkspaceMigrationObjectFieldInput,
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
property === 'defaultValue' &&
|
|
||||||
shouldNotOverrideDefaultValue(fieldMetadata.type)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commonFieldPropertiesToIgnore.includes(property)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
propertiesToStringify: fieldPropertiesToStringify,
|
|
||||||
};
|
|
||||||
const fromCompare = transformMetadataForComparison(
|
|
||||||
from,
|
|
||||||
compareFieldMetadataOptions,
|
|
||||||
);
|
|
||||||
const toCompare = transformMetadataForComparison(
|
|
||||||
to,
|
|
||||||
compareFieldMetadataOptions,
|
|
||||||
);
|
|
||||||
|
|
||||||
const fieldMetadataDifference = diff(fromCompare, toCompare);
|
|
||||||
|
|
||||||
return fieldMetadataDifference;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BuildWorkspaceMigrationV2FieldActionFromUpdatedFieldMetadataArgs =
|
|
||||||
FromTo<WorkspaceMigrationObjectFieldInput> & {
|
|
||||||
objectMetadataUniqueIdentifier: string;
|
|
||||||
};
|
|
||||||
// Still in wip
|
|
||||||
const buildWorkspaceMigrationV2FieldActionFromUpdatedFieldMetadata = ({
|
|
||||||
objectMetadataUniqueIdentifier,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
}: BuildWorkspaceMigrationV2FieldActionFromUpdatedFieldMetadataArgs) => {
|
|
||||||
const fieldMetadataDifferences = compareTwoWorkspaceMigrationFieldInput({
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
});
|
|
||||||
|
|
||||||
return fieldMetadataDifferences.flatMap<WorkspaceMigrationFieldActionV2>(
|
|
||||||
(difference) => {
|
|
||||||
switch (difference.type) {
|
|
||||||
case 'CREATE': {
|
|
||||||
return {
|
|
||||||
type: 'create_field',
|
|
||||||
field: difference.value,
|
|
||||||
fieldMetadataUniqueIdentifier: 'TODO',
|
|
||||||
objectMetadataUniqueIdentifier,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'CHANGE': {
|
|
||||||
// TODO prastoin
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
case 'REMOVE': {
|
|
||||||
return {
|
|
||||||
type: 'delete_field',
|
|
||||||
fieldMetadataUniqueIdentifier: difference.oldValue.uniqueIdentifier,
|
|
||||||
objectMetadataUniqueIdentifier,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type DeletedCreatedUpdatedFieldInputMatrix = {
|
|
||||||
objectMetadataUniqueIdentifier: string;
|
|
||||||
} & CustomDeletedCreatedUpdatedMatrix<
|
|
||||||
'fieldMetadata',
|
|
||||||
WorkspaceMigrationObjectFieldInput
|
|
||||||
>;
|
|
||||||
|
|
||||||
const updatedFieldMetadataMatriceMapDispatcher = (
|
|
||||||
updatedObjectMetadata: UniqueIdentifierWorkspaceMigrationObjectInputMapDispatcher['updated'],
|
|
||||||
): DeletedCreatedUpdatedFieldInputMatrix[] => {
|
|
||||||
const matriceAccumulator: DeletedCreatedUpdatedFieldInputMatrix[] = [];
|
|
||||||
|
|
||||||
for (const { from, to } of updatedObjectMetadata) {
|
|
||||||
const matrixResult = deletedCreatedUpdatedMatrixDispatcher({
|
|
||||||
from: from.fields,
|
|
||||||
to: to.fields,
|
|
||||||
});
|
|
||||||
|
|
||||||
matriceAccumulator.push({
|
|
||||||
objectMetadataUniqueIdentifier: from.uniqueIdentifier,
|
|
||||||
createdFieldMetadata: matrixResult.created,
|
|
||||||
deletedFieldMetadata: matrixResult.deleted,
|
|
||||||
updatedFieldMetadata: matrixResult.updated,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return matriceAccumulator;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BuildWorkspaceMigrationV2FieldActionsArgs = Pick<
|
|
||||||
CreatedDeletedUpdatedObjectMetadataInputMatrix,
|
|
||||||
'updatedObjectMetadata'
|
|
||||||
>;
|
|
||||||
export const buildWorkspaceMigrationV2FieldActions = ({
|
|
||||||
updatedObjectMetadata,
|
|
||||||
}: BuildWorkspaceMigrationV2FieldActionsArgs): WorkspaceMigrationFieldActionV2[] => {
|
|
||||||
const objectMetadataDeletedCreatedUpdatedFields =
|
|
||||||
updatedFieldMetadataMatriceMapDispatcher(updatedObjectMetadata);
|
|
||||||
|
|
||||||
|
export const buildWorkspaceMigrationV2FieldActions = (
|
||||||
|
objectMetadataDeletedCreatedUpdatedFields: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[],
|
||||||
|
): WorkspaceMigrationFieldActionV2[] => {
|
||||||
let allUpdatedObjectMetadataFieldActions: WorkspaceMigrationFieldActionV2[] =
|
let allUpdatedObjectMetadataFieldActions: WorkspaceMigrationFieldActionV2[] =
|
||||||
[];
|
[];
|
||||||
|
|
||||||
for (const {
|
for (const {
|
||||||
createdFieldMetadata,
|
createdFieldMetadata,
|
||||||
deletedFieldMetadata,
|
deletedFieldMetadata,
|
||||||
objectMetadataUniqueIdentifier,
|
|
||||||
updatedFieldMetadata,
|
updatedFieldMetadata,
|
||||||
|
objectMetadataInput,
|
||||||
} of objectMetadataDeletedCreatedUpdatedFields) {
|
} of objectMetadataDeletedCreatedUpdatedFields) {
|
||||||
const updateFieldAction =
|
const updateFieldActions = updatedFieldMetadata.flatMap<UpdateFieldAction>(
|
||||||
updatedFieldMetadata.flatMap<WorkspaceMigrationFieldActionV2>(
|
({ from, to }) => {
|
||||||
({ from, to }) =>
|
const updates = compareFieldMetadataInputAndGetUpdateFieldActions({
|
||||||
buildWorkspaceMigrationV2FieldActionFromUpdatedFieldMetadata({
|
from,
|
||||||
from,
|
to,
|
||||||
to,
|
});
|
||||||
objectMetadataUniqueIdentifier: objectMetadataUniqueIdentifier,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const createFieldAction = createdFieldMetadata.map((field) =>
|
if (updates.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'update_field',
|
||||||
|
fieldMetadataInput: to,
|
||||||
|
objectMetadataInput,
|
||||||
|
updates,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const createFieldAction = createdFieldMetadata.map((fieldMetadataInput) =>
|
||||||
getWorkspaceMigrationV2FieldCreateAction({
|
getWorkspaceMigrationV2FieldCreateAction({
|
||||||
field,
|
fieldMetadataInput,
|
||||||
objectMetadataUniqueIdentifier,
|
objectMetadataInput,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteFieldAction = deletedFieldMetadata.map((field) =>
|
const deleteFieldAction = deletedFieldMetadata.map((fieldMetadataInput) =>
|
||||||
getWorkspaceMigrationV2FieldDeleteAction({
|
getWorkspaceMigrationV2FieldDeleteAction({
|
||||||
field,
|
fieldMetadataInput,
|
||||||
objectMetadataUniqueIdentifier,
|
objectMetadataInput,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -209,7 +59,7 @@ export const buildWorkspaceMigrationV2FieldActions = ({
|
|||||||
allUpdatedObjectMetadataFieldActions.concat([
|
allUpdatedObjectMetadataFieldActions.concat([
|
||||||
...createFieldAction,
|
...createFieldAction,
|
||||||
...deleteFieldAction,
|
...deleteFieldAction,
|
||||||
...updateFieldAction,
|
...updateFieldActions,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,101 +1,14 @@
|
|||||||
import omit from 'lodash.omit';
|
|
||||||
import diff from 'microdiff';
|
|
||||||
import { assertUnreachable } from 'twenty-shared/utils';
|
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
|
||||||
import {
|
import {
|
||||||
FromTo,
|
|
||||||
UpdateObjectAction,
|
UpdateObjectAction,
|
||||||
WorkspaceMigrationActionV2,
|
WorkspaceMigrationV2ObjectAction,
|
||||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-action-v2';
|
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-action-v2';
|
||||||
import { WorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-object-input';
|
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 { CustomDeletedCreatedUpdatedMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||||
import { getWorkspaceMigrationV2FieldCreateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions';
|
|
||||||
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 { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
import { compareTwoWorkspaceMigrationObjectInput } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/workspace-migration-object-metadata-input-comparator.util';
|
||||||
|
|
||||||
// Start TODO prastoin refactor and strictly type
|
|
||||||
const objectPropertiesToIgnore = [
|
|
||||||
'id',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt',
|
|
||||||
'labelIdentifierFieldMetadataId',
|
|
||||||
'imageIdentifierFieldMetadataId',
|
|
||||||
'isActive',
|
|
||||||
'fields',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Not the same for standard and custom
|
|
||||||
const allowedObjectProps: (keyof Partial<ObjectMetadataEntity>)[] = [
|
|
||||||
'nameSingular',
|
|
||||||
'namePlural',
|
|
||||||
'labelSingular',
|
|
||||||
'labelPlural',
|
|
||||||
'description',
|
|
||||||
];
|
|
||||||
/// End
|
|
||||||
|
|
||||||
type ObjectWorkspaceMigrationUpdate = FromTo<WorkspaceMigrationObjectInput>;
|
|
||||||
|
|
||||||
const compareTwoWorkspaceMigrationObjectInput = ({
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
}: ObjectWorkspaceMigrationUpdate) => {
|
|
||||||
const fromCompare = transformMetadataForComparison(from, {
|
|
||||||
shouldIgnoreProperty: (property) =>
|
|
||||||
objectPropertiesToIgnore.includes(property),
|
|
||||||
});
|
|
||||||
const toCompare = transformMetadataForComparison(to, {
|
|
||||||
shouldIgnoreProperty: (property) =>
|
|
||||||
objectPropertiesToIgnore.includes(property),
|
|
||||||
});
|
|
||||||
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 (
|
|
||||||
!allowedObjectProps.includes(property as keyof ObjectMetadataEntity)
|
|
||||||
) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
property,
|
|
||||||
from: difference.oldValue,
|
|
||||||
to: difference.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'CREATE':
|
|
||||||
case 'REMOVE': {
|
|
||||||
// Should never occurs ? should throw ?
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
assertUnreachable(difference, 'TODO');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CreatedDeletedUpdatedObjectMetadataInputMatrix =
|
export type CreatedDeletedUpdatedObjectMetadataInputMatrix =
|
||||||
CustomDeletedCreatedUpdatedMatrix<
|
CustomDeletedCreatedUpdatedMatrix<
|
||||||
@ -106,44 +19,32 @@ export const buildWorkspaceMigrationV2ObjectActions = ({
|
|||||||
createdObjectMetadata,
|
createdObjectMetadata,
|
||||||
deletedObjectMetadata,
|
deletedObjectMetadata,
|
||||||
updatedObjectMetadata,
|
updatedObjectMetadata,
|
||||||
}: CreatedDeletedUpdatedObjectMetadataInputMatrix): WorkspaceMigrationActionV2[] => {
|
}: CreatedDeletedUpdatedObjectMetadataInputMatrix): WorkspaceMigrationV2ObjectAction[] => {
|
||||||
const createdObjectActions = createdObjectMetadata.flatMap(
|
const createdObjectActions = createdObjectMetadata.map(
|
||||||
(objectMetadata) => {
|
getWorkspaceMigrationV2ObjectCreateAction,
|
||||||
const createObjectAction =
|
|
||||||
getWorkspaceMigrationV2ObjectCreateAction(objectMetadata);
|
|
||||||
const createFieldActions = objectMetadata.fields.map((field) =>
|
|
||||||
getWorkspaceMigrationV2FieldCreateAction({
|
|
||||||
field,
|
|
||||||
objectMetadataUniqueIdentifier: objectMetadata.uniqueIdentifier,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return [createObjectAction, ...createFieldActions];
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const deletedObjectActions = deletedObjectMetadata.map(
|
const deletedObjectActions = deletedObjectMetadata.map(
|
||||||
getWorkspaceMigrationV2ObjectDeleteAction,
|
getWorkspaceMigrationV2ObjectDeleteAction,
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedObjectActions = updatedObjectMetadata
|
const updatedObjectActions =
|
||||||
.map<UpdateObjectAction | null>(({ from, to }) => {
|
updatedObjectMetadata.flatMap<UpdateObjectAction>(({ from, to }) => {
|
||||||
const objectUpdatedProperties = compareTwoWorkspaceMigrationObjectInput({
|
const objectUpdatedProperties = compareTwoWorkspaceMigrationObjectInput({
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (objectUpdatedProperties.length === 0) {
|
if (objectUpdatedProperties.length === 0) {
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
objectMetadataUniqueIdentifier: from.uniqueIdentifier,
|
|
||||||
type: 'update_object',
|
type: 'update_object',
|
||||||
|
objectMetadataInput: to,
|
||||||
updates: objectUpdatedProperties,
|
updates: objectUpdatedProperties,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.filter((action): action is UpdateObjectAction => action !== null);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...createdObjectActions,
|
...createdObjectActions,
|
||||||
|
|||||||
Reference in New Issue
Block a user