diff options
author | Jacek <jacek.poreda@sonarsource.com> | 2021-01-26 15:45:01 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-02-04 20:07:07 +0000 |
commit | 1f7132f59445a3a5a7c51724b770396dd8192c5e (patch) | |
tree | 5ff91636cbc7b43d4da7a498df570d235baf0f85 /server/sonar-alm-client/src | |
parent | 044247d48921aa5486c4cea1e2719207ff250fc9 (diff) | |
download | sonarqube-1f7132f59445a3a5a7c51724b770396dd8192c5e.tar.gz sonarqube-1f7132f59445a3a5a7c51724b770396dd8192c5e.zip |
SONAR-14371 Move Bitbucket Server http client to CE
Diffstat (limited to 'server/sonar-alm-client/src')
7 files changed, 706 insertions, 0 deletions
diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClient.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClient.java new file mode 100644 index 00000000000..758db6e799f --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClient.java @@ -0,0 +1,175 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.alm.client.bitbucketserver; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.annotations.SerializedName; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.sonar.alm.client.TimeoutConfiguration; +import org.sonar.api.server.ServerSide; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonarqube.ws.client.OkHttpClientBuilder; + +import static java.lang.String.format; +import static java.util.Locale.ENGLISH; +import static org.sonar.api.internal.apachecommons.lang.StringUtils.removeEnd; + +@ServerSide +public class BitbucketServerRestClient { + + private static final Logger LOG = Loggers.get(BitbucketServerRestClient.class); + private static final String GET = "GET"; + protected static final String UNABLE_TO_CONTACT_BITBUCKET_SERVER = "Unable to contact Bitbucket server"; + + protected final OkHttpClient client; + + public BitbucketServerRestClient(TimeoutConfiguration timeoutConfiguration) { + OkHttpClientBuilder okHttpClientBuilder = new OkHttpClientBuilder(); + client = okHttpClientBuilder + .setConnectTimeoutMs(timeoutConfiguration.getConnectTimeout()) + .setReadTimeoutMs(timeoutConfiguration.getReadTimeout()) + .build(); + } + + public RepositoryList getRepos(String serverUrl, String token, @Nullable String project, @Nullable String repo) { + String projectOrEmpty = Optional.ofNullable(project).orElse(""); + String repoOrEmpty = Optional.ofNullable(repo).orElse(""); + HttpUrl url = buildUrl(serverUrl, format("/rest/api/1.0/repos?projectname=%s&name=%s", projectOrEmpty, repoOrEmpty)); + return doGet(token, url, r -> buildGson().fromJson(r.body().charStream(), RepositoryList.class)); + } + + public Repository getRepo(String serverUrl, String token, String project, String repoSlug) { + HttpUrl url = buildUrl(serverUrl, format("/rest/api/1.0/projects/%s/repos/%s", project, repoSlug)); + return doGet(token, url, r -> buildGson().fromJson(r.body().charStream(), Repository.class)); + } + + public ProjectList getProjects(String serverUrl, String token) { + HttpUrl url = buildUrl(serverUrl, "/rest/api/1.0/projects"); + return doGet(token, url, r -> buildGson().fromJson(r.body().charStream(), ProjectList.class)); + } + + protected static HttpUrl buildUrl(@Nullable String serverUrl, String relativeUrl) { + if (serverUrl == null || !(serverUrl.toLowerCase(ENGLISH).startsWith("http://") || serverUrl.toLowerCase(ENGLISH).startsWith("https://"))) { + throw new IllegalArgumentException("url must start with http:// or https://"); + } + return HttpUrl.parse(removeEnd(serverUrl, "/") + relativeUrl); + } + + protected <G> G doGet(String token, HttpUrl url, Function<Response, G> handler) { + Request request = prepareRequestWithBearerToken(token, GET, url, null); + return doCall(request, handler); + } + + protected static Request prepareRequestWithBearerToken(String token, String method, HttpUrl url, @Nullable RequestBody body) { + return new Request.Builder() + .method(method, body) + .url(url) + .addHeader("Authorization", "Bearer " + token) + .addHeader("x-atlassian-token", "no-check") + .build(); + } + + protected <G> G doCall(Request request, Function<Response, G> handler) { + try (Response response = client.newCall(request).execute()) { + handleError(response); + return handler.apply(response); + } catch (JsonSyntaxException e) { + throw new IllegalArgumentException(UNABLE_TO_CONTACT_BITBUCKET_SERVER + ", got an unexpected response", e); + } catch (IOException e) { + throw new IllegalArgumentException(UNABLE_TO_CONTACT_BITBUCKET_SERVER, e); + } + } + + protected static void handleError(Response response) throws IOException { + if (!response.isSuccessful()) { + String errorMessage = getErrorMessage(response.body()); + LOG.debug(UNABLE_TO_CONTACT_BITBUCKET_SERVER + ": {} {}", response.code(), errorMessage); + if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { + throw new IllegalArgumentException("Invalid personal access token"); + } + throw new IllegalArgumentException(UNABLE_TO_CONTACT_BITBUCKET_SERVER); + } + } + + protected static boolean equals(@Nullable MediaType first, @Nullable MediaType second) { + String s1 = first == null ? null : first.toString().toLowerCase(ENGLISH).replace(" ", ""); + String s2 = second == null ? null : second.toString().toLowerCase(ENGLISH).replace(" ", ""); + return s1 != null && s2 != null && s1.equals(s2); + } + + protected static String getErrorMessage(ResponseBody body) throws IOException { + if (equals(MediaType.parse("application/json;charset=utf-8"), body.contentType())) { + try { + return Stream.of(buildGson().fromJson(body.charStream(), Errors.class).errorData) + .map(e -> e.exceptionName + " " + e.message) + .collect(Collectors.joining("\n")); + } catch (JsonParseException e) { + return body.string(); + } + } + return body.string(); + } + + protected static Gson buildGson() { + return new GsonBuilder() + .create(); + } + + protected static class Errors { + + @SerializedName("errors") + public Error[] errorData; + + public Errors() { + // http://stackoverflow.com/a/18645370/229031 + } + + public static class Error { + @SerializedName("message") + public String message; + + @SerializedName("exceptionName") + public String exceptionName; + + public Error() { + // http://stackoverflow.com/a/18645370/229031 + } + } + + } + +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Project.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Project.java new file mode 100644 index 00000000000..07643939358 --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Project.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.alm.client.bitbucketserver; + +import com.google.gson.annotations.SerializedName; + +public class Project { + + @SerializedName("key") + private String key; + + @SerializedName("name") + private String name; + + @SerializedName("id") + private long id; + + public Project() { + // http://stackoverflow.com/a/18645370/229031 + } + + public Project(String key, String name, long id) { + this.key = key; + this.name = name; + this.id = id; + } + + public String getKey() { + return key; + } + + public Project setKey(String key) { + this.key = key; + return this; + } + + public String getName() { + return name; + } + + public Project setName(String name) { + this.name = name; + return this; + } + + public long getId() { + return id; + } + + public Project setId(long id) { + this.id = id; + return this; + } + + @Override + public String toString() { + return "{" + + "key='" + key + '\'' + + ", name='" + name + '\'' + + ", id=" + id + + '}'; + } +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/ProjectList.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/ProjectList.java new file mode 100644 index 00000000000..5cd7e445032 --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/ProjectList.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.alm.client.bitbucketserver; + +import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; +import java.util.List; + +public class ProjectList { + + @SerializedName("values") + private List<Project> values; + + public ProjectList() { + // http://stackoverflow.com/a/18645370/229031 + this(new ArrayList<>()); + } + + public ProjectList(List<Project> values) { + this.values = values; + } + + public List<Project> getValues() { + return values; + } + + public ProjectList setValues(List<Project> values) { + this.values = values; + return this; + } + + @Override + public String toString() { + return "{" + + "values=" + values + + '}'; + } +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Repository.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Repository.java new file mode 100644 index 00000000000..5b13c4299fc --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Repository.java @@ -0,0 +1,94 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.alm.client.bitbucketserver; + +import com.google.gson.annotations.SerializedName; + +public class Repository { + + @SerializedName("slug") + private String slug; + + @SerializedName("name") + private String name; + + @SerializedName("id") + private long id; + + @SerializedName("project") + private Project project; + + public Repository() { + // http://stackoverflow.com/a/18645370/229031 + } + + public Repository(String slug, String name, long id, Project project) { + this.slug = slug; + this.name = name; + this.id = id; + this.project = project; + } + + public String getSlug() { + return slug; + } + + public Repository setSlug(String slug) { + this.slug = slug; + return this; + } + + public String getName() { + return name; + } + + public Repository setName(String name) { + this.name = name; + return this; + } + + public long getId() { + return id; + } + + public Repository setId(long id) { + this.id = id; + return this; + } + + public Project getProject() { + return project; + } + + public Repository setProject(Project project) { + this.project = project; + return this; + } + + @Override + public String toString() { + return "{" + + "slug='" + slug + '\'' + + ", name='" + name + '\'' + + ", id=" + id + + ", project=" + project + + '}'; + } +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/RepositoryList.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/RepositoryList.java new file mode 100644 index 00000000000..e7125f0f8ed --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/RepositoryList.java @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.alm.client.bitbucketserver; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; +import java.util.List; + +public class RepositoryList { + + @SerializedName("isLastPage") + private boolean isLastPage; + + @SerializedName("values") + private List<Repository> values; + + public RepositoryList() { + // http://stackoverflow.com/a/18645370/229031 + this(false, new ArrayList<>()); + } + + public RepositoryList(boolean isLastPage, List<Repository> values) { + this.isLastPage = isLastPage; + this.values = values; + } + + static RepositoryList parse(String json) { + return new Gson().fromJson(json, RepositoryList.class); + } + + public boolean isLastPage() { + return isLastPage; + } + + public RepositoryList setLastPage(boolean lastPage) { + isLastPage = lastPage; + return this; + } + + public List<Repository> getValues() { + return values; + } + + public RepositoryList setValues(List<Repository> values) { + this.values = values; + return this; + } + + @Override + public String toString() { + return "{" + + "isLastPage=" + isLastPage + + ", values=" + values + + '}'; + } +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/package-info.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/package-info.java new file mode 100644 index 00000000000..a265ee7293d --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.alm.client.bitbucketserver; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClientTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClientTest.java new file mode 100644 index 00000000000..aac841beb37 --- /dev/null +++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClientTest.java @@ -0,0 +1,205 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.alm.client.bitbucketserver; + +import java.io.IOException; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.sonar.alm.client.ConstantTimeoutConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.tuple; + +public class BitbucketServerRestClientTest { + private final MockWebServer server = new MockWebServer(); + private BitbucketServerRestClient underTest; + + @Before + public void prepare() throws IOException { + server.start(); + + underTest = new BitbucketServerRestClient(new ConstantTimeoutConfiguration(500)); + } + + @After + public void stopServer() throws IOException { + server.shutdown(); + } + + @Test + public void get_repos() { + server.enqueue(new MockResponse() + .setHeader("Content-Type", "application/json;charset=UTF-8") + .setBody("{\n" + + " \"isLastPage\": true,\n" + + " \"values\": [\n" + + " {\n" + + " \"slug\": \"banana\",\n" + + " \"id\": 2,\n" + + " \"name\": \"banana\",\n" + + " \"project\": {\n" + + " \"key\": \"HOY\",\n" + + " \"id\": 2,\n" + + " \"name\": \"hoy\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"potato\",\n" + + " \"id\": 1,\n" + + " \"name\": \"potato\",\n" + + " \"project\": {\n" + + " \"key\": \"HEY\",\n" + + " \"id\": 1,\n" + + " \"name\": \"hey\"\n" + + " }\n" + + " }\n" + + " ]\n" + + "}")); + + RepositoryList gsonBBSRepoList = underTest.getRepos(server.url("/").toString(), "token", "", ""); + assertThat(gsonBBSRepoList.isLastPage()).isTrue(); + assertThat(gsonBBSRepoList.getValues()).hasSize(2); + assertThat(gsonBBSRepoList.getValues()).extracting(Repository::getId, Repository::getName, Repository::getSlug, + g -> g.getProject().getId(), g -> g.getProject().getKey(), g -> g.getProject().getName()) + .containsExactlyInAnyOrder( + tuple(2L, "banana", "banana", 2L, "HOY", "hoy"), + tuple(1L, "potato", "potato", 1L, "HEY", "hey")); + } + + @Test + public void get_repo() { + server.enqueue(new MockResponse() + .setHeader("Content-Type", "application/json;charset=UTF-8") + .setBody( + " {" + + " \"slug\": \"banana-slug\"," + + " \"id\": 2,\n" + + " \"name\": \"banana\"," + + " \"project\": {\n" + + " \"key\": \"HOY\"," + + " \"id\": 3,\n" + + " \"name\": \"hoy\"" + + " }" + + " }")); + + Repository repository = underTest.getRepo(server.url("/").toString(), "token", "", ""); + assertThat(repository.getId()).isEqualTo(2L); + assertThat(repository.getName()).isEqualTo("banana"); + assertThat(repository.getSlug()).isEqualTo("banana-slug"); + assertThat(repository.getProject()) + .extracting(Project::getId, Project::getKey, Project::getName) + .contains(3L, "HOY", "hoy"); + } + + @Test + public void get_projects() { + server.enqueue(new MockResponse() + .setHeader("Content-Type", "application/json;charset=UTF-8") + .setBody("{\n" + + " \"isLastPage\": true,\n" + + " \"values\": [\n" + + " {\n" + + " \"key\": \"HEY\",\n" + + " \"id\": 1,\n" + + " \"name\": \"hey\"\n" + + " },\n" + + " {\n" + + " \"key\": \"HOY\",\n" + + " \"id\": 2,\n" + + " \"name\": \"hoy\"\n" + + " }\n" + + " ]\n" + + "}")); + + final ProjectList gsonBBSProjectList = underTest.getProjects(server.url("/").toString(), "token"); + assertThat(gsonBBSProjectList.getValues()).hasSize(2); + assertThat(gsonBBSProjectList.getValues()).extracting(Project::getId, Project::getKey, Project::getName) + .containsExactlyInAnyOrder( + tuple(1L, "HEY", "hey"), + tuple(2L, "HOY", "hoy")); + } + + @Test + public void invalid_url() { + assertThatThrownBy(() -> BitbucketServerRestClient.buildUrl("file://wrong-url", "")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("url must start with http:// or https://"); + } + + @Test + public void malformed_json() { + server.enqueue(new MockResponse() + .setHeader("Content-Type", "application/json;charset=UTF-8") + .setBody( + "I'm malformed JSON")); + + String serverUrl = server.url("/").toString(); + assertThatThrownBy(() -> underTest.getRepo(serverUrl, "token", "", "")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unable to contact Bitbucket server, got an unexpected response"); + } + + @Test + public void error_handling() { + server.enqueue(new MockResponse() + .setHeader("Content-Type", "application/json;charset=UTF-8") + .setResponseCode(400) + .setBody("{\n" + + " \"errors\": [\n" + + " {\n" + + " \"context\": null,\n" + + " \"message\": \"Bad message\",\n" + + " \"exceptionName\": \"com.atlassian.bitbucket.auth.BadException\"\n" + + " }\n" + + " ]\n" + + "}")); + + String serverUrl = server.url("/").toString(); + assertThatThrownBy(() -> underTest.getRepo(serverUrl, "token", "", "")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unable to contact Bitbucket server"); + } + + @Test + public void unauthorized_error() { + server.enqueue(new MockResponse() + .setHeader("Content-Type", "application/json;charset=UTF-8") + .setResponseCode(401) + .setBody("{\n" + + " \"errors\": [\n" + + " {\n" + + " \"context\": null,\n" + + " \"message\": \"Bad message\",\n" + + " \"exceptionName\": \"com.atlassian.bitbucket.auth.BadException\"\n" + + " }\n" + + " ]\n" + + "}")); + + String serverUrl = server.url("/").toString(); + assertThatThrownBy(() -> underTest.getRepo(serverUrl, "token", "", "")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid personal access token"); + } + +} |