diff --git a/packages/twenty-server/src/database/commands/database-command.module.ts b/packages/twenty-server/src/database/commands/database-command.module.ts index f4c28d729..4998843a7 100644 --- a/packages/twenty-server/src/database/commands/database-command.module.ts +++ b/packages/twenty-server/src/database/commands/database-command.module.ts @@ -10,7 +10,7 @@ import { WorkspaceModule } from 'src/core/workspace/workspace.module'; import { DataSeedWorkspaceCommand } from 'src/database/commands/data-seed-dev-workspace.command'; import { DataSeedDemoWorkspaceCommand } from 'src/database/commands/data-seed-demo-workspace.command'; import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module'; -import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module'; +import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/workspace-sync-metadata.module'; import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; @Module({ diff --git a/packages/twenty-server/src/workspace/workspace-manager/workspace-manager.module.ts b/packages/twenty-server/src/workspace/workspace-manager/workspace-manager.module.ts index c495de474..3f798e92d 100644 --- a/packages/twenty-server/src/workspace/workspace-manager/workspace-manager.module.ts +++ b/packages/twenty-server/src/workspace/workspace-manager/workspace-manager.module.ts @@ -4,7 +4,7 @@ import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module'; import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module'; -import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module'; +import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/workspace-sync-metadata.module'; import { WorkspaceManagerService } from './workspace-manager.service'; diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts index 3bab78c72..d680d0909 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts @@ -29,6 +29,7 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner { await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( options.workspaceId, ); + await this.workspaceSyncMetadataService.syncStandardObjectsAndFieldsMetadata( dataSourceMetadata.id, options.workspaceId, diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts index 64747784d..31eb92d44 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; -import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module'; +import { WorkspaceSyncMetadataModule } from 'src/workspace/workspace-sync-metadata/workspace-sync-metadata.module'; import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command'; diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/decorators/metadata.decorator.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/decorators/metadata.decorator.ts index 8c4702687..c34c36cce 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/decorators/metadata.decorator.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/decorators/metadata.decorator.ts @@ -31,6 +31,10 @@ export type RelationMetadataDecorator = { inverseSideFieldName?: string; }; +export type GateDecorator = { + featureFlag: string; +}; + function convertClassNameToObjectMetadataName(name: string): string { const classSuffix = 'ObjectMetadata'; let objectName = camelCase(name); @@ -48,6 +52,8 @@ export function ObjectMetadata( return (target) => { const isSystem = Reflect.getMetadata('isSystem', target) || false; + const gate = Reflect.getMetadata('gate', target) || undefined; + const objectName = convertClassNameToObjectMetadataName(target.name); Reflect.defineMetadata( @@ -55,6 +61,7 @@ export function ObjectMetadata( { nameSingular: objectName, ...metadata, + gate, targetTableName: objectName, isSystem, isCustom: false, @@ -82,6 +89,16 @@ export function IsSystem() { }; } +export function Gate(metadata: GateDecorator) { + return function (target: object, fieldKey?: string) { + if (fieldKey) { + Reflect.defineMetadata('gate', metadata, target, fieldKey); + } else { + Reflect.defineMetadata('gate', metadata, target); + } + }; +} + export function FieldMetadata( metadata: FieldMetadataDecorator, ): PropertyDecorator { @@ -94,6 +111,8 @@ export function FieldMetadata( const isSystem = Reflect.getMetadata('isSystem', target, fieldKey) || false; + const gate = Reflect.getMetadata('gate', target, fieldKey) || undefined; + const { joinColumn, ...fieldMetadata } = metadata; Reflect.defineMetadata( @@ -105,6 +124,7 @@ export function FieldMetadata( fieldKey, isNullable, isSystem, + gate, ), ...(joinColumn && fieldMetadata.type === FieldMetadataType.RELATION ? { @@ -119,6 +139,7 @@ export function FieldMetadata( joinColumn, isNullable, true, + gate, ), } : {}), @@ -133,6 +154,7 @@ function generateFieldMetadata( fieldKey: string, isNullable: boolean, isSystem: boolean, + gate: GateDecorator | undefined = undefined, ) { const targetColumnMap = JSON.stringify( generateTargetColumnMap(metadata.type, false, fieldKey), @@ -152,6 +174,7 @@ function generateFieldMetadata( description: metadata.description ?? null, icon: metadata.icon ?? null, defaultValue: defaultValue ? JSON.stringify(defaultValue) : null, + gate, }; } @@ -162,6 +185,8 @@ export function RelationMetadata( const existingRelationMetadata = Reflect.getMetadata('relationMetadata', target.constructor) || []; + const gate = Reflect.getMetadata('gate', target, fieldKey) || undefined; + const objectName = convertClassNameToObjectMetadataName( target.constructor.name, ); @@ -176,6 +201,7 @@ export function RelationMetadata( toObjectNameSingular: metadata.objectName, fromFieldMetadataName: fieldKey, toFieldMetadataName: metadata.inverseSideFieldName ?? objectName, + gate, }, ], target.constructor, diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata.ts index e21a5f25e..5082e1bf1 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata.ts @@ -6,6 +6,7 @@ import { FieldMetadata, IsNullable, RelationMetadata, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata'; import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata'; @@ -18,6 +19,9 @@ import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-meta description: 'A connected account', icon: 'IconAt', }) +@Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', +}) @IsSystem() export class ConnectedAccountObjectMetadata extends BaseObjectMetadata { @FieldMetadata({ diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata.ts index c8947c17c..ae87e1e6d 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata.ts @@ -6,6 +6,7 @@ import { FieldMetadata, IsNullable, RelationMetadata, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata'; import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata'; @@ -18,6 +19,9 @@ import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metada description: 'Message Channels', icon: 'IconMessage', }) +@Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', +}) @IsSystem() export class MessageChannelObjectMetadata extends BaseObjectMetadata { @FieldMetadata({ diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-recipient.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-recipient.object-metadata.ts index 3569eec03..a45455dfe 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-recipient.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-recipient.object-metadata.ts @@ -4,6 +4,7 @@ import { IsSystem, FieldMetadata, IsNullable, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata'; import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata'; @@ -17,6 +18,9 @@ import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-meta description: 'Message Recipients', icon: 'IconUserCircle', }) +@Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', +}) @IsSystem() export class MessageRecipientObjectMetadata extends BaseObjectMetadata { @FieldMetadata({ diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata.ts index 3e1c1edef..ad7f3961d 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata.ts @@ -6,6 +6,7 @@ import { FieldMetadata, IsNullable, RelationMetadata, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata'; import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata'; @@ -18,6 +19,9 @@ import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/sta description: 'Message Thread', icon: 'IconMessage', }) +@Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', +}) @IsSystem() export class MessageThreadObjectMetadata extends BaseObjectMetadata { @FieldMetadata({ diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata.ts index dd238eac4..7b9fa5c9d 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata.ts @@ -6,6 +6,7 @@ import { FieldMetadata, IsNullable, RelationMetadata, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata'; import { MessageRecipientObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-recipient.object-metadata'; @@ -18,6 +19,9 @@ import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metada description: 'Message', icon: 'IconMessage', }) +@Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', +}) @IsSystem() export class MessageObjectMetadata extends BaseObjectMetadata { @FieldMetadata({ diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata.ts index ec1724e63..3839ea7ef 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/person.object-metadata.ts @@ -8,6 +8,7 @@ import { IsNullable, RelationMetadata, IsSystem, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { ActivityTargetObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity-target.object-metadata'; import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata'; @@ -186,6 +187,9 @@ export class PersonObjectMetadata extends BaseObjectMetadata { objectName: 'messageRecipient', inverseSideFieldName: 'person', }) + @Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', + }) @IsNullable() messageRecipients: MessageRecipientObjectMetadata[]; } diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata.ts index 80c461897..e2256f26a 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata.ts @@ -7,6 +7,7 @@ import { FieldMetadata, IsNullable, RelationMetadata, + Gate, } from 'src/workspace/workspace-sync-metadata/decorators/metadata.decorator'; import { ActivityObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/activity.object-metadata'; import { AttachmentObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/attachment.object-metadata'; @@ -163,6 +164,9 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata { objectName: 'connectedAccount', inverseSideFieldName: 'accountOwner', }) + @Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', + }) @IsNullable() connectedAccounts: ConnectedAccountObjectMetadata[]; @@ -177,6 +181,9 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata { objectName: 'messageRecipient', inverseSideFieldName: 'workspaceMember', }) + @Gate({ + featureFlag: 'IS_MESSAGING_ENABLED', + }) @IsNullable() messageRecipients: MessageRecipientObjectMetadata[]; } diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/utils/metadata.parser.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/utils/metadata.parser.ts index 4684c2e27..c62c15b92 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/utils/metadata.parser.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/utils/metadata.parser.ts @@ -9,109 +9,156 @@ export class MetadataParser { metadata: typeof BaseObjectMetadata, workspaceId: string, dataSourceId: string, - ) { + workspaceFeatureFlagsMap: Record, + ): ObjectMetadataEntity | undefined { const objectMetadata = Reflect.getMetadata('objectMetadata', metadata); const fieldMetadata = Reflect.getMetadata('fieldMetadata', metadata); - if (objectMetadata) { - const fields = Object.values(fieldMetadata); - - return { - ...objectMetadata, - workspaceId, - dataSourceId, - fields: fields.map((field: FieldMetadataEntity) => ({ - ...field, - workspaceId, - isSystem: objectMetadata.isSystem || field.isSystem, - defaultValue: field.defaultValue || null, - options: field.options || null, - })), - }; + if (!objectMetadata) { + throw new Error( + `Object metadata decorator not found, can\'t parse ${metadata.name}`, + ); } - return undefined; + if (isGatedAndNotEnabled(objectMetadata, workspaceFeatureFlagsMap)) { + return undefined; + } + + const fields = Object.values(fieldMetadata).filter( + (field) => !isGatedAndNotEnabled(field, workspaceFeatureFlagsMap), + ); + + return { + ...objectMetadata, + workspaceId, + dataSourceId, + fields: fields.map((field: FieldMetadataEntity) => ({ + ...field, + workspaceId, + isSystem: objectMetadata.isSystem || field.isSystem, + defaultValue: field.defaultValue || null, + options: field.options || null, + })), + }; } static parseAllMetadata( metadataCollection: (typeof BaseObjectMetadata)[], workspaceId: string, dataSourceId: string, - ) { - return metadataCollection.map((metadata) => - MetadataParser.parseMetadata(metadata, workspaceId, dataSourceId), - ); + workspaceFeatureFlagsMap: Record, + ): ObjectMetadataEntity[] { + return metadataCollection + .map((metadata) => + MetadataParser.parseMetadata( + metadata, + workspaceId, + dataSourceId, + workspaceFeatureFlagsMap, + ), + ) + .filter( + (metadata): metadata is ObjectMetadataEntity => metadata !== undefined, + ); } static parseRelationMetadata( metadata: typeof BaseObjectMetadata, workspaceId: string, objectMetadataFromDB: Record, + workspaceFeatureFlagsMap: Record, ) { const objectMetadata = Reflect.getMetadata('objectMetadata', metadata); const relationMetadata = Reflect.getMetadata('relationMetadata', metadata); if (!relationMetadata) return []; - return relationMetadata.map((relation) => { - const fromObjectMetadata = - objectMetadataFromDB[relation.fromObjectNameSingular]; - - assert( - fromObjectMetadata, - `Object ${relation.fromObjectNameSingular} not found in DB - for relation defined in class ${objectMetadata.nameSingular}`, + if (!objectMetadata) { + throw new Error( + `Object metadata decorator not found, can\'t parse ${metadata.name}`, ); + } - const toObjectMetadata = - objectMetadataFromDB[relation.toObjectNameSingular]; + if (isGatedAndNotEnabled(objectMetadata, workspaceFeatureFlagsMap)) { + return []; + } - assert( - toObjectMetadata, - `Object ${relation.toObjectNameSingular} not found in DB - for relation defined in class ${objectMetadata.nameSingular}`, - ); + return relationMetadata + .filter( + (relation) => !isGatedAndNotEnabled(relation, workspaceFeatureFlagsMap), + ) + .map((relation) => { + const fromObjectMetadata = + objectMetadataFromDB[relation.fromObjectNameSingular]; - const fromFieldMetadata = - fromObjectMetadata?.fields[relation.fromFieldMetadataName]; + assert( + fromObjectMetadata, + `Object ${relation.fromObjectNameSingular} not found in DB + for fromRelation defined in class ${objectMetadata.nameSingular}`, + ); - assert( - fromFieldMetadata, - `Field ${relation.fromFieldMetadataName} not found in object ${relation.fromObjectNameSingular} - for relation defined in class ${objectMetadata.nameSingular}`, - ); + const toObjectMetadata = + objectMetadataFromDB[relation.toObjectNameSingular]; - const toFieldMetadata = - toObjectMetadata?.fields[relation.toFieldMetadataName]; + assert( + toObjectMetadata, + `Object ${relation.toObjectNameSingular} not found in DB + for toRelation defined in class ${objectMetadata.nameSingular}`, + ); - assert( - toFieldMetadata, - `Field ${relation.toFieldMetadataName} not found in object ${relation.toObjectNameSingular} - for relation defined in class ${objectMetadata.nameSingular}`, - ); + const fromFieldMetadata = + fromObjectMetadata?.fields[relation.fromFieldMetadataName]; - return { - relationType: relation.type, - fromObjectMetadataId: fromObjectMetadata?.id, - toObjectMetadataId: toObjectMetadata?.id, - fromFieldMetadataId: fromFieldMetadata?.id, - toFieldMetadataId: toFieldMetadata?.id, - workspaceId, - }; - }); + assert( + fromFieldMetadata, + `Field ${relation.fromFieldMetadataName} not found in object ${relation.fromObjectNameSingular} + for fromRelation defined in class ${objectMetadata.nameSingular}`, + ); + + const toFieldMetadata = + toObjectMetadata?.fields[relation.toFieldMetadataName]; + + assert( + toFieldMetadata, + `Field ${relation.toFieldMetadataName} not found in object ${relation.toObjectNameSingular} + for toRelation defined in class ${objectMetadata.nameSingular}`, + ); + + return { + relationType: relation.type, + fromObjectMetadataId: fromObjectMetadata?.id, + toObjectMetadataId: toObjectMetadata?.id, + fromFieldMetadataId: fromFieldMetadata?.id, + toFieldMetadataId: toFieldMetadata?.id, + workspaceId, + }; + }); } static parseAllRelations( metadataCollection: (typeof BaseObjectMetadata)[], workspaceId: string, objectMetadataFromDB: Record, + workspaceFeatureFlagsMap: Record, ) { return metadataCollection.flatMap((metadata) => MetadataParser.parseRelationMetadata( metadata, workspaceId, objectMetadataFromDB, + workspaceFeatureFlagsMap, ), ); } } + +function isGatedAndNotEnabled( + metadata, + workspaceFeatureFlagsMap: Record, +): boolean { + const featureFlagValue = + metadata.gate?.featureFlag && + workspaceFeatureFlagsMap[metadata.gate.featureFlag]; + + return metadata.gate?.featureFlag !== undefined && !featureFlagValue; +} diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync-metadata.module.ts similarity index 90% rename from packages/twenty-server/src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module.ts rename to packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync-metadata.module.ts index 6b993f97c..a00ab068e 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/worksapce-sync-metadata.module.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync-metadata.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity'; import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity'; import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity'; @@ -22,6 +23,7 @@ import { WorkspaceSyncMetadataService } from 'src/workspace/workspace-sync-metad ], 'metadata', ), + TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), ], exports: [WorkspaceSyncMetadataService], providers: [WorkspaceSyncMetadataService], diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync.metadata.service.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync.metadata.service.ts index 6a709e19f..0feecc1d5 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync.metadata.service.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/workspace-sync.metadata.service.ts @@ -16,9 +16,9 @@ import { } from 'src/metadata/relation-metadata/relation-metadata.entity'; import { MetadataParser } from 'src/workspace/workspace-sync-metadata/utils/metadata.parser'; import { - mapObjectMetadataByUniqueIdentifier, filterIgnoredProperties, convertStringifiedFieldsToJSON, + mapObjectMetadataByUniqueIdentifier, } from 'src/workspace/workspace-sync-metadata/utils/sync-metadata.util'; import { standardObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects'; import { @@ -29,6 +29,7 @@ import { } from 'src/metadata/workspace-migration/workspace-migration.entity'; import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory'; import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service'; +import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity'; @Injectable() export class WorkspaceSyncMetadataService { @@ -44,6 +45,8 @@ export class WorkspaceSyncMetadataService { private readonly relationMetadataRepository: Repository, @InjectRepository(WorkspaceMigrationEntity, 'metadata') private readonly workspaceMigrationRepository: Repository, + @InjectRepository(FeatureFlagEntity, 'core') + private readonly featureFlagRepository: Repository, ) {} /** @@ -58,13 +61,27 @@ export class WorkspaceSyncMetadataService { dataSourceId: string, workspaceId: string, ) { - const standardObjects = MetadataParser.parseAllMetadata( - standardObjectMetadata, - workspaceId, - dataSourceId, - ); - try { + const workspaceFeatureFlags = await this.featureFlagRepository.find({ + where: { workspaceId }, + }); + + const workspaceFeatureFlagsMap = workspaceFeatureFlags.reduce( + (result, currentFeatureFlag) => { + result[currentFeatureFlag.key] = currentFeatureFlag.value; + + return result; + }, + {}, + ); + + const standardObjects = MetadataParser.parseAllMetadata( + standardObjectMetadata, + workspaceId, + dataSourceId, + workspaceFeatureFlagsMap, + ); + const objectsInDB = await this.objectMetadataRepository.find({ where: { workspaceId, dataSourceId, isCustom: false }, relations: ['fields'], @@ -244,7 +261,11 @@ export class WorkspaceSyncMetadataService { // We run syncRelationMetadata after everything to ensure that all objects and fields are // in the DB before creating relations. - await this.syncRelationMetadata(workspaceId, dataSourceId); + await this.syncRelationMetadata( + workspaceId, + dataSourceId, + workspaceFeatureFlagsMap, + ); // Execute migrations await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( @@ -258,6 +279,7 @@ export class WorkspaceSyncMetadataService { private async syncRelationMetadata( workspaceId: string, dataSourceId: string, + workspaceFeatureFlagsMap: Record, ) { const objectsInDB = await this.objectMetadataRepository.find({ where: { workspaceId, dataSourceId, isCustom: false }, @@ -268,13 +290,8 @@ export class WorkspaceSyncMetadataService { standardObjectMetadata, workspaceId, objectsInDBByName, - ).reduce((result, currentObject) => { - const key = `${currentObject.fromObjectMetadataId}->${currentObject.fromFieldMetadataId}`; - - result[key] = currentObject; - - return result; - }, {}); + workspaceFeatureFlagsMap, + ); // TODO: filter out custom relations once isCustom has been added to relationMetadata table const relationsInDB = await this.relationMetadataRepository.find({