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> { interface GithubGraphqlRepoData<T = unknown> {
repository?: T; repository?: T;
} }
interface GithubGraphqlSearchData<T = unknown> {
search?: T;
}
export type GithubGraphqlResponse<T = unknown> = 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>>( async queryRepoField<T = Record<string, unknown>>(
query: string, query: string,
fieldName: string, fieldName: string,

View file

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