Skip to content

Commit

Permalink
feat(kno-5440): add slack functions
Browse files Browse the repository at this point in the history
  • Loading branch information
meryldakin committed Mar 5, 2024
1 parent 184342c commit 9b7a212
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 9 deletions.
47 changes: 38 additions & 9 deletions src/common/fetchClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,49 @@ export default class FetchClient {
return fetchResponse;
}

// private buildUrl(path: string, params?: FetchRequestConfig["params"]): URL {
// const url = new URL(this.config.baseURL + path);

// if (params) {
// Object.entries(params).forEach(([key, value]) => {
// // Send array values as individual values instead of a comma separated list
// // e.g. key[]=1&key[]=2&key[]=3 instead of key=1,2,3
// if (Array.isArray(value)) {
// for (const val of value) {
// url.searchParams.append(`${key}[]`, val);
// }
// } else {
// url.searchParams.append(key, value);
// }
// });
// }

// return url;
// }

private buildUrl(path: string, params?: FetchRequestConfig["params"]): URL {
const url = new URL(this.config.baseURL + path);

function appendParams(key: string, value: any, parentKey?: string) {
const fullKey = parentKey ? `${parentKey}[${key}]` : key;

if (typeof value === 'object' && value !== null && !(value instanceof Date) && !(value instanceof Array)) {
// If value is an object, recurse
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
appendParams(nestedKey, nestedValue, fullKey);
});
} else if (Array.isArray(value)) {
// Handle arrays by appending each value with the same key
value.forEach(val => url.searchParams.append(`${fullKey}[]`, val));
} else {
// For primitive values, simply append them
url.searchParams.append(fullKey, value);
}
}

if (params) {
Object.entries(params).forEach(([key, value]) => {
// Send array values as individual values instead of a comma separated list
// e.g. key[]=1&key[]=2&key[]=3 instead of key=1,2,3
if (Array.isArray(value)) {
for (const val of value) {
url.searchParams.append(`${key}[]`, val);
}
} else {
url.searchParams.append(key, value);
}
appendParams(key, value);
});
}

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./resources/workflows/interfaces";
export * from "./resources/users/interfaces";
export * from "./resources/preferences/interfaces";
export * from "./resources/slack/interfaces";
export * from "./common/interfaces";

export { Knock } from "./knock";
2 changes: 2 additions & 0 deletions src/knock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
TokenGrant,
TokenGrantOptions,
} from "./common/userTokens";
import { Slack } from "./resources/slack";

const DEFAULT_HOSTNAME = "https://api.knock.app";

Expand All @@ -44,6 +45,7 @@ class Knock {
readonly objects = new Objects(this);
readonly messages = new Messages(this);
readonly tenants = new Tenants(this);
readonly slack = new Slack(this);

constructor(readonly key?: string, readonly options: KnockOptions = {}) {
if (!key) {
Expand Down
87 changes: 87 additions & 0 deletions src/resources/slack/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Knock } from "../../knock";
import {
AuthCheckInput,
GetSlackChannelsInput,
PaginatedSlackChannelResponse,
RevokeAccessTokenInput,
} from "./interfaces";

const TENANT_COLLECTION = "$tenants";

function removeNullKeys<T extends object>(obj: T): Partial<T> {
Object.keys(obj).forEach(key => {
if (obj[key as keyof T] === null) {
delete obj[key as keyof T];
}
});
return obj;
}

export class Slack {
constructor(readonly knock: Knock) {}


async getChannels(
input: GetSlackChannelsInput,
): Promise<PaginatedSlackChannelResponse> {
const { knockChannelId, tenant } = input;
const queryOptions = input.queryOptions || {};


const params = {
access_token_object: {
object_id: tenant,
collection: TENANT_COLLECTION,
},
channel_id: knockChannelId,
query_options: removeNullKeys({
cursor: queryOptions.cursor || null,
limit: queryOptions.limit || null,
exclude_archived: queryOptions.excludeArchived || null,
team_id: queryOptions.teamId || null,
types: queryOptions.types || null,
}),
};

const { data } = await this.knock.get(
`/v1/providers/slack/${knockChannelId}/channels`,
params,
);

return data;
}

async authCheck({ tenant, knockChannelId }: AuthCheckInput) {
const params = {
access_token_object: {
object_id: tenant,
collection: TENANT_COLLECTION,
},
channel_id: knockChannelId,
};

const { data } = await this.knock.get(
`/v1/providers/slack/${knockChannelId}/auth_check`,
params,
);

return data;
}

async revokeAccessToken({ tenant, knockChannelId }: RevokeAccessTokenInput) {
const params = {
access_token_object: {
object_id: tenant,
collection: TENANT_COLLECTION,
},
channel_id: knockChannelId,
};

const { data } = await this.knock.get(
`/v1/providers/slack/${knockChannelId}/revoke_access`,
params,
);

return data;
}
}
34 changes: 34 additions & 0 deletions src/resources/slack/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export type GetSlackChannelsInput = {
tenant: string;
knockChannelId: string;
queryOptions?: {
limit?: number;
cursor?: string;
excludeArchived?: boolean;
teamId?: string;
types?: string;
};
};

export type SlackChannel = {
name: string;
id: string;
is_private: boolean;
is_im: boolean;
context_team_id: boolean;
};

export type PaginatedSlackChannelResponse = {
slack_channels: SlackChannel[];
next_cursor: string;
}

export type AuthCheckInput = {
tenant: string;
knockChannelId: string;
};

export type RevokeAccessTokenInput = {
tenant: string;
knockChannelId: string;
};

0 comments on commit 9b7a212

Please sign in to comment.