Skip to content

Commit 0c11cdc

Browse files
AlexTugarevroboquat
authored andcommitted
[bitbucket-server] handle pull-request context url
1 parent 9e2f4ac commit 0c11cdc

File tree

3 files changed

+257
-6
lines changed

3 files changed

+257
-6
lines changed

components/server/src/bitbucket-server/bitbucket-server-api.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export class BitbucketServerApi {
265265
);
266266
}
267267

268-
setWebhook(
268+
async setWebhook(
269269
user: User,
270270
params: { repoKind: "projects" | "users"; owner: string; repositorySlug: string },
271271
webhook: BitbucketServer.WebhookParams,
@@ -301,6 +301,17 @@ export class BitbucketServerApi {
301301
}
302302
return this.runQuery<BitbucketServer.Paginated<BitbucketServer.Repository>>(user, `/repos${q}`);
303303
}
304+
305+
async getPullRequest(
306+
user: User,
307+
params: { repoKind: "projects" | "users"; owner: string; repositorySlug: string; nr: number },
308+
): Promise<BitbucketServer.PullRequest> {
309+
const result = await this.runQuery<BitbucketServer.PullRequest>(
310+
user,
311+
`/${params.repoKind}/${params.owner}/repos/${params.repositorySlug}/pull-requests/${params.nr}`,
312+
);
313+
return result;
314+
}
304315
}
305316

306317
export namespace BitbucketServer {
@@ -329,6 +340,7 @@ export namespace BitbucketServer {
329340
id: number;
330341
name: string;
331342
public: boolean;
343+
type: "PERSONAL" | "NORMAL";
332344
}
333345

334346
export interface Branch {
@@ -375,6 +387,45 @@ export namespace BitbucketServer {
375387
message: string;
376388
}
377389

390+
export interface PullRequest {
391+
id: number;
392+
version: number;
393+
title: string;
394+
description: string;
395+
state: "OPEN" | string;
396+
open: boolean;
397+
closed: boolean;
398+
createdDate: number;
399+
updatedDate: number;
400+
fromRef: Ref;
401+
toRef: Ref;
402+
locked: boolean;
403+
author: {
404+
user: User;
405+
role: "AUTHOR" | string;
406+
approved: boolean;
407+
status: "UNAPPROVED" | string;
408+
};
409+
// reviewers: [];
410+
// participants: [];
411+
links: {
412+
self: [
413+
{
414+
//"https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123/pull-requests/1"
415+
href: string;
416+
},
417+
];
418+
};
419+
}
420+
421+
export interface Ref {
422+
id: string; // "refs/heads/foo"
423+
displayId: string; //"foo"
424+
latestCommit: string;
425+
type: "BRANCH" | string;
426+
repository: Repository;
427+
}
428+
378429
export interface Paginated<T> {
379430
isLastPage?: boolean;
380431
limit?: number;

components/server/src/bitbucket-server/bitbucket-server-context-parser.spec.ts

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class TestBitbucketServerContextParser {
8888
expect(result).to.deep.include({
8989
ref: "master",
9090
refType: "branch",
91-
revision: "535924584468074ec5dcbe935f4e68fbc3f0cb2d",
91+
revision: "9eea1cca9bb98f0caf7ae77c740d5d24548ff33c",
9292
path: "",
9393
isFile: false,
9494
repository: {
@@ -115,7 +115,7 @@ class TestBitbucketServerContextParser {
115115
expect(result).to.deep.include({
116116
ref: "master",
117117
refType: "branch",
118-
revision: "535924584468074ec5dcbe935f4e68fbc3f0cb2d",
118+
revision: "9eea1cca9bb98f0caf7ae77c740d5d24548ff33c",
119119
path: "",
120120
isFile: false,
121121
repository: {
@@ -142,7 +142,7 @@ class TestBitbucketServerContextParser {
142142
expect(result).to.deep.include({
143143
ref: "main",
144144
refType: "branch",
145-
revision: "a15d7d15adee54d0afdbe88148c8e587e8fb609d",
145+
revision: "d4bdb1459f9fc90756154bdda5eb23c39457a89c",
146146
path: "",
147147
isFile: false,
148148
repository: {
@@ -158,6 +158,152 @@ class TestBitbucketServerContextParser {
158158
title: "alextugarev/tada - main",
159159
});
160160
}
161+
162+
@test async test_commit_context_01() {
163+
const result = await this.parser.handle(
164+
{},
165+
this.user,
166+
"https://bitbucket.gitpod-self-hosted.com/users/jan/repos/yolo/commits/ec15264e536e9684034ea8e08f3afc3fd485b613",
167+
);
168+
169+
expect(result).to.deep.include({
170+
refType: "revision",
171+
revision: "ec15264e536e9684034ea8e08f3afc3fd485b613",
172+
path: "",
173+
isFile: false,
174+
repository: {
175+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/~jan/yolo.git",
176+
defaultBranch: "master",
177+
host: "bitbucket.gitpod-self-hosted.com",
178+
name: "YOLO",
179+
owner: "jan",
180+
private: true,
181+
repoKind: "users",
182+
webUrl: "https://bitbucket.gitpod-self-hosted.com/users/jan/repos/yolo",
183+
},
184+
title: "jan/yolo - ec15264e536e9684034ea8e08f3afc3fd485b613",
185+
});
186+
}
187+
188+
@test async test_PR_context_01() {
189+
const result = await this.parser.handle(
190+
{},
191+
this.user,
192+
"https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123/pull-requests/1/commits",
193+
);
194+
195+
expect(result).to.deep.include({
196+
title: "Let's do it",
197+
nr: 1,
198+
ref: "foo",
199+
refType: "branch",
200+
revision: "1384b6842d73b8705feaf45f3e8aa41f00529042",
201+
repository: {
202+
host: "bitbucket.gitpod-self-hosted.com",
203+
owner: "FOO",
204+
name: "repo123",
205+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/foo/repo123.git",
206+
webUrl: "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123",
207+
defaultBranch: "master",
208+
private: true,
209+
repoKind: "projects",
210+
},
211+
base: {
212+
ref: "master",
213+
refType: "branch",
214+
repository: {
215+
host: "bitbucket.gitpod-self-hosted.com",
216+
owner: "FOO",
217+
name: "repo123",
218+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/foo/repo123.git",
219+
webUrl: "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123",
220+
defaultBranch: "master",
221+
private: true,
222+
repoKind: "projects",
223+
},
224+
},
225+
});
226+
}
227+
228+
@test async test_PR_context_02() {
229+
const result = await this.parser.handle(
230+
{},
231+
this.user,
232+
"https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123/pull-requests/2/overview",
233+
);
234+
235+
expect(result).to.deep.include({
236+
title: "Let's do it again",
237+
nr: 2,
238+
ref: "foo",
239+
refType: "branch",
240+
revision: "1384b6842d73b8705feaf45f3e8aa41f00529042",
241+
repository: {
242+
host: "bitbucket.gitpod-self-hosted.com",
243+
owner: "LAL",
244+
name: "repo123",
245+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/lal/repo123.git",
246+
webUrl: "https://bitbucket.gitpod-self-hosted.com/projects/LAL/repos/repo123",
247+
defaultBranch: "master",
248+
private: true,
249+
repoKind: "projects",
250+
},
251+
base: {
252+
ref: "master",
253+
refType: "branch",
254+
repository: {
255+
host: "bitbucket.gitpod-self-hosted.com",
256+
owner: "FOO",
257+
name: "repo123",
258+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/foo/repo123.git",
259+
webUrl: "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123",
260+
defaultBranch: "master",
261+
private: true,
262+
repoKind: "projects",
263+
},
264+
},
265+
});
266+
}
267+
268+
@test async test_PR_context_03() {
269+
const result = await this.parser.handle(
270+
{},
271+
this.user,
272+
"https://bitbucket.gitpod-self-hosted.com/projects/LAL/repos/repo123/pull-requests/1/overview",
273+
);
274+
275+
expect(result).to.deep.include({
276+
title: "U turn",
277+
nr: 1,
278+
ref: "foo",
279+
refType: "branch",
280+
revision: "1384b6842d73b8705feaf45f3e8aa41f00529042",
281+
repository: {
282+
host: "bitbucket.gitpod-self-hosted.com",
283+
owner: "FOO",
284+
name: "repo123",
285+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/foo/repo123.git",
286+
webUrl: "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123",
287+
defaultBranch: "master",
288+
private: true,
289+
repoKind: "projects",
290+
},
291+
base: {
292+
ref: "master",
293+
refType: "branch",
294+
repository: {
295+
host: "bitbucket.gitpod-self-hosted.com",
296+
owner: "LAL",
297+
name: "repo123",
298+
cloneUrl: "https://bitbucket.gitpod-self-hosted.com/scm/lal/repo123.git",
299+
webUrl: "https://bitbucket.gitpod-self-hosted.com/projects/LAL/repos/repo123",
300+
defaultBranch: "master",
301+
private: true,
302+
repoKind: "projects",
303+
},
304+
},
305+
});
306+
}
161307
}
162308

163309
module.exports = new TestBitbucketServerContextParser();

components/server/src/bitbucket-server/bitbucket-server-context-parser.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
import { NavigatorContext, Repository, User, WorkspaceContext } from "@gitpod/gitpod-protocol";
7+
import { NavigatorContext, PullRequestContext, Repository, User, WorkspaceContext } from "@gitpod/gitpod-protocol";
88
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
99
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
1010
import { inject, injectable } from "inversify";
@@ -26,7 +26,7 @@ export class BitbucketServerContextParser extends AbstractContextParser implemen
2626

2727
try {
2828
const more: Partial<NavigatorContext> = {};
29-
const { repoKind, host, owner, repoName, /*moreSegments*/ searchParams } = await this.parseURL(
29+
const { repoKind, host, owner, repoName, moreSegments, searchParams } = await this.parseURL(
3030
user,
3131
contextUrl,
3232
);
@@ -36,6 +36,18 @@ export class BitbucketServerContextParser extends AbstractContextParser implemen
3636
more.refType = "branch";
3737
}
3838

39+
if (moreSegments[0] === "pull-requests" && !!moreSegments[1]) {
40+
const more = { nr: parseInt(moreSegments[1]) };
41+
return await this.handlePullRequestContext(ctx, user, repoKind, host, owner, repoName, more);
42+
}
43+
44+
if (moreSegments[0] === "commits" && !!moreSegments[1]) {
45+
more.ref = "";
46+
more.revision = moreSegments[1];
47+
more.refType = "revision";
48+
return await this.handleNavigatorContext(ctx, user, repoKind, host, owner, repoName, more);
49+
}
50+
3951
return await this.handleNavigatorContext(ctx, user, repoKind, host, owner, repoName, more);
4052
} catch (e) {
4153
span.addTags({ contextUrl }).log({ error: e });
@@ -203,4 +215,46 @@ export class BitbucketServerContextParser extends AbstractContextParser implemen
203215

204216
return result;
205217
}
218+
219+
protected async handlePullRequestContext(
220+
ctx: TraceContext,
221+
user: User,
222+
repoKind: "projects" | "users",
223+
host: string,
224+
owner: string,
225+
repoName: string,
226+
more: Partial<PullRequestContext> & { nr: number },
227+
): Promise<PullRequestContext> {
228+
const pr = await this.api.getPullRequest(user, {
229+
repoKind,
230+
repositorySlug: repoName,
231+
owner,
232+
nr: more.nr,
233+
});
234+
235+
const getRepository = async (ref: BitbucketServer.Ref) => {
236+
const repoKindFromRef = ref.repository.project.type === "PERSONAL" ? "users" : "projects";
237+
const defaultBranchFromRef = await this.api.getDefaultBranch(user, {
238+
repoKind: repoKindFromRef,
239+
owner: ref.repository.project.owner ? ref.repository.project.owner.slug : ref.repository.project.key,
240+
repositorySlug: ref.repository.slug,
241+
});
242+
return this.toRepository(host, ref.repository, repoKindFromRef, defaultBranchFromRef);
243+
};
244+
245+
return <PullRequestContext>{
246+
repository: await getRepository(pr.fromRef),
247+
title: pr.title,
248+
ref: pr.fromRef.displayId,
249+
refType: "branch",
250+
revision: pr.fromRef.latestCommit,
251+
base: {
252+
repository: await getRepository(pr.toRef),
253+
ref: pr.toRef.displayId,
254+
refType: "branch",
255+
},
256+
...more,
257+
owner,
258+
};
259+
}
206260
}

0 commit comments

Comments
 (0)