import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
+import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
+import org.apache.logging.log4j.util.Strings;
import org.sonar.alm.client.TimeoutConfiguration;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.log.Logger;
.build();
try (Response response = client.newCall(request).execute()) {
+ Headers headers = response.headers();
checkResponseIsSuccessful(response, "Could not get projects from GitLab instance");
List<Project> projectList = Project.parseJsonArray(response.body().string());
- int returnedPageNumber = parseAndGetIntegerHeader(response.header("X-Page"));
- int returnedPageSize = parseAndGetIntegerHeader(response.header("X-Per-Page"));
- int totalProjects = parseAndGetIntegerHeader(response.header("X-Total"));
+ int returnedPageNumber = parseAndGetIntegerHeader(headers.get("X-Page"));
+ int returnedPageSize = parseAndGetIntegerHeader(headers.get("X-Per-Page"));
+ String xtotal = headers.get("X-Total");
+ Integer totalProjects = Strings.isEmpty(xtotal) ? null : parseAndGetIntegerHeader(xtotal);
return new ProjectList(projectList, returnedPageNumber, returnedPageSize, totalProjects);
} catch (JsonSyntaxException e) {
throw new IllegalArgumentException("Could not parse GitLab answer to search projects. Got a non-json payload as result.");
package org.sonar.alm.client.gitlab;
import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
public class ProjectList {
private final List<Project> projects;
private final int pageNumber;
private final int pageSize;
- private final int total;
+ private final Integer total;
- public ProjectList(List<Project> projects, int pageNumber, int pageSize, int total) {
+ public ProjectList(List<Project> projects, int pageNumber, int pageSize, @Nullable Integer total) {
this.projects = projects;
this.pageNumber = pageNumber;
this.pageSize = pageSize;
return pageSize;
}
- public int getTotal() {
+ @CheckForNull
+ public Integer getTotal() {
return total;
}
}
assertThat(projectGitlabRequest.getMethod()).isEqualTo("GET");
}
+ @Test
+ public void search_projects_dont_fail_if_no_x_total() throws InterruptedException {
+ MockResponse projects = new MockResponse()
+ .setResponseCode(200)
+ .setBody("[\n"
+ + " {\n"
+ + " \"id\": 1,\n"
+ + " \"name\": \"SonarQube example 1\",\n"
+ + " \"name_with_namespace\": \"SonarSource / SonarQube / SonarQube example 1\",\n"
+ + " \"path\": \"sonarqube-example-1\",\n"
+ + " \"path_with_namespace\": \"sonarsource/sonarqube/sonarqube-example-1\",\n"
+ + " \"web_url\": \"https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-1\"\n"
+ + " }"
+ + "]");
+ projects.addHeader("X-Page", 1);
+ projects.addHeader("X-Per-Page", 10);
+ server.enqueue(projects);
+
+ ProjectList projectList = underTest.searchProjects(gitlabUrl, "pat", "example", 1, 10);
+
+ assertThat(projectList.getPageNumber()).isEqualTo(1);
+ assertThat(projectList.getPageSize()).isEqualTo(10);
+ assertThat(projectList.getTotal()).isNull();
+
+ assertThat(projectList.getProjects()).hasSize(1);
+ assertThat(projectList.getProjects()).extracting(
+ Project::getId, Project::getName, Project::getNameWithNamespace, Project::getPath, Project::getPathWithNamespace, Project::getWebUrl).containsExactly(
+ tuple(1L, "SonarQube example 1", "SonarSource / SonarQube / SonarQube example 1", "sonarqube-example-1", "sonarsource/sonarqube/sonarqube-example-1",
+ "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-1"));
+
+ RecordedRequest projectGitlabRequest = server.takeRequest(10, TimeUnit.SECONDS);
+ String gitlabUrlCall = projectGitlabRequest.getRequestUrl().toString();
+ assertThat(gitlabUrlCall).isEqualTo(server.url("") + "projects?archived=false&simple=true&membership=true&order_by=name&sort=asc&search=example&page=1&per_page=10");
+ assertThat(projectGitlabRequest.getMethod()).isEqualTo("GET");
+ }
+
+ @Test
+ public void search_projects_with_case_insensitive_pagination_headers() throws InterruptedException {
+ MockResponse projects1 = new MockResponse()
+ .setResponseCode(200)
+ .setBody("[\n"
+ + " {\n"
+ + " \"id\": 1,\n"
+ + " \"name\": \"SonarQube example 1\",\n"
+ + " \"name_with_namespace\": \"SonarSource / SonarQube / SonarQube example 1\",\n"
+ + " \"path\": \"sonarqube-example-1\",\n"
+ + " \"path_with_namespace\": \"sonarsource/sonarqube/sonarqube-example-1\",\n"
+ + " \"web_url\": \"https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-1\"\n"
+ + " }"
+ + "]");
+ projects1.addHeader("x-page", 1);
+ projects1.addHeader("x-Per-page", 1);
+ projects1.addHeader("X-Total", 2);
+ server.enqueue(projects1);
+
+ ProjectList projectList = underTest.searchProjects(gitlabUrl, "pat", "example", 1, 10);
+
+ assertThat(projectList.getPageNumber()).isEqualTo(1);
+ assertThat(projectList.getPageSize()).isEqualTo(1);
+ assertThat(projectList.getTotal()).isEqualTo(2);
+
+ assertThat(projectList.getProjects()).hasSize(1);
+ assertThat(projectList.getProjects()).extracting(
+ Project::getId, Project::getName, Project::getNameWithNamespace, Project::getPath, Project::getPathWithNamespace, Project::getWebUrl).containsExactly(
+ tuple(1L, "SonarQube example 1", "SonarSource / SonarQube / SonarQube example 1", "sonarqube-example-1", "sonarsource/sonarqube/sonarqube-example-1",
+ "https://example.gitlab.com/sonarsource/sonarqube/sonarqube-example-1"));
+
+ RecordedRequest projectGitlabRequest = server.takeRequest(10, TimeUnit.SECONDS);
+ String gitlabUrlCall = projectGitlabRequest.getRequestUrl().toString();
+ assertThat(gitlabUrlCall).isEqualTo(server.url("") + "projects?archived=false&simple=true&membership=true&order_by=name&sort=asc&search=example&page=1&per_page=10");
+ assertThat(projectGitlabRequest.getMethod()).isEqualTo("GET");
+ }
+
@Test
public void search_projects_projectName_param_should_be_encoded() throws InterruptedException {
MockResponse projects = new MockResponse()