6071 return only updated fields of records in zapier update trigger (#8193)
- move webhook triggers into `entity-events-to-db.listener.ts` - refactor event management - add a `@OnDatabaseEvent` decorator to manage database events - add updatedFields in updated events - update openApi webhooks docs - update zapier integration
This commit is contained in:
@ -0,0 +1,90 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
import { ArrayContains } from 'typeorm';
|
||||
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.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 { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WebhookWorkspaceEntity } from 'src/modules/webhook/standard-objects/webhook.workspace-entity';
|
||||
import { ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/types/object-record.base.event';
|
||||
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
|
||||
import {
|
||||
CallWebhookJob,
|
||||
CallWebhookJobData,
|
||||
} from 'src/modules/webhook/jobs/call-webhook.job';
|
||||
|
||||
@Processor(MessageQueue.webhookQueue)
|
||||
export class CallWebhookJobsJob {
|
||||
private readonly logger = new Logger(CallWebhookJobsJob.name);
|
||||
|
||||
constructor(
|
||||
@InjectMessageQueue(MessageQueue.webhookQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
@Process(CallWebhookJobsJob.name)
|
||||
async handle(
|
||||
workspaceEventBatch: WorkspaceEventBatch<ObjectRecordBaseEvent>,
|
||||
): Promise<void> {
|
||||
// If you change that function, double check it does not break Zapier
|
||||
// trigger in packages/twenty-zapier/src/triggers/trigger_record.ts
|
||||
// Also change the openApi schema for webhooks
|
||||
// packages/twenty-server/src/engine/core-modules/open-api/utils/computeWebhooks.utils.ts
|
||||
|
||||
const webhookRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WebhookWorkspaceEntity>(
|
||||
workspaceEventBatch.workspaceId,
|
||||
'webhook',
|
||||
);
|
||||
|
||||
const [nameSingular, operation] = workspaceEventBatch.name.split('.');
|
||||
|
||||
const webhooks = await webhookRepository.find({
|
||||
where: [
|
||||
{ operations: ArrayContains([`${nameSingular}.${operation}`]) },
|
||||
{ operations: ArrayContains([`*.${operation}`]) },
|
||||
{ operations: ArrayContains([`${nameSingular}.*`]) },
|
||||
{ operations: ArrayContains(['*.*']) },
|
||||
],
|
||||
});
|
||||
|
||||
for (const eventData of workspaceEventBatch.events) {
|
||||
const eventName = workspaceEventBatch.name;
|
||||
const objectMetadata = {
|
||||
id: eventData.objectMetadata.id,
|
||||
nameSingular: eventData.objectMetadata.nameSingular,
|
||||
};
|
||||
const workspaceId = workspaceEventBatch.workspaceId;
|
||||
const record = eventData.properties.after || eventData.properties.before;
|
||||
const updatedFields = eventData.properties.updatedFields;
|
||||
|
||||
webhooks.forEach((webhook) => {
|
||||
const webhookData = {
|
||||
targetUrl: webhook.targetUrl,
|
||||
eventName,
|
||||
objectMetadata,
|
||||
workspaceId,
|
||||
webhookId: webhook.id,
|
||||
eventDate: new Date(),
|
||||
record,
|
||||
...(updatedFields && { updatedFields }),
|
||||
};
|
||||
|
||||
this.messageQueueService.add<CallWebhookJobData>(
|
||||
CallWebhookJob.name,
|
||||
webhookData,
|
||||
{ retryLimit: 3 },
|
||||
);
|
||||
});
|
||||
|
||||
webhooks.length > 0 &&
|
||||
this.logger.log(
|
||||
`CallWebhookJobsJob on eventName '${workspaceEventBatch.name}' triggered webhooks with ids [\n"${webhooks.map((webhook) => webhook.id).join('",\n"')}"\n]`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
import { AnalyticsService } from 'src/engine/core-modules/analytics/analytics.service';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
|
||||
export type CallWebhookJobData = {
|
||||
targetUrl: string;
|
||||
eventName: string;
|
||||
objectMetadata: { id: string; nameSingular: string };
|
||||
workspaceId: string;
|
||||
webhookId: string;
|
||||
eventDate: Date;
|
||||
record: any;
|
||||
updatedFields?: string[];
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.webhookQueue)
|
||||
export class CallWebhookJob {
|
||||
private readonly logger = new Logger(CallWebhookJob.name);
|
||||
constructor(
|
||||
private readonly httpService: HttpService,
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
) {}
|
||||
|
||||
@Process(CallWebhookJob.name)
|
||||
async handle(data: CallWebhookJobData): Promise<void> {
|
||||
const commonPayload = {
|
||||
url: data.targetUrl,
|
||||
webhookId: data.webhookId,
|
||||
eventName: data.eventName,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this.httpService.axiosRef.post(
|
||||
data.targetUrl,
|
||||
data,
|
||||
);
|
||||
const success = response.status >= 200 && response.status < 300;
|
||||
const eventInput = {
|
||||
action: 'webhook.response',
|
||||
payload: {
|
||||
status: response.status,
|
||||
success,
|
||||
...commonPayload,
|
||||
},
|
||||
};
|
||||
|
||||
this.analyticsService.create(eventInput, 'webhook', data.workspaceId);
|
||||
} catch (err) {
|
||||
const eventInput = {
|
||||
action: 'webhook.response',
|
||||
payload: {
|
||||
success: false,
|
||||
...commonPayload,
|
||||
...(err.response && { status: err.response.status }),
|
||||
},
|
||||
};
|
||||
|
||||
this.analyticsService.create(eventInput, 'webhook', data.workspaceId);
|
||||
this.logger.error(
|
||||
`Error calling webhook on targetUrl '${data.targetUrl}': ${err}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
|
||||
import { CallWebhookJobsJob } from 'src/modules/webhook/jobs/call-webhook-jobs.job';
|
||||
import { CallWebhookJob } from 'src/modules/webhook/jobs/call-webhook.job';
|
||||
import { AnalyticsModule } from 'src/engine/core-modules/analytics/analytics.module';
|
||||
|
||||
@Module({
|
||||
imports: [HttpModule, AnalyticsModule],
|
||||
providers: [CallWebhookJobsJob, CallWebhookJob],
|
||||
})
|
||||
export class WebhookJobModule {}
|
||||
Reference in New Issue
Block a user