66edd96213
In dev testing it worked fine but on production the TTL is failing we add a 1 hour buffer for safety <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22300?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
140 lines
4.7 KiB
TypeScript
140 lines
4.7 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
|
|
import { type Subscription } from '@microsoft/microsoft-graph-types';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
import { WebhookSubscriptionChannelType } from 'twenty-shared/types';
|
|
|
|
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
|
import { MICROSOFT_SUBSCRIPTION_TTL_MS } from 'src/modules/connected-account/webhook-subscription-manager/drivers/microsoft/constants/microsoft-subscription-ttl-ms.constant';
|
|
import {
|
|
WebhookSubscriptionDriverException,
|
|
WebhookSubscriptionDriverExceptionCode,
|
|
} from 'src/modules/connected-account/webhook-subscription-manager/drivers/exceptions/webhook-subscription-driver.exception';
|
|
import {
|
|
type WebhookSubscriptionContext,
|
|
type WebhookSubscriptionDriver,
|
|
type WebhookSubscriptionResult,
|
|
} from 'src/modules/connected-account/webhook-subscription-manager/types/webhook-subscription-driver.type';
|
|
import { MicrosoftOAuth2ClientProvider } from 'src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client.provider';
|
|
import { MICROSOFT_SUBSCRIPTION_TTL_BUFFER_MS } from './constants/microsoft-subscription-ttl-ms-buffer.constant';
|
|
|
|
type MicrosoftGraphResourceConfig = Pick<
|
|
Subscription,
|
|
'resource' | 'changeType'
|
|
> & {
|
|
notificationPath: string;
|
|
};
|
|
|
|
const MICROSOFT_GRAPH_RESOURCE_CONFIG_BY_CHANNEL_TYPE: Record<
|
|
WebhookSubscriptionChannelType,
|
|
MicrosoftGraphResourceConfig
|
|
> = {
|
|
[WebhookSubscriptionChannelType.MESSAGING]: {
|
|
resource: '/me/messages',
|
|
changeType: 'created,updated',
|
|
notificationPath: 'webhooks/microsoft/messaging',
|
|
},
|
|
[WebhookSubscriptionChannelType.CALENDAR]: {
|
|
resource: '/me/events',
|
|
changeType: 'created,updated,deleted',
|
|
notificationPath: 'webhooks/microsoft/calendar',
|
|
},
|
|
};
|
|
|
|
@Injectable()
|
|
export class MicrosoftWebhookSubscriptionDriver implements WebhookSubscriptionDriver {
|
|
constructor(
|
|
private readonly microsoftOAuth2ClientProvider: MicrosoftOAuth2ClientProvider,
|
|
private readonly twentyConfigService: TwentyConfigService,
|
|
) {}
|
|
|
|
async createSubscription(
|
|
connectedAccountId: string,
|
|
channelType: WebhookSubscriptionChannelType,
|
|
clientState: string,
|
|
): Promise<WebhookSubscriptionResult> {
|
|
const resourceConfig =
|
|
MICROSOFT_GRAPH_RESOURCE_CONFIG_BY_CHANNEL_TYPE[channelType];
|
|
const graphClient =
|
|
await this.microsoftOAuth2ClientProvider.getClient(connectedAccountId);
|
|
|
|
const notificationUrl = `${this.twentyConfigService.get('SERVER_URL')}/${resourceConfig.notificationPath}`;
|
|
|
|
const SUBSCRIPTION_TTL_MS =
|
|
MICROSOFT_SUBSCRIPTION_TTL_MS - MICROSOFT_SUBSCRIPTION_TTL_BUFFER_MS;
|
|
|
|
const subscriptionPayload: Subscription = {
|
|
changeType: resourceConfig.changeType,
|
|
notificationUrl,
|
|
lifecycleNotificationUrl: notificationUrl,
|
|
resource: resourceConfig.resource,
|
|
expirationDateTime: new Date(
|
|
Date.now() + SUBSCRIPTION_TTL_MS,
|
|
).toISOString(),
|
|
clientState,
|
|
};
|
|
|
|
const subscription: Subscription = await graphClient
|
|
.api('/subscriptions')
|
|
.post(subscriptionPayload);
|
|
|
|
return this.toResult(subscription);
|
|
}
|
|
|
|
async renewSubscription(
|
|
context: WebhookSubscriptionContext,
|
|
): Promise<WebhookSubscriptionResult> {
|
|
const graphClient = await this.microsoftOAuth2ClientProvider.getClient(
|
|
context.connectedAccountId,
|
|
);
|
|
|
|
const SUBSCRIPTION_TTL_MS =
|
|
MICROSOFT_SUBSCRIPTION_TTL_MS - MICROSOFT_SUBSCRIPTION_TTL_BUFFER_MS;
|
|
|
|
const subscriptionPatch: Subscription = {
|
|
expirationDateTime: new Date(
|
|
Date.now() + SUBSCRIPTION_TTL_MS,
|
|
).toISOString(),
|
|
};
|
|
|
|
const renewedSubscription: Subscription = await graphClient
|
|
.api(`/subscriptions/${context.externalSubscriptionId}`)
|
|
.patch(subscriptionPatch);
|
|
|
|
return this.toResult(renewedSubscription);
|
|
}
|
|
|
|
async deleteSubscription(context: WebhookSubscriptionContext): Promise<void> {
|
|
if (!isDefined(context.externalSubscriptionId)) {
|
|
return;
|
|
}
|
|
|
|
const graphClient = await this.microsoftOAuth2ClientProvider.getClient(
|
|
context.connectedAccountId,
|
|
);
|
|
|
|
await graphClient
|
|
.api(`/subscriptions/${context.externalSubscriptionId}`)
|
|
.delete();
|
|
}
|
|
|
|
private toResult(subscription: Subscription): WebhookSubscriptionResult {
|
|
if (
|
|
!isDefined(subscription.id) ||
|
|
!isDefined(subscription.expirationDateTime)
|
|
) {
|
|
throw new WebhookSubscriptionDriverException(
|
|
'Microsoft Graph subscription response did not include an id or expiration',
|
|
WebhookSubscriptionDriverExceptionCode.PROVIDER_RESPONSE_INVALID,
|
|
);
|
|
}
|
|
|
|
return {
|
|
externalSubscriptionId: subscription.id,
|
|
externalResourceId: null,
|
|
expiresAt: new Date(subscription.expirationDateTime),
|
|
};
|
|
}
|
|
}
|