TimelineActivity migration to morph (#15652)
# TimelineActivity migration to morph - Creates `timelineActivities2` relations on Company, Dashboard, Note, Opportunity, Person, Task, Workflow, WorkflowRun, and WorkflowVersion entities with proper metadata and cascade delete behavior. It was required to create standard fields as well since the mapObjectMetadataByUniqueIdentifier needs it. otherwise the fields won't be considered - Feature Flag `IS_TIMELINE_ACTIVITY_MIGRATED` necessary to have the two states in parallel. It is used as a stamp once the migration has been run - Migration is done using the coreDataSource. Why ? even though is unsafe to use, the first implementation of the migration took forever on each workspace. See [this commit](https://github.com/twentyhq/twenty/pull/15652/commits/477011e8d7d4c580f79ba7ec4a8fb002a3ec86b2) The plan for this complex migration is as follows :  Note: we will need to rename fields in the release 1.12 (there is no easy way to do all this in one release)
This commit is contained in:
+1
@@ -272,6 +272,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
workflowId: string | null;
|
||||
|
||||
// todo: remove this decorator and the custom field
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
+16
-1
@@ -3,10 +3,16 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||
import { GetDataFromCacheWithRecomputeService } from 'src/engine/workspace-cache-storage/services/get-data-from-cache-with-recompute.service';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { CalendarEventParticipantMatchParticipantJob } from 'src/modules/calendar/calendar-event-participant-manager/jobs/calendar-event-participant-match-participant.job';
|
||||
import { CalendarEventParticipantPersonListener } from 'src/modules/calendar/calendar-event-participant-manager/listeners/calendar-event-participant-person.listener';
|
||||
@@ -20,10 +26,15 @@ import { MatchParticipantModule } from 'src/modules/match-participant/match-part
|
||||
imports: [
|
||||
WorkspaceDataSourceModule,
|
||||
WorkspaceModule,
|
||||
TypeOrmModule.forFeature([ObjectMetadataEntity, FieldMetadataEntity]),
|
||||
TypeOrmModule.forFeature([
|
||||
ObjectMetadataEntity,
|
||||
FieldMetadataEntity,
|
||||
FeatureFlagEntity,
|
||||
]),
|
||||
NestjsQueryTypeOrmModule.forFeature([WorkspaceEntity]),
|
||||
ContactCreationManagerModule,
|
||||
MatchParticipantModule,
|
||||
WorkspaceCacheModule,
|
||||
],
|
||||
providers: [
|
||||
CalendarEventParticipantService,
|
||||
@@ -31,6 +42,10 @@ import { MatchParticipantModule } from 'src/modules/match-participant/match-part
|
||||
CalendarEventParticipantListener,
|
||||
CalendarEventParticipantPersonListener,
|
||||
CalendarEventParticipantWorkspaceMemberListener,
|
||||
FeatureFlagService,
|
||||
WorkspaceFeatureFlagsMapCacheService,
|
||||
WorkspaceCacheStorageService,
|
||||
GetDataFromCacheWithRecomputeService,
|
||||
],
|
||||
exports: [CalendarEventParticipantService],
|
||||
})
|
||||
|
||||
+11
-1
@@ -5,12 +5,14 @@ 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';
|
||||
import { CustomWorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/custom-workspace-batch-event.type';
|
||||
import { type CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { TimelineActivityRepository } from 'src/modules/timeline/repositories/timeline-activity.repository';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
import { CustomWorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/custom-workspace-batch-event.type';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventParticipantListener {
|
||||
@@ -19,6 +21,7 @@ export class CalendarEventParticipantListener {
|
||||
private readonly timelineActivityRepository: TimelineActivityRepository,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
@OnCustomBatchEvent('calendarEventParticipant_matched')
|
||||
@@ -72,10 +75,17 @@ 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -266,6 +266,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
description: msg`Timeline Activities linked to the company`,
|
||||
icon: 'IconIconTimelineEvent',
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetCompany',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
|
||||
+1
@@ -97,6 +97,7 @@ export class DashboardWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
label: msg`Timeline Activities`,
|
||||
description: msg`Timeline activities linked to the dashboard`,
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetDashboard',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { FieldMetadataType, RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
import { FieldMetadataType, RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
@@ -237,6 +237,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceIsNullable()
|
||||
viewId: string;
|
||||
|
||||
// todo: remove this decorator and the custom field
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
+11
-1
@@ -5,12 +5,14 @@ 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';
|
||||
import { CustomWorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/custom-workspace-batch-event.type';
|
||||
import { type MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
|
||||
import { TimelineActivityRepository } from 'src/modules/timeline/repositories/timeline-activity.repository';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
import { CustomWorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/custom-workspace-batch-event.type';
|
||||
|
||||
@Injectable()
|
||||
export class MessageParticipantListener {
|
||||
@@ -19,6 +21,7 @@ export class MessageParticipantListener {
|
||||
private readonly timelineActivityRepository: TimelineActivityRepository,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
@OnCustomBatchEvent('messageParticipant_matched')
|
||||
@@ -71,10 +74,17 @@ 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -3,9 +3,16 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AuditModule } from 'src/engine/core-modules/audit/audit.module';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { GetDataFromCacheWithRecomputeService } from 'src/engine/workspace-cache-storage/services/get-data-from-cache-with-recompute.service';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { ContactCreationManagerModule } from 'src/modules/contact-creation-manager/contact-creation-manager.module';
|
||||
import { MatchParticipantModule } from 'src/modules/match-participant/match-participant.module';
|
||||
@@ -23,6 +30,8 @@ import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-o
|
||||
FeatureFlagEntity,
|
||||
WorkspaceEntity,
|
||||
ObjectMetadataEntity,
|
||||
RoleEntity,
|
||||
RoleTargetEntity,
|
||||
]),
|
||||
AuditModule,
|
||||
ContactCreationManagerModule,
|
||||
@@ -32,6 +41,7 @@ import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-o
|
||||
]),
|
||||
MessagingCommonModule,
|
||||
MatchParticipantModule,
|
||||
WorkspaceCacheModule,
|
||||
],
|
||||
providers: [
|
||||
MessagingMessageParticipantService,
|
||||
@@ -39,6 +49,10 @@ import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-o
|
||||
MessageParticipantListener,
|
||||
MessageParticipantPersonListener,
|
||||
MessageParticipantWorkspaceMemberListener,
|
||||
FeatureFlagService,
|
||||
WorkspaceFeatureFlagsMapCacheService,
|
||||
WorkspaceCacheStorageService,
|
||||
GetDataFromCacheWithRecomputeService,
|
||||
],
|
||||
exports: [MessagingMessageParticipantService],
|
||||
})
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
import { RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
@@ -95,6 +95,7 @@ export class NoteTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceJoinColumn('opportunity')
|
||||
opportunityId: string | null;
|
||||
|
||||
// todo: remove this decorator and the custom field
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
import {
|
||||
ActorMetadata,
|
||||
FieldMetadataType,
|
||||
RelationOnDeleteAction,
|
||||
type RichTextV2Metadata,
|
||||
} from 'twenty-shared/types';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceFieldIndex } from 'src/engine/twenty-orm/decorators/workspace-field-index.decorator';
|
||||
@@ -127,6 +127,7 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
description: msg`Timeline Activities linked to the note.`,
|
||||
icon: 'IconTimelineEvent',
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetNote',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
|
||||
+3
-2
@@ -1,17 +1,17 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
import {
|
||||
ActorMetadata,
|
||||
type CurrencyMetadata,
|
||||
FieldMetadataType,
|
||||
RelationOnDeleteAction,
|
||||
} from 'twenty-shared/types';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceFieldIndex } from 'src/engine/twenty-orm/decorators/workspace-field-index.decorator';
|
||||
@@ -219,6 +219,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
description: msg`Timeline Activities linked to the opportunity.`,
|
||||
icon: 'IconTimelineEvent',
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetOpportunity',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
|
||||
@@ -306,6 +306,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
description: msg`Events linked to the person`,
|
||||
icon: 'IconTimelineEvent',
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetPerson',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
import { RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
@@ -95,6 +95,7 @@ export class TaskTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceJoinColumn('opportunity')
|
||||
opportunityId: string | null;
|
||||
|
||||
// todo: remove this decorator and the custom field
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
|
||||
@@ -182,6 +182,7 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
description: msg`Timeline Activities linked to the task.`,
|
||||
icon: 'IconTimelineEvent',
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetTask',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
|
||||
+37
-3
@@ -8,6 +8,7 @@ import { objectRecordDiffMerge } from 'src/engine/core-modules/event-emitter/uti
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { type TimelineActivityPayload } from 'src/modules/timeline/types/timeline-activity-payload';
|
||||
import { buildTimelineActivityRelatedMorphFieldMetadataName } from 'src/modules/timeline/utils/timeline-activity-related-morph-field-metadata-name-builder.util';
|
||||
|
||||
type TimelineActivityPayloadWorkspaceIdAndObjectSingularName = {
|
||||
payloads: (Omit<TimelineActivityPayload, 'properties'> & {
|
||||
@@ -15,6 +16,7 @@ type TimelineActivityPayloadWorkspaceIdAndObjectSingularName = {
|
||||
})[];
|
||||
workspaceId: string;
|
||||
objectSingularName: string;
|
||||
isFeatureFlagTimelineActivityMigrated: boolean;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -27,6 +29,7 @@ export class TimelineActivityRepository {
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
}: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) {
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
@@ -38,6 +41,7 @@ export class TimelineActivityRepository {
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
});
|
||||
|
||||
const payloadsWithDiff = payloads
|
||||
@@ -59,10 +63,16 @@ export class TimelineActivityRepository {
|
||||
const payloadsToInsert: TimelineActivityPayloadWorkspaceIdAndObjectSingularName['payloads'] =
|
||||
[];
|
||||
|
||||
const timelineActivityPropertyName =
|
||||
await this.getTimelineActivityPropertyName(
|
||||
objectSingularName,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
);
|
||||
|
||||
for (const payload of payloadsWithDiff) {
|
||||
const recentTimelineActivity = recentTimelineActivities.find(
|
||||
(timelineActivity) =>
|
||||
timelineActivity[`${objectSingularName}Id`] ===
|
||||
timelineActivity[timelineActivityPropertyName] ===
|
||||
payload.recordId &&
|
||||
timelineActivity.workspaceMemberId ===
|
||||
payload.workspaceMemberId &&
|
||||
@@ -92,6 +102,7 @@ export class TimelineActivityRepository {
|
||||
objectSingularName,
|
||||
payloads: payloadsToInsert,
|
||||
workspaceId,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -101,6 +112,7 @@ export class TimelineActivityRepository {
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
}: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) {
|
||||
const timelineActivityTypeORMRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
@@ -113,8 +125,14 @@ export class TimelineActivityRepository {
|
||||
|
||||
const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
|
||||
|
||||
const timelineActivityPropertyName =
|
||||
await this.getTimelineActivityPropertyName(
|
||||
objectSingularName,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
);
|
||||
|
||||
const whereConditions: Record<string, unknown> = {
|
||||
[`${objectSingularName}Id`]: In(
|
||||
[timelineActivityPropertyName]: In(
|
||||
payloads.map((payload) => payload.recordId),
|
||||
),
|
||||
name: In(payloads.map((payload) => payload.name)),
|
||||
@@ -135,6 +153,7 @@ export class TimelineActivityRepository {
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
}: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) {
|
||||
if (payloads.length === 0) {
|
||||
return;
|
||||
@@ -149,12 +168,18 @@ export class TimelineActivityRepository {
|
||||
},
|
||||
);
|
||||
|
||||
const timelineActivityPropertyName =
|
||||
await this.getTimelineActivityPropertyName(
|
||||
objectSingularName,
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
);
|
||||
|
||||
return timelineActivityTypeORMRepository.insert(
|
||||
payloads.map((payload) => ({
|
||||
name: payload.name,
|
||||
properties: payload.properties,
|
||||
workspaceMemberId: payload.workspaceMemberId,
|
||||
[`${objectSingularName}Id`]: payload.recordId,
|
||||
[timelineActivityPropertyName]: payload.recordId,
|
||||
linkedRecordCachedName: payload.linkedRecordCachedName ?? '',
|
||||
linkedRecordId: payload.linkedRecordId,
|
||||
linkedObjectMetadataId: payload.linkedObjectMetadataId,
|
||||
@@ -187,4 +212,13 @@ export class TimelineActivityRepository {
|
||||
workspaceMemberId: workspaceMemberId,
|
||||
});
|
||||
}
|
||||
|
||||
private async getTimelineActivityPropertyName(
|
||||
objectSingularName: string,
|
||||
isFeatureFlagTimelineActivityMigrated: boolean,
|
||||
) {
|
||||
return isFeatureFlagTimelineActivityMigrated
|
||||
? `${buildTimelineActivityRelatedMorphFieldMetadataName(objectSingularName)}Id`
|
||||
: `${objectSingularName}Id`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { In } from 'typeorm';
|
||||
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { type ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/types/object-record.base.event';
|
||||
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';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
@@ -25,6 +27,7 @@ export class TimelineActivityService {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(TimelineActivityWorkspaceEntity)
|
||||
private readonly timelineActivityRepository: TimelineActivityRepository,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
@@ -76,11 +79,18 @@ export class TimelineActivityService {
|
||||
{} as Record<string, TimelineActivityPayload[]>,
|
||||
);
|
||||
|
||||
const isFeatureFlagTimelineActivityMigrated =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
for (const objectSingularName in payloadsByObjectSingularName) {
|
||||
this.timelineActivityRepository.upsertTimelineActivities({
|
||||
await this.timelineActivityRepository.upsertTimelineActivities({
|
||||
objectSingularName,
|
||||
workspaceId,
|
||||
payloads: payloadsByObjectSingularName[objectSingularName],
|
||||
isFeatureFlagTimelineActivityMigrated,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+73
-37
@@ -1,6 +1,7 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
import { FieldMetadataType, RelationOnDeleteAction } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
@@ -120,7 +121,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
workspaceMemberId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.person,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetPerson,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Person`,
|
||||
description: msg`Event person`,
|
||||
@@ -128,15 +129,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => PersonWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
targetPerson: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('person')
|
||||
personId: string | null;
|
||||
@WorkspaceJoinColumn('targetPerson')
|
||||
targetPersonId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.company,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetCompany,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Company`,
|
||||
description: msg`Event company`,
|
||||
@@ -144,15 +147,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => CompanyWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
targetCompany: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('company')
|
||||
companyId: string | null;
|
||||
@WorkspaceJoinColumn('targetCompany')
|
||||
targetCompanyId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.opportunity,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetOpportunity,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Opportunity`,
|
||||
description: msg`Event opportunity`,
|
||||
@@ -160,15 +165,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => OpportunityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
opportunity: Relation<OpportunityWorkspaceEntity> | null;
|
||||
targetOpportunity: Relation<OpportunityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('opportunity')
|
||||
opportunityId: string | null;
|
||||
@WorkspaceJoinColumn('targetOpportunity')
|
||||
targetOpportunityId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.note,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetNote,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Note`,
|
||||
description: msg`Event note`,
|
||||
@@ -176,15 +183,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => NoteWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
note: Relation<NoteWorkspaceEntity> | null;
|
||||
targetNote: Relation<NoteWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('note')
|
||||
noteId: string | null;
|
||||
@WorkspaceJoinColumn('targetNote')
|
||||
targetNoteId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.task,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetTask,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Task`,
|
||||
description: msg`Event task`,
|
||||
@@ -192,15 +201,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => TaskWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
task: Relation<TaskWorkspaceEntity> | null;
|
||||
targetTask: Relation<TaskWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('task')
|
||||
taskId: string | null;
|
||||
@WorkspaceJoinColumn('targetTask')
|
||||
targetTaskId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.workflow,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetWorkflow,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Workflow`,
|
||||
description: msg`Event workflow`,
|
||||
@@ -208,15 +219,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => WorkflowWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
targetWorkflow: Relation<WorkflowWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflow')
|
||||
workflowId: string | null;
|
||||
@WorkspaceJoinColumn('targetWorkflow')
|
||||
targetWorkflowId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.workflowVersion,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetWorkflowVersion,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`WorkflowVersion`,
|
||||
description: msg`Event workflow version`,
|
||||
@@ -224,15 +237,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => WorkflowVersionWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflowVersion: Relation<WorkflowVersionWorkspaceEntity> | null;
|
||||
targetWorkflowVersion: Relation<WorkflowVersionWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflowVersion')
|
||||
workflowVersionId: string | null;
|
||||
@WorkspaceJoinColumn('targetWorkflowVersion')
|
||||
targetWorkflowVersionId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.workflowRun,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetWorkflowRun,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Workflow Run`,
|
||||
description: msg`Event workflow run`,
|
||||
@@ -240,15 +255,17 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => WorkflowRunWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workflowRun: Relation<WorkflowRunWorkspaceEntity> | null;
|
||||
targetWorkflowRun: Relation<WorkflowRunWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('workflowRun')
|
||||
workflowRunId: string | null;
|
||||
@WorkspaceJoinColumn('targetWorkflowRun')
|
||||
targetWorkflowRunId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.dashboard,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetDashboard,
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
label: msg`Dashboard`,
|
||||
description: msg`Event dashboard`,
|
||||
@@ -256,17 +273,20 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideTarget: () => DashboardWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
isMorphRelation: true,
|
||||
morphId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
dashboard: Relation<DashboardWorkspaceEntity> | null;
|
||||
targetDashboard: Relation<DashboardWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceJoinColumn('dashboard')
|
||||
dashboardId: string | null;
|
||||
@WorkspaceJoinColumn('targetDashboard')
|
||||
targetDashboardId: string | null;
|
||||
|
||||
// todo: remove this decorator and the custom field
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.custom,
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetCustom,
|
||||
name: oppositeObjectMetadata.nameSingular,
|
||||
label: oppositeObjectMetadata.labelSingular,
|
||||
description: `Timeline Activity ${oppositeObjectMetadata.labelSingular}`,
|
||||
@@ -278,4 +298,20 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
custom: Relation<CustomWorkspaceEntity>;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationType.MANY_TO_ONE,
|
||||
argsFactory: (oppositeObjectMetadata) => ({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetCustom,
|
||||
name: oppositeObjectMetadata.nameSingular,
|
||||
label: oppositeObjectMetadata.labelSingular,
|
||||
description: `Timeline Activity ${oppositeObjectMetadata.labelSingular}`,
|
||||
joinColumn: `target${capitalize(oppositeObjectMetadata.nameSingular)}Id`,
|
||||
icon: 'IconTimeline',
|
||||
}),
|
||||
inverseSideTarget: () => CustomWorkspaceEntity,
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
targetCustom: Relation<CustomWorkspaceEntity>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
@@ -12,6 +13,7 @@ import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-o
|
||||
TimelineActivityWorkspaceEntity,
|
||||
]),
|
||||
TwentyORMModule,
|
||||
FeatureFlagModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
providers: [TimelineActivityService],
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
export const buildTimelineActivityRelatedMorphFieldMetadataName = (
|
||||
name: string,
|
||||
) => {
|
||||
return `target${capitalize(name)}`;
|
||||
};
|
||||
+1
@@ -284,6 +284,7 @@ export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
label: msg`Timeline Activities`,
|
||||
description: msg`Timeline activities linked to the run`,
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetWorkflowRun',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
||||
+1
@@ -198,6 +198,7 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
label: msg`Timeline Activities`,
|
||||
description: msg`Timeline activities linked to the version`,
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetWorkflowVersion',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
||||
+2
-1
@@ -9,9 +9,9 @@ import {
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { type FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceFieldIndex } from 'src/engine/twenty-orm/decorators/workspace-field-index.decorator';
|
||||
@@ -193,6 +193,7 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
label: msg`Timeline Activities`,
|
||||
description: msg`Timeline activities linked to the workflow`,
|
||||
inverseSideTarget: () => TimelineActivityWorkspaceEntity,
|
||||
inverseSideFieldKey: 'targetWorkflow',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
||||
Reference in New Issue
Block a user