New Timeline (#4936)
Refactored the code to introduce two different concepts: - AuditLogs (immutable, raw data) - TimelineActivities (user-friendly, transformed data) Still some work needed: - Add message, files, calendar events to timeline (~2 hours if done naively) - Refactor repository to try to abstract concept when we can (tbd, wait for Twenty ORM) - Introduce ability to display child timelines on parent timeline with filtering (~2 days) - Improve UI: add links to open note/task, improve diff display, etc (half a day) - Decide the path forward for Task vs Notes: either introduce a new field type "Record Type" and start going into that direction ; or split in two objects? - Trigger updates when a field is changed (will be solved by real-time / websockets: 2 weeks) - Integrate behavioral events (1 day for POC, 1 week for clean/documented) <img width="1248" alt="Screenshot 2024-04-12 at 09 24 49" src="https://github.com/twentyhq/twenty/assets/6399865/9428db1a-ab2b-492c-8b0b-d4d9a36e81fa">
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuditLogRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
public async insert(
|
||||
name: string,
|
||||
properties: string,
|
||||
workspaceMemberId: string | null,
|
||||
objectName: string,
|
||||
objectMetadataId: string,
|
||||
recordId: string,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`INSERT INTO ${dataSourceSchema}."auditLog"
|
||||
("name", "properties", "workspaceMemberId", "objectName", "objectMetadataId", "recordId")
|
||||
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||
[
|
||||
name,
|
||||
properties,
|
||||
workspaceMemberId,
|
||||
objectName,
|
||||
objectMetadataId,
|
||||
recordId,
|
||||
],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { objectRecordDiffMerge } from 'src/engine/integrations/event-emitter/utils/object-record-diff-merge';
|
||||
|
||||
@Injectable()
|
||||
export class TimelineActivityRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
async upsertOne(
|
||||
name: string,
|
||||
properties: Record<string, any>,
|
||||
objectName: string,
|
||||
recordId: string,
|
||||
workspaceId: string,
|
||||
workspaceMemberId?: string,
|
||||
linkedRecordCachedName?: string,
|
||||
linkedRecordId?: string,
|
||||
linkedObjectMetadataId?: string,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const recentTimelineActivity = await this.findRecentTimelineActivity(
|
||||
dataSourceSchema,
|
||||
name,
|
||||
objectName,
|
||||
recordId,
|
||||
workspaceMemberId,
|
||||
linkedRecordId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (recentTimelineActivity.length !== 0) {
|
||||
const newProps = objectRecordDiffMerge(
|
||||
recentTimelineActivity[0].properties,
|
||||
properties,
|
||||
);
|
||||
|
||||
return this.updateTimelineActivity(
|
||||
dataSourceSchema,
|
||||
recentTimelineActivity[0].id,
|
||||
newProps,
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
return this.insertTimelineActivity(
|
||||
dataSourceSchema,
|
||||
name,
|
||||
properties,
|
||||
objectName,
|
||||
recordId,
|
||||
workspaceMemberId,
|
||||
linkedRecordCachedName ?? '',
|
||||
linkedRecordId,
|
||||
linkedObjectMetadataId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async findRecentTimelineActivity(
|
||||
dataSourceSchema: string,
|
||||
name: string,
|
||||
objectName: string,
|
||||
recordId: string,
|
||||
workspaceMemberId: string | undefined,
|
||||
linkedRecordId: string | undefined,
|
||||
workspaceId: string,
|
||||
) {
|
||||
return this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."timelineActivity"
|
||||
WHERE "${objectName}Id" = $1
|
||||
AND ("name" = $2 OR "name" = $3)
|
||||
AND "workspaceMemberId" = $4
|
||||
AND "linkedRecordId" = $5
|
||||
AND "createdAt" >= NOW() - interval '10 minutes'`,
|
||||
[
|
||||
recordId,
|
||||
name,
|
||||
name.replace(/\.updated$/, '.created'),
|
||||
workspaceMemberId,
|
||||
linkedRecordId,
|
||||
],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async updateTimelineActivity(
|
||||
dataSourceSchema: string,
|
||||
id: string,
|
||||
properties: Record<string, any>,
|
||||
workspaceMemberId: string | undefined,
|
||||
workspaceId: string,
|
||||
) {
|
||||
return this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."timelineActivity"
|
||||
SET "properties" = $2, "workspaceMemberId" = $3
|
||||
WHERE "id" = $1`,
|
||||
[id, properties, workspaceMemberId],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async insertTimelineActivity(
|
||||
dataSourceSchema: string,
|
||||
name: string,
|
||||
properties: Record<string, any>,
|
||||
objectName: string,
|
||||
recordId: string,
|
||||
workspaceMemberId: string | undefined,
|
||||
linkedRecordCachedName: string,
|
||||
linkedRecordId: string | undefined,
|
||||
linkedObjectMetadataId: string | undefined,
|
||||
workspaceId: string,
|
||||
) {
|
||||
return this.workspaceDataSourceService.executeRawQuery(
|
||||
`INSERT INTO ${dataSourceSchema}."timelineActivity"
|
||||
("name", "properties", "workspaceMemberId", "${objectName}Id", "linkedRecordCachedName", "linkedRecordId", "linkedObjectMetadataId")
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||
[
|
||||
name,
|
||||
properties,
|
||||
workspaceMemberId,
|
||||
recordId,
|
||||
linkedRecordCachedName ?? '',
|
||||
linkedRecordId,
|
||||
linkedObjectMetadataId,
|
||||
],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user