Skip to content

Commit def4633

Browse files
Implement searchRepos for Bitbucket (#18856)
* implementing searchRepositories * implement searchRepos for BBS * limit fields * remove fields * adding gitlab searchRepos * remove gitlab searchRepos - not working will do in a followup * wrap calls w/ asyncBatch to limit concurrency
1 parent 4abea1c commit def4633

File tree

4 files changed

+79
-4
lines changed

4 files changed

+79
-4
lines changed

components/server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"@octokit/rest": "18.6.1",
6363
"@probot/get-private-key": "^1.1.1",
6464
"@types/jaeger-client": "^3.18.3",
65+
"async-batch": "^1.1.2",
6566
"base-64": "^1.0.0",
6667
"bitbucket": "^2.7.0",
6768
"body-parser": "^1.19.2",

components/server/src/bitbucket-server/bitbucket-server-repository-provider.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,21 @@ export class BitbucketServerRepositoryProvider implements RepositoryProvider {
188188
return commits.map((c) => c.id);
189189
}
190190

191-
// TODO: implement repo search
192191
public async searchRepos(user: User, searchString: string): Promise<RepositoryInfo[]> {
193-
return [];
192+
// Only load 1 page of 10 results for our searchString
193+
const results = await this.api.getRepos(user, { maxPages: 1, limit: 10, searchString });
194+
195+
const repos: RepositoryInfo[] = [];
196+
results.forEach((r) => {
197+
const cloneUrl = r.links.clone.find((u) => u.name === "http")?.href;
198+
if (cloneUrl) {
199+
repos.push({
200+
url: cloneUrl.replace("http://", "https://"),
201+
name: r.name,
202+
});
203+
}
204+
});
205+
206+
return repos;
194207
}
195208
}

components/server/src/bitbucket/bitbucket-repository-provider.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { URL } from "url";
1111
import { RepoURL } from "../repohost/repo-url";
1212
import { RepositoryProvider } from "../repohost/repository-provider";
1313
import { BitbucketApiFactory } from "./bitbucket-api-factory";
14+
import asyncBatch from "async-batch";
1415

1516
@injectable()
1617
export class BitbucketRepositoryProvider implements RepositoryProvider {
@@ -157,8 +158,63 @@ export class BitbucketRepositoryProvider implements RepositoryProvider {
157158
return commits.map((v: Schema.Commit) => v.hash!);
158159
}
159160

160-
// TODO: implement repo search
161+
//
162+
// Searching Bitbucket requires a workspace to be specified
163+
// This results in a two step process:
164+
// 1. Get all workspaces for the user
165+
// 2. Fan out and search each workspace for the repos
166+
//
161167
public async searchRepos(user: User, searchString: string): Promise<RepositoryInfo[]> {
162-
return [];
168+
const api = await this.apiFactory.create(user);
169+
170+
const workspaces = await api.workspaces.getWorkspaces({ pagelen: 25 });
171+
172+
const workspaceSlugs: string[] = (
173+
workspaces.data.values?.map((w) => {
174+
return w.slug || "";
175+
}) ?? []
176+
).filter(Boolean);
177+
178+
if (workspaceSlugs.length === 0) {
179+
return [];
180+
}
181+
182+
// Batch our requests to the api so we only make up to 5 calls in parallel
183+
const results = await asyncBatch(
184+
workspaceSlugs,
185+
async (workspaceSlug) => {
186+
return api.repositories.list({
187+
workspace: workspaceSlug,
188+
// name includes searchString
189+
q: `name ~ "${searchString}"`,
190+
// sort by most recently updatd first
191+
sort: "-updated_on",
192+
// limit to the first 10 results per workspace
193+
pagelen: 10,
194+
});
195+
},
196+
// 5 calls in parallel
197+
5,
198+
);
199+
200+
// Convert into RepositoryInfo
201+
const repos: RepositoryInfo[] = [];
202+
203+
results
204+
.map((result) => {
205+
return result.data.values ?? [];
206+
})
207+
// flatten out the array of arrays
208+
.flat()
209+
// convert into the format we want to return
210+
.forEach((repo) => {
211+
const name = repo.name;
212+
const url = repo.links?.html?.href;
213+
if (name && url) {
214+
repos.push({ name, url });
215+
}
216+
});
217+
218+
return repos;
163219
}
164220
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4424,6 +4424,11 @@ ast-types-flow@^0.0.7:
44244424
resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz"
44254425
integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
44264426

4427+
async-batch@^1.1.2:
4428+
version "1.1.2"
4429+
resolved "https://registry.yarnpkg.com/async-batch/-/async-batch-1.1.2.tgz#ecac7dee0f02a1a3a846438ba7d3f96746239d79"
4430+
integrity sha512-tBR4YIKi7s1cCtNHuguyXVBdRSkS6N8OhM1czrpE0W2j2yovj/IAxXD187cNX0d+kGxBbZLPwW5ASonYuZJprA==
4431+
44274432
async@^3.2.3:
44284433
version "3.2.4"
44294434
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"

0 commit comments

Comments
 (0)