diff --git a/src/plus/integrations/providers/gitlab/gitlab.ts b/src/plus/integrations/providers/gitlab/gitlab.ts index dc6ddc66d5333..f03b96150508a 100644 --- a/src/plus/integrations/providers/gitlab/gitlab.ts +++ b/src/plus/integrations/providers/gitlab/gitlab.ts @@ -45,6 +45,8 @@ import { fromGitLabMergeRequest, fromGitLabMergeRequestREST, fromGitLabMergeRequ // drop it as soon as we switch to @gitkraken/providers-api const gitlabUserIdPrefix = 'gid://gitlab/User/'; +const gitlabMergeRequestIdPrefix = 'gid://gitlab/MergeRequest/'; + function buildGitLabUserId(id: string | undefined): string | undefined { return id?.startsWith(gitlabUserIdPrefix) ? id.substring(gitlabUserIdPrefix.length) : id; } @@ -724,11 +726,12 @@ export class GitLabApi implements Disposable { return []; } try { + const perPageLimit = 20; // with bigger amount we exceed the max GraphQL complexity in the next query const restPRs = await this.request( provider, token, options?.baseUrl, - `v4/search/?scope=merge_requests&search=${search}`, + `v4/search/?scope=merge_requests&search=${search}&per_page=${perPageLimit}`, { method: 'GET', }, @@ -739,15 +742,80 @@ export class GitLabApi implements Disposable { return []; } - const prs: PullRequest[] = restPRs.map(pr => { - const fullRef = pr.references.full; - const project = { - owner: fullRef.substring(0, fullRef.lastIndexOf('/')), - repo: fullRef.substring(fullRef.lastIndexOf('/') + 1, fullRef.lastIndexOf('!')), - }; - return fromGitLabMergeRequestREST(pr, provider, project); - }); - return prs; + interface QueryResult { + data: Record<`mergeRequest_${number}`, GitLabMergeRequestFull>; + } + + const queryArgs = restPRs.map((_, i) => `$id_${i}: MergeRequestID!`).join('\n'); + const queryFields = restPRs + .map((_, i) => `mergeRequest_${i}: mergeRequest(id: $id_${i}) { ...mergeRequestFields }`) + .join('\n'); + // Set of fields includes only additional fields that are not included in GitLabMergeRequestREST. + // It's limited as much as possible to reduce complexity of the query. + const queryMrFields = `fragment mergeRequestFields on MergeRequest { + diffRefs { + baseSha + headSha + } + project { + id + fullPath + webUrl + } + sourceProject { + id + fullPath + webUrl + } + }`; + const query = `query getMergeRequests (${queryArgs}) {${queryFields}} ${queryMrFields}`; + + const params = restPRs.reduce>((ids, gitlabRestPr, i) => { + ids[`id_${i}`] = `${gitlabMergeRequestIdPrefix}${gitlabRestPr.id}`; + return ids; + }, {}); + const rsp = await this.graphql( + provider, + token, + options?.baseUrl, + query, + params, + cancellation, + scope, + ); + if (rsp?.data != null) { + const resultPRs = restPRs.reduce((accum, restPR, i) => { + const graphQlPR = rsp.data[`mergeRequest_${i}`]; + if (graphQlPR == null) { + return accum; + } + + const fullPr: GitLabMergeRequestFull = { + ...graphQlPR, + iid: String(restPR.iid), + id: String(restPR.id), + state: restPR.state, + author: { + id: buildGitLabUserId(restPR.author?.id) ?? '', + name: restPR.author?.name ?? 'Unknown', + avatarUrl: restPR.author?.avatar_url ?? '', + webUrl: restPR.author?.web_url ?? '', + }, + title: restPR.title, + description: restPR.description, + webUrl: restPR.web_url, + createdAt: restPR.created_at, + updatedAt: restPR.updated_at, + mergedAt: restPR.merged_at, + sourceBranch: restPR.source_branch, + targetBranch: restPR.target_branch, + }; + accum.push(fromGitLabMergeRequest(fullPr, provider)); + return accum; + }, []); + return resultPRs; + } + return []; } catch (ex) { if (ex instanceof RequestNotFoundError) return []; diff --git a/src/plus/integrations/providers/gitlab/models.ts b/src/plus/integrations/providers/gitlab/models.ts index 7b1c2eff6137d..34e691ed641a9 100644 --- a/src/plus/integrations/providers/gitlab/models.ts +++ b/src/plus/integrations/providers/gitlab/models.ts @@ -78,7 +78,7 @@ export interface GitLabMergeRequestFull extends GitLabMergeRequest { diffRefs: { baseSha: string | null; headSha: string; - }; + } | null; project: GitLabRepositoryStub; sourceProject: GitLabRepositoryStub; } @@ -228,7 +228,7 @@ function fromGitLabMergeRequestRefs(pr: GitLabMergeRequestFull): PullRequestRefs exists: true, url: pr.sourceProject.webUrl, repo: pr.sourceProject.fullPath, - sha: pr.diffRefs.baseSha || '', + sha: pr.diffRefs?.baseSha || '', }, head: { owner: getRepoNamespace(pr.project.fullPath), @@ -236,7 +236,7 @@ function fromGitLabMergeRequestRefs(pr: GitLabMergeRequestFull): PullRequestRefs exists: true, url: pr.project.webUrl, repo: pr.project.fullPath, - sha: pr.diffRefs.headSha, + sha: pr.diffRefs?.headSha || '', }, isCrossRepository: pr.sourceProject.id !== pr.project.id, };