Files
twenty/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service.ts
T
Weiko 1c04b2b0b7 [Flexible-schema] Refactor gql query runner to emit api event before processing to gql types (#8596)
Fixes https://github.com/twentyhq/twenty/issues/8300

## Context
API events were created too late and were already formatted as Gql
responses (including nesting with edges/node/type + formatting that
should not exist in an event payload). This PR moves the emit logic to
the resolver where we actually do the DB query
Note: Also added RESTORED events
2024-11-21 18:11:28 +01:00

149 lines
4.5 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { objectRecordChangedValues } from 'src/engine/core-modules/event-emitter/utils/object-record-changed-values';
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
@Injectable()
export class ApiEventEmitterService {
constructor(private readonly workspaceEventEmitter: WorkspaceEventEmitter) {}
public emitCreateEvents<T extends ObjectRecord>(
records: T[],
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.CREATED,
events: records.map((record) => ({
userId: authContext.user?.id,
recordId: record.id,
objectMetadata: objectMetadataItem,
properties: {
before: null,
after: record,
},
})),
workspaceId: authContext.workspace.id,
});
}
public emitUpdateEvents<T extends ObjectRecord>(
existingRecords: T[],
records: T[],
updatedFields: string[],
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
const mappedExistingRecords = existingRecords.reduce(
(acc, { id, ...record }) => ({
...acc,
[id]: record,
}),
{},
);
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.UPDATED,
events: records.map((record) => {
const before = mappedExistingRecords[record.id];
const after = record;
const diff = objectRecordChangedValues(
before,
after,
updatedFields,
objectMetadataItem,
);
return {
userId: authContext.user?.id,
recordId: record.id,
objectMetadata: objectMetadataItem,
properties: {
before,
after,
updatedFields,
diff,
},
};
}),
workspaceId: authContext.workspace.id,
});
}
public emitDeletedEvents<T extends ObjectRecord>(
records: T[],
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.DELETED,
events: records.map((record) => {
return {
userId: authContext.user?.id,
recordId: record.id,
objectMetadata: objectMetadataItem,
properties: {
before: record,
after: null,
},
};
}),
workspaceId: authContext.workspace.id,
});
}
public emitRestoreEvents<T extends ObjectRecord>(
records: T[],
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.RESTORED,
events: records.map((record) => {
return {
userId: authContext.user?.id,
recordId: record.id,
objectMetadata: objectMetadataItem,
properties: {
before: null,
after: record,
},
};
}),
workspaceId: authContext.workspace.id,
});
}
public emitDestroyEvents<T extends ObjectRecord>(
records: T[],
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.DESTROYED,
events: records.map((record) => {
return {
userId: authContext.user?.id,
recordId: record.id,
objectMetadata: objectMetadataItem,
properties: {
before: record,
after: null,
},
};
}),
workspaceId: authContext.workspace.id,
});
}
}