Skip to content

Commit

Permalink
Merge pull request #118 from VipinDevelops/pr-expand
Browse files Browse the repository at this point in the history
[FEATURE] Expand Pull Request Links
  • Loading branch information
samad-yar-khan authored Nov 15, 2023
2 parents 34f2648 + 3588f3d commit 7a7cca0
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 8 deletions.
28 changes: 23 additions & 5 deletions github/GithubApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
IConfigurationModify,
IHttp,
ILogger,
IMessageBuilder,
IMessageExtender,
IModify,
IPersistence,
Expand All @@ -14,6 +15,7 @@ import { App } from "@rocket.chat/apps-engine/definition/App";
import { IAppInfo } from "@rocket.chat/apps-engine/definition/metadata";
import { GithubCommand } from "./commands/GithubCommand";
import {
ButtonStyle,
IUIKitResponse,
UIKitBlockInteractionContext,
UIKitViewCloseInteractionContext,
Expand All @@ -32,6 +34,7 @@ import { createOAuth2Client } from "@rocket.chat/apps-engine/definition/oauth2/O
import {
sendDirectMessage,
sendDirectMessageOnInstall,
sendMessage,
sendNotification,
} from "./lib/message";
import { deleteOathToken } from "./processors/deleteOAthToken";
Expand All @@ -45,17 +48,31 @@ import { IJobContext, StartupType } from "@rocket.chat/apps-engine/definition/sc
import { IRoom } from "@rocket.chat/apps-engine/definition/rooms";
import { clearInteractionRoomData, getInteractionRoomData } from "./persistance/roomInteraction";
import { GHCommand } from "./commands/GhCommand";
import { IPreMessageSentExtend, IMessage } from "@rocket.chat/apps-engine/definition/messages";
import { IPreMessageSentExtend, IMessage,IPreMessageSentModify, IPostMessageSent } from "@rocket.chat/apps-engine/definition/messages";
import { handleGitHubCodeSegmentLink } from "./handlers/GitHubCodeSegmentHandler";
import { isGithubLink, hasGitHubCodeSegmentLink } from "./helpers/checkLinks";
import { isGithubLink, hasGitHubCodeSegmentLink, hasGithubPRLink } from "./helpers/checkLinks";
import { SendReminder } from "./handlers/SendReminder";
import { AppSettings, settings } from "./settings/settings";
import { ISetting } from "@rocket.chat/apps-engine/definition/settings";
export class GithubApp extends App implements IPreMessageSentExtend {
import { ISetting } from "@rocket.chat/apps-engine/definition/settings";import { handleGithubPRLink } from "./handlers/GithubPRlinkHandler";

export class GithubApp extends App implements IPreMessageSentExtend,IPostMessageSent{
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
super(info, logger, accessors);
}

async checkPostMessageSent?(message: IMessage, read: IRead, http: IHttp): Promise<boolean> {
if (await hasGithubPRLink(message)){
return true
}
return false;
}

async executePostMessageSent(message: IMessage, read: IRead, http: IHttp, persistence: IPersistence, modify: IModify): Promise<void> {

await handleGithubPRLink(message,read,http,persistence,modify)

}

public async checkPreMessageSentExtend(
message: IMessage,
read: IRead,
Expand All @@ -78,9 +95,10 @@ export class GithubApp extends App implements IPreMessageSentExtend {
if (await hasGitHubCodeSegmentLink(message)) {
await handleGitHubCodeSegmentLink(message, read, http, message.sender, message.room, extend);
}

return extend.getMessage();
}

public async authorizationCallback(
token: IAuthData,
user: IUser,
Expand Down
3 changes: 2 additions & 1 deletion github/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"classFile": "GithubApp.ts",
"description": "The ultimate app extending Rocket.Chat for all developers collaborating on Github",
"implements": [
"IPreMessageSentExtend"
"IPreMessageSentExtend",
"IPostMessageSent"
]
}
1 change: 1 addition & 0 deletions github/enum/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export enum AppEnum {
USERNAME_ALIAS = 'GitHub',
EMOJI_AVATAR = '',
USER_MESSAGED_BOT = 'You have messaged the bot user. This has no effect.',
APP_ID='826f0d95-9e25-48a6-a781-a32f147230a5'
}
1 change: 1 addition & 0 deletions github/enum/Modals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum ModalsEnum {
PULL_VIEW_LABEL = 'Pull Request',
MERGE_PULL_REQUEST_ACTION = 'merge-pull-request',
MERGE_PULL_REQUEST_LABEL = 'Merge',
APPROVE_PULL_REQUEST_ACTION = 'approve-pull-request',
COMMENT_PR_ACTION = 'comment-pull-request',
COMMENT_PR_LABEL = 'Add Comment',
COMMENT_ISSUE_ACTION = 'comment-issue',
Expand Down
45 changes: 44 additions & 1 deletion github/handlers/ExecuteBlockActionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } from "../helpers/githubSDK";
import { deleteSubscription, updateSubscription, getIssueTemplateCode, getPullRequestComments, getPullRequestData, getRepositoryIssues, getBasicUserInfo, getIssueData, getIssuesComments, approvePullRequest } from "../helpers/githubSDK";
import { Subscription } from "../persistance/subscriptions";
import { getAccessTokenForUser } from "../persistance/auth";
import { GithubApp } from "../GithubApp";
Expand Down Expand Up @@ -734,7 +734,50 @@ 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],
"pullNumber": splittedValues[1]
}
let repoDetails = await getRepoData(this.http,splittedValues[0],accessToken.token);

if(repoDetails?.permissions?.admin || repoDetails?.permissions?.push || repoDetails?.permissions?.maintain ){
const response = await approvePullRequest(this.http,data.repo,accessToken.token,data.pullNumber);

let message = `🤖 Pull Request successfully Approved ✔️ : https://github.com/${data.repo}/pull/${data.pullNumber}`

if(response.state == "APPROVED" && room ){
sendMessage(this.modify,room,user,message)
}

if(response.errors && room){
sendNotification(this.read,this.modify,user,room,response.errors[0]);
}

}else{
const unauthorizedMessageModal = await messageModal({
message:"Unauthorized Action 🤖 You dont have push rights ⚠️",
modify: this.modify,
read: this.read,
persistence: this.persistence,
http: this.http,
uikitcontext: context
})
return context
.getInteractionResponder()
.openModalViewResponse(unauthorizedMessageModal);
}
}
break;
}
case ModalsEnum.ISSUE_COMMENT_LIST_ACTION:{
let value: string = context.getInteractionData().value as string;
let splittedValues = value?.split(" ");
Expand Down
62 changes: 62 additions & 0 deletions github/handlers/GithubPRlinkHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { IUser } from "@rocket.chat/apps-engine/definition/users";
import { IHttp, IMessageBuilder, IModify, IPersistence, IRead } from "@rocket.chat/apps-engine/definition/accessors";
import { IMessage } from "@rocket.chat/apps-engine/definition/messages";
import { BlockBuilder, ButtonStyle, IBlock, TextObjectType } from "@rocket.chat/apps-engine/definition/uikit";
import { ModalsEnum } from "../enum/Modals";


export async function handleGithubPRLink(message: IMessage, read: IRead, http: IHttp, persistence: IPersistence, modify: IModify): Promise<String> {
try {
const githubPRLinkRegex = /\bhttps?:\/\/github\.com\/\S+\/pull\/\d+\b/;
const text = message.text!;
const prLinkMatch = text.match(githubPRLinkRegex);
const prLink = prLinkMatch?.[0];
const githubLinkPartsRegex = /(?:https?:\/\/github\.com\/)(\S+)\/(\S+)\/pull\/(\d+)/;
const linkPartsMatch = prLink?.match(githubLinkPartsRegex);
const username = linkPartsMatch?.[1];
const repositoryName = linkPartsMatch?.[2];
const pullNumber = linkPartsMatch?.[3];

if (!username || !repositoryName || !pullNumber) {
throw new Error("Invalid GitHub PR link");
}

const messageBuilder = await modify.getCreator().startMessage()
.setRoom(message.room)
.setSender(message.sender)
.setGroupable(true);

const block = modify.getCreator().getBlockBuilder();

block.addActionsBlock({
blockId: "githubdata",
elements: [
block.newButtonElement({
actionId: ModalsEnum.MERGE_PULL_REQUEST_ACTION,
text: block.newPlainTextObject("Merge"),
value: `${username}/${repositoryName} ${pullNumber}`,
style: ButtonStyle.PRIMARY
}),
block.newButtonElement({
actionId: ModalsEnum.PR_COMMENT_LIST_ACTION,
text: block.newPlainTextObject("Comment"),
value: `${username}/${repositoryName} ${pullNumber}`,
style: ButtonStyle.PRIMARY
}),
block.newButtonElement({
actionId: ModalsEnum.APPROVE_PULL_REQUEST_ACTION,
text: block.newPlainTextObject("Approve"),
value: `${username}/${repositoryName} ${pullNumber}`,
style: ButtonStyle.PRIMARY
})
]
})

messageBuilder.setBlocks(block);

return await modify.getCreator().finish(messageBuilder);
} catch (error) {
console.error("Error in handleGithubPRLink:", error);
return "Error: Unable to process the GitHub PR link.";
}
}
8 changes: 7 additions & 1 deletion github/helpers/checkLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ export async function hasGitHubCodeSegmentLink(message: IMessage): Promise<Boole
}
return false;
}

export async function hasGithubPRLink(message: IMessage): Promise<Boolean> {
let prLink: RegExp = /https?:\/\/github\.com\/[A-Za-z0-9_-]+\/[A-Za-z0-9_.-]+\/pull\/[0-9]+/;
if (prLink.test(message.text!)) {
return true;
}
return false;
}
export async function isGithubLink(message: IMessage) {
let githubLink: RegExp = /(?:https?:\/\/)?(?:www\.)?github\.com\//;
if (githubLink.test(message.text!)) {
Expand Down
31 changes: 31 additions & 0 deletions github/helpers/githubSDK.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IHttp, HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors";
import { IGitHubIssue } from "../definitions/githubIssue";
import { ModalsEnum } from "../enum/Modals";
import { IAuthData } from "@rocket.chat/apps-engine/definition/oauth2/IOAuth2";

const BaseHost = "https://github.com/";
const BaseApiHost = "https://api.github.com/";
Expand Down Expand Up @@ -432,6 +433,36 @@ export async function mergePullRequest(
return JSONResponse;
}

export async function approvePullRequest(
http: IHttp,
repoName: string,
access_token: string,
pullRequestNumber: string | number,
){
const response = await http.post(
`https://api.github.com/repos/${repoName}/pulls/${pullRequestNumber}/reviews`,
{
headers: {
Authorization: `token ${access_token}`,
"Content-Type": "application/json",
},
data:{
'event':"APPROVE"
}
}
);

// If it isn't a 2xx code, something wrong happened
let JSONResponse = JSON.parse(response.content || "{}");

if (!response.statusCode.toString().startsWith("2")) {
JSONResponse["serverError"] = true;
} else {
JSONResponse["serverError"] = false;
}

return JSONResponse;
}
export async function getBasicUserInfo(
http: IHttp,
access_token: String,
Expand Down

0 comments on commit 7a7cca0

Please sign in to comment.