]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14565 Update SonarQube main branch name during Bitbucket Server project onboarding
authorLukasz Jarocki <lukasz.jarocki@sonarsource.com>
Wed, 10 Mar 2021 11:22:56 +0000 (12:22 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 16 Mar 2021 20:08:15 +0000 (20:08 +0000)
server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClient.java
server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Branch.java [new file with mode: 0644]
server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BranchesList.java [new file with mode: 0644]
server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClientTest.java
server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BranchesListTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionTest.java

index e410ac25f565ab43c463cd1e3150e3975cbb8695..344c1f1d917a9845f91704e6961dc610be087e02 100644 (file)
@@ -102,6 +102,11 @@ public class BitbucketServerRestClient {
     return doGet(token, url, r -> buildGson().fromJson(r.body().charStream(), ProjectList.class));
   }
 
+  public BranchesList getBranches(String serverUrl, String token, String projectSlug, String repositorySlug){
+    HttpUrl url = buildUrl(serverUrl, format("/rest/api/1.0/projects/%s/repos/%s/branches", projectSlug, repositorySlug));
+    return doGet(token, url, r -> buildGson().fromJson(r.body().charStream(), BranchesList.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://");
diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Branch.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/Branch.java
new file mode 100644 (file)
index 0000000..2d70395
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 Branch {
+
+  @SerializedName("displayId")
+  private String name;
+
+  @SerializedName("isDefault")
+  private boolean isDefault;
+
+  public Branch(){
+    // http://stackoverflow.com/a/18645370/229031
+  }
+
+  public Branch(String name, boolean isDefault) {
+    this.name = name;
+    this.isDefault = isDefault;
+  }
+
+  public boolean isDefault() {
+    return isDefault;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+}
diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BranchesList.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BranchesList.java
new file mode 100644 (file)
index 0000000..316c638
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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;
+import java.util.Optional;
+
+public class BranchesList {
+
+  @SerializedName("values")
+  private List<Branch> branches;
+
+  public BranchesList() {
+    // http://stackoverflow.com/a/18645370/229031
+    this(new ArrayList<>());
+  }
+
+  public BranchesList(List<Branch> values) {
+    this.branches = values;
+  }
+
+  public Optional<Branch> findDefaultBranch() {
+    return branches.stream().filter(Branch::isDefault).findFirst();
+  }
+
+  public void addBranch(Branch branch) {
+    this.branches.add(branch);
+  }
+
+  public List<Branch> getBranches() {
+    return branches;
+  }
+}
index a4cafcf1941a0b05992dc3c15bcff420d89dbe13..ccfddb717d524f44edcff31de2cb2c1c34ee339c 100644 (file)
@@ -180,6 +180,85 @@ public class BitbucketServerRestClientTest {
             tuple(2L, "HOY", "hoy"));
   }
 
+  @Test
+  public void getBranches_given0Branches_returnEmptyList(){
+    String bodyWith0Branches = "{\n" +
+      "  \"size\": 0,\n" +
+      "  \"limit\": 25,\n" +
+      "  \"isLastPage\": true,\n" +
+      "  \"values\": [],\n" +
+      "  \"start\": 0\n" +
+      "}";
+    server.enqueue(new MockResponse()
+      .setHeader("Content-Type", "application/json;charset=UTF-8")
+      .setBody(bodyWith0Branches));
+
+    BranchesList branches = underTest.getBranches(server.url("/").toString(), "token", "projectSlug", "repoSlug");
+
+    assertThat(branches.getBranches()).isEmpty();
+  }
+
+  @Test
+  public void getBranches_given1Branch_returnListWithOneBranch(){
+    String bodyWith1Branch = "{\n" +
+      "  \"size\": 1,\n" +
+      "  \"limit\": 25,\n" +
+      "  \"isLastPage\": true,\n" +
+      "  \"values\": [{\n" +
+      "    \"id\": \"refs/heads/demo\",\n" +
+      "    \"displayId\": \"demo\",\n" +
+      "    \"type\": \"BRANCH\",\n" +
+      "    \"latestCommit\": \"3e30a6701af6f29f976e9a6609a6076b32a69ac3\",\n" +
+      "    \"latestChangeset\": \"3e30a6701af6f29f976e9a6609a6076b32a69ac3\",\n" +
+      "    \"isDefault\": false\n" +
+      "  }],\n" +
+      "  \"start\": 0\n" +
+      "}";
+    server.enqueue(new MockResponse()
+      .setHeader("Content-Type", "application/json;charset=UTF-8")
+      .setBody(bodyWith1Branch));
+
+    BranchesList branches = underTest.getBranches(server.url("/").toString(), "token", "projectSlug", "repoSlug");
+    assertThat(branches.getBranches()).hasSize(1);
+
+    Branch branch = branches.getBranches().get(0);
+    assertThat(branch.getName()).isEqualTo("demo");
+    assertThat(branch.isDefault()).isFalse();
+
+  }
+
+  @Test
+  public void getBranches_given2Branches_returnListWithTwoBranches(){
+    String bodyWith2Branches = "{\n" +
+      "  \"size\": 2,\n" +
+      "  \"limit\": 25,\n" +
+      "  \"isLastPage\": true,\n" +
+      "  \"values\": [{\n" +
+      "    \"id\": \"refs/heads/demo\",\n" +
+      "    \"displayId\": \"demo\",\n" +
+      "    \"type\": \"BRANCH\",\n" +
+      "    \"latestCommit\": \"3e30a6701af6f29f976e9a6609a6076b32a69ac3\",\n" +
+      "    \"latestChangeset\": \"3e30a6701af6f29f976e9a6609a6076b32a69ac3\",\n" +
+      "    \"isDefault\": false\n" +
+      "  }, {\n" +
+      "    \"id\": \"refs/heads/master\",\n" +
+      "    \"displayId\": \"master\",\n" +
+      "    \"type\": \"BRANCH\",\n" +
+      "    \"latestCommit\": \"66633864d27c531ff43892f6dfea6d91632682fa\",\n" +
+      "    \"latestChangeset\": \"66633864d27c531ff43892f6dfea6d91632682fa\",\n" +
+      "    \"isDefault\": true\n" +
+      "  }],\n" +
+      "  \"start\": 0\n" +
+      "}";
+    server.enqueue(new MockResponse()
+      .setHeader("Content-Type", "application/json;charset=UTF-8")
+      .setBody(bodyWith2Branches));
+
+    BranchesList branches = underTest.getBranches(server.url("/").toString(), "token", "projectSlug", "repoSlug");
+
+    assertThat(branches.getBranches()).hasSize(2);
+  }
+
   @Test
   public void invalid_url() {
     assertThatThrownBy(() -> BitbucketServerRestClient.buildUrl("file://wrong-url", ""))
diff --git a/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BranchesListTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BranchesListTest.java
new file mode 100644 (file)
index 0000000..80fb549
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 org.junit.Test;
+
+import static org.assertj.core.api.Assertions.*;
+import java.util.Optional;
+
+public class BranchesListTest {
+
+  @Test
+  public void findDefaultBranch_givenNoBranches_returnEmptyOptional(){
+    BranchesList branchesList = new BranchesList();
+
+    Optional<Branch> defaultBranch = branchesList.findDefaultBranch();
+
+    assertThat(defaultBranch).isNotPresent();
+  }
+
+  @Test
+  public void findDefaultBranch_givenBranchesWithoutDefaultOne_returnEmptyOptional(){
+    BranchesList branchesList = new BranchesList();
+    branchesList.addBranch(new Branch("1", false));
+    branchesList.addBranch(new Branch("2", false));
+
+    Optional<Branch> defaultBranch = branchesList.findDefaultBranch();
+
+    assertThat(defaultBranch).isNotPresent();
+  }
+
+  @Test
+  public void findDefaultBranch_givenBranchesWithDefaultOne_returnOptionalWithThisBranch(){
+    BranchesList branchesList = new BranchesList();
+    branchesList.addBranch(new Branch("1", false));
+    branchesList.addBranch(new Branch("2", false));
+    branchesList.addBranch(new Branch("default", true));
+
+    Optional<Branch> defaultBranchOptional = branchesList.findDefaultBranch();
+
+    assertThat(defaultBranchOptional).isPresent();
+    assertThat(defaultBranchOptional.get().isDefault()).isTrue();
+    assertThat(defaultBranchOptional.get().getName()).isEqualTo("default");
+  }
+}
index 748230a781f37d19a9a7ffe9621593a47331d252..a1477a09e3dd8003bff7b8dcd965f7cadc6aca85 100644 (file)
@@ -21,6 +21,8 @@ package org.sonar.server.almintegration.ws.bitbucketserver;
 
 import java.util.Optional;
 import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
+import org.sonar.alm.client.bitbucketserver.Branch;
+import org.sonar.alm.client.bitbucketserver.BranchesList;
 import org.sonar.alm.client.bitbucketserver.Repository;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
@@ -34,6 +36,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
 import org.sonar.server.almintegration.ws.ImportHelper;
 import org.sonar.server.component.ComponentUpdater;
+import org.sonar.server.component.NewComponent;
 import org.sonar.server.project.ProjectDefaultVisibility;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.Projects;
@@ -44,6 +47,7 @@ import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING;
 import static org.sonar.server.almintegration.ws.ImportHelper.toCreateResponse;
 import static org.sonar.server.component.NewComponent.newComponentBuilder;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import javax.annotation.Nullable;
 
 public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsAction {
 
@@ -117,22 +121,35 @@ public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsActi
       String url = requireNonNull(almSettingDto.getUrl(), "ALM url cannot be null");
       Repository repo = bitbucketServerRestClient.getRepo(url, pat, projectKey, repoSlug);
 
-      ComponentDto componentDto = createProject(dbSession, repo);
+      String defaultBranchName = getDefaultBranchName(pat, projectKey, repoSlug, url);
+
+      ComponentDto componentDto = createProject(dbSession, repo, defaultBranchName);
+
       populatePRSetting(dbSession, repo, componentDto, almSettingDto);
 
+      componentUpdater.commitAndIndex(dbSession, componentDto);
+
       return toCreateResponse(componentDto);
     }
   }
+  
+  private String getDefaultBranchName(String pat, String projectKey, String repoSlug, String url) {
+    BranchesList branches = bitbucketServerRestClient.getBranches(url, pat, projectKey, repoSlug);
+    Optional<Branch> defaultBranch = branches.findDefaultBranch();
+    return defaultBranch.map(Branch::getName).orElse(null);
+  }
 
-  private ComponentDto createProject(DbSession dbSession, Repository repo) {
+  private ComponentDto createProject(DbSession dbSession, Repository repo, @Nullable String defaultBranchName) {
     boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
-    return componentUpdater.create(dbSession, newComponentBuilder()
+    NewComponent newProject = newComponentBuilder()
       .setKey(repo.getProject().getKey() + "_" + repo.getSlug())
       .setName(repo.getName())
       .setPrivate(visibility)
       .setQualifier(PROJECT)
-      .build(),
-      userSession.isLoggedIn() ? userSession.getUuid() : null);
+      .build();
+    String userUuid = userSession.isLoggedIn() ? userSession.getUuid() : null;
+
+    return componentUpdater.createWithoutCommit(dbSession, newProject, userUuid, defaultBranchName, p -> {});
   }
 
   private void populatePRSetting(DbSession dbSession, Repository repo, ComponentDto componentDto, AlmSettingDto almSettingDto) {
@@ -143,7 +160,6 @@ public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsActi
       .setProjectUuid(componentDto.uuid())
       .setMonorepo(false);
     dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto);
-    dbSession.commit();
   }
 
 }
index 811f79fad20d239075d2962d31c21acaf9ecf72f..62d0fca00f809abe32abc87936f701a6392015f1 100644 (file)
  */
 package org.sonar.server.almintegration.ws.bitbucketserver;
 
-import java.util.Optional;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
+import org.sonar.alm.client.bitbucketserver.Branch;
+import org.sonar.alm.client.bitbucketserver.BranchesList;
 import org.sonar.alm.client.bitbucketserver.Project;
 import org.sonar.alm.client.bitbucketserver.Repository;
 import org.sonar.api.server.ws.WebService;
@@ -34,6 +36,7 @@ import org.sonar.core.util.SequenceUuidFactory;
 import org.sonar.db.DbTester;
 import org.sonar.db.alm.pat.AlmPatDto;
 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;
@@ -61,6 +64,11 @@ import static org.mockito.Mockito.when;
 import static org.sonar.db.alm.integration.pat.AlmPatsTesting.newAlmPatDto;
 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
 import static org.sonar.db.permission.GlobalPermission.SCAN;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 public class ImportBitbucketServerProjectActionTest {
 
@@ -81,6 +89,14 @@ public class ImportBitbucketServerProjectActionTest {
   private final WsActionTester ws = new WsActionTester(new ImportBitbucketServerProjectAction(db.getDbClient(), userSession,
     bitbucketServerRestClient, projectDefaultVisibility, componentUpdater, importHelper));
 
+  private static BranchesList defaultBranchesList;
+
+  @BeforeClass
+  public static void beforeAll() {
+    Branch defaultBranch = new Branch("default", true);
+    defaultBranchesList = new BranchesList(Collections.singletonList(defaultBranch));
+  }
+
   @Before
   public void before() {
     when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
@@ -98,6 +114,7 @@ public class ImportBitbucketServerProjectActionTest {
     Project project = getGsonBBSProject();
     Repository repo = getGsonBBSRepo(project);
     when(bitbucketServerRestClient.getRepo(any(), any(), any(), any())).thenReturn(repo);
+    when(bitbucketServerRestClient.getBranches(any(), any(), any(), any())).thenReturn(defaultBranchesList);
 
     Projects.CreateWsResponse response = ws.newRequest()
       .setParam("almSetting", almSetting.getKey())
@@ -132,6 +149,8 @@ public class ImportBitbucketServerProjectActionTest {
     expectedException.expectMessage("Could not create null, key already exists: " + projectKey);
 
     when(bitbucketServerRestClient.getRepo(any(), any(), any(), any())).thenReturn(repo);
+    when(bitbucketServerRestClient.getBranches(any(), any(), any(), any())).thenReturn(defaultBranchesList);
+    
     ws.newRequest()
       .setParam("almSetting", almSetting.getKey())
       .setParam("projectKey", "projectKey")
@@ -207,6 +226,74 @@ public class ImportBitbucketServerProjectActionTest {
       .execute();
   }
 
+  @Test
+  public void handle_givenNoDefaultBranchFound_doNotUpdateDefaultBranchName() {
+    BranchesList branchesList = new BranchesList();
+    Branch branch = new Branch("not_a_master", false);
+    branchesList.addBranch(branch);
+
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user).addPermission(PROVISION_PROJECTS);
+    AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting();
+    db.almPats().insert(dto -> {
+      dto.setAlmSettingUuid(almSetting.getUuid());
+      dto.setUserUuid(user.getUuid());
+    });
+    Project project = getGsonBBSProject();
+    Repository repo = getGsonBBSRepo(project);
+    when(bitbucketServerRestClient.getRepo(any(), any(), any(), any())).thenReturn(repo);
+    when(bitbucketServerRestClient.getBranches(any(), any(), any(), any())).thenReturn(branchesList);
+
+    Projects.CreateWsResponse response = ws.newRequest()
+      .setParam("almSetting", almSetting.getKey())
+      .setParam("projectKey", "projectKey")
+      .setParam("repositorySlug", "repo-slug")
+      .executeProtobuf(Projects.CreateWsResponse.class);
+
+    Projects.CreateWsResponse.Project result = response.getProject();
+
+    Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
+
+    Collection<BranchDto> branchDtos = db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get());
+    List<BranchDto> collect = branchDtos.stream().filter(BranchDto::isMain).collect(Collectors.toList());
+    String mainBranchName = collect.iterator().next().getKey();
+    assertThat(mainBranchName).isEqualTo("master");
+  }
+
+  @Test
+  public void handle_givenDefaultBranchNamedDefault_updateDefaultBranchNameToDefault() {
+    BranchesList branchesList = new BranchesList();
+    Branch branch = new Branch("default", true);
+    branchesList.addBranch(branch);
+
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user).addPermission(PROVISION_PROJECTS);
+    AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting();
+    db.almPats().insert(dto -> {
+      dto.setAlmSettingUuid(almSetting.getUuid());
+      dto.setUserUuid(user.getUuid());
+    });
+    Project project = getGsonBBSProject();
+    Repository repo = getGsonBBSRepo(project);
+    when(bitbucketServerRestClient.getRepo(any(), any(), any(), any())).thenReturn(repo);
+    when(bitbucketServerRestClient.getBranches(any(), any(), any(), any())).thenReturn(branchesList);
+
+    Projects.CreateWsResponse response = ws.newRequest()
+      .setParam("almSetting", almSetting.getKey())
+      .setParam("projectKey", "projectKey")
+      .setParam("repositorySlug", "repo-slug")
+      .executeProtobuf(Projects.CreateWsResponse.class);
+
+    Projects.CreateWsResponse.Project result = response.getProject();
+
+    Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
+
+    Collection<BranchDto> branchDtos = db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get());
+    List<BranchDto> collect = branchDtos.stream().filter(BranchDto::isMain).collect(Collectors.toList());
+    String mainBranchName = collect.iterator().next().getKey();
+    assertThat(mainBranchName).isEqualTo("default");
+  }
+
   @Test
   public void definition() {
     WebService.Action def = ws.getDef();