From 7b63cf14bc1697f1c1a79a56831c75ea60434cea Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Wed, 13 Mar 2024 10:27:34 +0100 Subject: [PATCH] Build listener to backfill position (#4432) * Build listener to backfill position * Fix tests --------- Co-authored-by: Thomas Trompette --- .../types/object-record-create.event.ts | 6 ++ .../src/integrations/integrations.module.ts | 4 +- .../integrations/message-queue/jobs.module.ts | 7 ++ .../message-queue/message-queue.constants.ts | 1 + .../record-position-query.factory.spec.ts | 11 ++-- .../record-position-query.factory.ts | 65 +++++++++++++++++-- .../query-runner-args.factory.spec.ts | 31 ++------- .../workspace-query-runner/factories/index.ts | 8 ++- .../factories/query-runner-args.factory.ts | 51 +++------------ .../factories/record-position.factory.ts | 48 ++++++++++++++ .../jobs/record-position-backfill.job.ts | 28 ++++++++ .../listeners/record-position.listener.ts | 56 ++++++++++++++++ .../record-position-backfill-module.ts | 17 +++++ .../record-position-backfill-service.ts | 49 ++++++++++++++ .../workspace-query-runner.module.ts | 7 +- .../workspace-query-runner.service.ts | 6 +- 16 files changed, 314 insertions(+), 81 deletions(-) create mode 100644 packages/twenty-server/src/workspace/workspace-query-runner/factories/record-position.factory.ts create mode 100644 packages/twenty-server/src/workspace/workspace-query-runner/jobs/record-position-backfill.job.ts create mode 100644 packages/twenty-server/src/workspace/workspace-query-runner/listeners/record-position.listener.ts create mode 100644 packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-module.ts create mode 100644 packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-service.ts diff --git a/packages/twenty-server/src/integrations/event-emitter/types/object-record-create.event.ts b/packages/twenty-server/src/integrations/event-emitter/types/object-record-create.event.ts index d9dbbeb234..be66f1928b 100644 --- a/packages/twenty-server/src/integrations/event-emitter/types/object-record-create.event.ts +++ b/packages/twenty-server/src/integrations/event-emitter/types/object-record-create.event.ts @@ -1,6 +1,12 @@ import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata'; +export type CreatedObjectMetadata = { + nameSingular: string; + isCustom: boolean; +}; + export class ObjectRecordCreateEvent { workspaceId: string; createdRecord: T; + createdObjectMetadata: CreatedObjectMetadata; } diff --git a/packages/twenty-server/src/integrations/integrations.module.ts b/packages/twenty-server/src/integrations/integrations.module.ts index 64d39f56bf..04f23918e6 100644 --- a/packages/twenty-server/src/integrations/integrations.module.ts +++ b/packages/twenty-server/src/integrations/integrations.module.ts @@ -40,7 +40,9 @@ import { MessageQueueModule } from './message-queue/message-queue.module'; useFactory: emailModuleFactory, inject: [EnvironmentService], }), - EventEmitterModule.forRoot(), + EventEmitterModule.forRoot({ + wildcard: true, + }), CacheStorageModule, ], exports: [], diff --git a/packages/twenty-server/src/integrations/message-queue/jobs.module.ts b/packages/twenty-server/src/integrations/message-queue/jobs.module.ts index c3d2442c52..1d97d71e35 100644 --- a/packages/twenty-server/src/integrations/message-queue/jobs.module.ts +++ b/packages/twenty-server/src/integrations/message-queue/jobs.module.ts @@ -34,6 +34,8 @@ import { StripeModule } from 'src/core/billing/stripe/stripe.module'; import { Workspace } from 'src/core/workspace/workspace.entity'; import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity'; import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity'; +import { RecordPositionBackfillJob } from 'src/workspace/workspace-query-runner/jobs/record-position-backfill.job'; +import { RecordPositionBackfillModule } from 'src/workspace/workspace-query-runner/services/record-position-backfill-module'; @Module({ imports: [ @@ -56,6 +58,7 @@ import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity'; UserModule, UserWorkspaceModule, WorkspaceDataSourceModule, + RecordPositionBackfillModule, ], providers: [ { @@ -100,6 +103,10 @@ import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity'; useClass: DeleteConnectedAccountAssociatedDataJob, }, { provide: UpdateSubscriptionJob.name, useClass: UpdateSubscriptionJob }, + { + provide: RecordPositionBackfillJob.name, + useClass: RecordPositionBackfillJob, + }, ], }) export class JobsModule { diff --git a/packages/twenty-server/src/integrations/message-queue/message-queue.constants.ts b/packages/twenty-server/src/integrations/message-queue/message-queue.constants.ts index 56af4ba6f4..25da112388 100644 --- a/packages/twenty-server/src/integrations/message-queue/message-queue.constants.ts +++ b/packages/twenty-server/src/integrations/message-queue/message-queue.constants.ts @@ -7,4 +7,5 @@ export enum MessageQueue { cronQueue = 'cron-queue', emailQueue = 'email-queue', billingQueue = 'billing-queue', + recordPositionBackfillQueue = 'record-position-backfill-queue', } diff --git a/packages/twenty-server/src/workspace/workspace-query-builder/factories/__tests__/record-position-query.factory.spec.ts b/packages/twenty-server/src/workspace/workspace-query-builder/factories/__tests__/record-position-query.factory.spec.ts index 80430dd6dd..0cb99dadca 100644 --- a/packages/twenty-server/src/workspace/workspace-query-builder/factories/__tests__/record-position-query.factory.spec.ts +++ b/packages/twenty-server/src/workspace/workspace-query-builder/factories/__tests__/record-position-query.factory.spec.ts @@ -1,12 +1,13 @@ -import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface'; - -import { RecordPositionQueryFactory } from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; +import { + RecordPositionQueryFactory, + RecordPositionQueryType, +} from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; describe('RecordPositionQueryFactory', () => { const objectMetadataItem = { isCustom: false, nameSingular: 'company', - } as ObjectMetadataInterface; + }; const dataSourceSchema = 'workspace_test'; const factory: RecordPositionQueryFactory = new RecordPositionQueryFactory(); @@ -19,6 +20,7 @@ describe('RecordPositionQueryFactory', () => { const positionValue = 'first'; const result = await factory.create( + RecordPositionQueryType.GET, positionValue, objectMetadataItem, dataSourceSchema, @@ -34,6 +36,7 @@ describe('RecordPositionQueryFactory', () => { const positionValue = 'last'; const result = await factory.create( + RecordPositionQueryType.GET, positionValue, objectMetadataItem, dataSourceSchema, diff --git a/packages/twenty-server/src/workspace/workspace-query-builder/factories/record-position-query.factory.ts b/packages/twenty-server/src/workspace/workspace-query-builder/factories/record-position-query.factory.ts index d6b55a134d..40df6ab2c4 100644 --- a/packages/twenty-server/src/workspace/workspace-query-builder/factories/record-position-query.factory.ts +++ b/packages/twenty-server/src/workspace/workspace-query-builder/factories/record-position-query.factory.ts @@ -1,21 +1,74 @@ import { Injectable } from '@nestjs/common'; -import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface'; +export enum RecordPositionQueryType { + GET = 'GET', + UPDATE = 'UPDATE', +} @Injectable() export class RecordPositionQueryFactory { async create( + recordPositionQueryType: RecordPositionQueryType, + positionValue: 'first' | 'last' | number, + objectMetadata: { isCustom: boolean; nameSingular: string }, + dataSourceSchema: string, + recordId?: string, + ): Promise { + const name = + (objectMetadata.isCustom ? '_' : '') + objectMetadata.nameSingular; + + switch (recordPositionQueryType) { + case RecordPositionQueryType.GET: + if (typeof positionValue === 'number') { + throw new Error( + 'RecordPositionQueryType.GET requires positionValue to be a number', + ); + } + + return this.createForGet(positionValue, name, dataSourceSchema); + case RecordPositionQueryType.UPDATE: + if (typeof positionValue !== 'number') { + throw new Error( + 'RecordPositionQueryType.UPDATE requires positionValue to be a number', + ); + } + + if (!recordId) { + throw new Error( + 'RecordPositionQueryType.UPDATE requires recordId to be defined', + ); + } + + return this.createForUpdate( + positionValue, + name, + dataSourceSchema, + recordId, + ); + default: + throw new Error('Invalid RecordPositionQueryType'); + } + } + + private async createForGet( positionValue: 'first' | 'last', - objectMetadataItem: ObjectMetadataInterface, + name: string, dataSourceSchema: string, ): Promise { const orderByDirection = positionValue === 'first' ? 'ASC' : 'DESC'; - const name = - (objectMetadataItem.isCustom ? '_' : '') + - objectMetadataItem.nameSingular; - return `SELECT position FROM ${dataSourceSchema}."${name}" WHERE "position" IS NOT NULL ORDER BY "position" ${orderByDirection} LIMIT 1`; } + + private async createForUpdate( + positionValue: number, + name: string, + dataSourceSchema: string, + recordId: string, + ): Promise { + return `UPDATE ${dataSourceSchema}."${name}" + SET "position" = ${positionValue} + WHERE "id" = '${recordId}'`; + } } diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts b/packages/twenty-server/src/workspace/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts index 20c76b5302..e1d6efa990 100644 --- a/packages/twenty-server/src/workspace/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts +++ b/packages/twenty-server/src/workspace/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts @@ -3,43 +3,33 @@ import { Test, TestingModule } from '@nestjs/testing'; import { WorkspaceQueryRunnerOptions } from 'src/workspace/workspace-query-runner/interfaces/query-runner-option.interface'; import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface'; -import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service'; -import { RecordPositionQueryFactory } from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; import { QueryRunnerArgsFactory } from 'src/workspace/workspace-query-runner/factories/query-runner-args.factory'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; +import { RecordPositionFactory } from 'src/workspace/workspace-query-runner/factories/record-position.factory'; describe('QueryRunnerArgsFactory', () => { - const workspaceDataSourceService = { - getSchemaName: jest.fn().mockResolvedValue('test schema'), - executeRawQuery: jest.fn(), - }; - const recordPositionQueryFactory = { - create: jest.fn().mockResolvedValue('test query'), + const recordPositionFactory = { + create: jest.fn().mockResolvedValue(2), }; const options = { fieldMetadataCollection: [ { name: 'position', type: FieldMetadataType.POSITION }, ] as FieldMetadataInterface[], + objectMetadataItem: { isCustom: true, nameSingular: 'test' }, } as WorkspaceQueryRunnerOptions; let factory: QueryRunnerArgsFactory; beforeEach(async () => { - jest.resetAllMocks(); - const module: TestingModule = await Test.createTestingModule({ providers: [ QueryRunnerArgsFactory, { - provide: RecordPositionQueryFactory, + provide: RecordPositionFactory, useValue: { - create: recordPositionQueryFactory.create, + create: recordPositionFactory.create, }, }, - { - provide: WorkspaceDataSourceService, - useValue: workspaceDataSourceService, - }, ], }).compile(); @@ -63,17 +53,10 @@ describe('QueryRunnerArgsFactory', () => { it('should override args when of type array', async () => { const args = { data: [{ id: 1 }, { position: 'last' }] }; - workspaceDataSourceService.executeRawQuery.mockResolvedValue([ - { position: 1 }, - ]); - const result = await factory.create(args, options); expect(result).toEqual({ - data: [ - { id: 1 }, - { position: 2 }, // Calculates 1 + 1 - ], + data: [{ id: 1 }, { position: 2 }], }); }); }); diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/factories/index.ts b/packages/twenty-server/src/workspace/workspace-query-runner/factories/index.ts index fcff782e04..c24cd55218 100644 --- a/packages/twenty-server/src/workspace/workspace-query-runner/factories/index.ts +++ b/packages/twenty-server/src/workspace/workspace-query-runner/factories/index.ts @@ -1,3 +1,7 @@ -import { QueryRunnerArgsFactory } from 'src/workspace/workspace-query-runner/factories/query-runner-args.factory'; +import { RecordPositionFactory } from './record-position.factory'; +import { QueryRunnerArgsFactory } from './query-runner-args.factory'; -export const workspaceQueryRunnerFactories = [QueryRunnerArgsFactory]; +export const workspaceQueryRunnerFactories = [ + QueryRunnerArgsFactory, + RecordPositionFactory, +]; diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/workspace/workspace-query-runner/factories/query-runner-args.factory.ts index 0ac36b3950..3769424433 100644 --- a/packages/twenty-server/src/workspace/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/workspace/workspace-query-runner/factories/query-runner-args.factory.ts @@ -1,19 +1,15 @@ import { Injectable } from '@nestjs/common'; -import { WorkspaceQueryRunnerOptions } from 'src/workspace/workspace-query-runner/interfaces/query-runner-option.interface'; import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface'; -import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/workspace/workspace-query-runner/interfaces/query-runner-option.interface'; -import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service'; -import { RecordPositionQueryFactory } from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; +import { RecordPositionFactory } from './record-position.factory'; + @Injectable() export class QueryRunnerArgsFactory { - constructor( - private readonly workspaceDataSourceService: WorkspaceDataSourceService, - private readonly recordPositionQueryFactory: RecordPositionQueryFactory, - ) {} + constructor(private readonly recordPositionFactory: RecordPositionFactory) {} async create( args: Record, @@ -54,9 +50,12 @@ export class QueryRunnerArgsFactory { case FieldMetadataType.POSITION: return [ key, - await this.buildPositionValue( + await this.recordPositionFactory.create( value, - options.objectMetadataItem, + { + isCustom: options.objectMetadataItem.isCustom, + nameSingular: options.objectMetadataItem.nameSingular, + }, options.workspaceId, ), ]; @@ -70,36 +69,4 @@ export class QueryRunnerArgsFactory { return Object.fromEntries(newArgEntries); } - - private async buildPositionValue( - value: number | 'first' | 'last', - objectMetadataItem: ObjectMetadataInterface, - workspaceId: string, - ) { - if (typeof value === 'number') { - return value; - } - - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - const query = await this.recordPositionQueryFactory.create( - value, - objectMetadataItem, - dataSourceSchema, - ); - - const records = await this.workspaceDataSourceService.executeRawQuery( - query, - [], - workspaceId, - undefined, - ); - - return ( - (value === 'first' - ? records[0]?.position / 2 - : records[0]?.position + 1) || 1 - ); - } } diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/factories/record-position.factory.ts b/packages/twenty-server/src/workspace/workspace-query-runner/factories/record-position.factory.ts new file mode 100644 index 0000000000..1c31b16570 --- /dev/null +++ b/packages/twenty-server/src/workspace/workspace-query-runner/factories/record-position.factory.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; + +import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service'; +import { + RecordPositionQueryFactory, + RecordPositionQueryType, +} from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; + +@Injectable() +export class RecordPositionFactory { + constructor( + private readonly workspaceDataSourceService: WorkspaceDataSourceService, + private readonly recordPositionQueryFactory: RecordPositionQueryFactory, + ) {} + + async create( + value: number | 'first' | 'last', + objectMetadata: { isCustom: boolean; nameSingular: string }, + workspaceId: string, + ): Promise { + if (typeof value === 'number') { + return value; + } + + const dataSourceSchema = + this.workspaceDataSourceService.getSchemaName(workspaceId); + + const query = await this.recordPositionQueryFactory.create( + RecordPositionQueryType.GET, + value, + objectMetadata, + dataSourceSchema, + ); + + const records = await this.workspaceDataSourceService.executeRawQuery( + query, + [], + workspaceId, + undefined, + ); + + return ( + (value === 'first' + ? records[0]?.position / 2 + : records[0]?.position + 1) || 1 + ); + } +} diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/jobs/record-position-backfill.job.ts b/packages/twenty-server/src/workspace/workspace-query-runner/jobs/record-position-backfill.job.ts new file mode 100644 index 0000000000..938acfe46b --- /dev/null +++ b/packages/twenty-server/src/workspace/workspace-query-runner/jobs/record-position-backfill.job.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; + +import { MessageQueueJob } from 'src/integrations/message-queue/interfaces/message-queue-job.interface'; + +import { RecordPositionBackfillService } from 'src/workspace/workspace-query-runner/services/record-position-backfill-service'; + +export type RecordPositionBackfillJobData = { + workspaceId: string; + objectMetadata: { nameSingular: string; isCustom: boolean }; + recordId: string; +}; + +@Injectable() +export class RecordPositionBackfillJob + implements MessageQueueJob +{ + constructor( + private readonly recordPositionBackfillService: RecordPositionBackfillService, + ) {} + + async handle(data: RecordPositionBackfillJobData): Promise { + this.recordPositionBackfillService.backfill( + data.workspaceId, + data.objectMetadata, + data.recordId, + ); + } +} diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/listeners/record-position.listener.ts b/packages/twenty-server/src/workspace/workspace-query-runner/listeners/record-position.listener.ts new file mode 100644 index 0000000000..63537cc322 --- /dev/null +++ b/packages/twenty-server/src/workspace/workspace-query-runner/listeners/record-position.listener.ts @@ -0,0 +1,56 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { + CreatedObjectMetadata, + ObjectRecordCreateEvent, +} from 'src/integrations/event-emitter/types/object-record-create.event'; +import { MessageQueue } from 'src/integrations/message-queue/message-queue.constants'; +import { MessageQueueService } from 'src/integrations/message-queue/services/message-queue.service'; +import { + RecordPositionBackfillJob, + RecordPositionBackfillJobData, +} from 'src/workspace/workspace-query-runner/jobs/record-position-backfill.job'; + +@Injectable() +export class RecordPositionListener { + constructor( + @Inject(MessageQueue.recordPositionBackfillQueue) + private readonly messageQueueService: MessageQueueService, + ) {} + + @OnEvent('*.created') + async handleAllCreate(payload: ObjectRecordCreateEvent) { + if (!hasPositionField(payload.createdObjectMetadata)) { + return; + } + + if (hasPositionSet(payload.createdRecord)) { + return; + } + + this.messageQueueService.add( + RecordPositionBackfillJob.name, + { + workspaceId: payload.workspaceId, + recordId: payload.createdRecord.id, + objectMetadata: payload.createdObjectMetadata, + }, + ); + } +} + +const hasPositionField = ( + createdObjectMetadata: CreatedObjectMetadata, +): boolean => { + return ( + createdObjectMetadata.isCustom || + ['opportunity', 'company', 'people'].includes( + createdObjectMetadata.nameSingular, + ) + ); +}; + +const hasPositionSet = (createdRecord: any): boolean => { + return !!createdRecord?.position; +}; diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-module.ts b/packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-module.ts new file mode 100644 index 0000000000..de7a9bf69a --- /dev/null +++ b/packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; + +import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module'; +import { RecordPositionQueryFactory } from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; +import { RecordPositionFactory } from 'src/workspace/workspace-query-runner/factories/record-position.factory'; +import { RecordPositionBackfillService } from 'src/workspace/workspace-query-runner/services/record-position-backfill-service'; + +@Module({ + imports: [WorkspaceDataSourceModule], + providers: [ + RecordPositionFactory, + RecordPositionQueryFactory, + RecordPositionBackfillService, + ], + exports: [RecordPositionBackfillService], +}) +export class RecordPositionBackfillModule {} diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-service.ts b/packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-service.ts new file mode 100644 index 0000000000..b511c93e3d --- /dev/null +++ b/packages/twenty-server/src/workspace/workspace-query-runner/services/record-position-backfill-service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; + +import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface'; + +import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service'; +import { + RecordPositionQueryFactory, + RecordPositionQueryType, +} from 'src/workspace/workspace-query-builder/factories/record-position-query.factory'; +import { RecordPositionFactory } from 'src/workspace/workspace-query-runner/factories/record-position.factory'; + +@Injectable() +export class RecordPositionBackfillService { + constructor( + private readonly recordPositionFactory: RecordPositionFactory, + private readonly recordPositionQueryFactory: RecordPositionQueryFactory, + private readonly workspaceDataSourceService: WorkspaceDataSourceService, + ) {} + + async backfill( + workspaceId: string, + objectMetadata: { nameSingular: string; isCustom: boolean }, + recordId: string, + ) { + const position = await this.recordPositionFactory.create( + 'last', + objectMetadata as ObjectMetadataInterface, + workspaceId, + ); + + const dataSourceSchema = + this.workspaceDataSourceService.getSchemaName(workspaceId); + + const query = await this.recordPositionQueryFactory.create( + RecordPositionQueryType.UPDATE, + position, + objectMetadata as ObjectMetadataInterface, + dataSourceSchema, + recordId, + ); + + this.workspaceDataSourceService.executeRawQuery( + query, + [], + workspaceId, + undefined, + ); + } +} diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.module.ts b/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.module.ts index c31f854c04..d549b90d2c 100644 --- a/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.module.ts +++ b/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.module.ts @@ -4,6 +4,7 @@ import { WorkspaceQueryBuilderModule } from 'src/workspace/workspace-query-build import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module'; import { WorkspacePreQueryHookModule } from 'src/workspace/workspace-query-runner/workspace-pre-query-hook/workspace-pre-query-hook.module'; import { workspaceQueryRunnerFactories } from 'src/workspace/workspace-query-runner/factories'; +import { RecordPositionListener } from 'src/workspace/workspace-query-runner/listeners/record-position.listener'; import { WorkspaceQueryRunnerService } from './workspace-query-runner.service'; @@ -13,7 +14,11 @@ import { WorkspaceQueryRunnerService } from './workspace-query-runner.service'; WorkspaceDataSourceModule, WorkspacePreQueryHookModule, ], - providers: [WorkspaceQueryRunnerService, ...workspaceQueryRunnerFactories], + providers: [ + WorkspaceQueryRunnerService, + ...workspaceQueryRunnerFactories, + RecordPositionListener, + ], exports: [WorkspaceQueryRunnerService], }) export class WorkspaceQueryRunnerModule {} diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts b/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts index 42de864a3a..a2b8eabdcd 100644 --- a/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts +++ b/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts @@ -242,7 +242,11 @@ export class WorkspaceQueryRunnerService { parsedResults.forEach((record) => { this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.created`, { workspaceId, - createdRecord: [this.removeNestedProperties(record)], + createdRecord: this.removeNestedProperties(record), + createdObjectMetadata: { + nameSingular: objectMetadataItem.nameSingular, + isCustom: objectMetadataItem.isCustom, + }, } satisfies ObjectRecordCreateEvent); });