Skip to content

Commit

Permalink
Merge pull request #46 from gentlementlegen/feat/close-prs
Browse files Browse the repository at this point in the history
feat: close PRs on unassign
  • Loading branch information
gentlementlegen authored Sep 14, 2024
2 parents 182998e + cc9e891 commit e9b7bd6
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 15 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Start | Stop",
"description": "Assign or un-assign yourself from an issue.",
"ubiquity:listeners": ["issue_comment.created", "issues.assigned", "pull_request.opened"],
"ubiquity:listeners": ["issue_comment.created", "issues.assigned", "issues.unassigned", "pull_request.opened"],
"commands": {
"start": {
"ubiquity:example": "/start",
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/shared/check-assignments.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Context, isContextCommentCreated } from "../../types";
import { Context } from "../../types";
import { getOwnerRepoFromHtmlUrl } from "../../utils/issue";

async function getUserStopComments(context: Context, username: string): Promise<number> {
if (!isContextCommentCreated(context)) {
if (!("issue" in context.payload)) {
throw new Error("The context does not contain an issue.");
}
const { payload, octokit, logger } = context;
Expand Down Expand Up @@ -55,7 +55,7 @@ export async function hasUserBeenUnassigned(context: Context, username: string):
}

async function getAssignmentEvents(context: Context) {
if (!isContextCommentCreated(context)) {
if (!("issue" in context.payload)) {
throw new Error("The context does not contain an issue.");
}
const { repository, issue } = context.payload;
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/shared/stop.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Assignee, Context, Sender } from "../../types";
import { addCommentToIssue, closePullRequestForAnIssue } from "../../utils/issue";
import { closePullRequestForAnIssue } from "../../utils/issue";
import { HttpStatusCode, Result } from "../result-types";

export async function stop(
Expand Down
17 changes: 14 additions & 3 deletions src/handlers/user-start-stop.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Repository } from "@octokit/graphql-schema";
import { Context, isContextCommentCreated, Label } from "../types";
import { Context, isIssueCommentEvent, Label } from "../types";
import { QUERY_CLOSING_ISSUE_REFERENCES } from "../utils/get-closing-issue-references";
import { addCommentToIssue, getOwnerRepoFromHtmlUrl } from "../utils/issue";
import { addCommentToIssue, closePullRequestForAnIssue, getOwnerRepoFromHtmlUrl } from "../utils/issue";
import { HttpStatusCode, Result } from "./result-types";
import { getDeadline } from "./shared/generate-assignment-comment";
import { start } from "./shared/start";
import { stop } from "./shared/stop";

export async function userStartStop(context: Context): Promise<Result> {
if (!isContextCommentCreated(context)) {
if (!isIssueCommentEvent(context)) {
return { status: HttpStatusCode.NOT_MODIFIED };
}
const { payload } = context;
Expand Down Expand Up @@ -91,3 +91,14 @@ export async function userPullRequest(context: Context<"pull_request.opened">):
}
return { status: HttpStatusCode.NOT_MODIFIED };
}

export async function userUnassigned(context: Context): Promise<Result> {
if (!("issue" in context.payload)) {
context.logger.debug("Payload does not contain an issue, skipping issues.unassigned event.");
return { status: HttpStatusCode.NOT_MODIFIED };
}
const { payload } = context;
const { issue, sender, repository } = payload;
await closePullRequestForAnIssue(context, issue.number, repository, sender.login);
return { status: HttpStatusCode.OK, content: "Linked pull-requests closed." };
}
4 changes: 3 additions & 1 deletion src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Octokit } from "@octokit/rest";
import { createClient } from "@supabase/supabase-js";
import { LogReturn, Logs } from "@ubiquity-dao/ubiquibot-logger";
import { createAdapters } from "./adapters";
import { userPullRequest, userSelfAssign, userStartStop } from "./handlers/user-start-stop";
import { userPullRequest, userSelfAssign, userStartStop, userUnassigned } from "./handlers/user-start-stop";
import { Context, Env, PluginInputs } from "./types";
import { addCommentToIssue } from "./utils/issue";

Expand Down Expand Up @@ -32,6 +32,8 @@ export async function startStopTask(inputs: PluginInputs, env: Env) {
return await userSelfAssign(context as Context<"issues.assigned">);
case "pull_request.opened":
return await userPullRequest(context as Context<"pull_request.opened">);
case "issues.unassigned":
return await userUnassigned(context);
default:
context.logger.error(`Unsupported event: ${context.eventName}`);
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { createAdapters } from "../adapters";
import { Env } from "./env";
import { Logs } from "@ubiquity-dao/ubiquibot-logger";

export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened";
export type SupportedEventsU = "issue_comment.created" | "issues.assigned" | "pull_request.opened" | "issues.unassigned";

export type SupportedEvents = {
[K in SupportedEventsU]: K extends WebhookEventName ? WebhookEvent<K> : never;
};

export function isContextCommentCreated(context: Context): context is Context<"issue_comment.created"> {
export function isIssueCommentEvent(context: Context): context is Context<"issue_comment.created"> {
return "issue" in context.payload;
}

Expand Down
5 changes: 2 additions & 3 deletions src/utils/issue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ms from "ms";
import { Context, isContextCommentCreated } from "../types/context";
import { Context } from "../types/context";
import { GitHubIssueSearch, Review } from "../types/payload";
import { getLinkedPullRequests, GetLinkedResults } from "./get-linked-prs";

Expand Down Expand Up @@ -35,7 +35,7 @@ export async function addCommentToIssue(context: Context, message: string | null
return;
}

if (!isContextCommentCreated(context)) {
if (!("issue" in context.payload)) {
context.logger.error("Cannot post without a referenced issue.");
return;
}
Expand Down Expand Up @@ -120,7 +120,6 @@ export async function closePullRequestForAnIssue(context: Context, issueNumber:
return logger.info(`No PRs were closed`);
}

await addCommentToIssue(context, comment);
return logger.info(comment);
}

Expand Down
22 changes: 21 additions & 1 deletion tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { db } from "./__mocks__/db";
import { server } from "./__mocks__/node";
import usersGet from "./__mocks__/users-get.json";
import { expect, describe, beforeAll, beforeEach, afterAll, afterEach } from "@jest/globals";
import { userStartStop } from "../src/handlers/user-start-stop";
import { userStartStop, userUnassigned } from "../src/handlers/user-start-stop";
import issueTemplate from "./__mocks__/issue-template";
import { createAdapters } from "../src/adapters";
import { createClient } from "@supabase/supabase-js";
Expand Down Expand Up @@ -112,6 +112,26 @@ describe("User start/stop", () => {
);
});

test("Author's manual unassign should close linked issue", async () => {
const infoSpy = jest.spyOn(console, "info").mockImplementation(() => {});
const issue = db.issue.findFirst({ where: { id: { equals: 2 } } }) as unknown as Issue;
const sender = db.users.findFirst({ where: { id: { equals: 2 } } }) as unknown as PayloadSender;
const context = createContext(issue, sender, "") as Context<"issues.unassigned">;

context.adapters = createAdapters(getSupabase(), context);

const { content } = await userUnassigned(context);

expect(content).toEqual("Linked pull-requests closed.");
const logs = infoSpy.mock.calls.flat();
expect(logs[0]).toMatch(/Opened prs/);
expect(cleanLogString(logs[3])).toMatch(
cleanLogString(
" › ```diff# These linked pull requests are closed: http://github.com/ubiquity/test-repo/pull/2 http://github.com/ubiquity/test-repo/pull/3"
)
);
});

test("User can't stop an issue they're not assigned to", async () => {
const issue = db.issue.findFirst({ where: { id: { equals: 2 } } }) as unknown as Issue;
const sender = db.users.findFirst({ where: { id: { equals: 1 } } }) as unknown as PayloadSender;
Expand Down

0 comments on commit e9b7bd6

Please sign in to comment.