diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 19e95e9607..e5586d2868 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1503,7 +1503,6 @@ export enum FeatureFlagKey { IS_RECORD_PAGE_LAYOUT_ENABLED = 'IS_RECORD_PAGE_LAYOUT_ENABLED', IS_SSE_DB_EVENTS_ENABLED = 'IS_SSE_DB_EVENTS_ENABLED', IS_TASK_TARGET_MIGRATED = 'IS_TASK_TARGET_MIGRATED', - IS_TIMELINE_ACTIVITY_MIGRATED = 'IS_TIMELINE_ACTIVITY_MIGRATED', IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED' } diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index e95f7527b9..43787e83d9 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -1475,7 +1475,6 @@ export enum FeatureFlagKey { IS_RECORD_PAGE_LAYOUT_ENABLED = 'IS_RECORD_PAGE_LAYOUT_ENABLED', IS_SSE_DB_EVENTS_ENABLED = 'IS_SSE_DB_EVENTS_ENABLED', IS_TASK_TARGET_MIGRATED = 'IS_TASK_TARGET_MIGRATED', - IS_TIMELINE_ACTIVITY_MIGRATED = 'IS_TIMELINE_ACTIVITY_MIGRATED', IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED' } diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts index 6a1a92642c..5bea7d908d 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts +++ b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts @@ -1,28 +1,17 @@ import { useLinkedObjectsTitle } from '@/activities/timeline-activities/hooks/useLinkedObjectsTitle'; import { type TimelineActivity } from '@/activities/timeline-activities/types/TimelineActivity'; import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; -import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { capitalize, isDefined } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; // do we need to test this? export const useTimelineActivities = ( targetableObject: ActivityTargetableObject, ) => { - const isTimelineActivityMigrated = useIsFeatureEnabled( - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, - ); - - const targetableObjectFieldIdName = isTimelineActivityMigrated - ? `target${capitalize(targetableObject.targetObjectNameSingular)}Id` - : getActivityTargetObjectFieldIdName({ - nameSingular: targetableObject.targetObjectNameSingular, - }); + const targetableObjectFieldIdName = `target${capitalize(targetableObject.targetObjectNameSingular)}Id`; const { objectMetadataItem: timelineActivityMetadata } = useObjectMetadataItem({ diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index cb11410b3b..81ee66e1e5 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -10,7 +10,6 @@ export enum FeatureFlagKey { IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED', IS_EMAILING_DOMAIN_ENABLED = 'IS_EMAILING_DOMAIN_ENABLED', IS_DASHBOARD_V2_ENABLED = 'IS_DASHBOARD_V2_ENABLED', - IS_TIMELINE_ACTIVITY_MIGRATED = 'IS_TIMELINE_ACTIVITY_MIGRATED', IS_ATTACHMENT_MIGRATED = 'IS_ATTACHMENT_MIGRATED', IS_NOTE_TARGET_MIGRATED = 'IS_NOTE_TARGET_MIGRATED', IS_TASK_TARGET_MIGRATED = 'IS_TASK_TARGET_MIGRATED', diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts index 20f975fcd3..b567f912ed 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts @@ -90,10 +90,7 @@ export const buildDefaultRelationFlatFieldMetadatasForCustomObject = ({ objectMetadataNameSingular, ); const isFeatureFlagEnabled = - (objectMetadataNameSingular === 'timelineActivity' && - existingFeatureFlagsMap[ - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED - ]) || + objectMetadataNameSingular === 'timelineActivity' || (objectMetadataNameSingular === 'attachment' && existingFeatureFlagsMap[FeatureFlagKey.IS_ATTACHMENT_MIGRATED]) || (objectMetadataNameSingular === 'noteTarget' && diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index 5495ff5f13..515e95a668 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -56,11 +56,6 @@ export const seedFeatureFlags = async ({ workspaceId: workspaceId, value: true, }, - { - key: FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, - workspaceId: workspaceId, - value: true, - }, { key: FeatureFlagKey.IS_ATTACHMENT_MIGRATED, workspaceId: workspaceId, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/constant/default-feature-flags.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/constant/default-feature-flags.ts index 34fd272252..3c06510065 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/constant/default-feature-flags.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/constant/default-feature-flags.ts @@ -1,7 +1,6 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; export const DEFAULT_FEATURE_FLAGS = [ - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, FeatureFlagKey.IS_ATTACHMENT_MIGRATED, FeatureFlagKey.IS_NOTE_TARGET_MIGRATED, FeatureFlagKey.IS_TASK_TARGET_MIGRATED, diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/listeners/calendar-event-participant.listener.ts b/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/listeners/calendar-event-participant.listener.ts index 60b605051d..9dabdf9dbd 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/listeners/calendar-event-participant.listener.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/listeners/calendar-event-participant.listener.ts @@ -5,7 +5,6 @@ import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; @@ -75,17 +74,10 @@ export class CalendarEventParticipantListener { .filter(isDefined); }); - const isFeatureFlagTimelineActivityMigrated = - await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, - batchEvent.workspaceId, - ); - await this.timelineActivityRepository.upsertTimelineActivities({ objectSingularName: 'person', workspaceId: batchEvent.workspaceId, payloads: timelineActivityPayloads.filter(isDefined), - isFeatureFlagTimelineActivityMigrated, }); } } diff --git a/packages/twenty-server/src/modules/messaging/message-participant-manager/listeners/message-participant.listener.ts b/packages/twenty-server/src/modules/messaging/message-participant-manager/listeners/message-participant.listener.ts index 2da29730a5..320cfe9c77 100644 --- a/packages/twenty-server/src/modules/messaging/message-participant-manager/listeners/message-participant.listener.ts +++ b/packages/twenty-server/src/modules/messaging/message-participant-manager/listeners/message-participant.listener.ts @@ -5,7 +5,6 @@ import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; @@ -74,17 +73,10 @@ export class MessageParticipantListener { .filter(isDefined); }); - const isFeatureFlagTimelineActivityMigrated = - await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, - batchEvent.workspaceId, - ); - await this.timelineActivityRepository.upsertTimelineActivities({ objectSingularName: 'person', workspaceId: batchEvent.workspaceId, payloads: timelineActivityPayloads.filter(isDefined), - isFeatureFlagTimelineActivityMigrated, }); } } diff --git a/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts b/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts index c9460ac2f5..c904b58980 100644 --- a/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts +++ b/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts @@ -16,7 +16,6 @@ type TimelineActivityPayloadWorkspaceIdAndObjectSingularName = { })[]; workspaceId: string; objectSingularName: string; - isFeatureFlagTimelineActivityMigrated: boolean; }; @Injectable() @@ -29,7 +28,6 @@ export class TimelineActivityRepository { objectSingularName, workspaceId, payloads, - isFeatureFlagTimelineActivityMigrated, }: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) { const authContext = buildSystemAuthContext(workspaceId); @@ -38,7 +36,6 @@ export class TimelineActivityRepository { objectSingularName, workspaceId, payloads, - isFeatureFlagTimelineActivityMigrated, }); const payloadsWithDiff = payloads @@ -61,10 +58,7 @@ export class TimelineActivityRepository { []; const timelineActivityPropertyName = - await this.getTimelineActivityPropertyName( - objectSingularName, - isFeatureFlagTimelineActivityMigrated, - ); + await this.getTimelineActivityPropertyName(objectSingularName); for (const payload of payloadsWithDiff) { const recentTimelineActivity = recentTimelineActivities.find( @@ -98,7 +92,6 @@ export class TimelineActivityRepository { objectSingularName, payloads: payloadsToInsert, workspaceId, - isFeatureFlagTimelineActivityMigrated, }); }, authContext); } @@ -107,7 +100,6 @@ export class TimelineActivityRepository { objectSingularName, workspaceId, payloads, - isFeatureFlagTimelineActivityMigrated, }: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) { const timelineActivityTypeORMRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -121,10 +113,7 @@ export class TimelineActivityRepository { const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000); const timelineActivityPropertyName = - await this.getTimelineActivityPropertyName( - objectSingularName, - isFeatureFlagTimelineActivityMigrated, - ); + await this.getTimelineActivityPropertyName(objectSingularName); const whereConditions: Record = { [timelineActivityPropertyName]: In( @@ -148,7 +137,6 @@ export class TimelineActivityRepository { objectSingularName, workspaceId, payloads, - isFeatureFlagTimelineActivityMigrated, }: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) { if (payloads.length === 0) { return; @@ -164,10 +152,7 @@ export class TimelineActivityRepository { ); const timelineActivityPropertyName = - await this.getTimelineActivityPropertyName( - objectSingularName, - isFeatureFlagTimelineActivityMigrated, - ); + await this.getTimelineActivityPropertyName(objectSingularName); return timelineActivityTypeORMRepository.insert( payloads.map((payload) => ({ @@ -208,12 +193,7 @@ export class TimelineActivityRepository { }); } - private async getTimelineActivityPropertyName( - objectSingularName: string, - isFeatureFlagTimelineActivityMigrated: boolean, - ) { - return isFeatureFlagTimelineActivityMigrated - ? `${buildTimelineActivityRelatedMorphFieldMetadataName(objectSingularName)}Id` - : `${objectSingularName}Id`; + private async getTimelineActivityPropertyName(objectSingularName: string) { + return `${buildTimelineActivityRelatedMorphFieldMetadataName(objectSingularName)}Id`; } } diff --git a/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts b/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts index 6325d14ba3..174f1e2b4c 100644 --- a/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts +++ b/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts @@ -1,12 +1,11 @@ import { Injectable } from '@nestjs/common'; +import { type ObjectRecordBaseEvent } from 'twenty-shared/database-events'; import { type ObjectRecord } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { In } from 'typeorm'; -import { type ObjectRecordBaseEvent } from 'twenty-shared/database-events'; import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; @@ -19,6 +18,7 @@ import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.work import { TimelineActivityRepository } from 'src/modules/timeline/repositories/timeline-activity.repository'; import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity'; import { type TimelineActivityPayload } from 'src/modules/timeline/types/timeline-activity-payload'; +import { extractObjectSingularNameFromTargetColumnName } from 'src/modules/timeline/utils/extract-object-singular-name-from-target-column-name.util'; type ActivityType = 'note' | 'task'; @@ -67,7 +67,7 @@ export class TimelineActivityService { const payloadsByObjectSingularName = timelineActivitiesPayloads.reduce( (acc, payload) => { const computedObjectSingularName = - payload.overrideObjectSingularName ?? objectSingularName; + payload.objectSingularName ?? objectSingularName; acc[computedObjectSingularName] = [ ...(acc[computedObjectSingularName] || []), @@ -79,18 +79,11 @@ export class TimelineActivityService { {} as Record, ); - const isFeatureFlagTimelineActivityMigrated = - await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, - workspaceId, - ); - for (const objectSingularName in payloadsByObjectSingularName) { await this.timelineActivityRepository.upsertTimelineActivities({ objectSingularName, workspaceId, payloads: payloadsByObjectSingularName[objectSingularName], - isFeatureFlagTimelineActivityMigrated, }); } } @@ -255,7 +248,7 @@ export class TimelineActivityService { linkedRecordId: activityId, linkedObjectMetadataId: objectMetadata.id, properties: event.properties, - overrideObjectSingularName: objectMetadata.nameSingular, + objectSingularName: objectMetadata.nameSingular, } satisfies TimelineActivityPayload; }); }) @@ -361,9 +354,12 @@ export class TimelineActivityService { targetColumnName ]; + const objectSingularName = + extractObjectSingularNameFromTargetColumnName(targetColumnName); + return { name: `linked-${activityType}.${action}`, - overrideObjectSingularName: targetColumnName.replace(/Id$/, ''), + objectSingularName, recordId, linkedRecordCachedName: activity.title, linkedRecordId: activity.id, diff --git a/packages/twenty-server/src/modules/timeline/types/timeline-activity-payload.ts b/packages/twenty-server/src/modules/timeline/types/timeline-activity-payload.ts index c4ab8ea17a..109cf21fd6 100644 --- a/packages/twenty-server/src/modules/timeline/types/timeline-activity-payload.ts +++ b/packages/twenty-server/src/modules/timeline/types/timeline-activity-payload.ts @@ -8,5 +8,5 @@ export type TimelineActivityPayload = { workspaceMemberId?: string; name: string; recordId: string; - overrideObjectSingularName?: string; + objectSingularName?: string; }; diff --git a/packages/twenty-server/src/modules/timeline/utils/__tests__/extract-object-singular-name-from-target-column-name.util.spec.ts b/packages/twenty-server/src/modules/timeline/utils/__tests__/extract-object-singular-name-from-target-column-name.util.spec.ts new file mode 100644 index 0000000000..cebe316cba --- /dev/null +++ b/packages/twenty-server/src/modules/timeline/utils/__tests__/extract-object-singular-name-from-target-column-name.util.spec.ts @@ -0,0 +1,27 @@ +import { extractObjectSingularNameFromTargetColumnName } from 'src/modules/timeline/utils/extract-object-singular-name-from-target-column-name.util'; + +describe('extractObjectSingularNameFromTargetColumnName', () => { + it('should extract "person" from "targetPersonId"', () => { + expect( + extractObjectSingularNameFromTargetColumnName('targetPersonId'), + ).toBe('person'); + }); + + it('should extract "arrowTarget" from "targetArrowTargetId"', () => { + expect( + extractObjectSingularNameFromTargetColumnName('targetArrowTargetId'), + ).toBe('arrowTarget'); + }); + + it('should extract "idCard" from "targetIdCardId"', () => { + expect( + extractObjectSingularNameFromTargetColumnName('targetIdCardId'), + ).toBe('idCard'); + }); + + it('should extract "cardId" from "targetCardIdId"', () => { + expect( + extractObjectSingularNameFromTargetColumnName('targetCardIdId'), + ).toBe('cardId'); + }); +}); diff --git a/packages/twenty-server/src/modules/timeline/utils/extract-object-singular-name-from-target-column-name.util.ts b/packages/twenty-server/src/modules/timeline/utils/extract-object-singular-name-from-target-column-name.util.ts new file mode 100644 index 0000000000..1a74c8f780 --- /dev/null +++ b/packages/twenty-server/src/modules/timeline/utils/extract-object-singular-name-from-target-column-name.util.ts @@ -0,0 +1,23 @@ +const ID_SUFFIX = 'Id'; +const TARGET_PREFIX = 'target'; + +export const extractObjectSingularNameFromTargetColumnName = ( + targetColumnName: string, +): string => { + let result = targetColumnName; + + if (result.endsWith(ID_SUFFIX)) { + result = result.slice(0, -ID_SUFFIX.length); + } + + if ( + result.startsWith(TARGET_PREFIX) && + result.length > TARGET_PREFIX.length + ) { + result = + result.charAt(TARGET_PREFIX.length).toLowerCase() + + result.slice(TARGET_PREFIX.length + 1); + } + + return result; +};