--- /dev/null
+/*
+ * 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.gitlab;
+
+import com.google.gson.annotations.SerializedName;
+import javax.annotation.Nullable;
+
+public class GitLabBranch {
+
+ @SerializedName("name")
+ private final String name;
+
+ @SerializedName("default")
+ private final boolean isDefault;
+
+ public GitLabBranch() {
+ // http://stackoverflow.com/a/18645370/229031
+ this(null, false);
+ }
+
+ public GitLabBranch(@Nullable String name, boolean isDefault) {
+ this.name = name;
+ this.isDefault = isDefault;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isDefault() {
+ return isDefault;
+ }
+}
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
.build();
}
-
public void checkReadPermission(@Nullable String gitlabUrl, @Nullable String personalAccessToken) {
checkProjectAccess(gitlabUrl, personalAccessToken, "Could not validate GitLab read permission. Got an unexpected answer.");
}
}
}
+ public List<GitLabBranch> getBranches(String gitlabUrl, String pat, Long gitlabProjectId) {
+ String url = String.format("%s/projects/%s/repository/branches", gitlabUrl, gitlabProjectId);
+ LOG.debug(String.format("get branches : [%s]", url));
+ Request request = new Request.Builder()
+ .addHeader(PRIVATE_TOKEN, pat)
+ .get()
+ .url(url)
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ checkResponseIsSuccessful(response);
+ String body = response.body().string();
+ LOG.trace(String.format("loading branches payload result : [%s]", body));
+ return Arrays.asList(new GsonBuilder().create().fromJson(body, GitLabBranch[].class));
+ } catch (JsonSyntaxException e) {
+ throw new IllegalArgumentException("Could not parse GitLab answer to retrieve project branches. Got a non-json payload as result.");
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
public ProjectList searchProjects(String gitlabUrl, String personalAccessToken, @Nullable String projectName,
int pageNumber, int pageSize) {
String url = String.format("%s/projects?archived=false&simple=true&membership=true&order_by=name&sort=asc&search=%s&page=%d&per_page=%d",
.setBody("{\"error\":\"invalid_token\",\"error_description\":\"Token was revoked. You have to re-authorize from the user.\"}");
server.enqueue(response);
- String gitlabUrl = this.gitlabUrl;
assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "pat", "example", 1, 2))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Your GitLab token was revoked");
"\"scope\":\"api read_api\"}");
server.enqueue(response);
- String gitlabUrl = this.gitlabUrl;
assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "pat", "example", 1, 2))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Your GitLab token has insufficient scope");
.setBody("error in pat");
server.enqueue(response);
- String gitlabUrl = this.gitlabUrl;
assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "pat", "example", 1, 2))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid personal access token");
.setBody("non json payload");
server.enqueue(response);
- String instanceUrl = gitlabUrl;
- assertThatThrownBy(() -> underTest.getProject(instanceUrl, "pat", 12345L))
+ assertThatThrownBy(() -> underTest.getProject(gitlabUrl, "pat", 12345L))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Could not parse GitLab answer to retrieve a project. Got a non-json payload as result.");
}
+ @Test
+ public void get_branches(){
+ MockResponse response = new MockResponse()
+ .setResponseCode(200)
+ .setBody("[{\n"
+ + " \"name\": \"main\",\n"
+ + " \"default\": true\n"
+ + "},{\n"
+ + " \"name\": \"other\",\n"
+ + " \"default\": false\n"
+ + "}]");
+ server.enqueue(response);
+
+ assertThat(underTest.getBranches(gitlabUrl, "pat", 12345L))
+ .extracting(GitLabBranch::getName, GitLabBranch::isDefault)
+ .containsExactly(
+ tuple("main", true),
+ tuple("other", false)
+ );
+ }
+
+ @Test
+ public void get_branches_fail_if_non_json_payload() {
+ MockResponse response = new MockResponse()
+ .setResponseCode(200)
+ .setBody("non json payload");
+ server.enqueue(response);
+
+ String instanceUrl = gitlabUrl;
+ assertThatThrownBy(() -> underTest.getBranches(instanceUrl, "pat", 12345L))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Could not parse GitLab answer to retrieve project branches. Got a non-json payload as result.");
+ }
+
+ @Test
+ public void get_branches_fail_if_exception() throws IOException {
+ server.shutdown();
+
+ String instanceUrl = gitlabUrl;
+ assertThatThrownBy(() -> underTest.getBranches(instanceUrl, "pat", 12345L))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Failed to connect to");
+ }
+
@Test
public void search_projects() throws InterruptedException {
MockResponse projects = new MockResponse()
projects.addHeader("X-Total", "bad-total-number");
server.enqueue(projects);
- String gitlabInstanceUrl = gitlabUrl;
- assertThatThrownBy(() -> underTest.searchProjects(gitlabInstanceUrl, "pat", "example", 1, 10))
+ assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "pat", "example", 1, 10))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Could not parse pagination number");
}
.setBody("[ ]");
server.enqueue(projects);
- String gitlabInstanceUrl = gitlabUrl;
- assertThatThrownBy(() -> underTest.searchProjects(gitlabInstanceUrl, "pat", "example", 1, 10))
+ assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "pat", "example", 1, 10))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Pagination data from GitLab response is missing");
}
.setBody("test");
server.enqueue(projects);
- String gitlabUrl = this.gitlabUrl;
assertThatThrownBy(() -> underTest.searchProjects(gitlabUrl, "pat", "example", 1, 2))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Could not get projects from GitLab instance");
import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.alm.client.gitlab.GitLabBranch;
import org.sonar.alm.client.gitlab.GitlabHttpClient;
import org.sonar.alm.client.gitlab.Project;
import org.sonar.api.server.ws.Request;
long gitlabProjectId = request.mandatoryParamAsLong(PARAM_GITLAB_PROJECT_ID);
- String url = requireNonNull(almSettingDto.getUrl(), "ALM url cannot be null");
- Project gitlabProject = gitlabHttpClient.getProject(url, pat, gitlabProjectId);
+ String gitlabUrl = requireNonNull(almSettingDto.getUrl(), "ALM gitlabUrl cannot be null");
+ Project gitlabProject = gitlabHttpClient.getProject(gitlabUrl, pat, gitlabProjectId);
- ComponentDto componentDto = createProject(dbSession, gitlabProject);
+ Optional<String> almMainBranchName = getAlmDefaultBranch(pat, gitlabProjectId, gitlabUrl);
+ ComponentDto componentDto = createProject(dbSession, gitlabProject, almMainBranchName.orElse(null));
populateMRSetting(dbSession, gitlabProjectId, componentDto, almSettingDto);
+ componentUpdater.commitAndIndex(dbSession, componentDto);
return ImportHelper.toCreateResponse(componentDto);
}
}
+ private Optional<String> getAlmDefaultBranch(String pat, long gitlabProjectId, String gitlabUrl) {
+ Optional<GitLabBranch> almMainBranch = gitlabHttpClient.getBranches(gitlabUrl, pat, gitlabProjectId).stream().filter(GitLabBranch::isDefault).findFirst();
+ return almMainBranch.map(GitLabBranch::getName);
+ }
+
private void populateMRSetting(DbSession dbSession, Long gitlabProjectId, ComponentDto componentDto, AlmSettingDto almSettingDto) {
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, new ProjectAlmSettingDto()
.setProjectUuid(componentDto.projectUuid())
.setAlmRepo(gitlabProjectId.toString())
.setAlmSlug(null)
.setMonorepo(false));
- dbSession.commit();
}
- private ComponentDto createProject(DbSession dbSession, Project gitlabProject) {
+ private ComponentDto createProject(DbSession dbSession, Project gitlabProject, @Nullable String mainBranchName) {
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
String sqProjectKey = generateProjectKey(gitlabProject.getPathWithNamespace(), uuidFactory.create());
- return componentUpdater.create(dbSession, newComponentBuilder()
+ return componentUpdater.createWithoutCommit(dbSession, newComponentBuilder()
.setKey(sqProjectKey)
.setName(gitlabProject.getName())
.setPrivate(visibility)
.setQualifier(PROJECT)
.build(),
- userSession.getUuid());
+ userSession.getUuid(), mainBranchName, s -> {
+ });
}
@VisibleForTesting
import java.util.Optional;
import java.util.stream.IntStream;
+import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.alm.client.gitlab.GitLabBranch;
import org.sonar.alm.client.gitlab.GitlabHttpClient;
import org.sonar.alm.client.gitlab.Project;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbTester;
import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.db.component.BranchDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.almintegration.ws.ImportHelper;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Projects;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
});
Project project = getGitlabProject();
when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project);
+ when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(singletonList(new GitLabBranch("master", true)));
when(uuidFactory.create()).thenReturn("uuid");
Projects.CreateWsResponse response = ws.newRequest()
assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
}
+ @Test
+ public void import_project_with_specific_different_default_branch() {
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addPermission(PROVISION_PROJECTS);
+ AlmSettingDto almSetting = db.almSettings().insertGitlabAlmSetting();
+ db.almPats().insert(dto -> {
+ dto.setAlmSettingUuid(almSetting.getUuid());
+ dto.setUserUuid(user.getUuid());
+ dto.setPersonalAccessToken("PAT");
+ });
+ Project project = getGitlabProject();
+ when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project);
+ when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(singletonList(new GitLabBranch("main", true)));
+ when(uuidFactory.create()).thenReturn("uuid");
+
+ Projects.CreateWsResponse response = ws.newRequest()
+ .setParam("almSetting", almSetting.getKey())
+ .setParam("gitlabProjectId", "12345")
+ .executeProtobuf(Projects.CreateWsResponse.class);
+
+ verify(gitlabHttpClient).getProject(almSetting.getUrl(), "PAT", 12345L);
+ verify(gitlabHttpClient).getBranches(almSetting.getUrl(), "PAT", 12345L);
+
+ Projects.CreateWsResponse.Project result = response.getProject();
+ assertThat(result.getKey()).isEqualTo(project.getPathWithNamespace() + "_uuid");
+ assertThat(result.getName()).isEqualTo(project.getName());
+
+ Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
+ assertThat(projectDto).isPresent();
+ assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
+
+ Assertions.assertThat(db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get()))
+ .extracting(BranchDto::getKey, BranchDto::isMain)
+ .containsExactlyInAnyOrder(tuple("main", true));
+ }
+
+ @Test
+ public void import_project_no_gitlab_default_branch() {
+ UserDto user = db.users().insertUser();
+ userSession.logIn(user).addPermission(PROVISION_PROJECTS);
+ AlmSettingDto almSetting = db.almSettings().insertGitlabAlmSetting();
+ db.almPats().insert(dto -> {
+ dto.setAlmSettingUuid(almSetting.getUuid());
+ dto.setUserUuid(user.getUuid());
+ dto.setPersonalAccessToken("PAT");
+ });
+ Project project = getGitlabProject();
+ when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project);
+ when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(emptyList());
+ when(uuidFactory.create()).thenReturn("uuid");
+
+ Projects.CreateWsResponse response = ws.newRequest()
+ .setParam("almSetting", almSetting.getKey())
+ .setParam("gitlabProjectId", "12345")
+ .executeProtobuf(Projects.CreateWsResponse.class);
+
+ verify(gitlabHttpClient).getProject(almSetting.getUrl(), "PAT", 12345L);
+ verify(gitlabHttpClient).getBranches(almSetting.getUrl(), "PAT", 12345L);
+
+ Projects.CreateWsResponse.Project result = response.getProject();
+ assertThat(result.getKey()).isEqualTo(project.getPathWithNamespace() + "_uuid");
+ assertThat(result.getName()).isEqualTo(project.getName());
+
+ Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
+ assertThat(projectDto).isPresent();
+ assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
+
+ Assertions.assertThat(db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get()))
+ .extracting(BranchDto::getKey, BranchDto::isMain)
+ .containsExactlyInAnyOrder(tuple("master", true));
+ }
+
@Test
public void generate_project_key_less_than_250() {
String name = "abcdeert";