Skip to content

Commit

Permalink
fix(sdk): expose getIntegration() (#3080)
Browse files Browse the repository at this point in the history
## Changes


- Expose `getIntegration()` in the SDK
The shape of the response is determined by the include, which is maybe a
bad idea I never know. On one hand it's pretty standard, on the other it
makes caching and types hard :\
  • Loading branch information
bodinsamuel authored Dec 3, 2024
1 parent 5bd57b6 commit 7adb8de
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ exports[`buildModelTs > should return empty (with sdk) 1`] = `
// ------ SDK
import { Nango } from '@nangohq/node';
import type { AxiosInstance, AxiosInterceptorManager, AxiosRequestConfig } from 'axios';
import type { AxiosInstance, AxiosInterceptorManager, AxiosRequestConfig, AxiosResponse } from 'axios';
import type { SyncConfig } from '../models/Sync.js';
import type { DBTeam, RunnerFlags } from '@nangohq/types';
import type { DBTeam, GetPublicIntegration, RunnerFlags } from '@nangohq/types';
export declare const oldLevelToNewLevel: {
readonly debug: "debug";
readonly info: "info";
Expand Down Expand Up @@ -76,14 +76,6 @@ interface ParamsSerializerOptions extends SerializerOptions {
encode?: ParamEncoder;
serialize?: CustomParamsSerializer;
}
export interface AxiosResponse<T = any, D = any> {
data: T;
status: number;
statusText: string;
headers: any;
config: D;
request?: any;
}
interface Pagination {
type: string;
limit?: number;
Expand Down Expand Up @@ -366,6 +358,10 @@ export declare class NangoAction {
patch<T = any>(config: Omit<ProxyConfiguration, 'method'>): Promise<AxiosResponse<T>>;
delete<T = any>(config: Omit<ProxyConfiguration, 'method'>): Promise<AxiosResponse<T>>;
getToken(): Promise<string | OAuth1Token | OAuth2ClientCredentials | BasicApiCredentials | ApiKeyCredentials | AppCredentials | AppStoreCredentials | UnauthCredentials | CustomCredentials | TbaCredentials | TableauCredentials | JwtCredentials | BillCredentials | TwoStepCredentials | SignatureCredentials>;
/**
* Get current integration
*/
getIntegration(queries?: GetPublicIntegration['Querystring']): Promise<GetPublicIntegration['Success']['data']>;
getConnection(providerConfigKeyOverride?: string, connectionIdOverride?: string): Promise<Connection>;
setMetadata(metadata: Metadata): Promise<AxiosResponse<MetadataChangeResponse>>;
updateMetadata(metadata: Metadata): Promise<AxiosResponse<MetadataChangeResponse>>;
Expand Down
6 changes: 4 additions & 2 deletions packages/node-client/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,17 @@ export class Nango {
params: string | GetPublicIntegration['Params'],
queries?: boolean | GetPublicIntegration['Querystring']
): Promise<{ config: Integration | IntegrationWithCreds } | GetPublicIntegration['Success']> {
const headers = { 'Content-Type': 'application/json' };

if (typeof params === 'string') {
const url = `${this.serverUrl}/config/${params}`;
const response = await this.http.get(url, { headers: this.enrichHeaders({}), params: { include_creds: queries } });
const response = await this.http.get(url, { headers: this.enrichHeaders(headers), params: { include_creds: queries } });
return response.data;
} else {
const url = new URL(`${this.serverUrl}/integrations/${params.uniqueKey}`);
addQueryParams(url, queries as GetPublicIntegration['Querystring']);

const response = await this.http.get(url.href, { headers: this.enrichHeaders({}) });
const response = await this.http.get(url.href, { headers: this.enrichHeaders(headers) });
return response.data;
}
}
Expand Down
48 changes: 23 additions & 25 deletions packages/shared/lib/sdk/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Nango, getUserAgent } from '@nangohq/node';
import type { AdminAxiosProps } from '@nangohq/node';
import paginateService from '../services/paginate.service.js';
import proxyService from '../services/proxy.service.js';
import type { AxiosInstance, AxiosInterceptorManager, AxiosRequestConfig } from 'axios';
import type { AxiosInstance, AxiosInterceptorManager, AxiosRequestConfig, AxiosResponse } from 'axios';
import axios, { AxiosError } from 'axios';
import { getPersistAPIUrl } from '../utils/utils.js';
import type { UserProvidedProxyConfiguration } from '../models/Proxy.js';
Expand Down Expand Up @@ -90,15 +90,6 @@ interface ParamsSerializerOptions extends SerializerOptions {
serialize?: CustomParamsSerializer;
}

export interface AxiosResponse<T = any, D = any> {
data: T;
status: number;
statusText: string;
headers: any;
config: D;
request?: any;
}

interface UserLogParameters {
level?: LogLevel;
}
Expand Down Expand Up @@ -407,6 +398,7 @@ export interface EnvironmentVariable {
}

const MEMOIZED_CONNECTION_TTL = 60000;
const MEMOIZED_INTEGRATION_TTL = 10 * 60 * 1000;
const RECORDS_VALIDATION_SAMPLE = 5;

export const defaultPersistApi = axios.create({
Expand Down Expand Up @@ -442,11 +434,8 @@ export class NangoAction {

public ActionError = ActionError;

private memoizedConnections: Map<string, { connection: Connection; timestamp: number } | undefined> = new Map<
string,
{ connection: Connection; timestamp: number }
>();
private memoizedIntegration: GetPublicIntegration['Success']['data'] | undefined;
private memoizedConnections = new Map<string, { connection: Connection; timestamp: number }>();
private memoizedIntegration = new Map<string, { integration: GetPublicIntegration['Success']['data']; timestamp: number }>();

constructor(config: NangoProps, { persistApi }: { persistApi: AxiosInstance } = { persistApi: defaultPersistApi }) {
this.connectionId = config.connectionId;
Expand Down Expand Up @@ -658,6 +647,23 @@ export class NangoAction {
return this.nango.getToken(this.providerConfigKey, this.connectionId);
}

/**
* Get current integration
*/
public async getIntegration(queries?: GetPublicIntegration['Querystring']): Promise<GetPublicIntegration['Success']['data']> {
this.throwIfAborted();

const key = queries?.include?.join(',') || 'default';
const has = this.memoizedIntegration.get(key);
if (has && MEMOIZED_INTEGRATION_TTL > Date.now() - has.timestamp) {
return has.integration;
}

const { data: integration } = await this.nango.getIntegration({ uniqueKey: this.providerConfigKey }, queries);
this.memoizedIntegration.set(key, { integration, timestamp: Date.now() });
return integration;
}

public async getConnection(providerConfigKeyOverride?: string, connectionIdOverride?: string): Promise<Connection> {
this.throwIfAborted();

Expand Down Expand Up @@ -709,16 +715,8 @@ export class NangoAction {

public async getWebhookURL(): Promise<string | null | undefined> {
this.throwIfAborted();
if (this.memoizedIntegration) {
return this.memoizedIntegration.webhook_url;
}

const { data: integration } = await this.nango.getIntegration({ uniqueKey: this.providerConfigKey }, { include: ['webhook'] });
if (!integration || !integration.provider) {
throw Error(`There was no provider found for the provider config key: ${this.providerConfigKey}`);
}
this.memoizedIntegration = integration;
return this.memoizedIntegration.webhook_url;
const integration = await this.getIntegration({ include: ['webhook'] });
return integration.webhook_url;
}

/**
Expand Down

0 comments on commit 7adb8de

Please sign in to comment.