Skip to content

Commit ee08854

Browse files
committed
Makes by URL or ID searched GitLab PRs checkoutable
(#3788, #3795)
1 parent 8aec18f commit ee08854

File tree

3 files changed

+148
-11
lines changed

3 files changed

+148
-11
lines changed

src/plus/integrations/integration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ export abstract class IntegrationBase<
556556
const connected = this.maybeConnected ?? (await this.isConnected());
557557
if (!connected) return undefined;
558558

559-
const pr = this.container.cache.getPullRequest(id, resource, this, () => ({
559+
const pr = await this.container.cache.getPullRequest(id, resource, this, () => ({
560560
value: (async () => {
561561
try {
562562
const result = await this.getProviderPullRequest?.(this._session!, resource, id);

src/plus/integrations/providers/gitlab/gitlab.ts

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ import type {
3535
GitLabCommit,
3636
GitLabIssue,
3737
GitLabMergeRequest,
38+
GitLabMergeRequestFull,
3839
GitLabMergeRequestREST,
3940
GitLabMergeRequestState,
4041
GitLabProjectREST,
4142
GitLabUser,
4243
} from './models';
43-
import { fromGitLabMergeRequestREST, fromGitLabMergeRequestState } from './models';
44+
import { fromGitLabMergeRequest, fromGitLabMergeRequestREST, fromGitLabMergeRequestState } from './models';
4445

4546
// drop it as soon as we switch to @gitkraken/providers-api
4647
const gitlabUserIdPrefix = 'gid://gitlab/User/';
@@ -585,24 +586,73 @@ export class GitLabApi implements Disposable {
585586
): Promise<PullRequest | undefined> {
586587
const scope = getLogScope();
587588

588-
const projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);
589-
if (!projectId) return undefined;
589+
interface QueryResult {
590+
data: {
591+
project: {
592+
mergeRequest: GitLabMergeRequestFull | null;
593+
} | null;
594+
};
595+
}
590596

591597
try {
592-
const mr = await this.request<GitLabMergeRequestREST>(
598+
const query = `query getMergeRequest(
599+
$fullPath: ID!
600+
$iid: String!
601+
) {
602+
project(fullPath: $fullPath) {
603+
mergeRequest(iid: $iid) {
604+
id,
605+
iid
606+
state,
607+
author {
608+
id
609+
name
610+
avatarUrl
611+
webUrl
612+
}
613+
diffRefs {
614+
baseSha
615+
headSha
616+
}
617+
title
618+
description
619+
webUrl
620+
createdAt
621+
updatedAt
622+
mergedAt
623+
targetBranch
624+
sourceBranch
625+
project {
626+
id
627+
fullPath
628+
webUrl
629+
}
630+
sourceProject {
631+
id
632+
fullPath
633+
webUrl
634+
}
635+
}
636+
}
637+
}`;
638+
639+
const rsp = await this.graphql<QueryResult>(
593640
provider,
594641
token,
595642
options?.baseUrl,
596-
`v4/projects/${projectId}/merge_requests/${id}`,
643+
query,
597644
{
598-
method: 'GET',
645+
fullPath: `${owner}/${repo}`,
646+
iid: String(id),
599647
},
600648
cancellation,
601649
scope,
602650
);
603-
if (mr == null) return undefined;
604651

605-
return fromGitLabMergeRequestREST(mr, provider, { owner: owner, repo: repo });
652+
if (rsp?.data?.project?.mergeRequest == null) return undefined;
653+
654+
const pr = rsp.data.project.mergeRequest;
655+
return fromGitLabMergeRequest(pr, provider);
606656
} catch (ex) {
607657
if (ex instanceof RequestNotFoundError) return undefined;
608658

src/plus/integrations/providers/gitlab/models.ts

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { HostingIntegrationId } from '../../../../constants.integrations';
2-
import type { PullRequestState } from '../../../../git/models/pullRequest';
3-
import { PullRequest } from '../../../../git/models/pullRequest';
2+
import type { PullRequestRefs, PullRequestState } from '../../../../git/models/pullRequest';
3+
import { PullRequest, PullRequestMergeableState } from '../../../../git/models/pullRequest';
44
import type { PullRequestUrlIdentity } from '../../../../git/models/pullRequest.utils';
55
import type { Provider } from '../../../../git/models/remoteProvider';
66
import type { Integration } from '../../integration';
@@ -67,6 +67,24 @@ export interface GitLabMergeRequest {
6767
webUrl: string;
6868
}
6969

70+
export interface GitLabRepositoryStub {
71+
id: string;
72+
fullPath: string;
73+
webUrl: string;
74+
}
75+
76+
export interface GitLabMergeRequestFull extends GitLabMergeRequest {
77+
id: string;
78+
targetBranch: string;
79+
sourceBranch: string;
80+
diffRefs: {
81+
baseSha: string | null;
82+
headSha: string;
83+
};
84+
project: GitLabRepositoryStub;
85+
sourceProject: GitLabRepositoryStub;
86+
}
87+
7088
export type GitLabMergeRequestState = 'opened' | 'closed' | 'locked' | 'merged';
7189

7290
export function fromGitLabMergeRequestState(state: GitLabMergeRequestState): PullRequestState {
@@ -93,6 +111,15 @@ export interface GitLabMergeRequestREST {
93111
updated_at: string;
94112
closed_at: string | null;
95113
merged_at: string | null;
114+
diff_refs: {
115+
base_sha: string;
116+
head_sha: string;
117+
start_sha: string;
118+
};
119+
source_branch: string;
120+
source_project_id: number;
121+
target_branch: string;
122+
target_project_id: number;
96123
web_url: string;
97124
}
98125

@@ -167,3 +194,63 @@ export function getGitLabPullRequestIdentityFromMaybeUrl(url: string): RequireSo
167194

168195
return { prNumber: match[2], ownerAndRepo: match[1], provider: HostingIntegrationId.GitLab };
169196
}
197+
198+
export function fromGitLabMergeRequest(pr: GitLabMergeRequestFull, provider: Provider): PullRequest {
199+
const [owner, repo] = pr.project.fullPath.split('/');
200+
201+
return new PullRequest(
202+
provider,
203+
{
204+
// author
205+
id: pr.author?.id ?? '',
206+
name: pr.author?.name ?? 'Unknown',
207+
avatarUrl: pr.author?.avatarUrl ?? '',
208+
url: pr.author?.webUrl ?? '',
209+
},
210+
pr.iid, // id
211+
pr.id, // nodeId
212+
pr.title,
213+
pr.webUrl || '',
214+
{
215+
// IssueRepository
216+
owner: owner,
217+
repo: repo,
218+
url: pr.project.webUrl,
219+
},
220+
fromGitLabMergeRequestState(pr.state), // PullRequestState
221+
new Date(pr.createdAt),
222+
new Date(pr.updatedAt),
223+
// TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL
224+
pr.state !== 'closed' ? undefined : new Date(pr.updatedAt),
225+
pr.mergedAt == null ? undefined : new Date(pr.mergedAt),
226+
PullRequestMergeableState.Unknown,
227+
undefined, // viewerCanUpdate
228+
fromGitLabMergeRequestRefs(pr), // PullRequestRefs
229+
);
230+
}
231+
232+
function fromGitLabMergeRequestRefs(pr: GitLabMergeRequestFull): PullRequestRefs | undefined {
233+
return {
234+
base: {
235+
owner: getRepoNamespace(pr.sourceProject.fullPath),
236+
branch: pr.sourceBranch,
237+
exists: true,
238+
url: pr.sourceProject.webUrl,
239+
repo: pr.sourceProject.fullPath,
240+
sha: pr.diffRefs.baseSha || '',
241+
},
242+
head: {
243+
owner: getRepoNamespace(pr.project.fullPath),
244+
branch: pr.targetBranch,
245+
exists: true,
246+
url: pr.project.webUrl,
247+
repo: pr.project.fullPath,
248+
sha: pr.diffRefs.headSha,
249+
},
250+
isCrossRepository: pr.sourceProject.id !== pr.project.id,
251+
};
252+
}
253+
254+
function getRepoNamespace(projectFullPath: string) {
255+
return projectFullPath.split('/').slice(0, -1).join('/');
256+
}

0 commit comments

Comments
 (0)