8643 fix sentry error (#8644)

- fixes missing data in event payload when adding a new workspaceMember
- add strong typing to database event emitters
This commit is contained in:
martmull
2024-11-21 17:09:36 +01:00
committed by GitHub
parent 395da91071
commit 39373b4a28
61 changed files with 460 additions and 311 deletions

View File

@ -0,0 +1,13 @@
import { OnEvent } from '@nestjs/event-emitter';
import { CustomEventName } from 'src/engine/workspace-event-emitter/types/custom-event-name.type';
export function OnCustomBatchEvent(event: CustomEventName): MethodDecorator {
return (
target: object,
propertyKey: string,
descriptor: PropertyDescriptor,
) => {
OnEvent(event)(target, propertyKey, descriptor);
};
}

View File

@ -2,7 +2,7 @@ import { OnEvent } from '@nestjs/event-emitter';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
export function OnDatabaseEvent(
export function OnDatabaseBatchEvent(
object: string,
action: DatabaseEventAction,
): MethodDecorator {

View File

@ -17,9 +17,10 @@ export class ApiEventEmitterService {
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emit(
`${objectMetadataItem.nameSingular}.${DatabaseEventAction.CREATED}`,
records.map((record) => ({
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.CREATED,
events: records.map((record) => ({
userId: authContext.user?.id,
recordId: record.id,
objectMetadata: objectMetadataItem,
@ -28,8 +29,8 @@ export class ApiEventEmitterService {
after: this.removeGraphQLAndNestedProperties(record),
},
})),
authContext.workspace.id,
);
workspaceId: authContext.workspace.id,
});
}
public emitUpdateEvents<T extends ObjectRecord>(
@ -47,9 +48,10 @@ export class ApiEventEmitterService {
{},
);
this.workspaceEventEmitter.emit(
`${objectMetadataItem.nameSingular}.${DatabaseEventAction.UPDATED}`,
records.map((record) => {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.UPDATED,
events: records.map((record) => {
const before = this.removeGraphQLAndNestedProperties(
mappedExistingRecords[record.id],
);
@ -73,8 +75,8 @@ export class ApiEventEmitterService {
},
};
}),
authContext.workspace.id,
);
workspaceId: authContext.workspace.id,
});
}
public emitDeletedEvents<T extends ObjectRecord>(
@ -82,9 +84,10 @@ export class ApiEventEmitterService {
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emit(
`${objectMetadataItem.nameSingular}.${DatabaseEventAction.DELETED}`,
records.map((record) => {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.DELETED,
events: records.map((record) => {
return {
userId: authContext.user?.id,
recordId: record.id,
@ -95,8 +98,8 @@ export class ApiEventEmitterService {
},
};
}),
authContext.workspace.id,
);
workspaceId: authContext.workspace.id,
});
}
public emitDestroyEvents<T extends ObjectRecord>(
@ -104,9 +107,10 @@ export class ApiEventEmitterService {
authContext: AuthContext,
objectMetadataItem: ObjectMetadataInterface,
): void {
this.workspaceEventEmitter.emit(
`${objectMetadataItem.nameSingular}.${DatabaseEventAction.DESTROYED}`,
records.map((record) => {
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: objectMetadataItem.nameSingular,
action: DatabaseEventAction.DESTROYED,
events: records.map((record) => {
return {
userId: authContext.user?.id,
recordId: record.id,
@ -117,8 +121,8 @@ export class ApiEventEmitterService {
},
};
}),
authContext.workspace.id,
);
workspaceId: authContext.workspace.id,
});
}
private removeGraphQLAndNestedProperties<T extends ObjectRecord>(record: T) {

View File

@ -0,0 +1 @@
export const USER_SIGNUP_EVENT_NAME = 'user_signup';

View File

@ -16,6 +16,9 @@ export class RecordPositionBackfillJob {
@Process(RecordPositionBackfillJob.name)
async handle(data: RecordPositionBackfillJobData): Promise<void> {
this.recordPositionBackfillService.backfill(data.workspaceId, data.dryRun);
await this.recordPositionBackfillService.backfill(
data.workspaceId,
data.dryRun,
);
}
}

View File

@ -6,10 +6,10 @@ import { ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/typ
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
import { CreateAuditLogFromInternalEvent } from 'src/modules/timeline/jobs/create-audit-log-from-internal-event';
import { UpsertTimelineActivityFromInternalEvent } from 'src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job';
import { OnDatabaseEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-event.decorator';
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
import { CallWebhookJobsJob } from 'src/modules/webhook/jobs/call-webhook-jobs.job';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
@ -22,55 +22,49 @@ export class EntityEventsToDbListener {
private readonly webhookQueueService: MessageQueueService,
) {}
@OnDatabaseEvent('*', DatabaseEventAction.CREATED)
async handleCreate(
payload: WorkspaceEventBatch<ObjectRecordCreateEvent<any>>,
) {
return this.handle(payload);
@OnDatabaseBatchEvent('*', DatabaseEventAction.CREATED)
async handleCreate(batchEvent: WorkspaceEventBatch<ObjectRecordCreateEvent>) {
return this.handle(batchEvent);
}
@OnDatabaseEvent('*', DatabaseEventAction.UPDATED)
async handleUpdate(
payload: WorkspaceEventBatch<ObjectRecordUpdateEvent<any>>,
) {
return this.handle(payload);
@OnDatabaseBatchEvent('*', DatabaseEventAction.UPDATED)
async handleUpdate(batchEvent: WorkspaceEventBatch<ObjectRecordUpdateEvent>) {
return this.handle(batchEvent);
}
@OnDatabaseEvent('*', DatabaseEventAction.DELETED)
async handleDelete(
payload: WorkspaceEventBatch<ObjectRecordUpdateEvent<any>>,
) {
return this.handle(payload);
@OnDatabaseBatchEvent('*', DatabaseEventAction.DELETED)
async handleDelete(batchEvent: WorkspaceEventBatch<ObjectRecordUpdateEvent>) {
return this.handle(batchEvent);
}
@OnDatabaseEvent('*', DatabaseEventAction.DESTROYED)
@OnDatabaseBatchEvent('*', DatabaseEventAction.DESTROYED)
async handleDestroy(
payload: WorkspaceEventBatch<ObjectRecordUpdateEvent<any>>,
batchEvent: WorkspaceEventBatch<ObjectRecordUpdateEvent>,
) {
return this.handle(payload);
return this.handle(batchEvent);
}
private async handle(payload: WorkspaceEventBatch<ObjectRecordBaseEvent>) {
const filteredEvents = payload.events.filter(
private async handle(batchEvent: WorkspaceEventBatch<ObjectRecordBaseEvent>) {
const filteredEvents = batchEvent.events.filter(
(event) => event.objectMetadata?.isAuditLogged,
);
await this.entityEventsToDbQueueService.add<
WorkspaceEventBatch<ObjectRecordBaseEvent>
>(CreateAuditLogFromInternalEvent.name, {
...payload,
...batchEvent,
events: filteredEvents,
});
await this.entityEventsToDbQueueService.add<
WorkspaceEventBatch<ObjectRecordBaseEvent>
>(UpsertTimelineActivityFromInternalEvent.name, {
...payload,
...batchEvent,
events: filteredEvents,
});
await this.webhookQueueService.add<
WorkspaceEventBatch<ObjectRecordBaseEvent>
>(CallWebhookJobsJob.name, payload, { retryLimit: 3 });
>(CallWebhookJobsJob.name, batchEvent, { retryLimit: 3 });
}
}

View File

@ -1,12 +1,13 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { OnDatabaseEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-event.decorator';
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { AnalyticsService } from 'src/engine/core-modules/analytics/analytics.service';
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
import { TelemetryService } from 'src/engine/core-modules/telemetry/telemetry.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
import { USER_SIGNUP_EVENT_NAME } from 'src/engine/api/graphql/workspace-query-runner/constants/user-signup-event-name.constants';
import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator';
@Injectable()
export class TelemetryListener {
@ -15,10 +16,8 @@ export class TelemetryListener {
private readonly telemetryService: TelemetryService,
) {}
@OnDatabaseEvent('*', DatabaseEventAction.CREATED)
async handleAllCreate(
payload: WorkspaceEventBatch<ObjectRecordCreateEvent<any>>,
) {
@OnDatabaseBatchEvent('*', DatabaseEventAction.CREATED)
async handleAllCreate(payload: WorkspaceEventBatch<ObjectRecordCreateEvent>) {
await Promise.all(
payload.events.map((eventPayload) =>
this.analyticsService.create(
@ -33,15 +32,15 @@ export class TelemetryListener {
);
}
@OnEvent('user.signup')
@OnCustomBatchEvent(USER_SIGNUP_EVENT_NAME)
async handleUserSignup(
payload: WorkspaceEventBatch<ObjectRecordCreateEvent<any>>,
payload: WorkspaceEventBatch<ObjectRecordCreateEvent>,
) {
await Promise.all(
payload.events.map(async (eventPayload) => {
this.analyticsService.create(
{
action: 'user.signup',
action: USER_SIGNUP_EVENT_NAME,
payload: {},
},
eventPayload.userId,
@ -50,7 +49,7 @@ export class TelemetryListener {
this.telemetryService.create(
{
action: 'user.signup',
action: USER_SIGNUP_EVENT_NAME,
payload: {
payload,
userId: undefined,