diff --git a/src/plus/integrations/providers/gitlab.ts b/src/plus/integrations/providers/gitlab.ts index 2265550567162..bffcaf06c350e 100644 --- a/src/plus/integrations/providers/gitlab.ts +++ b/src/plus/integrations/providers/gitlab.ts @@ -267,6 +267,29 @@ abstract class GitLabIntegrationBase< return Promise.resolve(undefined); } + protected override async searchProviderPullRequests( + { accessToken }: AuthenticationSession, + searchQuery: string, + repos?: GitLabRepositoryDescriptor[], + cancellation?: CancellationToken, + ): Promise { + const api = await this.container.gitlab; + if (!api) { + return undefined; + } + + return api.searchPullRequests( + this, + accessToken, + { + search: searchQuery, + repos: repos?.map(r => `${r.owner}/${r.name}`), + baseUrl: this.apiBaseUrl, + }, + cancellation, + ); + } + protected override async mergeProviderPullRequest( _session: AuthenticationSession, pr: PullRequest, diff --git a/src/plus/integrations/providers/gitlab/gitlab.ts b/src/plus/integrations/providers/gitlab/gitlab.ts index 276182c6a2b1e..5d52ba6222322 100644 --- a/src/plus/integrations/providers/gitlab/gitlab.ts +++ b/src/plus/integrations/providers/gitlab/gitlab.ts @@ -661,6 +661,50 @@ export class GitLabApi implements Disposable { } } + @debug({ args: { 0: p => p.name, 1: '' } }) + async searchPullRequests( + provider: Provider, + token: string, + options?: { search?: string; user?: string; repos?: string[]; baseUrl?: string; avatarSize?: number }, + cancellation?: CancellationToken, + ): Promise { + const scope = getLogScope(); + const search = options?.search; + if (!search) { + return []; + } + try { + const gitlabPRs = await this.request( + provider, + token, + options?.baseUrl, + `v4/search/?scope=merge_requests&search=${search}`, + { + method: 'GET', + }, + cancellation, + scope, + ); + if (gitlabPRs.length === 0) { + return []; + } + + const prs: PullRequest[] = gitlabPRs.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; + } catch (ex) { + if (ex instanceof RequestNotFoundError) return []; + + throw this.handleException(ex, provider, scope); + } + } + private async findUser( provider: Provider, token: string, diff --git a/src/plus/integrations/providers/gitlab/models.ts b/src/plus/integrations/providers/gitlab/models.ts index 846588c795540..a86d70356099e 100644 --- a/src/plus/integrations/providers/gitlab/models.ts +++ b/src/plus/integrations/providers/gitlab/models.ts @@ -1,5 +1,5 @@ import type { PullRequestState } from '../../../../git/models/pullRequest'; -import { PullRequest } from '../../../../git/models/pullRequest'; +import { PullRequest, PullRequestMergeableState } from '../../../../git/models/pullRequest'; import type { Provider } from '../../../../git/models/remoteProvider'; import type { Integration } from '../../integration'; import type { ProviderPullRequest } from '../models'; @@ -91,7 +91,12 @@ export interface GitLabMergeRequestREST { updated_at: string; closed_at: string | null; merged_at: string | null; + detailed_merge_status: 'conflict' | 'mergeable' | string; // https://docs.gitlab.com/ee/api/merge_requests.html#merge-status web_url: string; + references: { + full: string; + short: string; + }; } export function fromGitLabMergeRequestREST( @@ -108,7 +113,7 @@ export function fromGitLabMergeRequestREST( url: pr.author?.web_url ?? '', }, String(pr.iid), - undefined, + String(pr.id), pr.title, pr.web_url, repo, @@ -117,6 +122,11 @@ export function fromGitLabMergeRequestREST( new Date(pr.updated_at), pr.closed_at == null ? undefined : new Date(pr.closed_at), pr.merged_at == null ? undefined : new Date(pr.merged_at), + pr.detailed_merge_status === 'mergeable' + ? PullRequestMergeableState.Mergeable + : pr.detailed_merge_status === 'conflict' + ? PullRequestMergeableState.Conflicting + : PullRequestMergeableState.Unknown, ); }