diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-cached-field-metadata-entity-to-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-cached-field-metadata-entity-to-flat-field-metadata.util.ts index 5802c2e261..18fe18810b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-cached-field-metadata-entity-to-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-cached-field-metadata-entity-to-flat-field-metadata.util.ts @@ -83,6 +83,10 @@ export const fromCachedFieldMetadataEntityToFlatFieldMetadata = < !isCachedFieldMetadataEntityOfType( relationTargetCachedFieldMetadata, FieldMetadataType.RELATION, + ) && + !isCachedFieldMetadataEntityOfType( + relationTargetCachedFieldMetadata, + FieldMetadataType.MORPH_RELATION, ) ) { throw new WorkspaceMetadataCacheException( diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/dev-seeder.module.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/dev-seeder.module.ts index 64ff1f712a..1b2d335d9b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/dev-seeder.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/dev-seeder.module.ts @@ -12,6 +12,7 @@ import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadat import { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module'; import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; +import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module'; import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; @@ -38,6 +39,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp TypeOrmModule.forFeature([Workspace, ObjectMetadataEntity]), ObjectPermissionModule, WorkspacePermissionsCacheModule, + WorkspaceMetadataCacheModule, ], exports: [DevSeederService], providers: [ diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-relation-field-seeds.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-relation-field-seeds.constant.ts new file mode 100644 index 0000000000..abe444c2c7 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-relation-field-seeds.constant.ts @@ -0,0 +1,23 @@ +import { FieldMetadataType, RelationType } from 'twenty-shared/types'; + +import { type FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type'; + +export const PET_CUSTOM_RELATION_FIELD_SEEDS: (FieldMetadataSeed & { + targetObjectMetadataNames: string[]; +})[] = [ + { + type: FieldMetadataType.MORPH_RELATION, + label: 'Owner of the Pet (rocket or survey)', + name: 'owner', + icon: 'IconRelationManyToOne', + morphRelationsCreationPayload: [ + { + type: RelationType.MANY_TO_ONE, + targetObjectMetadataId: 'to-be-resolved-later', + targetFieldLabel: 'Owned by', + targetFieldIcon: 'IconRelationOneToMany', + }, + ], + targetObjectMetadataNames: ['surveyResult', 'rocket'], + }, +]; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service.ts index fd30b4dfa0..2ff39f2911 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service.ts @@ -1,11 +1,15 @@ import { Injectable } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; +import { isDefined } from 'twenty-shared/utils'; import { DataSource } from 'typeorm'; import { type DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; +import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; +import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; +import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { SEED_APPLE_WORKSPACE_ID, SEED_YCOMBINATOR_WORKSPACE_ID, @@ -13,6 +17,7 @@ import { import { COMPANY_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/company-custom-field-seeds.constant'; import { PERSON_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/person-custom-field-seeds.constant'; import { PET_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-field-seeds.constant'; +import { PET_CUSTOM_RELATION_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-relation-field-seeds.constant'; import { SURVEY_RESULT_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/survey-results-field-seeds.constant'; import { PET_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/pet-custom-object-seed.constant'; import { ROCKET_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/rocket-custom-object-seed.constant'; @@ -26,6 +31,7 @@ export class DevSeederMetadataService { constructor( private readonly objectMetadataService: ObjectMetadataService, private readonly fieldMetadataService: FieldMetadataService, + private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService, @InjectDataSource() private readonly coreDataSource: DataSource, ) {} @@ -35,6 +41,10 @@ export class DevSeederMetadataService { { objects: { seed: ObjectMetadataSeed; fields?: FieldMetadataSeed[] }[]; fields: { objectName: string; seeds: FieldMetadataSeed[] }[]; + relations?: { + objectName: string; + seeds: (FieldMetadataSeed & { targetObjectMetadataNames: string[] })[]; + }[]; } > = { [SEED_APPLE_WORKSPACE_ID]: { @@ -50,6 +60,9 @@ export class DevSeederMetadataService { { objectName: 'company', seeds: COMPANY_CUSTOM_FIELD_SEEDS }, { objectName: 'person', seeds: PERSON_CUSTOM_FIELD_SEEDS }, ], + relations: [ + { objectName: 'pet', seeds: PET_CUSTOM_RELATION_FIELD_SEEDS }, + ], }, [SEED_YCOMBINATOR_WORKSPACE_ID]: { objects: [ @@ -169,4 +182,108 @@ export class DevSeederMetadataService { schemaName: dataSourceMetadata.schema, }); } + + public async seedRelations({ workspaceId }: { workspaceId: string }) { + const config = this.workspaceConfigs[workspaceId]; + + if (!config) { + throw new Error( + `Workspace configuration not found for workspaceId: ${workspaceId}`, + ); + } + + if (!config.relations) { + return; + } + + for (const relation of config.relations) { + await this.seedCustomRelation({ + workspaceId, + relation, + }); + } + } + + private async seedCustomRelation({ + workspaceId, + relation, + }: { + workspaceId: string; + relation: { + objectName: string; + seeds: (FieldMetadataSeed & { targetObjectMetadataNames: string[] })[]; + }; + }): Promise { + const { objectMetadataMaps } = + await this.workspaceMetadataCacheService.getExistingOrRecomputeMetadataMaps( + { + workspaceId, + }, + ); + + const relationFieldInputs = this.createFieldInputs({ + relation, + objectMetadataMaps, + workspaceId, + }); + + await this.fieldMetadataService.createMany(relationFieldInputs); + } + + private createFieldInputs({ + relation, + objectMetadataMaps, + workspaceId, + }: { + relation: { + objectName: string; + seeds: (FieldMetadataSeed & { targetObjectMetadataNames: string[] })[]; + }; + objectMetadataMaps: ObjectMetadataMaps; + workspaceId: string; + }): CreateFieldInput[] { + const objectMetadataId = + objectMetadataMaps.idByNameSingular[relation.objectName]; + + if (!isDefined(objectMetadataId)) { + throw new Error( + `Object metadata id not found for: ${relation.objectName}`, + ); + } + + const relationFieldInputs = relation.seeds.map((seed) => ({ + type: seed.type, + label: seed.label, + name: seed.name, + objectMetadataId, + workspaceId, + morphRelationsCreationPayload: seed.targetObjectMetadataNames.map( + (targetObjectMetadataName) => { + const targetObjectMetadataId = + objectMetadataMaps.idByNameSingular[targetObjectMetadataName]; + + if (!isDefined(targetObjectMetadataId)) { + throw new Error( + `Target object metadata id not found for: ${targetObjectMetadataName}`, + ); + } + + if (!isDefined(seed.morphRelationsCreationPayload)) { + throw new Error('Morph relations creation payload is not defined'); + } + + return { + type: seed.morphRelationsCreationPayload[0].type, + targetFieldLabel: + seed.morphRelationsCreationPayload[0].targetFieldLabel, + targetFieldIcon: + seed.morphRelationsCreationPayload[0].targetFieldIcon, + targetObjectMetadataId, + }; + }, + ), + })); + + return relationFieldInputs; + } } diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts index 4f44e9aecd..b29a142070 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts @@ -66,6 +66,10 @@ export class DevSeederService { workspaceId, }); + await this.devSeederMetadataService.seedRelations({ + workspaceId, + }); + await this.devSeederPermissionsService.initPermissions(workspaceId); await this.devSeederDataService.seed({