Refactor timeline activity insert (#13696)

In this PR:
- refactor timelineActivity computation to batch it
- make sure to pass the authContext to insert-query-builder in order to
pass user information to timelineActivity
- refactor message-participant /calendar-participant creation to batch
more
- fix favorite on view race condition (FE)
- deprecate PARTIAL_CALENDAR_EVENT_FETCH_LIST syncStage as we will
deprecate partial vs full notion (we will just leverage cursor emptyness
or not)
- introduce calendar / messging SCHEDULED syncStage that will allow
better performance granularity later
- activate quick message import after message list fetch to speed
performance on small message lists
This commit is contained in:
Charles Bochet
2025-08-06 22:11:26 +02:00
committed by GitHub
parent 453a6167a5
commit 5631dd122e
34 changed files with 684 additions and 572 deletions
@@ -44,6 +44,7 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/deep-partial-entity-with-nested-relation-fields.type';
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
import { RelationNestedQueries } from 'src/engine/twenty-orm/relation-nested-queries/relation-nested-queries';
import {
OperationType,
@@ -54,7 +55,6 @@ import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { getObjectMetadataFromEntityTarget } from 'src/engine/twenty-orm/utils/get-object-metadata-from-entity-target.util';
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
type PermissionOptions = {
shouldBypassPermissionChecks?: boolean;
@@ -173,6 +173,7 @@ export class WorkspaceEntityManager extends EntityManager {
| QueryDeepPartialEntityWithNestedRelationFields<Entity>[],
selectedColumns: string[] | '*' = '*',
permissionOptions?: PermissionOptions,
authContext?: AuthContext,
): Promise<InsertResult> {
const metadata = this.connection.getMetadata(target);
@@ -183,6 +184,7 @@ export class WorkspaceEntityManager extends EntityManager {
permissionOptions,
)
.insert()
.setAuthContext(authContext ?? {})
.values(entity)
.returning(selectedColumns)
.execute();
@@ -273,6 +273,7 @@ describe('WorkspaceRepository', () => {
shouldBypassPermissionChecks: false,
objectRecordsPermissions: mockObjectRecordsPermissions,
},
undefined,
);
});
@@ -251,4 +251,10 @@ export class WorkspaceInsertQueryBuilder<
TwentyORMExceptionCode.METHOD_NOT_ALLOWED,
);
}
setAuthContext(authContext: AuthContext): WorkspaceInsertQueryBuilder<T> {
this.authContext = authContext;
return this;
}
}
@@ -554,6 +554,7 @@ export class WorkspaceRepository<
entity,
selectedColumns,
permissionOptions,
this.authContext,
);
}
@@ -0,0 +1,9 @@
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
export const computeEventName = (objectName: string, action: string) => {
if (!checkStringIsDatabaseEventAction(action)) {
throw new Error('Invalid action');
}
return `${objectName}.${action}`;
};
@@ -0,0 +1,20 @@
import { isDefined } from 'twenty-shared/utils';
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
export const parseEventNameOrThrow = (eventName: string) => {
const [objectSingularName, action] = eventName.split('.');
if (!checkStringIsDatabaseEventAction(action)) {
throw new Error('Invalid event name');
}
if (!isDefined(objectSingularName)) {
throw new Error('Invalid event name');
}
return {
objectSingularName,
action,
};
};
@@ -16,6 +16,7 @@ import { objectRecordChangedValues } from 'src/engine/core-modules/event-emitter
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { CustomEventName } from 'src/engine/workspace-event-emitter/types/custom-event-name.type';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { computeEventName } from 'src/engine/workspace-event-emitter/utils/compute-event-name';
type ActionEventMap<T> = {
[DatabaseEventAction.CREATED]: ObjectRecordCreateEvent<T>;
@@ -147,7 +148,7 @@ export class WorkspaceEventEmitter {
return;
}
const eventName = `${objectMetadataNameSingular}.${action}`;
const eventName = computeEventName(objectMetadataNameSingular, action);
this.eventEmitter.emit(eventName, {
name: eventName,