]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14804 Identify already imported bitbucket cloud repository when onboarding
authorZipeng WU <zipeng.wu@sonarsource.com>
Fri, 14 May 2021 13:53:50 +0000 (15:53 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 21 May 2021 20:03:37 +0000 (20:03 +0000)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/SearchBitbucketCloudReposAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/ws/bitbucketcloud/SearchBitbucketCloudReposActionTest.java

index 15267d12951abf41ade4f95b47099070103e98b6..5675392144ebcf4331a6f95daa0de657d14e8d31 100644 (file)
 package org.sonar.server.almintegration.ws.bitbucketcloud;
 
 import java.util.List;
-import java.util.Optional;
+import java.util.Map;
 import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BinaryOperator;
 import org.sonar.alm.client.bitbucket.bitbucketcloud.Repository;
 import org.sonar.alm.client.bitbucket.bitbucketcloud.RepositoryList;
 import org.sonar.api.server.ws.Request;
@@ -31,16 +34,20 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.alm.pat.AlmPatDto;
 import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.db.alm.setting.ProjectAlmSettingDto;
+import org.sonar.db.project.ProjectDto;
 import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.AlmIntegrations.BBCRepo;
 import org.sonarqube.ws.AlmIntegrations.SearchBitbucketcloudReposWsResponse;
-import org.sonarqube.ws.Common;
+import org.sonarqube.ws.Common.Paging;
 
 import static java.util.Objects.requireNonNull;
 import static java.util.Optional.ofNullable;
 import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toSet;
 import static org.sonar.api.server.ws.WebService.Param.PAGE;
 import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
@@ -48,6 +55,7 @@ import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class SearchBitbucketCloudReposAction implements AlmIntegrationsWsAction {
 
+  private static final BinaryOperator<String> resolveCollisionByNaturalOrder = (a, b) -> a.compareTo(b) < 0 ? a : b;
   private static final String PARAM_ALM_SETTING = "almSetting";
   private static final String PARAM_REPO_NAME = "repositoryName";
   private static final int DEFAULT_PAGE_SIZE = 20;
@@ -112,29 +120,45 @@ public class SearchBitbucketCloudReposAction implements AlmIntegrationsWsAction
 
       RepositoryList repositoryList = bitbucketCloudRestClient.searchRepos(pat, workspace, repoName, page, pageSize);
 
+      Map<String, String> sqProjectKeyByRepoSlug = getSqProjectKeyByRepoSlug(dbSession, almSettingDto, repositoryList.getValues());
+
       List<BBCRepo> bbcRepos = repositoryList.getValues().stream()
-        .map(repository -> toBBCRepo(repository, workspace))
+        .map(repository -> toBBCRepo(repository, workspace, sqProjectKeyByRepoSlug))
         .collect(toList());
 
       SearchBitbucketcloudReposWsResponse.Builder builder = SearchBitbucketcloudReposWsResponse.newBuilder()
         .setIsLastPage(repositoryList.getNext() == null)
-        .setPaging(Common.Paging.newBuilder()
-          .setPageIndex(page)
-          .setPageSize(pageSize)
-          .build())
+        .setPaging(Paging.newBuilder().setPageIndex(page).setPageSize(pageSize).build())
         .addAllRepositories(bbcRepos);
       return builder.build();
     }
   }
 
-  private static BBCRepo toBBCRepo(Repository gsonBBCRepo, String workspace) {
-    return BBCRepo.newBuilder()
+  private Map<String, String> getSqProjectKeyByRepoSlug(DbSession dbSession, AlmSettingDto almSettingDto, List<Repository> repositories) {
+    Set<String> slugs = repositories.stream().map(Repository::getSlug).collect(toSet());
+
+    List<ProjectAlmSettingDto> projectAlmSettingDtos = dbClient.projectAlmSettingDao().selectByAlmSettingAndSlugs(dbSession, almSettingDto, slugs);
+
+    Map<String, String> repoSlugByProjectUuid = projectAlmSettingDtos.stream()
+      .collect(toMap(ProjectAlmSettingDto::getProjectUuid, ProjectAlmSettingDto::getAlmSlug));
+
+    return dbClient.projectDao().selectByUuids(dbSession, repoSlugByProjectUuid.keySet())
+      .stream()
+      .collect(toMap(p -> repoSlugByProjectUuid.get(p.getUuid()), ProjectDto::getKey, resolveCollisionByNaturalOrder));
+  }
+
+  private static BBCRepo toBBCRepo(Repository gsonBBCRepo, String workspace, Map<String, String> sqProjectKeyByRepoSlug) {
+    BBCRepo.Builder builder = BBCRepo.newBuilder()
       .setSlug(gsonBBCRepo.getSlug())
       .setUuid(gsonBBCRepo.getUuid())
       .setName(gsonBBCRepo.getName())
       .setWorkspace(workspace)
-      .setProjectKey(gsonBBCRepo.getProject().getKey())
-      .build();
+      .setProjectKey(gsonBBCRepo.getProject().getKey());
+
+    String sqProjectKey = sqProjectKeyByRepoSlug.get(gsonBBCRepo.getSlug());
+    ofNullable(sqProjectKey).ifPresent(builder::setSqProjectKey);
+
+    return builder.build();
   }
 
 }
index 4f740e6c43aa5532552fe44be744d6b324c9e214..4d4ded4f9a17b82934a70f9b19cdcc7938336606 100644 (file)
@@ -76,6 +76,8 @@ public class SearchBitbucketCloudReposActionTest {
       dto.setPersonalAccessToken("abc:xyz");
       dto.setUserUuid(user.getUuid());
     });
+    ProjectDto projectDto = db.components().insertPrivateProjectDto();
+    db.almSettings().insertBitbucketCloudProjectAlmSetting(almSetting, projectDto, s -> s.setAlmSlug("repo-slug-2"));
 
     SearchBitbucketcloudReposWsResponse response = ws.newRequest()
       .setParam("almSetting", almSetting.getKey())
@@ -87,9 +89,36 @@ public class SearchBitbucketCloudReposActionTest {
     assertThat(response.getPaging().getPageIndex()).isEqualTo(1);
     assertThat(response.getPaging().getPageSize()).isEqualTo(100);
     assertThat(response.getRepositoriesList())
-      .extracting(BBCRepo::getUuid, BBCRepo::getName, BBCRepo::getSlug, BBCRepo::getProjectKey, BBCRepo::getWorkspace)
+      .extracting(BBCRepo::getUuid, BBCRepo::getName, BBCRepo::getSlug, BBCRepo::getProjectKey, BBCRepo::getSqProjectKey, BBCRepo::getWorkspace)
       .containsExactlyInAnyOrder(
-        tuple("REPO-UUID-ONE", "repoName1", "repo-slug-1", "projectKey1", almSetting.getAppId()));
+        tuple("REPO-UUID-ONE", "repoName1", "repo-slug-1", "projectKey1", "", almSetting.getAppId()),
+        tuple("REPO-UUID-TWO", "repoName2", "repo-slug-2", "projectKey2", projectDto.getKey(), almSetting.getAppId()));
+  }
+
+  @Test
+  public void use_projectKey_to_disambiguate_when_multiple_projects_are_binded_on_one_bitbucket_repo() {
+    when(bitbucketCloudRestClient.searchRepos(any(), any(), any(), any(), any())).thenReturn(getRepositoryList());
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user).addPermission(PROVISION_PROJECTS);
+    AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting();
+    db.almPats().insert(dto -> {
+      dto.setAlmSettingUuid(almSetting.getUuid());
+      dto.setUserUuid(user.getUuid());
+    });
+    ProjectDto project1 = db.components().insertPrivateProjectDto(p -> p.setDbKey("B"));
+    ProjectDto project2 = db.components().insertPrivateProjectDto(p -> p.setDbKey("A"));
+    db.almSettings().insertBitbucketProjectAlmSetting(almSetting, project1, s -> s.setAlmSlug("repo-slug-2"));
+    db.almSettings().insertBitbucketProjectAlmSetting(almSetting, project2, s -> s.setAlmSlug("repo-slug-2"));
+
+    SearchBitbucketcloudReposWsResponse response = ws.newRequest()
+      .setParam("almSetting", almSetting.getKey())
+      .executeProtobuf(SearchBitbucketcloudReposWsResponse.class);
+
+    assertThat(response.getRepositoriesList())
+      .extracting(BBCRepo::getUuid, BBCRepo::getName, BBCRepo::getSlug, BBCRepo::getProjectKey, BBCRepo::getSqProjectKey)
+      .containsExactlyInAnyOrder(
+        tuple("REPO-UUID-ONE", "repoName1", "repo-slug-1", "projectKey1", ""),
+        tuple("REPO-UUID-TWO", "repoName2", "repo-slug-2", "projectKey2", "A"));
   }
 
   @Test
@@ -168,7 +197,7 @@ public class SearchBitbucketCloudReposActionTest {
   private RepositoryList getRepositoryList() {
     return new RepositoryList(
       "http://next.url",
-      asList(getBBCRepo1()),
+      asList(getBBCRepo1(), getBBCRepo2()),
       1,
       100);
   }
@@ -178,4 +207,10 @@ public class SearchBitbucketCloudReposActionTest {
     return new Repository("REPO-UUID-ONE", "repo-slug-1", "repoName1", project1, null);
   }
 
+  private Repository getBBCRepo2() {
+    Project project2 = new Project("PROJECT-UUID-TWO", "projectKey2", "projectName2");
+    Repository repo2 = new Repository("REPO-UUID-TWO", "repo-slug-2", "repoName2", project2, null);
+    return repo2;
+  }
+
 }