diff --git a/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts index e6d095e9e..c0ab6d405 100644 --- a/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/index-metadata/index-metadata.service.ts @@ -13,6 +13,7 @@ import { generateDeterministicIndexName } from 'src/engine/metadata-modules/inde import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; import { + WorkspaceMigrationIndexAction, WorkspaceMigrationIndexActionType, WorkspaceMigrationTableAction, WorkspaceMigrationTableActionType, @@ -103,6 +104,55 @@ export class IndexMetadataService { ); } + async recomputeIndexMetadataForObject( + workspaceId: string, + updatedObjectMetadata: ObjectMetadataEntity, + ) { + const indexesToRecompute = await this.indexMetadataRepository.find({ + where: { + objectMetadataId: updatedObjectMetadata.id, + workspaceId, + }, + relations: ['indexFieldMetadatas.fieldMetadata'], + }); + + const recomputedIndexes: { + indexMetadata: IndexMetadataEntity; + previousName: string; + newName: string; + }[] = []; + + for (const index of indexesToRecompute) { + const previousIndexName = index.name; + const tableName = computeObjectTargetTable(updatedObjectMetadata); + + const indexFieldsMetadataOrdered = index.indexFieldMetadatas.sort( + (a, b) => a.order - b.order, + ); + + const columnNames = indexFieldsMetadataOrdered.map( + (indexFieldMetadata) => indexFieldMetadata.fieldMetadata.name, + ); + + const newIndexName = `IDX_${generateDeterministicIndexName([ + tableName, + ...columnNames, + ])}`; + + await this.indexMetadataRepository.update(index.id, { + name: newIndexName, + }); + + recomputedIndexes.push({ + indexMetadata: index, + previousName: previousIndexName, + newName: newIndexName, + }); + } + + return recomputedIndexes; + } + async deleteIndexMetadata( workspaceId: string, objectMetadata: ObjectMetadataEntity, @@ -179,4 +229,55 @@ export class IndexMetadataService { [migration], ); } + + async createIndexRecomputeMigrations( + workspaceId: string, + objectMetadata: ObjectMetadataEntity, + recomputedIndexes: { + indexMetadata: IndexMetadataEntity; + previousName: string; + newName: string; + }[], + ) { + for (const recomputedIndex of recomputedIndexes) { + const { previousName, newName, indexMetadata } = recomputedIndex; + + const tableName = computeObjectTargetTable(objectMetadata); + + const indexFieldsMetadataOrdered = indexMetadata.indexFieldMetadatas.sort( + (a, b) => a.order - b.order, + ); + + const columnNames = indexFieldsMetadataOrdered.map( + (indexFieldMetadata) => indexFieldMetadata.fieldMetadata.name, + ); + + const migration = { + name: tableName, + action: WorkspaceMigrationTableActionType.ALTER_INDEXES, + indexes: [ + { + action: WorkspaceMigrationIndexActionType.DROP, + name: previousName, + columns: [], + isUnique: indexMetadata.isUnique, + } satisfies WorkspaceMigrationIndexAction, + { + action: WorkspaceMigrationIndexActionType.CREATE, + columns: columnNames, + name: newName, + isUnique: indexMetadata.isUnique, + where: indexMetadata.indexWhereClause, + type: indexMetadata.indexType, + } satisfies WorkspaceMigrationIndexAction, + ], + } satisfies WorkspaceMigrationTableAction; + + await this.workspaceMigrationService.createCustomMigration( + generateMigrationName(`update-${objectMetadata.nameSingular}-index`), + workspaceId, + [migration], + ); + } + } } diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.module.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.module.ts index 6b0074d1e..2c2cac74a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.module.ts @@ -13,6 +13,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature- import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { IndexMetadataModule } from 'src/engine/metadata-modules/index-metadata/index-metadata.module'; import { BeforeUpdateOneObject } from 'src/engine/metadata-modules/object-metadata/hooks/before-update-one-object.hook'; import { ObjectMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/object-metadata/interceptors/object-metadata-graphql-api-exception.interceptor'; import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metadata/object-metadata.resolver'; @@ -49,6 +50,7 @@ import { UpdateObjectPayload } from './dtos/update-object.input'; WorkspaceMetadataVersionModule, RemoteTableRelationsModule, SearchModule, + IndexMetadataModule, ], services: [ ObjectMetadataService, diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts index 9a82a9515..cd5c8e377 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts @@ -10,6 +10,7 @@ import { FindManyOptions, FindOneOptions, In, Not, Repository } from 'typeorm'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service'; import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input'; import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input'; import { @@ -26,7 +27,6 @@ import { validateObjectMetadataInputOrThrow, } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util'; import { RemoteTableRelationsService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service'; -import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util'; import { SearchService } from 'src/engine/metadata-modules/search/search.service'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; @@ -55,6 +55,7 @@ export class ObjectMetadataService extends TypeOrmQueryService - this.objectMetadataRelationService.createMetadata( - objectMetadataInput.workspaceId, - createdObjectMetadata, - mapUdtNameToFieldType( - objectMetadataInput.primaryKeyColumnType ?? 'uuid', - ), - objectMetadataInput.primaryKeyFieldMetadataSettings, - relationType, - ), - ), - ); - - await this.objectMetadataMigrationService.createRelationMigrations( - createdObjectMetadata, - createdRelatedObjectMetadata, - ); - } - private async handleObjectNameAndLabelUpdates( existingObjectMetadata: ObjectMetadataEntity, objectMetadataForUpdate: ObjectMetadataEntity, @@ -447,17 +430,37 @@ export class ObjectMetadataService extends TypeOrmQueryService - relations.map((relation) => relation.toObjectMetadataId), - ); - - const foreignKeyFieldMetadataForStandardRelation = - await this.fieldMetadataRepository.find({ - where: { - isCustom: false, - settings: { - isForeignKey: true, - }, - name: `${existingObjectMetadata.nameSingular}Id`, - workspaceId: workspaceId, - }, - }); - - await Promise.all( - foreignKeyFieldMetadataForStandardRelation.map( - async (foreignKeyFieldMetadata) => { - if ( - relatedObjectsIds.includes( - foreignKeyFieldMetadata.objectMetadataId, - ) - ) { - const relatedObject = - await this.objectMetadataRepository.findOneBy({ - id: foreignKeyFieldMetadata.objectMetadataId, - workspaceId: workspaceId, - }); - - if (relatedObject) { - // 1. Update to and from relation fieldMetadata - const toFieldRelationFieldMetadataId = - await this.fieldMetadataRepository - .findOneByOrFail({ - name: existingObjectMetadata.nameSingular, - objectMetadataId: relatedObject.id, - workspaceId: workspaceId, - }) - .then((field) => field.id); - - const { description: descriptionForToField } = - buildDescriptionForRelationFieldMetadataOnToField({ - relationObjectMetadataNamePlural: relatedObject.namePlural, - targetObjectLabelSingular: - updatedObjectMetadata.labelSingular, - }); - - await this.fieldMetadataRepository.update( - toFieldRelationFieldMetadataId, - { - name: updatedObjectMetadata.nameSingular, - label: updatedObjectMetadata.labelSingular, - description: descriptionForToField, - }, - ); - - const fromFieldRelationFieldMetadataId = - await this.relationMetadataRepository - .findOneByOrFail({ - fromObjectMetadataId: existingObjectMetadata.id, - toObjectMetadataId: relatedObject.id, - toFieldMetadataId: toFieldRelationFieldMetadataId, - workspaceId, - }) - .then((relation) => relation?.fromFieldMetadataId); - - await this.fieldMetadataRepository.update( - fromFieldRelationFieldMetadataId, - { - description: - buildDescriptionForRelationFieldMetadataOnFromField({ - relationObjectMetadataNamePlural: - relatedObject.namePlural, - targetObjectLabelSingular: - updatedObjectMetadata.labelSingular, - }).description, - }, - ); - - // 2. Update foreign key fieldMetadata - const { - name: updatedNameForForeignKeyFieldMetadata, - label: updatedLabelForForeignKeyFieldMetadata, - description: updatedDescriptionForForeignKeyFieldMetadata, - } = buildNameLabelAndDescriptionForForeignKeyFieldMetadata({ - targetObjectNameSingular: updatedObjectMetadata.nameSingular, - targetObjectLabelSingular: - updatedObjectMetadata.labelSingular, - relatedObjectLabelSingular: relatedObject.labelSingular, - }); - - await this.fieldMetadataRepository.update( - foreignKeyFieldMetadata.id, - { - name: updatedNameForForeignKeyFieldMetadata, - label: updatedLabelForForeignKeyFieldMetadata, - description: updatedDescriptionForForeignKeyFieldMetadata, - }, - ); - - const relatedObjectTableName = - computeObjectTargetTable(relatedObject); - const columnName = `${existingObjectMetadata.nameSingular}Id`; - const columnType = fieldMetadataTypeToColumnType( - foreignKeyFieldMetadata.type, - ); - - await this.workspaceMigrationService.createCustomMigration( - generateMigrationName( - `rename-${existingObjectMetadata.nameSingular}-to-${updatedObjectMetadata.nameSingular}-in-${relatedObject.nameSingular}`, - ), - workspaceId, - [ - { - name: relatedObjectTableName, - action: WorkspaceMigrationTableActionType.ALTER, - columns: [ - { - action: WorkspaceMigrationColumnActionType.ALTER, - currentColumnDefinition: { - columnName, - columnType, - isNullable: true, - defaultValue: null, - }, - alteredColumnDefinition: { - columnName: `${updatedObjectMetadata.nameSingular}Id`, - columnType, - isNullable: true, - defaultValue: null, - }, - }, - ], - }, - ], - ); - } - } - }, + await this.workspaceMigrationService.createCustomMigration( + generateMigrationName( + `rename-${existingObjectMetadata.nameSingular}-to-${updatedObjectMetadata.nameSingular}-in-${relatedObjectMetadata.nameSingular}`, ), + workspaceId, + [ + { + name: relatedObjectTableName, + action: WorkspaceMigrationTableActionType.ALTER, + columns: [ + { + action: WorkspaceMigrationColumnActionType.ALTER, + currentColumnDefinition: { + columnName, + columnType, + isNullable: true, + defaultValue: null, + }, + alteredColumnDefinition: { + columnName: `${updatedObjectMetadata.nameSingular}Id`, + columnType, + isNullable: true, + defaultValue: null, + }, + }, + ], + }, + ], ); } } diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts index 74dac774e..a720a59e8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service.ts @@ -18,17 +18,27 @@ import { RelationMetadataType, RelationOnDeleteAction, } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; +import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util'; import { CUSTOM_OBJECT_STANDARD_FIELD_IDS, STANDARD_OBJECT_FIELD_IDS, } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons'; +import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; import { createForeignKeyDeterministicUuid, createRelationDeterministicUuid, } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util'; import { capitalize } from 'src/utils/capitalize'; +const DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS = [ + STANDARD_OBJECT_IDS.timelineActivity, + STANDARD_OBJECT_IDS.favorite, + STANDARD_OBJECT_IDS.attachment, + STANDARD_OBJECT_IDS.noteTarget, + STANDARD_OBJECT_IDS.taskTarget, +]; + @Injectable() export class ObjectMetadataRelationService { constructor( @@ -40,46 +50,63 @@ export class ObjectMetadataRelationService { private readonly relationMetadataRepository: Repository, ) {} - public async createMetadata( + public async createRelationsAndForeignKeysMetadata( + workspaceId: string, + createdObjectMetadata: ObjectMetadataEntity, + { primaryKeyFieldMetadataSettings, primaryKeyColumnType }, + ) { + const relatedObjectMetadataCollection = await Promise.all( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.map( + async (relationObjectMetadataStandardId) => + this.createRelationAndForeignKeyMetadata( + workspaceId, + createdObjectMetadata, + mapUdtNameToFieldType(primaryKeyColumnType ?? 'uuid'), + primaryKeyFieldMetadataSettings, + relationObjectMetadataStandardId, + ), + ), + ); + + return relatedObjectMetadataCollection; + } + + private async createRelationAndForeignKeyMetadata( workspaceId: string, createdObjectMetadata: ObjectMetadataEntity, objectPrimaryKeyType: FieldMetadataType, objectPrimaryKeyFieldSettings: | FieldMetadataSettings | undefined, - relatedObjectMetadataName: string, + relationObjectMetadataStandardId: string, ) { const relatedObjectMetadata = await this.objectMetadataRepository.findOneByOrFail({ - nameSingular: relatedObjectMetadataName, + standardId: relationObjectMetadataStandardId, workspaceId: workspaceId, + isCustom: false, }); - await this.createForeignKeyFieldMetadata( - workspaceId, - createdObjectMetadata, - relatedObjectMetadata, - objectPrimaryKeyType, - objectPrimaryKeyFieldSettings, - ); + const relationFieldMetadataCollection = + await this.createRelationFieldMetadas( + workspaceId, + createdObjectMetadata, + relatedObjectMetadata, + objectPrimaryKeyType, + objectPrimaryKeyFieldSettings, + ); - const relationFieldMetadata = await this.createRelationFields( + await this.createRelationMetadataFromFieldMetadatas( workspaceId, createdObjectMetadata, relatedObjectMetadata, - ); - - await this.createRelationMetadata( - workspaceId, - createdObjectMetadata, - relatedObjectMetadata, - relationFieldMetadata, + relationFieldMetadataCollection, ); return relatedObjectMetadata; } - private async createForeignKeyFieldMetadata( + private async createRelationFieldMetadas( workspaceId: string, createdObjectMetadata: ObjectMetadataEntity, relatedObjectMetadata: ObjectMetadataEntity, @@ -88,99 +115,187 @@ export class ObjectMetadataRelationService { | FieldMetadataSettings | undefined, ) { - const customStandardFieldId = - STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom; - - if (!customStandardFieldId) { - throw new Error( - `Custom standard field ID not found for ${relatedObjectMetadata.nameSingular}`, - ); - } - - const { name, label, description } = - buildNameLabelAndDescriptionForForeignKeyFieldMetadata({ - targetObjectNameSingular: createdObjectMetadata.nameSingular, - targetObjectLabelSingular: createdObjectMetadata.labelSingular, - relatedObjectLabelSingular: relatedObjectMetadata.labelSingular, - }); - - await this.fieldMetadataRepository.save({ - standardId: createForeignKeyDeterministicUuid({ - objectId: createdObjectMetadata.id, - standardId: customStandardFieldId, - }), - objectMetadataId: relatedObjectMetadata.id, - workspaceId: workspaceId, - isCustom: false, - isActive: true, - type: objectPrimaryKeyType, - name, - label, - description, - icon: undefined, - isNullable: true, - isSystem: true, - defaultValue: undefined, - settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true }, - }); - } - - private async createRelationFields( - workspaceId: string, - createdObjectMetadata: ObjectMetadataEntity, - relatedObjectMetadata: ObjectMetadataEntity, - ) { - return await this.fieldMetadataRepository.save([ - this.createFromField( + return this.fieldMetadataRepository.save([ + this.buildFromFieldMetadata( workspaceId, createdObjectMetadata, relatedObjectMetadata, ), - this.createToField( + this.buildToFieldMetadata( workspaceId, createdObjectMetadata, relatedObjectMetadata, ), + this.buildForeignKeyFieldMetadata( + workspaceId, + createdObjectMetadata, + relatedObjectMetadata, + objectPrimaryKeyType, + objectPrimaryKeyFieldSettings, + ), ]); } - private createFromField( + public async updateRelationsAndForeignKeysMetadata( workspaceId: string, - createdObjectMetadata: ObjectMetadataEntity, + updatedObjectMetadata: ObjectMetadataEntity, + ): Promise< + { + relatedObjectMetadata: ObjectMetadataEntity; + foreignKeyFieldMetadata: FieldMetadataEntity; + toFieldMetadata: FieldMetadataEntity; + fromFieldMetadata: FieldMetadataEntity; + }[] + > { + return await Promise.all( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.map( + async (relationObjectMetadataStandardId) => + this.updateRelationAndForeignKeyMetadata( + workspaceId, + updatedObjectMetadata, + relationObjectMetadataStandardId, + ), + ), + ); + } + + private async updateRelationAndForeignKeyMetadata( + workspaceId: string, + updatedObjectMetadata: ObjectMetadataEntity, + relationObjectMetadataStandardId: string, + ) { + const relatedObjectMetadata = + await this.objectMetadataRepository.findOneByOrFail({ + standardId: relationObjectMetadataStandardId, + workspaceId: workspaceId, + isCustom: false, + }); + + const toFieldMetadataUpdateCriteria = { + standardId: createRelationDeterministicUuid({ + objectId: updatedObjectMetadata.id, + standardId: + STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom, + }), + objectMetadataId: relatedObjectMetadata.id, + workspaceId: workspaceId, + }; + const toFieldMetadataUpdateData = this.buildToFieldMetadata( + workspaceId, + updatedObjectMetadata, + relatedObjectMetadata, + true, + ); + const toFieldMetadataToUpdate = + await this.fieldMetadataRepository.findOneBy( + toFieldMetadataUpdateCriteria, + ); + const toFieldMetadata = await this.fieldMetadataRepository.save({ + ...toFieldMetadataToUpdate, + ...toFieldMetadataUpdateData, + }); + + const fromFieldMetadataUpdateCriteria = { + standardId: + CUSTOM_OBJECT_STANDARD_FIELD_IDS[relatedObjectMetadata.namePlural], + objectMetadataId: updatedObjectMetadata.id, + workspaceId: workspaceId, + }; + const fromFieldMetadataUpdateData = this.buildFromFieldMetadata( + workspaceId, + updatedObjectMetadata, + relatedObjectMetadata, + true, + ); + const fromFieldMetadataToUpdate = + await this.fieldMetadataRepository.findOneBy( + fromFieldMetadataUpdateCriteria, + ); + const fromFieldMetadata = await this.fieldMetadataRepository.save({ + ...fromFieldMetadataToUpdate, + ...fromFieldMetadataUpdateData, + }); + + const foreignKeyFieldMetadataUpdateCriteria = { + standardId: createForeignKeyDeterministicUuid({ + objectId: updatedObjectMetadata.id, + standardId: + STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom, + }), + objectMetadataId: relatedObjectMetadata.id, + workspaceId: workspaceId, + }; + const foreignKeyFieldMetadataUpdateData = this.buildForeignKeyFieldMetadata( + workspaceId, + updatedObjectMetadata, + relatedObjectMetadata, + FieldMetadataType.UUID, + undefined, + true, + ); + const foreignKeyFieldMetadataToUpdate = + await this.fieldMetadataRepository.findOneBy( + foreignKeyFieldMetadataUpdateCriteria, + ); + const foreignKeyFieldMetadata = await this.fieldMetadataRepository.save({ + ...foreignKeyFieldMetadataToUpdate, + ...foreignKeyFieldMetadataUpdateData, + }); + + return { + relatedObjectMetadata, + foreignKeyFieldMetadata, + toFieldMetadata, + fromFieldMetadata, + }; + } + + private buildFromFieldMetadata( + workspaceId: string, + objectMetadata: ObjectMetadataEntity, relatedObjectMetadata: ObjectMetadataEntity, + isUpdate = false, ) { const relationObjectMetadataNamePlural = relatedObjectMetadata.namePlural; const { description } = buildDescriptionForRelationFieldMetadataOnFromField( { relationObjectMetadataNamePlural, - targetObjectLabelSingular: createdObjectMetadata.labelSingular, + targetObjectLabelSingular: objectMetadata.labelSingular, }, ); return { - standardId: - CUSTOM_OBJECT_STANDARD_FIELD_IDS[relationObjectMetadataNamePlural], - objectMetadataId: createdObjectMetadata.id, - workspaceId: workspaceId, - isCustom: false, - isActive: true, - isSystem: true, - type: FieldMetadataType.RELATION, - name: relatedObjectMetadata.namePlural, - label: capitalize(relationObjectMetadataNamePlural), description, - icon: - STANDARD_OBJECT_ICONS[relatedObjectMetadata.nameSingular] || - 'IconBuildingSkyscraper', - isNullable: true, + ...(!isUpdate + ? { + standardId: + CUSTOM_OBJECT_STANDARD_FIELD_IDS[ + relationObjectMetadataNamePlural + ], + objectMetadataId: objectMetadata.id, + workspaceId: workspaceId, + isCustom: false, + isActive: true, + isSystem: true, + type: FieldMetadataType.RELATION, + name: relatedObjectMetadata.namePlural, + label: capitalize(relationObjectMetadataNamePlural), + description, + icon: + STANDARD_OBJECT_ICONS[relatedObjectMetadata.nameSingular] || + 'IconBuildingSkyscraper', + isNullable: true, + } + : {}), }; } - private createToField( + private buildToFieldMetadata( workspaceId: string, - createdObjectMetadata: ObjectMetadataEntity, + objectMetadata: ObjectMetadataEntity, relatedObjectMetadata: ObjectMetadataEntity, + isUpdate = false, ) { const customStandardFieldId = STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom; @@ -193,35 +308,96 @@ export class ObjectMetadataRelationService { const { description } = buildDescriptionForRelationFieldMetadataOnToField({ relationObjectMetadataNamePlural: relatedObjectMetadata.namePlural, - targetObjectLabelSingular: createdObjectMetadata.labelSingular, + targetObjectLabelSingular: objectMetadata.labelSingular, }); return { - standardId: createRelationDeterministicUuid({ - objectId: createdObjectMetadata.id, - standardId: customStandardFieldId, - }), - objectMetadataId: relatedObjectMetadata.id, - workspaceId: workspaceId, - isCustom: false, - isActive: true, - isSystem: true, - type: FieldMetadataType.RELATION, - name: createdObjectMetadata.nameSingular, - label: createdObjectMetadata.labelSingular, + name: objectMetadata.nameSingular, + label: objectMetadata.labelSingular, description, - icon: 'IconBuildingSkyscraper', - isNullable: true, + ...(!isUpdate + ? { + standardId: createRelationDeterministicUuid({ + objectId: objectMetadata.id, + standardId: customStandardFieldId, + }), + objectMetadataId: relatedObjectMetadata.id, + workspaceId: workspaceId, + isCustom: false, + isActive: true, + isSystem: true, + type: FieldMetadataType.RELATION, + name: objectMetadata.nameSingular, + label: objectMetadata.labelSingular, + description, + icon: 'IconBuildingSkyscraper', + isNullable: true, + } + : {}), }; } - private async createRelationMetadata( + private buildForeignKeyFieldMetadata( + workspaceId: string, + objectMetadata: ObjectMetadataEntity, + relatedObjectMetadata: ObjectMetadataEntity, + objectPrimaryKeyType: FieldMetadataType, + objectPrimaryKeyFieldSettings: + | FieldMetadataSettings + | undefined, + isUpdate = false, + ) { + const customStandardFieldId = + STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom; + + if (!customStandardFieldId) { + throw new Error( + `Custom standard field ID not found for ${relatedObjectMetadata.nameSingular}`, + ); + } + + const { name, label, description } = + buildNameLabelAndDescriptionForForeignKeyFieldMetadata({ + targetObjectNameSingular: objectMetadata.nameSingular, + targetObjectLabelSingular: objectMetadata.labelSingular, + relatedObjectLabelSingular: relatedObjectMetadata.labelSingular, + }); + + return { + name, + label, + description, + ...(!isUpdate + ? { + standardId: createForeignKeyDeterministicUuid({ + objectId: objectMetadata.id, + standardId: customStandardFieldId, + }), + objectMetadataId: relatedObjectMetadata.id, + workspaceId: workspaceId, + isCustom: false, + isActive: true, + type: objectPrimaryKeyType, + name, + label, + description, + icon: undefined, + isNullable: true, + isSystem: true, + defaultValue: undefined, + settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true }, + } + : {}), + }; + } + + private async createRelationMetadataFromFieldMetadatas( workspaceId: string, createdObjectMetadata: ObjectMetadataEntity, relatedObjectMetadata: ObjectMetadataEntity, - relationFieldMetadata: FieldMetadataEntity[], + relationFieldMetadataCollection: FieldMetadataEntity[], ) { - const relationFieldMetadataMap = relationFieldMetadata.reduce( + const relationFieldMetadataMap = relationFieldMetadataCollection.reduce( (acc, fieldMetadata: FieldMetadataEntity) => { if (fieldMetadata.type === FieldMetadataType.RELATION) { acc[fieldMetadata.objectMetadataId] = fieldMetadata; @@ -247,7 +423,10 @@ export class ObjectMetadataRelationService { ]); } - async updateObjectRelationships(objectMetadataId: string, isActive: boolean) { + async updateObjectRelationshipsActivationStatus( + objectMetadataId: string, + isActive: boolean, + ) { const affectedRelations = await this.relationMetadataRepository.find({ where: [ { fromObjectMetadataId: objectMetadataId },