This commit is contained in:
RahulGautamSingh 2025-01-23 04:49:49 +05:30 committed by GitHub
commit 9a9be5e800
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 148 additions and 23 deletions

View file

@ -85,3 +85,35 @@ mutation EnablePullRequestAutoMerge(
}
}
`;
export const getIssuesByIssueTypeQuery = `
query(
$queryStr: String!
$count: Int,
$cursor: String
) {
search(
type: ISSUE
first: $count,
after: $cursor,
query: $queryStr
) {
nodes {
... on Issue {
number
title
url
labels (first:5) {
nodes {
name
}
}
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
`;

View file

@ -40,6 +40,9 @@ export interface GithubHttpOptions extends HttpOptions {
interface GithubGraphqlRepoData<T = unknown> {
repository?: T;
}
interface GithubGraphqlSearchData<T = unknown> {
search?: T;
}
export type GithubGraphqlResponse<T = unknown> =
| {
@ -422,6 +425,78 @@ export class GithubHttp extends Http<GithubHttpOptions> {
}
}
async querySearchField<T = Record<string, unknown>>(
query: string,
fieldName: string,
options: GraphqlOptions = {},
): Promise<T[]> {
const result: T[] = [];
const { paginate = true } = options;
let optimalCount: null | number = null;
let count = getGraphqlPageSize(
fieldName,
options.count ?? MAX_GRAPHQL_PAGE_SIZE,
);
let limit = options.limit ?? 1000;
let cursor: string | null = null;
let isIterating = true;
while (isIterating) {
const res = await this.requestGraphql<GithubGraphqlSearchData<T>>(query, {
...options,
count: Math.min(count, limit),
cursor,
paginate,
});
const searchData = res?.data;
if (
is.nonEmptyObject(searchData) &&
!is.nullOrUndefined(searchData[fieldName])
) {
optimalCount = count;
const {
nodes = [],
edges = [],
pageInfo,
} = searchData[fieldName] as GraphqlPaginatedContent<T>;
result.push(...nodes);
result.push(...edges);
limit = Math.max(0, limit - nodes.length - edges.length);
if (limit === 0) {
isIterating = false;
} else if (paginate && pageInfo) {
const { hasNextPage, endCursor } = pageInfo;
if (hasNextPage && endCursor) {
cursor = endCursor;
} else {
isIterating = false;
}
}
} else {
count = Math.floor(count / 2);
if (count === 0) {
logger.warn({ query, options, res }, 'Error fetching GraphQL nodes');
isIterating = false;
}
}
if (!paginate) {
isIterating = false;
}
}
if (optimalCount && optimalCount < MAX_GRAPHQL_PAGE_SIZE) {
setGraphqlPageSize(fieldName, optimalCount);
}
return result;
}
async queryRepoField<T = Record<string, unknown>>(
query: string,
fieldName: string,

View file

@ -1,10 +1,9 @@
import { DateTime } from 'luxon';
import { logger } from '../../lib/logger';
import { getIssuesByIssueTypeQuery } from '../../lib/modules/platform/github/graphql';
import * as hostRules from '../../lib/util/host-rules';
import { GithubHttp } from '../../lib/util/http/github';
import { getQueryString } from '../../lib/util/url';
const gitHubApiUrl = 'https://api.github.com/search/issues?';
const githubApi = new GithubHttp();
if (process.env.GITHUB_TOKEN) {
@ -15,17 +14,12 @@ if (process.env.GITHUB_TOKEN) {
});
}
type GithubApiQueryResponse = {
total_count: number;
incomplete_results: boolean;
items: ItemsEntity[];
};
export type ItemsEntity = {
html_url: string;
number: number;
title: string;
labels: LabelsEntity[];
issueType: 'Bug' | 'Feature';
};
export type LabelsEntity = {
@ -46,6 +40,39 @@ export interface Items {
features: ItemsEntity[];
}
async function getIssuesByIssueType(
issueType: 'Bug' | 'Feature',
): Promise<ItemsEntity[]> {
const queryString = `type:${issueType}, repo:renovatebot/renovate, state:open`;
const res = (await githubApi.querySearchField<unknown>(
getIssuesByIssueTypeQuery,
'search',
{
variables: {
queryStr: queryString,
},
paginate: true,
readOnly: true,
},
)) as {
labels: { nodes: { name: string }[] };
number: number;
title: string;
url: string;
}[];
return (
res.map((issue) => {
return {
...issue,
issueType,
labels: issue.labels.nodes,
html_url: issue.url,
};
}) ?? []
);
}
export async function getOpenGitHubItems(): Promise<RenovateOpenItems> {
const result: RenovateOpenItems = {
managers: {},
@ -59,18 +86,10 @@ export async function getOpenGitHubItems(): Promise<RenovateOpenItems> {
return result;
}
const q = `repo:renovatebot/renovate type:issue is:open`;
const per_page = 100;
try {
const query = getQueryString({ q, per_page });
const res = await githubApi.getJsonUnchecked<GithubApiQueryResponse>(
gitHubApiUrl + query,
{
paginationField: 'items',
paginate: true,
},
const rawItems: ItemsEntity[] = (await getIssuesByIssueType('Bug')).concat(
await getIssuesByIssueType('Feature'),
);
const rawItems = res.body?.items ?? [];
result.managers = extractIssues(rawItems, 'manager:');
result.platforms = extractIssues(rawItems, 'platform:');
@ -91,9 +110,8 @@ function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems {
const issuesMap: OpenItems = {};
for (const item of items) {
const type = item.labels
.find((l) => l.name.startsWith('type:'))
?.name.split(':')[1];
const type = item.issueType;
if (!type) {
continue;
}
@ -107,10 +125,10 @@ function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems {
issuesMap[label] = { bugs: [], features: [] };
}
switch (type) {
case 'bug':
case 'Bug':
issuesMap[label]?.bugs.push(item);
break;
case 'feature':
case 'Feature':
issuesMap[label]?.features.push(item);
break;
default: