Skip to content

Commit

Permalink
feat(oauth): gated setup for intercom dual mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry Fontanier committed Jul 19, 2024
1 parent 51ebbf6 commit 193c459
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 62 deletions.
14 changes: 8 additions & 6 deletions connectors/src/connectors/intercom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
revokeSyncCollection,
revokeSyncHelpCenter,
} from "@connectors/connectors/intercom/lib/help_center_permissions";
import { getIntercomAccessToken } from "@connectors/connectors/intercom/lib/intercom_access_token";
import { fetchIntercomWorkspace } from "@connectors/connectors/intercom/lib/intercom_api";
import {
getHelpCenterArticleIdFromInternalId,
Expand Down Expand Up @@ -61,16 +62,17 @@ export class IntercomConnectorManager extends BaseConnectorManager<null> {
dataSourceConfig: DataSourceConfig;
connectionId: string;
}): Promise<Result<string, Error>> {
const nangoConnectionId = connectionId;
const intercomAccessToken = await getIntercomAccessToken(connectionId);

if (!NANGO_INTERCOM_CONNECTOR_ID) {
throw new Error("NANGO_INTERCOM_CONNECTOR_ID not set");
}
// if (!NANGO_INTERCOM_CONNECTOR_ID) {
// throw new Error("NANGO_INTERCOM_CONNECTOR_ID not set");
// }

let connector = null;

try {
const intercomWorkspace = await fetchIntercomWorkspace(nangoConnectionId);
const intercomWorkspace =
await fetchIntercomWorkspace(intercomAccessToken);
if (!intercomWorkspace) {
return new Err(
new Error(
Expand All @@ -91,7 +93,7 @@ export class IntercomConnectorManager extends BaseConnectorManager<null> {
connector = await ConnectorResource.makeNew(
"intercom",
{
connectionId: nangoConnectionId,
connectionId,
workspaceAPIKey: dataSourceConfig.workspaceAPIKey,
workspaceId: dataSourceConfig.workspaceId,
dataSourceName: dataSourceConfig.dataSourceName,
Expand Down
40 changes: 40 additions & 0 deletions connectors/src/connectors/intercom/lib/intercom_access_token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { getOAuthConnectionAccessToken } from "@dust-tt/types";

import { apiConfig } from "@connectors/lib/api/config";
import { getAccessTokenFromNango } from "@connectors/lib/nango_helpers";
import { isDualUseOAuthConnectionId } from "@connectors/lib/oauth";
import logger from "@connectors/logger/logger";

const { NANGO_INTERCOM_CONNECTOR_ID } = process.env;

export async function getIntercomAccessToken(
connectionId: string
): Promise<string> {
if (isDualUseOAuthConnectionId(connectionId)) {
const tokRes = await getOAuthConnectionAccessToken({
config: apiConfig.getOAuthAPIConfig(),
logger,
provider: "intercom",
connectionId,
});
if (tokRes.isErr()) {
logger.error(
{ connectionId, error: tokRes.error },
"Error retrieving Intercom access token"
);
throw new Error("Error retrieving Intercom access token");
}

return tokRes.value.access_token;
} else {
// TODO(@fontanierh) INTERCOM_MIGRATION remove once migrated
if (!NANGO_INTERCOM_CONNECTOR_ID) {
throw new Error("NANGO_INTERCOM_CONNECTOR_ID is not defined");
}
return getAccessTokenFromNango({
connectionId: connectionId,
integrationId: NANGO_INTERCOM_CONNECTOR_ID,
useCache: true,
});
}
}
71 changes: 28 additions & 43 deletions connectors/src/connectors/intercom/lib/intercom_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@ import {
ExternalOauthTokenError,
ProviderWorkflowError,
} from "@connectors/lib/error";
import { getAccessTokenFromNango } from "@connectors/lib/nango_helpers";
import logger from "@connectors/logger/logger";

const { NANGO_INTERCOM_CONNECTOR_ID } = process.env;

/**
* Utility function to call the Intercom API.
* It centralizes fetching the Access Token from Nango, calling the API and handling global errors.
*/
async function queryIntercomAPI({
nangoConnectionId,
accessToken,
path,
method,
body,
}: {
nangoConnectionId: string;
accessToken: string;
path: string;
method: "GET" | "POST";
body?: {
Expand All @@ -43,16 +40,6 @@ async function queryIntercomAPI({
};
};
}) {
if (!NANGO_INTERCOM_CONNECTOR_ID) {
throw new Error("NANGO_NOTION_CONNECTOR_ID not set");
}

const accessToken = await getAccessTokenFromNango({
connectionId: nangoConnectionId,
integrationId: NANGO_INTERCOM_CONNECTOR_ID,
useCache: true,
});

// Intercom will route to the correct region based on the token.
// https://developers.intercom.com/docs/build-an-integration/learn-more/rest-apis/#regional-hosting
const rawResponse = await fetch(`https://api.intercom.io/${path}`, {
Expand Down Expand Up @@ -114,15 +101,13 @@ async function queryIntercomAPI({
/**
* Return the Intercom Workspace.
*/
export async function fetchIntercomWorkspace(
nangoConnectionId: string
): Promise<{
export async function fetchIntercomWorkspace(accessToken: string): Promise<{
id: string;
name: string;
region: string;
} | null> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: "me",
method: "GET",
});
Expand All @@ -146,13 +131,13 @@ export async function fetchIntercomWorkspace(
* Return the list of Help Centers of the Intercom workspace
*/
export async function fetchIntercomHelpCenters(
nangoConnectionId: string
accessToken: string
): Promise<IntercomHelpCenterType[]> {
const response: {
type: "list";
data: IntercomHelpCenterType[];
} = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: "help_center/help_centers",
method: "GET",
});
Expand All @@ -164,11 +149,11 @@ export async function fetchIntercomHelpCenters(
* Return the detail of Help Center
*/
export async function fetchIntercomHelpCenter(
nangoConnectionId: string,
accessToken: string,
helpCenterId: string
): Promise<IntercomHelpCenterType | null> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `help_center/help_centers/${helpCenterId}`,
method: "GET",
});
Expand All @@ -180,7 +165,7 @@ export async function fetchIntercomHelpCenter(
* Return the list of Collections filtered by Help Center and parent Collection.
*/
export async function fetchIntercomCollections(
nangoConnectionId: string,
accessToken: string,
helpCenterId: string,
parentId: string | null
): Promise<IntercomCollectionType[]> {
Expand All @@ -189,7 +174,7 @@ export async function fetchIntercomCollections(
const collections: IntercomCollectionType[] = [];
do {
response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `help_center/collections?page=${page}&per_page=12`,
method: "GET",
});
Expand Down Expand Up @@ -220,11 +205,11 @@ export async function fetchIntercomCollections(
* Return the detail of a Collection.
*/
export async function fetchIntercomCollection(
nangoConnectionId: string,
accessToken: string,
collectionId: string
): Promise<IntercomCollectionType | null> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `help_center/collections/${collectionId}`,
method: "GET",
});
Expand All @@ -236,12 +221,12 @@ export async function fetchIntercomCollection(
* Return the Articles that are children of a given Collection.
*/
export async function fetchIntercomArticles({
nangoConnectionId,
accessToken,
helpCenterId,
page = 1,
pageSize = 12,
}: {
nangoConnectionId: string;
accessToken: string;
helpCenterId: string;
page: number;
pageSize?: number;
Expand All @@ -259,7 +244,7 @@ export async function fetchIntercomArticles({
};
}> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `articles/search?help_center_id=${helpCenterId}&state=published&page=${page}&per_page=${pageSize}`,
method: "GET",
});
Expand All @@ -271,10 +256,10 @@ export async function fetchIntercomArticles({
* Return the list of Teams.
*/
export async function fetchIntercomTeams(
nangoConnectionId: string
accessToken: string
): Promise<IntercomTeamType[]> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `teams`,
method: "GET",
});
Expand All @@ -286,11 +271,11 @@ export async function fetchIntercomTeams(
* Return the detail of a Team.
*/
export async function fetchIntercomTeam(
nangoConnectionId: string,
accessToken: string,
teamId: string
): Promise<IntercomTeamType | null> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `teams/${teamId}`,
method: "GET",
});
Expand All @@ -303,13 +288,13 @@ export async function fetchIntercomTeam(
* Filtered on a slidingWindow and closed Conversations.
*/
export async function fetchIntercomConversations({
nangoConnectionId,
accessToken,
teamId,
slidingWindow,
cursor = null,
pageSize = 20,
}: {
nangoConnectionId: string;
accessToken: string;
teamId?: string;
slidingWindow: number;
cursor: string | null;
Expand Down Expand Up @@ -354,7 +339,7 @@ export async function fetchIntercomConversations({
}

const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `conversations/search`,
method: "POST",
body: {
Expand All @@ -377,13 +362,13 @@ export async function fetchIntercomConversations({
* Filtered on closed Conversations.
*/
export async function fetchIntercomConversationsForDay({
nangoConnectionId,
accessToken,
minCreatedAt,
maxCreatedAt,
cursor = null,
pageSize = 20,
}: {
nangoConnectionId: string;
accessToken: string;
minCreatedAt: number;
maxCreatedAt: number;
cursor: string | null;
Expand All @@ -398,7 +383,7 @@ export async function fetchIntercomConversationsForDay({
};
}> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `conversations/search`,
method: "POST",
body: {
Expand Down Expand Up @@ -431,14 +416,14 @@ export async function fetchIntercomConversationsForDay({
* Return the detail of a Conversation.
*/
export async function fetchIntercomConversation({
nangoConnectionId,
accessToken,
conversationId,
}: {
nangoConnectionId: string;
accessToken: string;
conversationId: string;
}): Promise<IntercomConversationWithPartsType | null> {
const response = await queryIntercomAPI({
nangoConnectionId,
accessToken,
path: `conversations/${conversationId}`,
method: "GET",
});
Expand Down
14 changes: 8 additions & 6 deletions connectors/src/connectors/intercom/temporal/activities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ModelId } from "@dust-tt/types";
import { Op } from "sequelize";

import { getIntercomAccessToken } from "@connectors/connectors/intercom/lib/intercom_access_token";
import {
fetchIntercomArticles,
fetchIntercomCollection,
Expand Down Expand Up @@ -359,9 +360,9 @@ export async function syncArticleBatchActivity({
if (!helpCenter) {
throw new Error("[Intercom] HelpCenter not found");
}

const accessToken = await getIntercomAccessToken(connector.connectionId);
const result = await fetchIntercomArticles({
nangoConnectionId: connector.connectionId,
accessToken,
helpCenterId,
page,
pageSize: INTERCOM_ARTICLE_BATCH_SIZE,
Expand Down Expand Up @@ -509,17 +510,19 @@ export async function getNextConversationBatchToSyncActivity({

let result;

const accessToken = await getIntercomAccessToken(connector.connectionId);

if (teamId) {
result = await fetchIntercomConversations({
nangoConnectionId: connector.connectionId,
accessToken,
teamId,
slidingWindow: intercomWorkspace.conversationsSlidingWindow,
cursor,
pageSize: INTERCOM_CONVO_BATCH_SIZE,
});
} else {
result = await fetchIntercomConversations({
nangoConnectionId: connector.connectionId,
accessToken,
slidingWindow: intercomWorkspace.conversationsSlidingWindow,
cursor,
pageSize: INTERCOM_CONVO_BATCH_SIZE,
Expand Down Expand Up @@ -549,7 +552,6 @@ export async function syncConversationBatchActivity({
currentSyncMs: number;
}): Promise<void> {
const connector = await _getIntercomConnectorOrRaise(connectorId);
const nangoConnectionId = connector.connectionId;
const dataSourceConfig = dataSourceConfigFromConnector(connector);
const loggerArgs = {
workspaceId: dataSourceConfig.workspaceId,
Expand All @@ -564,7 +566,7 @@ export async function syncConversationBatchActivity({
(conversationId) =>
fetchAndSyncConversation({
connectorId,
nangoConnectionId,
connectionId: connector.connectionId,
dataSourceConfig,
conversationId,
currentSyncMs,
Expand Down
Loading

0 comments on commit 193c459

Please sign in to comment.