diff --git a/packages/twenty-server/src/engine/core-modules/metrics/types/metrics-keys.type.ts b/packages/twenty-server/src/engine/core-modules/metrics/types/metrics-keys.type.ts index 5760c83a56..eac434bf4a 100644 --- a/packages/twenty-server/src/engine/core-modules/metrics/types/metrics-keys.type.ts +++ b/packages/twenty-server/src/engine/core-modules/metrics/types/metrics-keys.type.ts @@ -53,4 +53,6 @@ export enum MetricsKeys { AiChatTurnCompleted = 'ai-chat/turn-completed', AiChatTurnFailed = 'ai-chat/turn-failed', WorkspaceMetadataCacheLocalEviction = 'workspace-metadata-cache/local-eviction', + ConnectedAccountSyncWebhookReceivedMessaging = 'connected-account-sync-webhook/received/messaging', + ConnectedAccountSyncWebhookReceivedCalendar = 'connected-account-sync-webhook/received/calendar', } diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-calendar-notification.handler.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-calendar-notification.handler.ts index bfa67b2a8e..8d4cb84410 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-calendar-notification.handler.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-calendar-notification.handler.ts @@ -1,6 +1,6 @@ import { timingSafeEqual } from 'crypto'; -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { isNonEmptyString } from '@sniptt/guards'; @@ -8,6 +8,8 @@ import { WebhookSubscriptionStatus } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; +import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; +import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity'; import { WebhookSyncTriggerService } from 'src/modules/connected-account/webhook-subscription-manager/services/webhook-sync-trigger.service'; import { @@ -21,13 +23,21 @@ const GOOGLE_CALENDAR_SYNC_RESOURCE_STATE = 'sync'; @Injectable() export class GoogleCalendarNotificationHandler implements WebhookNotificationHandler { + private readonly logger = new Logger(GoogleCalendarNotificationHandler.name); + constructor( @InjectRepository(CalendarChannelEntity) private readonly calendarChannelRepository: Repository, private readonly webhookSyncTriggerService: WebhookSyncTriggerService, + private readonly metricsService: MetricsService, ) {} async handle(request: GoogleCalendarChannelNotification): Promise { + this.metricsService.incrementCounterBy({ + key: MetricsKeys.ConnectedAccountSyncWebhookReceivedCalendar, + amount: 1, + }); + if (request.resourceState === GOOGLE_CALENDAR_SYNC_RESOURCE_STATE) { return; } @@ -44,6 +54,10 @@ export class GoogleCalendarNotificationHandler implements WebhookNotificationHan }); if (!isDefined(calendarChannel)) { + this.logger.warn( + `No calendar channel found for Google channel ${request.channelId}`, + ); + return; } @@ -72,5 +86,9 @@ export class GoogleCalendarNotificationHandler implements WebhookNotificationHan calendarChannel.id, calendarChannel.workspaceId, ); + + this.logger.log( + `Triggered calendar sync for calendar channel ${calendarChannel.id} from Google notification`, + ); } } diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts index fd144b20ed..3960e51e5f 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { isNonEmptyString } from '@sniptt/guards'; @@ -10,6 +10,8 @@ import { import { isDefined } from 'twenty-shared/utils'; import { In, Repository } from 'typeorm'; +import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; +import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity'; import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity'; @@ -31,8 +33,11 @@ export type GoogleMessagingNotificationRequest = { @Injectable() export class GoogleMessagingNotificationHandler implements WebhookNotificationHandler { + private readonly logger = new Logger(GoogleMessagingNotificationHandler.name); + constructor( private readonly twentyConfigService: TwentyConfigService, + private readonly metricsService: MetricsService, @InjectRepository(ConnectedAccountEntity) private readonly connectedAccountRepository: Repository, @InjectRepository(MessageChannelEntity) @@ -43,6 +48,11 @@ export class GoogleMessagingNotificationHandler implements WebhookNotificationHa async handle(request: GoogleMessagingNotificationRequest): Promise { await this.verify(request.authorizationHeader); + this.metricsService.incrementCounterBy({ + key: MetricsKeys.ConnectedAccountSyncWebhookReceivedMessaging, + amount: 1, + }); + const decodedData = this.decodeMessageData(request.body); if (!isDefined(decodedData)) { @@ -61,6 +71,10 @@ export class GoogleMessagingNotificationHandler implements WebhookNotificationHa ); if (connectedAccountIds.length === 0) { + this.logger.warn( + 'No Google connected account matches Gmail notification', + ); + return; } @@ -71,12 +85,20 @@ export class GoogleMessagingNotificationHandler implements WebhookNotificationHa }, }); + if (messageChannels.length === 0) { + return; + } + for (const messageChannel of messageChannels) { await this.webhookSyncTriggerService.triggerMessagingSync( messageChannel.id, messageChannel.workspaceId, ); } + + this.logger.log( + `Triggered messaging sync for ${messageChannels.length} message channels from Gmail notification`, + ); } private async verify(authorizationHeader: string | undefined): Promise { diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-webhook-driver.module.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-webhook-driver.module.ts index cfc2423362..14cc3044e8 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-webhook-driver.module.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-webhook-driver.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module'; import { CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity'; import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity'; @@ -12,6 +13,7 @@ import { GoogleMessagingNotificationHandler } from 'src/modules/connected-accoun @Module({ imports: [ + MetricsModule, TwentyConfigModule, WebhookSubscriptionModule, TypeOrmModule.forFeature([ diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-calendar-notification.handler.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-calendar-notification.handler.ts index b8fbaf84a6..1942e4fbaa 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-calendar-notification.handler.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-calendar-notification.handler.ts @@ -7,6 +7,8 @@ import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { In, Repository } from 'typeorm'; +import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; +import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity'; import { CalendarWebhookSubscriptionService } from 'src/modules/connected-account/webhook-subscription-manager/services/calendar-webhook-subscription.service'; import { WebhookSyncTriggerService } from 'src/modules/connected-account/webhook-subscription-manager/services/webhook-sync-trigger.service'; @@ -26,9 +28,17 @@ export class MicrosoftCalendarNotificationHandler implements WebhookNotification private readonly calendarChannelRepository: Repository, private readonly calendarWebhookSubscriptionService: CalendarWebhookSubscriptionService, private readonly webhookSyncTriggerService: WebhookSyncTriggerService, + private readonly metricsService: MetricsService, ) {} async handle(notifications: MicrosoftGraphNotification[]): Promise { + if (notifications.length > 0) { + this.metricsService.incrementCounterBy({ + key: MetricsKeys.ConnectedAccountSyncWebhookReceivedCalendar, + amount: notifications.length, + }); + } + const subscriptionIds = notifications .map((notification) => notification.subscriptionId) .filter(isNonEmptyString); @@ -96,6 +106,10 @@ export class MicrosoftCalendarNotificationHandler implements WebhookNotification calendarChannel.id, calendarChannel.workspaceId, ); + + this.logger.log( + `Triggered calendar sync for calendar channel ${calendarChannel.id} from Microsoft notification`, + ); } } } diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-messaging-notification.handler.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-messaging-notification.handler.ts index c33ebf0904..716c358e14 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-messaging-notification.handler.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-messaging-notification.handler.ts @@ -7,6 +7,8 @@ import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { In, Repository } from 'typeorm'; +import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; +import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity'; import { MessagingWebhookSubscriptionService } from 'src/modules/connected-account/webhook-subscription-manager/services/messaging-webhook-subscription.service'; import { WebhookSyncTriggerService } from 'src/modules/connected-account/webhook-subscription-manager/services/webhook-sync-trigger.service'; @@ -26,9 +28,17 @@ export class MicrosoftMessagingNotificationHandler implements WebhookNotificatio private readonly messageChannelRepository: Repository, private readonly messagingWebhookSubscriptionService: MessagingWebhookSubscriptionService, private readonly webhookSyncTriggerService: WebhookSyncTriggerService, + private readonly metricsService: MetricsService, ) {} async handle(notifications: MicrosoftGraphNotification[]): Promise { + if (notifications.length > 0) { + this.metricsService.incrementCounterBy({ + key: MetricsKeys.ConnectedAccountSyncWebhookReceivedMessaging, + amount: notifications.length, + }); + } + const subscriptionIds = notifications .map((notification) => notification.subscriptionId) .filter(isNonEmptyString); @@ -95,6 +105,10 @@ export class MicrosoftMessagingNotificationHandler implements WebhookNotificatio messageChannel.id, messageChannel.workspaceId, ); + + this.logger.log( + `Triggered messaging sync for message channel ${messageChannel.id} from Microsoft notification`, + ); } } } diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-webhook-driver.module.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-webhook-driver.module.ts index 8c9f4bab85..4b92d2e21b 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-webhook-driver.module.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/microsoft/microsoft-webhook-driver.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module'; import { CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity'; import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity'; @@ -11,6 +12,7 @@ import { MicrosoftMessagingNotificationHandler } from 'src/modules/connected-acc @Module({ imports: [ + MetricsModule, TwentyConfigModule, WebhookSubscriptionModule, TypeOrmModule.forFeature([MessageChannelEntity, CalendarChannelEntity]),