From ede77f82153dcd691bd736fdda08e55db73db830 Mon Sep 17 00:00:00 2001 From: Jeffrey Yu <35394596+JeffreytheCoder@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:50:49 -0700 Subject: [PATCH 1/5] feat: migrate issues GET APIs to GithubSDK --- github/helpers/githubSDKclass.ts | 118 +++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/github/helpers/githubSDKclass.ts b/github/helpers/githubSDKclass.ts index 24f8994..1531859 100644 --- a/github/helpers/githubSDKclass.ts +++ b/github/helpers/githubSDKclass.ts @@ -2,6 +2,7 @@ import { IHttp, HttpStatusCode, IRead } from "@rocket.chat/apps-engine/definitio import { UserInfo } from "os"; import { UserInformation } from "../definitions/Userinfo"; import { ModalsEnum } from "../enum/Modals"; +import { IGitHubIssue } from "../definitions/githubIssue"; class GitHubApi { private http: IHttp; @@ -17,10 +18,10 @@ class GitHubApi { } private async getRequest(url: string): Promise { - const response = await this.http.get(url, { + const response = await this.http.get(this.BaseApiHost + url, { headers: { - Authorization: `token ${this.accessToken}`, "Content-Type": "application/json", + ...(this.accessToken && { Authorization: `token ${this.accessToken}` }), }, }); @@ -33,9 +34,7 @@ class GitHubApi { public async getBasicUserInfo(): Promise { try { - const response = await this.getRequest( - this.BaseApiHost + 'user' - ); + const response = await this.getRequest('user'); return { username: response.login, name: response.name, @@ -46,7 +45,114 @@ class GitHubApi { avatar: response.avatar_url } } catch (error) { - throw error; + throw error; + } + } + + public async getIssueTemplates(repoName: string) { + try { + const response = await this.getRequest(`repos/${repoName}/contents/.github/ISSUE_TEMPLATE`); + return { + templates: JSON.parse(response.content || "{}"), + repository: repoName, + template_not_found: false, + } + } catch (error) { + return { + template_not_found: true, + } + } + } + + public async getIssueTemplateCode(templateDownloadUrl: string) { + try { + const response = await this.getRequest(templateDownloadUrl); + return { + template: response.content || "", + } + } catch (error) { + return { + template: "", + } + } + } + + public async getIssueData( + repoInfo: String, + issueNumber: String, + ): Promise { + try { + const response = await this.getRequest(`repos/${repoInfo}/issues/${issueNumber}`); + + const getAssignees = (assignees: any[]): string[] => assignees.map((val): string => { + return val.login as string; + }) + + return { + issue_id: response.id as string, + issue_compact: response.body as string, + html_url: response.html_url as string, + repo_url: response.repository_url as string, + user_login: response.user.login as string, + user_avatar: response.user.avatar_url as string, + number: response.number as number, + title: response.title as string, + body: response.body as string, + assignees: getAssignees(response.assignees), + state: response.state as string, + last_updated_at: response.updated_at as string, + comments: response.comments as number, + reactions: { + total_count: response.reactions["total_count"], + plus_one: response.reactions["+1"], + minus_one: response.reactions["-1"], + laugh: response.reactions["laugh"], + hooray: response.reactions["hooray"], + confused: response.reactions["confused"], + heart: response.reactions["heart"], + rocket: response.reactions["rocket"], + eyes: response.reactions["eyes"] + } + } + } catch (error) { + return { + issue_compact: "Error Fetching Issue", + issue_id: 0 + } + } + } + + public async getIssuesComments( + repoName: string, + issueNumber: string | number + ) { + try { + const response = await this.getRequest(`repos/${repoName}/issues/${issueNumber}/comments`); + return { + data: response, + serverError: false + } + } catch (error) { + return { + serverError: true, + ...error + } + } + } + + public async getRepositoryIssues(repoName: string) { + try { + const response = await this.getRequest(`repos/${repoName}/issues`); + return { + issues: response, + repository: repoName, + serverError: false + } + } catch (error) { + return { + serverError: true, + ...error + } } } } From 5e7351088216a4502679d647968ffeafbd78e726 Mon Sep 17 00:00:00 2001 From: Jeffrey Yu <35394596+JeffreytheCoder@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:58:13 -0700 Subject: [PATCH 2/5] migrate getUserAssignedIssues --- github/helpers/githubSDKclass.ts | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/github/helpers/githubSDKclass.ts b/github/helpers/githubSDKclass.ts index 1531859..255f6b7 100644 --- a/github/helpers/githubSDKclass.ts +++ b/github/helpers/githubSDKclass.ts @@ -77,6 +77,59 @@ class GitHubApi { } } + public async getUserAssignedIssues( + username: String, + filter: { + filter: String, + state: String, + sort: String + }, + ): Promise { + let url: string = ""; + switch (filter.filter) { + case ModalsEnum.CREATED_ISSUE_FILTER: + url = `https://api.github.com/search/issues?q=is:${filter.state}+is:issue+sort:${filter.sort.substring(5)}-desc+author:${username}` + break; + case ModalsEnum.ASSIGNED_ISSUE_FILTER: + url = `https://api.github.com/search/issues?q=is:${filter.state}+is:issue+sort:${filter.sort.substring(5)}-desc+assignee:${username}` + break; + case ModalsEnum.MENTIONED_ISSUE_FILTER: + url = `https://api.github.com/search/issues?q=is:${filter.state}+is:issue+sort:${filter.sort.substring(5)}-desc+mentions:${username}` + default: + break; + } + + try { + const response = await this.getRequest(url); + + const getAssignees = (assignees: any[]): string[] => assignees.map((val): string => { + return val.login as string; + }) + + const modifiedResponse: Array = response.items.map((value): IGitHubIssue => { + return { + issue_id: value.id as string, + issue_compact: value.body as string, + repo_url: value.repository_url as string, + user_login: value.user.login as string, + user_avatar: value.user.avatar_url as string, + number: value.number as number, + title: value.title as string, + body: value.body as string, + assignees: getAssignees(value.assignees), + state: value.state as string, + last_updated_at: value.updated_at as string, + comments: value.comments as number, + } + }) + + return modifiedResponse; + } + catch (e) { + return []; + } + } + public async getIssueData( repoInfo: String, issueNumber: String, From 800e6bda9fc73630238e28530fee2246e13d0957 Mon Sep 17 00:00:00 2001 From: Jeffrey Yu <35394596+JeffreytheCoder@users.noreply.github.com> Date: Fri, 10 May 2024 11:38:32 -0700 Subject: [PATCH 3/5] Add singleton instance to githubApiClass --- github/handlers/UserProfileHandler.ts | 30 +++-- github/helpers/githubSDKclass.ts | 184 +++++++++++++++++--------- github/modals/UserProfileModal.ts | 156 ++++++++++++---------- 3 files changed, 226 insertions(+), 144 deletions(-) diff --git a/github/handlers/UserProfileHandler.ts b/github/handlers/UserProfileHandler.ts index 0c94295..d95f06b 100644 --- a/github/handlers/UserProfileHandler.ts +++ b/github/handlers/UserProfileHandler.ts @@ -1,4 +1,9 @@ -import { IRead, IPersistence, IHttp, IModify } from "@rocket.chat/apps-engine/definition/accessors"; +import { + IRead, + IPersistence, + IHttp, + IModify, +} from "@rocket.chat/apps-engine/definition/accessors"; import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; import { SlashCommandContext } from "@rocket.chat/apps-engine/definition/slashcommands"; import { UIKitInteractionContext } from "@rocket.chat/apps-engine/definition/uikit"; @@ -16,37 +21,34 @@ export async function handleUserProfileRequest( room: IRoom, modify: IModify, uikitcontext?: UIKitInteractionContext -){ +) { let access_token = await getAccessTokenForUser( read, context.getSender(), app.oauth2Config ); - if (access_token?.token){ + if (access_token?.token) { const triggerId = context.getTriggerId(); - if (triggerId){ + if (triggerId) { const modal = await userProfileModal({ - access_token: access_token.token, + app: app, modify: modify, read: read, persistence: persistence, http: http, - slashcommandcontext: context + slashcommandcontext: context, }); - await modify.getUiController().openModalView( - modal, - {triggerId}, - context.getSender() - ); + await modify + .getUiController() + .openModalView(modal, { triggerId }, context.getSender()); } - }else { + } else { await sendNotification( read, modify, context.getSender(), room, "Login is Mandatory for getting User Info ! `/github login`" - ) + ); } - } diff --git a/github/helpers/githubSDKclass.ts b/github/helpers/githubSDKclass.ts index 255f6b7..ed3d89c 100644 --- a/github/helpers/githubSDKclass.ts +++ b/github/helpers/githubSDKclass.ts @@ -1,27 +1,64 @@ -import { IHttp, HttpStatusCode, IRead } from "@rocket.chat/apps-engine/definition/accessors"; -import { UserInfo } from "os"; +import { IHttp, IRead } from "@rocket.chat/apps-engine/definition/accessors"; import { UserInformation } from "../definitions/Userinfo"; import { ModalsEnum } from "../enum/Modals"; import { IGitHubIssue } from "../definitions/githubIssue"; +import { AppSettingsEnum } from "../settings/settings"; +import { getAccessTokenForUser } from "../persistance/auth"; +import { IUser } from "@rocket.chat/apps-engine/definition/users"; +import { GithubApp } from "../GithubApp"; +import { IAuthData } from "@rocket.chat/apps-engine/definition/oauth2/IOAuth2"; class GitHubApi { + private static instance: GitHubApi; private http: IHttp; private BaseHost: string; private BaseApiHost: string; - private accessToken: String; + private accessToken: IAuthData | undefined; - constructor(http: IHttp, accessToken: String, BaseHost: string, BaseApiHost: string) { + constructor(http: IHttp) { this.http = http; - this.accessToken = accessToken; - this.BaseApiHost = BaseApiHost; - this.BaseHost = BaseHost; + } + + private async initialize( + read: IRead, + user: IUser, + app: GithubApp + ): Promise { + const environmentReader = read.getEnvironmentReader(); + this.BaseHost = await environmentReader + .getSettings() + .getValueById(AppSettingsEnum.BaseHostID); + this.BaseApiHost = await environmentReader + .getSettings() + .getValueById(AppSettingsEnum.BaseApiHostID); + + this.accessToken = await getAccessTokenForUser( + read, + user, + app.oauth2Config + ); + } + + public static async getInstance( + http: IHttp, + read: IRead, + user: IUser, + app: GithubApp + ): Promise { + if (!GitHubApi.instance) { + GitHubApi.instance = new GitHubApi(http); + await GitHubApi.instance.initialize(read, user, app); + } + return GitHubApi.instance; } private async getRequest(url: string): Promise { const response = await this.http.get(this.BaseApiHost + url, { headers: { "Content-Type": "application/json", - ...(this.accessToken && { Authorization: `token ${this.accessToken}` }), + ...(this.accessToken && { + Authorization: `token ${this.accessToken}`, + }), }, }); @@ -34,7 +71,7 @@ class GitHubApi { public async getBasicUserInfo(): Promise { try { - const response = await this.getRequest('user'); + const response = await this.getRequest("user"); return { username: response.login, name: response.name, @@ -42,8 +79,8 @@ class GitHubApi { bio: response.bio, followers: response.followers, following: response.following, - avatar: response.avatar_url - } + avatar: response.avatar_url, + }; } catch (error) { throw error; } @@ -51,16 +88,18 @@ class GitHubApi { public async getIssueTemplates(repoName: string) { try { - const response = await this.getRequest(`repos/${repoName}/contents/.github/ISSUE_TEMPLATE`); + const response = await this.getRequest( + `repos/${repoName}/contents/.github/ISSUE_TEMPLATE` + ); return { templates: JSON.parse(response.content || "{}"), repository: repoName, template_not_found: false, - } + }; } catch (error) { return { template_not_found: true, - } + }; } } @@ -69,32 +108,44 @@ class GitHubApi { const response = await this.getRequest(templateDownloadUrl); return { template: response.content || "", - } + }; } catch (error) { return { template: "", - } + }; } } public async getUserAssignedIssues( username: String, filter: { - filter: String, - state: String, - sort: String - }, + filter: String; + state: String; + sort: String; + } ): Promise { let url: string = ""; switch (filter.filter) { case ModalsEnum.CREATED_ISSUE_FILTER: - url = `https://api.github.com/search/issues?q=is:${filter.state}+is:issue+sort:${filter.sort.substring(5)}-desc+author:${username}` + url = `https://api.github.com/search/issues?q=is:${ + filter.state + }+is:issue+sort:${filter.sort.substring( + 5 + )}-desc+author:${username}`; break; case ModalsEnum.ASSIGNED_ISSUE_FILTER: - url = `https://api.github.com/search/issues?q=is:${filter.state}+is:issue+sort:${filter.sort.substring(5)}-desc+assignee:${username}` + url = `https://api.github.com/search/issues?q=is:${ + filter.state + }+is:issue+sort:${filter.sort.substring( + 5 + )}-desc+assignee:${username}`; break; case ModalsEnum.MENTIONED_ISSUE_FILTER: - url = `https://api.github.com/search/issues?q=is:${filter.state}+is:issue+sort:${filter.sort.substring(5)}-desc+mentions:${username}` + url = `https://api.github.com/search/issues?q=is:${ + filter.state + }+is:issue+sort:${filter.sort.substring( + 5 + )}-desc+mentions:${username}`; default: break; } @@ -102,44 +153,49 @@ class GitHubApi { try { const response = await this.getRequest(url); - const getAssignees = (assignees: any[]): string[] => assignees.map((val): string => { - return val.login as string; - }) - - const modifiedResponse: Array = response.items.map((value): IGitHubIssue => { - return { - issue_id: value.id as string, - issue_compact: value.body as string, - repo_url: value.repository_url as string, - user_login: value.user.login as string, - user_avatar: value.user.avatar_url as string, - number: value.number as number, - title: value.title as string, - body: value.body as string, - assignees: getAssignees(value.assignees), - state: value.state as string, - last_updated_at: value.updated_at as string, - comments: value.comments as number, + const getAssignees = (assignees: any[]): string[] => + assignees.map((val): string => { + return val.login as string; + }); + + const modifiedResponse: Array = response.items.map( + (value): IGitHubIssue => { + return { + issue_id: value.id as string, + issue_compact: value.body as string, + repo_url: value.repository_url as string, + user_login: value.user.login as string, + user_avatar: value.user.avatar_url as string, + number: value.number as number, + title: value.title as string, + body: value.body as string, + assignees: getAssignees(value.assignees), + state: value.state as string, + last_updated_at: value.updated_at as string, + comments: value.comments as number, + }; } - }) + ); return modifiedResponse; - } - catch (e) { + } catch (e) { return []; } } public async getIssueData( repoInfo: String, - issueNumber: String, + issueNumber: String ): Promise { try { - const response = await this.getRequest(`repos/${repoInfo}/issues/${issueNumber}`); + const response = await this.getRequest( + `repos/${repoInfo}/issues/${issueNumber}` + ); - const getAssignees = (assignees: any[]): string[] => assignees.map((val): string => { - return val.login as string; - }) + const getAssignees = (assignees: any[]): string[] => + assignees.map((val): string => { + return val.login as string; + }); return { issue_id: response.id as string, @@ -164,14 +220,14 @@ class GitHubApi { confused: response.reactions["confused"], heart: response.reactions["heart"], rocket: response.reactions["rocket"], - eyes: response.reactions["eyes"] - } - } + eyes: response.reactions["eyes"], + }, + }; } catch (error) { return { issue_compact: "Error Fetching Issue", - issue_id: 0 - } + issue_id: 0, + }; } } @@ -180,16 +236,18 @@ class GitHubApi { issueNumber: string | number ) { try { - const response = await this.getRequest(`repos/${repoName}/issues/${issueNumber}/comments`); + const response = await this.getRequest( + `repos/${repoName}/issues/${issueNumber}/comments` + ); return { data: response, - serverError: false - } + serverError: false, + }; } catch (error) { return { serverError: true, - ...error - } + ...error, + }; } } @@ -199,13 +257,13 @@ class GitHubApi { return { issues: response, repository: repoName, - serverError: false - } + serverError: false, + }; } catch (error) { return { serverError: true, - ...error - } + ...error, + }; } } } diff --git a/github/modals/UserProfileModal.ts b/github/modals/UserProfileModal.ts index 77ca4cf..772892e 100644 --- a/github/modals/UserProfileModal.ts +++ b/github/modals/UserProfileModal.ts @@ -1,6 +1,15 @@ -import { IHttp, IModify, IPersistence, IRead } from "@rocket.chat/apps-engine/definition/accessors"; +import { + IHttp, + IModify, + IPersistence, + IRead, +} from "@rocket.chat/apps-engine/definition/accessors"; import { SlashCommandContext } from "@rocket.chat/apps-engine/definition/slashcommands"; -import { ButtonStyle, TextObjectType, UIKitInteractionContext } from "@rocket.chat/apps-engine/definition/uikit"; +import { + ButtonStyle, + TextObjectType, + UIKitInteractionContext, +} from "@rocket.chat/apps-engine/definition/uikit"; import { IUIKitModalViewParam } from "@rocket.chat/apps-engine/definition/uikit/UIKitInteractionResponder"; import { ModalsEnum } from "../enum/Modals"; import { GitHubApi } from "../helpers/githubSDKclass"; @@ -10,49 +19,63 @@ import { storeInteractionRoomData, } from "../persistance/roomInteraction"; import { AppSettingsEnum } from "../settings/settings"; +import { GithubApp } from "../GithubApp"; export async function userProfileModal({ - access_token, + app, modify, read, persistence, http, slashcommandcontext, - uikitcontext -} : { - access_token: String, - modify : IModify, - read: IRead, - persistence: IPersistence, - http: IHttp, - slashcommandcontext: SlashCommandContext, - uikitcontext?: UIKitInteractionContext -}) : Promise { - + uikitcontext, +}: { + app: GithubApp; + modify: IModify; + read: IRead; + persistence: IPersistence; + http: IHttp; + slashcommandcontext: SlashCommandContext; + uikitcontext?: UIKitInteractionContext; +}): Promise { const viewId = ModalsEnum.USER_PROFILE_VIEW; const block = modify.getCreator().getBlockBuilder(); - const room = slashcommandcontext?.getRoom() || uikitcontext?.getInteractionData().room; - const user = slashcommandcontext?.getSender() || uikitcontext?.getInteractionData().user; + const room = + slashcommandcontext?.getRoom() || + uikitcontext?.getInteractionData().room; + const user = + slashcommandcontext?.getSender() || + uikitcontext?.getInteractionData().user; - if (user?.id){ + if (user?.id) { let roomId; - if (room?.id){ + if (room?.id) { roomId = room.id; await storeInteractionRoomData(persistence, user.id, roomId); - } - else { - roomId = (await getInteractionRoomData(read.getPersistenceReader(), user.id)).roomId; + } else { + roomId = ( + await getInteractionRoomData( + read.getPersistenceReader(), + user.id + ) + ).roomId; } } let userInfo: UserInformation | undefined; try { - let BaseHost = await read.getEnvironmentReader().getSettings().getValueById(AppSettingsEnum.BaseHostID); - let BaseApiHost = await read.getEnvironmentReader().getSettings().getValueById(AppSettingsEnum.BaseApiHostID); - const gitHubApiClient = new GitHubApi( + let BaseHost = await read + .getEnvironmentReader() + .getSettings() + .getValueById(AppSettingsEnum.BaseHostID); + let BaseApiHost = await read + .getEnvironmentReader() + .getSettings() + .getValueById(AppSettingsEnum.BaseApiHostID); + const gitHubApiClient = await GitHubApi.getInstance( http, - access_token, - BaseHost, - BaseApiHost + read, + user, + app ); userInfo = await gitHubApiClient.getBasicUserInfo(); } catch (error) { @@ -64,58 +87,57 @@ export async function userProfileModal({ elements: [block.newPlainTextObject(userInfo.email, true)], }); - block.addSectionBlock({ - text: block.newPlainTextObject(userInfo.bio), - accessory : block.newImageElement({ - imageUrl: userInfo.avatar, - altText: userInfo.name - }) - }) + block.addSectionBlock({ + text: block.newPlainTextObject(userInfo.bio), + accessory: block.newImageElement({ + imageUrl: userInfo.avatar, + altText: userInfo.name, + }), + }); - block.addContextBlock({ - elements: [ - block.newPlainTextObject(`followers: ${userInfo.followers}`), - block.newPlainTextObject(`following: ${userInfo.following}`) - ] - }); + block.addContextBlock({ + elements: [ + block.newPlainTextObject(`followers: ${userInfo.followers}`), + block.newPlainTextObject(`following: ${userInfo.following}`), + ], + }); - block.addDividerBlock(); + block.addDividerBlock(); - block.addSectionBlock({ - text: block.newPlainTextObject("Select from the following options.") - }) + block.addSectionBlock({ + text: block.newPlainTextObject( + "Select from the following options." + ), + }); - block.addActionsBlock({ - elements : [ - block.newButtonElement({ - text : { - text : "Share Profile", - type : TextObjectType.PLAINTEXT - }, - actionId: ModalsEnum.SHARE_PROFILE, - style : ButtonStyle.PRIMARY - }), - block.newButtonElement( - { + block.addActionsBlock({ + elements: [ + block.newButtonElement({ + text: { + text: "Share Profile", + type: TextObjectType.PLAINTEXT, + }, + actionId: ModalsEnum.SHARE_PROFILE, + style: ButtonStyle.PRIMARY, + }), + block.newButtonElement({ actionId: ModalsEnum.TRIGGER_ISSUES_MODAL, value: "Trigger Issues Modal", text: { type: TextObjectType.PLAINTEXT, - text: "Issues" + text: "Issues", }, - style: ButtonStyle.PRIMARY - }, - ) - ] - }) -} - return { + style: ButtonStyle.PRIMARY, + }), + ], + }); + } + return { id: viewId, title: { type: TextObjectType.PLAINTEXT, text: userInfo ? userInfo.name : "User Profile", }, - blocks: block.getBlocks() - } - + blocks: block.getBlocks(), + }; } From 95fa2414d2774a33632555311c321be6465417f0 Mon Sep 17 00:00:00 2001 From: Jeffrey Yu <35394596+JeffreytheCoder@users.noreply.github.com> Date: Fri, 10 May 2024 21:11:05 -0700 Subject: [PATCH 4/5] Fix to use token string --- github/helpers/githubSDKclass.ts | 5 +++-- github/modals/UserProfileModal.ts | 8 -------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/github/helpers/githubSDKclass.ts b/github/helpers/githubSDKclass.ts index ed3d89c..58851c8 100644 --- a/github/helpers/githubSDKclass.ts +++ b/github/helpers/githubSDKclass.ts @@ -13,7 +13,7 @@ class GitHubApi { private http: IHttp; private BaseHost: string; private BaseApiHost: string; - private accessToken: IAuthData | undefined; + private accessToken: string | undefined; constructor(http: IHttp) { this.http = http; @@ -32,11 +32,12 @@ class GitHubApi { .getSettings() .getValueById(AppSettingsEnum.BaseApiHostID); - this.accessToken = await getAccessTokenForUser( + const accessTokenResponse = await getAccessTokenForUser( read, user, app.oauth2Config ); + this.accessToken = accessTokenResponse?.token; } public static async getInstance( diff --git a/github/modals/UserProfileModal.ts b/github/modals/UserProfileModal.ts index 772892e..c3ce452 100644 --- a/github/modals/UserProfileModal.ts +++ b/github/modals/UserProfileModal.ts @@ -63,14 +63,6 @@ export async function userProfileModal({ } let userInfo: UserInformation | undefined; try { - let BaseHost = await read - .getEnvironmentReader() - .getSettings() - .getValueById(AppSettingsEnum.BaseHostID); - let BaseApiHost = await read - .getEnvironmentReader() - .getSettings() - .getValueById(AppSettingsEnum.BaseApiHostID); const gitHubApiClient = await GitHubApi.getInstance( http, read, From 78bb99529ecb16e15d888c3840b328df6c9fb9df Mon Sep 17 00:00:00 2001 From: Jeffrey Yu <35394596+JeffreytheCoder@users.noreply.github.com> Date: Fri, 10 May 2024 22:41:38 -0700 Subject: [PATCH 5/5] Apply instance to migrated issues GET methods --- github/handlers/ExecuteBlockActionHandler.ts | 48 +++++++++++--------- github/handlers/ExecuteViewSubmitHandler.ts | 41 ++++++++++------- github/helpers/githubSDKclass.ts | 24 +++++----- github/modals/IssueDisplayModal.ts | 16 +++++-- github/modals/UserIssuesModal.ts | 19 +++++--- 5 files changed, 88 insertions(+), 60 deletions(-) diff --git a/github/handlers/ExecuteBlockActionHandler.ts b/github/handlers/ExecuteBlockActionHandler.ts index 93c3c88..41384e7 100644 --- a/github/handlers/ExecuteBlockActionHandler.ts +++ b/github/handlers/ExecuteBlockActionHandler.ts @@ -18,7 +18,7 @@ import { } from "@rocket.chat/apps-engine/definition/uikit"; import { AddSubscriptionModal } from "../modals/addSubscriptionsModal"; import { deleteSubscriptionsModal } from "../modals/deleteSubscriptions"; -import { deleteSubscription, updateSubscription, getIssueTemplateCode, getPullRequestComments, getPullRequestData, getRepositoryIssues, getBasicUserInfo, getIssueData, getIssuesComments, approvePullRequest } from "../helpers/githubSDK"; +import { deleteSubscription, updateSubscription, getPullRequestComments, getPullRequestData, getRepositoryIssues, getIssuesComments, approvePullRequest } from "../helpers/githubSDK"; import { Subscription } from "../persistance/subscriptions"; import { getAccessTokenForUser } from "../persistance/auth"; import { GithubApp } from "../GithubApp"; @@ -55,6 +55,7 @@ import { githubSearchModal } from "../modals/githubSearchModal"; import { NewIssueStarterModal } from "../modals/newIssueStarterModal"; import { removeRepoReminder, unsubscribedPR } from "../persistance/remind"; import { reminderModal } from "../modals/remindersModal"; +import { GitHubApi } from "../helpers/githubSDKclass"; export class ExecuteBlockActionHandler { @@ -71,6 +72,12 @@ export class ExecuteBlockActionHandler { context: UIKitBlockInteractionContext ): Promise { const data = context.getInteractionData(); + const gitHubApiClient = await GitHubApi.getInstance( + this.http, + this.read, + data.user, + this.app + ); try { const { actionId } = data; @@ -126,7 +133,7 @@ export class ExecuteBlockActionHandler { const repoName = value?.split(",")[0] ?? ""; const issueNumber = value?.split(",")[1] ?? ""; - const issueInfo : IGitHubIssue = await getIssueData(repoName, issueNumber, access_token.token, this.http); + const issueInfo : IGitHubIssue = await gitHubApiClient.getIssueData(repoName, issueNumber); const block = this.modify.getCreator().getBlockBuilder(); @@ -178,7 +185,7 @@ export class ExecuteBlockActionHandler { const issueDisplayModal = await IssueDisplayModal({ repoName : repoInfo, issueNumber : issueNumber, - access_token : access_token.token, + app: this.app, modify : this.modify, read : this.read, persistence : this.persistence, @@ -244,7 +251,7 @@ export class ExecuteBlockActionHandler { let access_token = await getAccessTokenForUser(this.read, user, this.app.oauth2Config) as IAuthData; const issueModal = await userIssuesModal({ - access_token : access_token.token, + app: this.app, filter : filter, modify : this.modify, read : this.read, @@ -270,7 +277,7 @@ export class ExecuteBlockActionHandler { const issuesModal = await userIssuesModal({ filter : filter, - access_token : access_token.token, + app: this.app, modify: this.modify, read : this.read, persistence : this.persistence, @@ -398,8 +405,7 @@ export class ExecuteBlockActionHandler { if(accessToken && actionDetailsArray?.length == 2){ if(actionDetailsArray[1] !== ModalsEnum.BLANK_GITHUB_TEMPLATE){ - - let templateResponse = await getIssueTemplateCode(this.http,actionDetailsArray[1],accessToken.token); + let templateResponse = await gitHubApiClient.getIssueTemplateCode(actionDetailsArray[1]); let data = {}; if(templateResponse?.template){ data = { @@ -671,13 +677,13 @@ export class ExecuteBlockActionHandler { return context.getInteractionResponder().openModalViewResponse(shareProfileMod); } case ModalsEnum.APPROVE_PULL_REQUEST_ACTION:{ - + let value: string = context.getInteractionData().value as string; let splittedValues = value?.split(" "); let { user } = await context.getInteractionData(); let { room} = await context.getInteractionData(); let accessToken = await getAccessTokenForUser(this.read, user, this.app.oauth2Config) as IAuthData; - + if(splittedValues.length==2 && accessToken?.token){ let data={ "repo" : splittedValues[0], @@ -722,8 +728,8 @@ export class ExecuteBlockActionHandler { if(splittedValues.length==2){ let repoName = splittedValues[0]; let issueNumber = splittedValues[1]; - let issueComments = await getIssuesComments(this.http,repoName,accessToken?.token,issueNumber); - let issueData = await getIssueData(repoName,issueNumber,accessToken?.token,this.http); + let issueComments = await gitHubApiClient.getIssuesComments(repoName, issueNumber); + let issueData = await gitHubApiClient.getIssueData(repoName, issueNumber); if(issueData?.issue_compact === "Error Fetching Issue" || issueComments?.issueData){ if(issueData?.issue_compact === "Error Fetching Issue"){ const unauthorizedMessageModal = await messageModal({ @@ -906,7 +912,7 @@ export class ExecuteBlockActionHandler { let { user } = await context.getInteractionData(); let accessToken = await getAccessTokenForUser(this.read, user, this.app.oauth2Config); if (!accessToken) { - let response = await getRepositoryIssues(this.http,repository); + let response = await gitHubApiClient.getRepositoryIssues(repository); let data = { issues: response.issues, pushRights : false, //no access token, so user has no pushRights to the repo, @@ -916,7 +922,7 @@ export class ExecuteBlockActionHandler { await this.modify.getUiController().updateModalView(issuesListModal, { triggerId: context.getInteractionData().triggerId }, context.getInteractionData().user); }else{ let repoDetails = await getRepoData(this.http,repository,accessToken.token); - let response = await getRepositoryIssues(this.http,repository); + let response = await gitHubApiClient.getRepositoryIssues(repository); let data = { issues: response.issues, pushRights : repoDetails?.permissions?.push || repoDetails?.permissions?.admin, @@ -1026,8 +1032,8 @@ export class ExecuteBlockActionHandler { } } break; - } - + } + case ModalsEnum.GITHUB_LOGIN_ACTION :{ const {user, room} = context.getInteractionData(); if(room){ @@ -1073,7 +1079,7 @@ export class ExecuteBlockActionHandler { }); return context.getInteractionResponder().openModalViewResponse(newIssueModal); } - + case ModalsEnum.TRIGGER_SEARCH_MODAL: { const searchModal = await githubSearchModal({ modify: this.modify, @@ -1096,13 +1102,13 @@ export class ExecuteBlockActionHandler { const message = `You have unsubscribed from repository [${repo} Pull Request #${number}](https://github.com/${repo}/pull/${number})`; await sendNotification(this.read, this.modify, user, room as IRoom, message); - + } - + case ModalsEnum.REMINDER_REMOVE_REPO_ACTION : { - const {value, user} = context.getInteractionData(); + const {value, user} = context.getInteractionData(); await removeRepoReminder(this.read, this.persistence, value as string, user); - + const updatedReminderModal = await reminderModal({modify: this.modify, read:this.read, persistence: this.persistence, http: this.http, uikitcontext: context}); return context.getInteractionResponder().updateModalViewResponse( updatedReminderModal); @@ -1114,4 +1120,4 @@ export class ExecuteBlockActionHandler { return context.getInteractionResponder().successResponse(); } -} \ No newline at end of file +} diff --git a/github/handlers/ExecuteViewSubmitHandler.ts b/github/handlers/ExecuteViewSubmitHandler.ts index 38c34b4..227875c 100644 --- a/github/handlers/ExecuteViewSubmitHandler.ts +++ b/github/handlers/ExecuteViewSubmitHandler.ts @@ -17,7 +17,7 @@ import { IGitHubSearchResultData } from '../definitions/searchResultData'; import { githubSearchErrorModal } from '../modals/githubSearchErrorModal'; import { GithubSearchResultStorage } from '../persistance/searchResults'; import { githubSearchResultShareModal } from '../modals/githubSearchResultsShareModal'; -import { addSubscribedEvents, createSubscription, updateSubscription, createNewIssue, getIssueTemplates,githubSearchIssuesPulls, mergePullRequest, addNewPullRequestComment, getPullRequestData, getPullRequestComments, getRepoData, getRepositoryIssues, updateGithubIssues, addNewIssueComment, getIssuesComments, getIssueData, getBasicUserInfo } from '../helpers/githubSDK'; +import { addSubscribedEvents, createSubscription, updateSubscription, createNewIssue, githubSearchIssuesPulls, mergePullRequest, addNewPullRequestComment, getPullRequestData, getPullRequestComments, getRepoData, getRepositoryIssues, updateGithubIssues, addNewIssueComment, getIssuesComments, getIssueData, getBasicUserInfo } from '../helpers/githubSDK'; import { NewIssueModal } from '../modals/newIssueModal'; import { issueTemplateSelectionModal } from '../modals/issueTemplateSelectionModal'; import { githubIssuesListModal } from '../modals/githubIssuesListModal'; @@ -29,6 +29,7 @@ import { issueCommentsModal } from '../modals/issueCommentsModal'; import { createReminder } from './CreateReminder'; import { RocketChatAssociationModel, RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; import { IAuthData } from '@rocket.chat/apps-engine/definition/oauth2/IOAuth2'; +import { GitHubApi } from '../helpers/githubSDKclass'; export class ExecuteViewSubmitHandler { constructor( private readonly app: GithubApp, @@ -40,6 +41,12 @@ export class ExecuteViewSubmitHandler { public async run(context: UIKitViewSubmitInteractionContext) { const { user, view } = context.getInteractionData(); + const gitHubApiClient = await GitHubApi.getInstance( + this.http, + this.read, + user, + this.app + ); try { switch (view.id) { @@ -154,12 +161,12 @@ export class ExecuteViewSubmitHandler { }else{ await sendNotification(this.read,this.modify,user,room,`Invalid Issue !`); } - } + } break; } case ModalsEnum.NEW_ISSUE_STARTER_VIEW:{ const { roomId } = await getInteractionRoomData(this.read.getPersistenceReader(), user.id); - + if (roomId) { let room = await this.read.getRoomReader().getById(roomId) as IRoom; let repository = view.state?.[ModalsEnum.REPO_NAME_INPUT]?.[ModalsEnum.REPO_NAME_INPUT_ACTION] as string; @@ -167,9 +174,9 @@ export class ExecuteViewSubmitHandler { if (!accessToken) { await sendNotification(this.read, this.modify, user, room, `Login To Github ! -> /github login`); }else{ - + repository=repository?.trim(); - let response = await getIssueTemplates(this.http,repository,accessToken.token); + const response = await gitHubApiClient.getIssueTemplates(repository); if((!response.template_not_found) && response?.templates?.length){ const issueTemplateSelection = await issueTemplateSelectionModal({ data: response, modify: this.modify, read: this.read, persistence: this.persistence, http: this.http, uikitcontext: context }); return context @@ -185,7 +192,7 @@ export class ExecuteViewSubmitHandler { .openModalViewResponse(createNewIssue); } } - } + } break; } case ModalsEnum.SEARCH_VIEW: { @@ -227,7 +234,7 @@ export class ExecuteViewSubmitHandler { }else{ resourceState = resourceState?.trim(); } - + let accessToken = await getAccessTokenForUser(this.read, user, this.app.oauth2Config); if(repository?.length == 0 && labelsArray?.length == 0 && authorsArray?.length == 0){ await sendNotification(this.read, this.modify, user, room, "*Invalid Search Query !*"); @@ -404,7 +411,7 @@ export class ExecuteViewSubmitHandler { return context .getInteractionResponder() .openModalViewResponse(unauthorizedMessageModal); - }else{ + }else{ let pullRequestComments = await getPullRequestComments(this.http,repository,accessToken.token,pullNumber); let pullRequestData = await getPullRequestData(this.http,repository,accessToken.token,pullNumber); if(pullRequestData?.serverError || pullRequestComments?.pullRequestData){ @@ -482,9 +489,9 @@ export class ExecuteViewSubmitHandler { return context .getInteractionResponder() .openModalViewResponse(unauthorizedMessageModal); - }else{ - let issueComments = await getIssuesComments(this.http,repository,accessToken?.token,issueNumber); - let issueData = await getIssueData(repository,issueNumber,accessToken.token,this.http); + }else{ + let issueComments = await gitHubApiClient.getIssuesComments(repository,issueNumber); + let issueData = await gitHubApiClient.getIssueData(repository,issueNumber); if(issueData?.issue_compact === "Error Fetching Issue" || issueComments?.issueData){ if(issueData?.issue_compact === "Error Fetching Issue"){ const unauthorizedMessageModal = await messageModal({ @@ -546,10 +553,10 @@ export class ExecuteViewSubmitHandler { let pushRights: boolean= false; let accessToken = await getAccessTokenForUser(this.read, user, this.app.oauth2Config); if (!accessToken) { - response = await getRepositoryIssues(this.http,repository); + response = await gitHubApiClient.getRepositoryIssues(repository); }else{ let repoDetails = await getRepoData(this.http,repository,accessToken.token); - response = await getRepositoryIssues(this.http,repository); + response = await gitHubApiClient.getRepositoryIssues(repository); pushRights = repoDetails?.permissions?.push || repoDetails?.permissions?.admin; } if(response.serverError){ @@ -626,7 +633,7 @@ export class ExecuteViewSubmitHandler { .getInteractionResponder() .openModalViewResponse(issuesListModal); } - } + } break; } case ModalsEnum.ADD_ISSUE_ASSIGNEE_VIEW: { @@ -696,7 +703,7 @@ export class ExecuteViewSubmitHandler { } } } - } + } break; } case ModalsEnum.ISSUE_LIST_VIEW:{ @@ -742,7 +749,7 @@ export class ExecuteViewSubmitHandler { let repository = view.state?.[ModalsEnum.REPO_NAME_INPUT]?.[ModalsEnum.REPO_NAME_INPUT_ACTION] as string; await createReminder(repository,room,this.read,this.app,this.persistence,this.modify,this.http,user) - + } break; } @@ -816,4 +823,4 @@ export class ExecuteViewSubmitHandler { success: true, }; } -} \ No newline at end of file +} diff --git a/github/helpers/githubSDKclass.ts b/github/helpers/githubSDKclass.ts index 58851c8..d8099a7 100644 --- a/github/helpers/githubSDKclass.ts +++ b/github/helpers/githubSDKclass.ts @@ -21,7 +21,7 @@ class GitHubApi { private async initialize( read: IRead, - user: IUser, + user: IUser | undefined, app: GithubApp ): Promise { const environmentReader = read.getEnvironmentReader(); @@ -32,18 +32,20 @@ class GitHubApi { .getSettings() .getValueById(AppSettingsEnum.BaseApiHostID); - const accessTokenResponse = await getAccessTokenForUser( - read, - user, - app.oauth2Config - ); - this.accessToken = accessTokenResponse?.token; + if (user) { + const accessTokenResponse = await getAccessTokenForUser( + read, + user, + app.oauth2Config + ); + this.accessToken = accessTokenResponse?.token; + } } public static async getInstance( http: IHttp, read: IRead, - user: IUser, + user: IUser | undefined, app: GithubApp ): Promise { if (!GitHubApi.instance) { @@ -128,21 +130,21 @@ class GitHubApi { let url: string = ""; switch (filter.filter) { case ModalsEnum.CREATED_ISSUE_FILTER: - url = `https://api.github.com/search/issues?q=is:${ + url = `search/issues?q=is:${ filter.state }+is:issue+sort:${filter.sort.substring( 5 )}-desc+author:${username}`; break; case ModalsEnum.ASSIGNED_ISSUE_FILTER: - url = `https://api.github.com/search/issues?q=is:${ + url = `search/issues?q=is:${ filter.state }+is:issue+sort:${filter.sort.substring( 5 )}-desc+assignee:${username}`; break; case ModalsEnum.MENTIONED_ISSUE_FILTER: - url = `https://api.github.com/search/issues?q=is:${ + url = `search/issues?q=is:${ filter.state }+is:issue+sort:${filter.sort.substring( 5 diff --git a/github/modals/IssueDisplayModal.ts b/github/modals/IssueDisplayModal.ts index 89d480e..a4aab4c 100644 --- a/github/modals/IssueDisplayModal.ts +++ b/github/modals/IssueDisplayModal.ts @@ -6,16 +6,18 @@ import { IGitHubIssue } from "../definitions/githubIssue"; import { IGithubReactions } from "../definitions/githubReactions"; import { ModalsEnum } from "../enum/Modals"; import { OcticonIcons } from "../enum/OcticonIcons"; -import { getIssueData, getUserAssignedIssues } from "../helpers/githubSDK"; +import { getIssueData } from "../helpers/githubSDK"; import { CreateIssueStatsBar } from "../lib/CreateIssueStatsBar"; import { CreateReactionsBar } from "../lib/CreateReactionsBar"; import { getInteractionRoomData, storeInteractionRoomData } from "../persistance/roomInteraction"; import { BodyMarkdownRenderer } from "../processors/bodyMarkdowmRenderer"; +import { GitHubApi } from "../helpers/githubSDKclass"; +import { GithubApp } from "../GithubApp"; export async function IssueDisplayModal ({ repoName, issueNumber, - access_token, + app, modify, read, persistence, @@ -25,7 +27,7 @@ export async function IssueDisplayModal ({ } : { repoName : String, issueNumber : String, - access_token: String, + app: GithubApp, modify : IModify, read: IRead, persistence: IPersistence, @@ -37,6 +39,12 @@ export async function IssueDisplayModal ({ const block = modify.getCreator().getBlockBuilder(); const room = slashcommandcontext?.getRoom() || uikitcontext?.getInteractionData().room; const user = slashcommandcontext?.getSender() || uikitcontext?.getInteractionData().user; + const gitHubApiClient = await GitHubApi.getInstance( + http, + read, + user, + app + ); if (user?.id){ let roomId; @@ -49,7 +57,7 @@ export async function IssueDisplayModal ({ } } - const issueInfo : IGitHubIssue = await getIssueData(repoName, issueNumber, access_token, http); + const issueInfo : IGitHubIssue = await gitHubApiClient.getIssueData(repoName, issueNumber); if (issueInfo.issue_id == 0){ block.addSectionBlock({ diff --git a/github/modals/UserIssuesModal.ts b/github/modals/UserIssuesModal.ts index 435688e..5412ea3 100644 --- a/github/modals/UserIssuesModal.ts +++ b/github/modals/UserIssuesModal.ts @@ -14,15 +14,16 @@ import { import { IUIKitModalViewParam } from "@rocket.chat/apps-engine/definition/uikit/UIKitInteractionResponder"; import { ModalsEnum } from "../enum/Modals"; import { OcticonIcons } from "../enum/OcticonIcons"; -import { getBasicUserInfo, getUserAssignedIssues } from "../helpers/githubSDK"; import { getInteractionRoomData, storeInteractionRoomData, } from "../persistance/roomInteraction"; +import { GitHubApi } from "../helpers/githubSDKclass"; +import { GithubApp } from "../GithubApp"; export async function userIssuesModal({ filter, - access_token, + app, modify, read, persistence, @@ -35,7 +36,7 @@ export async function userIssuesModal({ state: string; sort: string; }; - access_token: String; + app: GithubApp; modify: IModify; read: IRead; persistence: IPersistence; @@ -51,6 +52,12 @@ export async function userIssuesModal({ const user = slashcommandcontext?.getSender() || uikitcontext?.getInteractionData().user; + const gitHubApiClient = await GitHubApi.getInstance( + http, + read, + user, + app + ); if (user?.id) { let roomId; @@ -67,12 +74,10 @@ export async function userIssuesModal({ } } - const userInfo = await getBasicUserInfo(http, access_token); + const userInfo = await gitHubApiClient.getBasicUserInfo(); - const repoInfo = await getUserAssignedIssues( - http, + const repoInfo = await gitHubApiClient.getUserAssignedIssues( userInfo.username, - access_token, filter );