Fix missing timeline activity events (#8459)

Fixes bug introduced in https://github.com/twentyhq/twenty/pull/8193

Taking into account linked event name `linked-{eventName}` as before
issue

## Before
`linked-created` and `linked-updated` activity targets were not created
in `timelineActivity` table

## After
`linked-created` and `linked-updated` activity targets are created in
`timelineActivity` table
This commit is contained in:
martmull 2024-11-12 12:04:58 +01:00 committed by GitHub
parent bec4da496d
commit aadcb49dcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 35 deletions

View File

@ -1,9 +1,7 @@
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { RecordPositionBackfillJob } from 'src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job'; import { RecordPositionBackfillJob } from 'src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job';
import { RecordPositionBackfillModule } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module'; import { RecordPositionBackfillModule } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module';
import { AnalyticsModule } from 'src/engine/core-modules/analytics/analytics.module';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';

View File

@ -6,7 +6,8 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { TimelineActivityRepository } from 'src/modules/timeline/repositiories/timeline-activity.repository'; import { TimelineActivityRepository } from 'src/modules/timeline/repositiories/timeline-activity.repository';
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity'; import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
type TransformedEvent = ObjectRecordBaseEvent & { type TimelineActivity = ObjectRecordBaseEvent & {
name: string;
objectName?: string; objectName?: string;
linkedRecordCachedName?: string; linkedRecordCachedName?: string;
linkedRecordId?: string; linkedRecordId?: string;
@ -35,26 +36,30 @@ export class TimelineActivityService {
eventName: string; eventName: string;
workspaceId: string; workspaceId: string;
}) { }) {
const events = await this.transformEvent({ event, workspaceId, eventName }); const timelineActivities = await this.transformEventToTimelineActivities({
event,
workspaceId,
eventName,
});
if (!events || events.length === 0) return; if (!timelineActivities || timelineActivities.length === 0) return;
for (const event of events) { for (const timelineActivity of timelineActivities) {
await this.timelineActivityRepository.upsertOne( await this.timelineActivityRepository.upsertOne(
eventName, timelineActivity.name,
event.properties, timelineActivity.properties,
event.objectName ?? event.objectMetadata.nameSingular, timelineActivity.objectName ?? event.objectMetadata.nameSingular,
event.recordId, timelineActivity.recordId,
workspaceId, workspaceId,
event.workspaceMemberId, timelineActivity.workspaceMemberId,
event.linkedRecordCachedName, timelineActivity.linkedRecordCachedName,
event.linkedRecordId, timelineActivity.linkedRecordId,
event.linkedObjectMetadataId, timelineActivity.linkedObjectMetadataId,
); );
} }
} }
private async transformEvent({ private async transformEventToTimelineActivities({
event, event,
workspaceId, workspaceId,
eventName, eventName,
@ -62,16 +67,17 @@ export class TimelineActivityService {
event: ObjectRecordBaseEvent; event: ObjectRecordBaseEvent;
workspaceId: string; workspaceId: string;
eventName: string; eventName: string;
}): Promise<TransformedEvent[]> { }): Promise<TimelineActivity[] | undefined> {
if (['note', 'task'].includes(event.objectMetadata.nameSingular)) { if (['note', 'task'].includes(event.objectMetadata.nameSingular)) {
const linkedObjects = await this.handleLinkedObjects({ const linkedTimelineActivities = await this.getLinkedTimelineActivities({
event, event,
workspaceId, workspaceId,
eventName, eventName,
}); });
// 2 timelines, one for the linked object and one for the task/note // 2 timelines, one for the linked object and one for the task/note
if (linkedObjects?.length > 0) return [...linkedObjects, event]; if (linkedTimelineActivities && linkedTimelineActivities?.length > 0)
return [...linkedTimelineActivities, { ...event, name: eventName }];
} }
if ( if (
@ -79,13 +85,17 @@ export class TimelineActivityService {
event.objectMetadata.nameSingular, event.objectMetadata.nameSingular,
) )
) { ) {
return await this.handleLinkedObjects({ event, workspaceId, eventName }); return await this.getLinkedTimelineActivities({
event,
workspaceId,
eventName,
});
} }
return [event]; return [{ ...event, name: eventName }];
} }
private async handleLinkedObjects({ private async getLinkedTimelineActivities({
event, event,
workspaceId, workspaceId,
eventName, eventName,
@ -93,13 +103,13 @@ export class TimelineActivityService {
event: ObjectRecordBaseEvent; event: ObjectRecordBaseEvent;
workspaceId: string; workspaceId: string;
eventName: string; eventName: string;
}) { }): Promise<TimelineActivity[] | undefined> {
const dataSourceSchema = const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId); this.workspaceDataSourceService.getSchemaName(workspaceId);
switch (event.objectMetadata.nameSingular) { switch (event.objectMetadata.nameSingular) {
case 'noteTarget': case 'noteTarget':
return this.processActivityTarget({ return this.computeActivityTargets({
event, event,
dataSourceSchema, dataSourceSchema,
activityType: 'note', activityType: 'note',
@ -107,7 +117,7 @@ export class TimelineActivityService {
workspaceId, workspaceId,
}); });
case 'taskTarget': case 'taskTarget':
return this.processActivityTarget({ return this.computeActivityTargets({
event, event,
dataSourceSchema, dataSourceSchema,
activityType: 'task', activityType: 'task',
@ -116,7 +126,7 @@ export class TimelineActivityService {
}); });
case 'note': case 'note':
case 'task': case 'task':
return this.processActivity({ return this.computeActivities({
event, event,
dataSourceSchema, dataSourceSchema,
activityType: event.objectMetadata.nameSingular, activityType: event.objectMetadata.nameSingular,
@ -128,7 +138,7 @@ export class TimelineActivityService {
} }
} }
private async processActivity({ private async computeActivities({
event, event,
dataSourceSchema, dataSourceSchema,
activityType, activityType,
@ -144,14 +154,14 @@ export class TimelineActivityService {
const activityTargets = const activityTargets =
await this.workspaceDataSourceService.executeRawQuery( await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."${this.targetObjects[activityType]}" `SELECT * FROM ${dataSourceSchema}."${this.targetObjects[activityType]}"
WHERE "${activityType}Id" = $1`, WHERE "${activityType}Id" = $1`,
[event.recordId], [event.recordId],
workspaceId, workspaceId,
); );
const activity = await this.workspaceDataSourceService.executeRawQuery( const activity = await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."${activityType}" `SELECT * FROM ${dataSourceSchema}."${activityType}"
WHERE "id" = $1`, WHERE "id" = $1`,
[event.recordId], [event.recordId],
workspaceId, workspaceId,
); );
@ -184,12 +194,12 @@ export class TimelineActivityService {
linkedRecordCachedName: activity[0].title, linkedRecordCachedName: activity[0].title,
linkedRecordId: activity[0].id, linkedRecordId: activity[0].id,
linkedObjectMetadataId: event.objectMetadata.id, linkedObjectMetadataId: event.objectMetadata.id,
} as TransformedEvent; } as TimelineActivity;
}) })
.filter((event): event is TransformedEvent => event !== undefined); .filter((event): event is TimelineActivity => event !== undefined);
} }
private async processActivityTarget({ private async computeActivityTargets({
event, event,
dataSourceSchema, dataSourceSchema,
activityType, activityType,
@ -201,11 +211,11 @@ export class TimelineActivityService {
activityType: string; activityType: string;
eventName: string; eventName: string;
workspaceId: string; workspaceId: string;
}) { }): Promise<TimelineActivity[] | undefined> {
const activityTarget = const activityTarget =
await this.workspaceDataSourceService.executeRawQuery( await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."${this.targetObjects[activityType]}" `SELECT * FROM ${dataSourceSchema}."${this.targetObjects[activityType]}"
WHERE "id" = $1`, WHERE "id" = $1`,
[event.recordId], [event.recordId],
workspaceId, workspaceId,
); );
@ -214,7 +224,7 @@ export class TimelineActivityService {
const activity = await this.workspaceDataSourceService.executeRawQuery( const activity = await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."${activityType}" `SELECT * FROM ${dataSourceSchema}."${activityType}"
WHERE "id" = $1`, WHERE "id" = $1`,
[activityTarget[0].activityId], [activityTarget[0].activityId],
workspaceId, workspaceId,
); );
@ -247,7 +257,7 @@ export class TimelineActivityService {
linkedRecordCachedName: activity[0].title, linkedRecordCachedName: activity[0].title,
linkedRecordId: activity[0].id, linkedRecordId: activity[0].id,
linkedObjectMetadataId: activityObjectMetadataId, linkedObjectMetadataId: activityObjectMetadataId,
} as TransformedEvent, } as TimelineActivity,
]; ];
} }
} }