Skip to content

Commit

Permalink
fix: using graphql to fetch prs
Browse files Browse the repository at this point in the history
  • Loading branch information
gentlementlegen committed Oct 29, 2024
1 parent 32a4457 commit c49982b
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 22 deletions.
141 changes: 141 additions & 0 deletions src/utils/get-pull-requests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { Organization, PullRequest, PullRequestState, Repository } from "@octokit/graphql-schema";
import { Context } from "../types";

type QueryResponse = {
organization: Pick<Organization, "repositories"> & {
repositories: {
nodes: Array<
Pick<Repository, "name"> & {
pullRequests: {
nodes: Array<
Pick<PullRequest, "number" | "title" | "url" | "createdAt"> & {
author: {
login: string;
} | null;
}
>;
pageInfo: {
endCursor: string | null;
hasNextPage: boolean;
};
};
}
>;
pageInfo: {
endCursor: string | null;
hasNextPage: boolean;
};
};
};
};

interface TransformedPullRequest {
repository: string;
number: number;
title: string;
url: string;
author: string | null;
createdAt: string;
}

interface FetchPullRequestsParams {
context: Context;
organization: string;
state?: PullRequestState[];
}

const QUERY_PULL_REQUESTS = /* GraphQL */ `
query ($organization: String!, $state: [PullRequestState!]!, $repoAfter: String, $prAfter: String) {
organization(login: $organization) {
repositories(first: 100, after: $repoAfter) {
nodes {
name
pullRequests(states: $state, first: 100, after: $prAfter) {
nodes {
number
title
url
author {
login
}
createdAt
}
pageInfo {
endCursor
hasNextPage
}
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
`;

async function getAllPullRequests({ context, organization, state = ["OPEN"] }: FetchPullRequestsParams): Promise<TransformedPullRequest[]> {
const { octokit } = context;
const allPullRequests: TransformedPullRequest[] = [];
let hasNextRepoPage = true;
let repoAfter: string | null = null;

while (hasNextRepoPage) {
try {
const response = (await octokit.graphql(QUERY_PULL_REQUESTS, {
organization,
state,
repoAfter,
prAfter: null,
})) as QueryResponse;

const { repositories } = response.organization;

for (const repo of repositories.nodes) {
let hasNextPrPage = true;
let prAfter: string | null = null;

while (hasNextPrPage) {
const prResponse = (await octokit.graphql<QueryResponse>(QUERY_PULL_REQUESTS, {
organization,
state,
repoAfter,
prAfter,
})) as QueryResponse;

const currentRepo = prResponse.organization.repositories.nodes.find((r) => r?.name === repo.name);

if (currentRepo && currentRepo.pullRequests.nodes?.length) {
const transformedPrs = (currentRepo.pullRequests.nodes.filter((o) => o) as PullRequest[]).map((pr) => ({
repository: repo.name,
number: pr.number,
title: pr.title,
url: pr.url,
author: pr.author?.login ?? null,
createdAt: pr.createdAt,
}));

allPullRequests.push(...transformedPrs);
}

hasNextPrPage = currentRepo?.pullRequests.pageInfo.hasNextPage ?? false;
prAfter = currentRepo?.pullRequests.pageInfo.endCursor ?? null;

await new Promise((resolve) => setTimeout(resolve, 1000));
}
}

hasNextRepoPage = repositories.pageInfo.hasNextPage;
repoAfter = repositories.pageInfo.endCursor;
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}

return allPullRequests;
}

export { getAllPullRequests };
export type { FetchPullRequestsParams, TransformedPullRequest };
45 changes: 23 additions & 22 deletions src/utils/issue.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { RestEndpointMethodTypes } from "@octokit/rest";
import ms from "ms";
import { Context } from "../types/context";
import { GitHubIssueSearch, Review } from "../types/payload";
import { getLinkedPullRequests, GetLinkedResults } from "./get-linked-prs";
import { getAllPullRequests, TransformedPullRequest } from "./get-pull-requests";

export function isParentIssue(body: string) {
const parentPattern = /-\s+\[( |x)\]\s+#\d+/;
Expand Down Expand Up @@ -170,21 +170,21 @@ export async function addAssignees(context: Context, issueNo: number, assignees:
await confirmMultiAssignment(context, issueNo, assignees);
}

export async function getAllPullRequests(context: Context, state: "open" | "closed" | "all" = "open", username: string) {
const { payload } = context;
const query: RestEndpointMethodTypes["search"]["issuesAndPullRequests"]["parameters"] = {
q: `org:${payload.repository.owner.login} author:${username} state:${state}`,
per_page: 100,
order: "desc",
sort: "created",
};

try {
return (await context.octokit.paginate(context.octokit.search.issuesAndPullRequests, query)) as GitHubIssueSearch["items"];
} catch (err: unknown) {
throw new Error(context.logger.error("Fetching all pull requests failed!", { error: err as Error, query }).logMessage.raw);
}
}
// export async function getAllPullRequests(context: Context, state: "open" | "closed" | "all" = "open", username: string) {
// const { payload } = context;
// const query: RestEndpointMethodTypes["search"]["issuesAndPullRequests"]["parameters"] = {
// q: `org:${payload.repository.owner.login} author:${username} state:${state}`,
// per_page: 100,
// order: "desc",
// sort: "created",
// };
//
// try {
// return (await context.octokit.paginate(context.octokit.search.issuesAndPullRequests, query)) as GitHubIssueSearch["items"];
// } catch (err: unknown) {
// throw new Error(context.logger.error("Fetching all pull requests failed!", { error: err as Error, query }).logMessage.raw);
// }
// }

export async function getAllPullRequestReviews(context: Context, pullNumber: number, owner: string, repo: string) {
const {
Expand Down Expand Up @@ -220,11 +220,12 @@ export async function getAvailableOpenedPullRequests(context: Context, username:
if (!reviewDelayTolerance) return [];

const openedPullRequests = await getOpenedPullRequests(context, username);
const result = [] as typeof openedPullRequests;
const result: TransformedPullRequest[] = [];

for (let i = 0; i < openedPullRequests.length; i++) {
for (let i = 0; openedPullRequests && i < openedPullRequests.length; i++) {
const openedPullRequest = openedPullRequests[i];
const { owner, repo } = getOwnerRepoFromHtmlUrl(openedPullRequest.html_url);
if (!openedPullRequest) continue;
const { owner, repo } = getOwnerRepoFromHtmlUrl(openedPullRequest.url);
const reviews = await getAllPullRequestReviews(context, openedPullRequest.number, owner, repo);

if (reviews.length > 0) {
Expand All @@ -234,7 +235,7 @@ export async function getAvailableOpenedPullRequests(context: Context, username:
}
}

if (reviews.length === 0 && new Date().getTime() - new Date(openedPullRequest.created_at).getTime() >= getTimeValue(reviewDelayTolerance)) {
if (reviews.length === 0 && new Date().getTime() - new Date(openedPullRequest.createdAt).getTime() >= getTimeValue(reviewDelayTolerance)) {
result.push(openedPullRequest);
}
}
Expand All @@ -252,8 +253,8 @@ export function getTimeValue(timeString: string): number {
}

async function getOpenedPullRequests(context: Context, username: string): Promise<ReturnType<typeof getAllPullRequests>> {
const prs = await getAllPullRequests(context, "open", username);
return prs.filter((pr) => pr.pull_request && pr.state === "open");
const prs = await getAllPullRequests({ context, state: ["OPEN"], organization: context.payload.repository.owner.login });
return prs.filter((pr) => pr.author === username);
}

/**
Expand Down

0 comments on commit c49982b

Please sign in to comment.