From 53bb534af831572ad053bba22a5034a41f757905 Mon Sep 17 00:00:00 2001 From: Guillim Date: Sun, 2 Nov 2025 13:31:48 +0100 Subject: [PATCH] MORPH for relationDecorator (#15420) # Adding MORPH support to the relationDecorator Extends the @WorkspaceRelation decorator to support morph relations by adding isMorphRelation and morphId options The implementation enforces type safety through TypeScript discriminated unions, ensuring morphId is required when isMorphRelation: true, and includes runtime validation that throws a descriptive error if morphId is missing. The morph relation metadata is properly stored in metadataArgsStorage and integrates seamlessly with the existing StandardFieldRelationFactory to convert decorated fields into FieldMetadataType.MORPH_RELATION entities with appropriate database constraints. To test : in person entity, add ```ts @WorkspaceRelation({ standardId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordCompany, type: RelationType.MANY_TO_ONE, label: msg`Test Related Record of Company`, description: msg`Test relation to company`, icon: 'IconLink', inverseSideTarget: () => CompanyWorkspaceEntity, inverseSideFieldKey: 'testRelatedPeople', onDelete: RelationOnDeleteAction.CASCADE, isMorphRelation: true, morphId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordMorphId, }) @WorkspaceIsNullable() @WorkspaceIsSystem() testRelatedRecordCompany: Relation | null; @WorkspaceJoinColumn('testRelatedRecordCompany') testRelatedRecordCompanyId: string | null; @WorkspaceRelation({ standardId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordOpportunity, type: RelationType.MANY_TO_ONE, label: msg`Test Related Record of Opportunity`, description: msg`Test relation to opportunity`, icon: 'IconLink', inverseSideTarget: () => OpportunityWorkspaceEntity, inverseSideFieldKey: 'testRelatedPeople', onDelete: RelationOnDeleteAction.CASCADE, isMorphRelation: true, morphId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordMorphId, }) @WorkspaceIsNullable() @WorkspaceIsSystem() testRelatedRecordOpportunity: Relation | null; @WorkspaceJoinColumn('testRelatedRecordOpportunity') testRelatedRecordOpportunityId: string | null; ``` in opportunity, ```ts @WorkspaceRelation({ standardId: OPPORTUNITY_STANDARD_FIELD_IDS.testRelatedPeople, type: RelationType.ONE_TO_MANY, label: msg`Test Related People`, description: msg`People linked to the opportunity via Test Related Record`, icon: 'IconUsers', inverseSideTarget: () => PersonWorkspaceEntity, inverseSideFieldKey: 'testRelatedRecordOpportunity', onDelete: RelationOnDeleteAction.SET_NULL, }) @WorkspaceIsNullable() @WorkspaceIsSystem() testRelatedPeople: Relation; ``` in company, ```ts @WorkspaceRelation({ standardId: COMPANY_STANDARD_FIELD_IDS.testRelatedPeople, type: RelationType.ONE_TO_MANY, label: msg`Test Related People`, description: msg`People linked to the company via Test Related Record`, icon: 'IconUsers', inverseSideTarget: () => PersonWorkspaceEntity, inverseSideFieldKey: 'testRelatedRecordCompany', onDelete: RelationOnDeleteAction.SET_NULL, }) @WorkspaceIsNullable() @WorkspaceIsSystem() testRelatedPeople: Relation; ``` In constants/standard-field-ids.ts, add the standard fields ids ```ts // COMPANY_STANDARD_FIELD_IDS testRelatedPeople: '20202020-7a90-47e9-b31f-916a716fd212', // OPPORTUNITY_STANDARD_FIELD_IDS testRelatedPeople: '20202020-7a90-47e9-b31f-916a716fd213', // PERSON_STANDARD_FIELD_IDS testRelatedRecordOpportunity: '20202020-5a90-47e9-b31f-916a716fd211', testRelatedRecordCompany: '20202020-1eb2-4298-910b-66b015b36d72', testRelatedRecordMorphId: '20202020-6ccf-48e3-bb92-bc9961bc011e', ``` --- .../workspace-relation.decorator.ts | 49 ++++++++++++++++--- ...kspace-relation-metadata-args.interface.ts | 10 ++++ ...workspace-migration-index.factory.utils.ts | 4 ++ ...kspace-migration-field-relation.factory.ts | 16 ++++-- .../workspace-migration-runner.service.ts | 4 +- .../workspace-field-relation.comparator.ts | 24 +++++++-- .../standard-field-relation.factory.ts | 17 +++++-- .../interfaces/comparator.interface.ts | 20 ++++++-- .../workspace-metadata-updater.service.ts | 44 ++++++++++++----- ...ce-sync-field-metadata-relation.service.ts | 16 ++++-- .../storage/workspace-sync.storage.ts | 33 ++++++++++--- 11 files changed, 190 insertions(+), 47 deletions(-) diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts index 1f728c0af8..6ceec303ba 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts @@ -1,4 +1,6 @@ import { type MessageDescriptor } from '@lingui/core'; +import { isDefined, isUUID } from 'class-validator'; +import { CustomError } from 'twenty-shared/utils'; import { type ObjectType } from 'typeorm'; import { type RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface'; @@ -9,7 +11,7 @@ import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/ import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { TypedReflect } from 'src/utils/typed-reflect'; -interface WorkspaceRelationBaseOptions { +interface WorkspaceRelationMinimumBaseOptions { standardId: string; label: MessageDescriptor; description?: @@ -21,17 +23,32 @@ interface WorkspaceRelationBaseOptions { onDelete?: RelationOnDeleteAction; } -interface WorkspaceOtherRelationOptions - extends WorkspaceRelationBaseOptions { - type: RelationType.ONE_TO_MANY; +interface WorkspaceRegularRelationBaseOptions + extends WorkspaceRelationMinimumBaseOptions { + isMorphRelation?: false; } -interface WorkspaceManyToOneRelationOptions - extends WorkspaceRelationBaseOptions { - type: RelationType.MANY_TO_ONE; - inverseSideFieldKey: keyof TClass; +interface WorkspaceMorphRelationBaseOptions + extends WorkspaceRelationMinimumBaseOptions { + isMorphRelation: true; + morphId: string; } +type WorkspaceRelationBaseOptions = + | WorkspaceRegularRelationBaseOptions + | WorkspaceMorphRelationBaseOptions; + +type WorkspaceOtherRelationOptions = + WorkspaceRelationBaseOptions & { + type: RelationType.ONE_TO_MANY; + }; + +type WorkspaceManyToOneRelationOptions = + WorkspaceRelationBaseOptions & { + type: RelationType.MANY_TO_ONE; + inverseSideFieldKey: keyof TClass; + }; + type WorkspaceRelationOptions = | WorkspaceOtherRelationOptions | WorkspaceManyToOneRelationOptions; @@ -73,6 +90,20 @@ export function WorkspaceRelation( const label = options.label.message ?? ''; const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name; + if (options.isMorphRelation && !isDefined(options.morphId)) { + throw new CustomError( + 'Morph relation must have a morph id', + 'MORPH_RELATION_MUST_HAVE_MORPH_ID', + ); + } + + if (options.isMorphRelation && !isUUID(options.morphId)) { + throw new CustomError( + 'Morph ID must be a valid UUID', + 'INVALID_MORPH_ID_FORMAT', + ); + } + metadataArgsStorage.addRelations({ target: object.constructor, standardId: options.standardId, @@ -97,6 +128,8 @@ export function WorkspaceRelation( isSystem, isUIReadOnly, gate, + isMorphRelation: options.isMorphRelation ?? false, + morphId: options.isMorphRelation ? options.morphId : undefined, isLabelSyncedWithName, }); }; diff --git a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts index c37b731da6..2debd271da 100644 --- a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts +++ b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts @@ -91,4 +91,14 @@ export interface WorkspaceRelationMetadataArgs { readonly isActive?: boolean; readonly isLabelSyncedWithName: boolean; + + /** + * Is morph relation. + */ + readonly isMorphRelation: boolean; + + /** + * Morph id. + */ + readonly morphId?: string; } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/utils/workspace-migration-index.factory.utils.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/utils/workspace-migration-index.factory.utils.ts index 014a6741a7..5addb6cde0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/utils/workspace-migration-index.factory.utils.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/utils/workspace-migration-index.factory.utils.ts @@ -57,6 +57,10 @@ export const createIndexMigration = async ( isFieldMetadataEntityOfType( fieldMetadata, FieldMetadataType.RELATION, + ) || + isFieldMetadataEntityOfType( + fieldMetadata, + FieldMetadataType.MORPH_RELATION, ) ) { if (!fieldMetadata.settings) { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory.ts index 9d2d2c1504..5bfe5358a9 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory.ts @@ -26,7 +26,9 @@ export class WorkspaceMigrationFieldRelationFactory { async create( originalObjectMetadataCollection: ObjectMetadataEntity[], - fieldMetadataCollection: FieldMetadataEntity[], + fieldMetadataCollection: FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[], action: | WorkspaceMigrationBuilderAction.CREATE | WorkspaceMigrationBuilderAction.DELETE, @@ -34,7 +36,9 @@ export class WorkspaceMigrationFieldRelationFactory { async create( originalObjectMetadataCollection: ObjectMetadataEntity[], - fieldMetadataUpdateCollection: FieldMetadataUpdate[], + fieldMetadataUpdateCollection: FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[], action: WorkspaceMigrationBuilderAction.UPDATE, ): Promise[]>; @@ -44,8 +48,12 @@ export class WorkspaceMigrationFieldRelationFactory { async create( originalObjectMetadataCollection: ObjectMetadataEntity[], fieldMetadataCollectionOrFieldMetadataUpdateCollection: - | FieldMetadataEntity[] - | FieldMetadataUpdate[], + | FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[] + | FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[], action: WorkspaceMigrationBuilderAction, ): Promise[]> { const originalObjectMetadataMap = originalObjectMetadataCollection.reduce( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service.ts index 842854f11a..289450c708 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service.ts @@ -189,10 +189,12 @@ export class WorkspaceMigrationRunnerService { case WorkspaceMigrationTableActionType.ALTER_INDEXES: if (tableMigration.indexes && tableMigration.indexes.length > 0) { + const tableName = tableMigration.newName ?? tableMigration.name; + await this.handleIndexesChanges( queryRunner, schemaName, - tableMigration.newName ?? tableMigration.name, + tableName, tableMigration.indexes, ); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field-relation.comparator.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field-relation.comparator.ts index de66c48630..ce4d545800 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field-relation.comparator.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field-relation.comparator.ts @@ -25,13 +25,21 @@ export class WorkspaceFieldRelationComparator { constructor() {} public compare( - originalFieldMetadataCollection: FieldMetadataEntity[], - standardFieldMetadataCollection: FieldMetadataEntity[], + originalFieldMetadataCollection: FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[], + standardFieldMetadataCollection: FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[], ): FieldRelationComparatorResult[] { const result: FieldRelationComparatorResult[] = []; const propertiesMap: Record< string, - Partial> + Partial< + FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > + > > = {}; // Double security to only compare non-custom fields @@ -103,7 +111,9 @@ export class WorkspaceFieldRelationComparator { fieldMetadataDifferenceMap, )) { const findField = ( - field: FieldMetadataEntity, + field: FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >, ) => { return field.standardId === fieldId; }; @@ -211,6 +221,8 @@ export class WorkspaceFieldRelationComparator { standardId: standardFieldMetadata.standardId ?? undefined, description: relationFieldMetadata.description ?? undefined, icon: relationFieldMetadata.icon ?? undefined, + type: standardFieldMetadata.type, + morphId: standardFieldMetadata.morphId ?? undefined, }, }); } else if (allOldPropertiesAreNull) { @@ -222,6 +234,8 @@ export class WorkspaceFieldRelationComparator { standardId: standardFieldMetadata.standardId ?? undefined, description: relationFieldMetadata.description ?? undefined, icon: relationFieldMetadata.icon ?? undefined, + type: standardFieldMetadata.type, + morphId: standardFieldMetadata.morphId ?? undefined, }, }); } else if (allNewPropertiesAreNull) { @@ -238,6 +252,8 @@ export class WorkspaceFieldRelationComparator { standardId: standardFieldMetadata.standardId ?? undefined, description: relationFieldMetadata.description ?? undefined, icon: relationFieldMetadata.icon ?? undefined, + type: standardFieldMetadata.type, + morphId: standardFieldMetadata.morphId ?? undefined, }, }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field-relation.factory.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field-relation.factory.ts index ee2a73238b..f8cb9ef0bf 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field-relation.factory.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field-relation.factory.ts @@ -21,7 +21,9 @@ export class StandardFieldRelationFactory { computeRelationFieldsForCustomObject( customObjectMetadata: ObjectMetadataEntity, originalObjectMetadataMap: Record, - ): FieldMetadataEntity[] { + ): FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[] { const workspaceRelationMetadataArgsCollection = metadataArgsStorage.filterRelations(CustomWorkspaceEntity); @@ -111,7 +113,9 @@ export class StandardFieldRelationFactory { standardObjectMetadataWorkspaceEntity: typeof BaseWorkspaceEntity, context: WorkspaceSyncContext, originalObjectMetadataMap: Record, - ): FieldMetadataEntity[] { + ): FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[] { const workspaceEntityMetadataArgs = metadataArgsStorage.filterEntities( standardObjectMetadataWorkspaceEntity, ); @@ -282,9 +286,13 @@ export class StandardFieldRelationFactory { ); } + const type = workspaceRelationMetadataArgs.isMorphRelation + ? FieldMetadataType.MORPH_RELATION + : FieldMetadataType.RELATION; + return { ...sourceFieldMetadata, - type: FieldMetadataType.RELATION, + type, settings: { relationType: workspaceRelationMetadataArgs.type, onDelete: @@ -293,9 +301,10 @@ export class StandardFieldRelationFactory { : undefined, joinColumnName, }, + morphId: workspaceRelationMetadataArgs.morphId ?? null, relationTargetObjectMetadataId: targetObjectMetadata.id, relationTargetFieldMetadataId: targetFieldMetadata.id, - } satisfies FieldMetadataEntity; + } satisfies FieldMetadataEntity; }); return [...staticRelations, ...relationsFromDynamicRelations]; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface.ts index 590470cc43..7edf6bbf42 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface.ts @@ -56,16 +56,30 @@ export type FieldComparatorResult = export type FieldRelationComparatorResult = | ComparatorSkipResult | ComparatorCreateResult< - Partial> & { + Partial< + ComputedPartialFieldMetadata< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > + > & { id: string; + type: FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION; } > | ComparatorUpdateResult< - Partial> & { + Partial< + ComputedPartialFieldMetadata< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > + > & { id: string; + type: FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION; } > - | ComparatorDeleteResult>; + | ComparatorDeleteResult< + FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > + >; export type IndexComparatorResult = | ComparatorCreateResult> diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service.ts index f3e06676c2..f943b70fe8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service.ts @@ -17,6 +17,7 @@ import { type PartialFieldMetadata } from 'src/engine/workspace-manager/workspac import { type PartialIndexMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-index-metadata.interface'; import { type UpdaterOptions } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/updater-options.interface'; +import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils'; import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { type FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @@ -212,21 +213,31 @@ export class WorkspaceMetadataUpdaterService { storage: WorkspaceSyncStorage, options?: UpdaterOptions, ): Promise<{ - createdFieldRelationMetadataCollection: FieldMetadataUpdate[]; - updatedFieldRelationMetadataCollection: FieldMetadataUpdate[]; - deletedFieldRelationMetadataCollection: FieldMetadataUpdate[]; + createdFieldRelationMetadataCollection: FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[]; + updatedFieldRelationMetadataCollection: FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[]; + deletedFieldRelationMetadataCollection: FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[]; }> { - let createdFieldRelationMetadataCollection: FieldMetadataUpdate[] = - []; - let updatedFieldRelationMetadataCollection: FieldMetadataUpdate[] = - []; + let createdFieldRelationMetadataCollection: FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[] = []; + let updatedFieldRelationMetadataCollection: FieldMetadataUpdate< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[] = []; /** * Create field relation metadata */ if (!options || options.actions.includes('create')) { createdFieldRelationMetadataCollection = await this.updateEntities< - FieldMetadataEntity + FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > >( manager, FieldMetadataEntity, @@ -240,7 +251,9 @@ export class WorkspaceMetadataUpdaterService { */ if (!options || options.actions.includes('update')) { updatedFieldRelationMetadataCollection = await this.updateEntities< - FieldMetadataEntity + FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > >( manager, FieldMetadataEntity, @@ -320,14 +333,21 @@ export class WorkspaceMetadataUpdaterService { const fieldMetadata = originalObjectMetadataCollection .find((object) => object.id === indexMetadata.objectMetadataId) ?.fields.find((field) => { - if ( - isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION) - ) { + if (isFieldMetadataRelationOrMorphRelation(field)) { if (field.settings?.joinColumnName === column) { return true; } } + if ( + isFieldMetadataEntityOfType( + field, + FieldMetadataType.MORPH_RELATION, + ) + ) { + return; + } + if (field.name === column) { return true; } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-field-metadata-relation.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-field-metadata-relation.service.ts index 5a9f4f60ba..36cb2af08a 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-field-metadata-relation.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-field-metadata-relation.service.ts @@ -177,8 +177,12 @@ export class WorkspaceSyncFieldMetadataRelationService { const originalFieldRelationMetadataCollection = (originalObjectMetadata?.fields.filter( - (field) => field.type === FieldMetadataType.RELATION, - ) ?? []) as FieldMetadataEntity[]; + (field) => + field.type === FieldMetadataType.RELATION || + field.type === FieldMetadataType.MORPH_RELATION, + ) ?? []) as FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[]; if (originalFieldRelationMetadataCollection.length === 0) { continue; @@ -204,8 +208,12 @@ export class WorkspaceSyncFieldMetadataRelationService { for (const customObjectMetadata of customObjectMetadataCollection) { const originalFieldRelationMetadataCollection = (customObjectMetadata.fields.filter( - (field) => field.type === FieldMetadataType.RELATION, - ) ?? []) as FieldMetadataEntity[]; + (field) => + field.type === FieldMetadataType.RELATION || + field.type === FieldMetadataType.MORPH_RELATION, + ) ?? []) as FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[]; const customFieldMetadataRelationCollection = this.standardFieldRelationFactory.computeRelationFieldsForCustomObject( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage.ts index dae51d856d..201df25892 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage.ts @@ -30,17 +30,24 @@ export class WorkspaceSyncStorage { // Field relation metadata private readonly _fieldRelationMetadataCreateCollection: (Partial< - ComputedPartialFieldMetadata + ComputedPartialFieldMetadata< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > > & { id: string; + type?: FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION; })[] = []; private readonly _fieldRelationMetadataUpdateCollection: (Partial< - ComputedPartialFieldMetadata + ComputedPartialFieldMetadata< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > > & { id: string; + type?: FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION; })[] = []; - private readonly _fieldRelationMetadataDeleteCollection: FieldMetadataEntity[] = - []; + private readonly _fieldRelationMetadataDeleteCollection: FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >[] = []; // Index metadata private readonly _indexMetadataCreateCollection: Partial[] = @@ -124,23 +131,35 @@ export class WorkspaceSyncStorage { } addCreateFieldRelationMetadata( - field: Partial> & { + field: Partial< + ComputedPartialFieldMetadata< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > + > & { id: string; + type: FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION; }, ) { this._fieldRelationMetadataCreateCollection.push(field); } addUpdateFieldRelationMetadata( - field: Partial> & { + field: Partial< + ComputedPartialFieldMetadata< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + > + > & { id: string; + type: FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION; }, ) { this._fieldRelationMetadataUpdateCollection.push(field); } addDeleteFieldRelationMetadata( - field: FieldMetadataEntity, + field: FieldMetadataEntity< + FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION + >, ) { this._fieldRelationMetadataDeleteCollection.push(field); }