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;
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;
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;
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();
}
}
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())
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
private RepositoryList getRepositoryList() {
return new RepositoryList(
"http://next.url",
- asList(getBBCRepo1()),
+ asList(getBBCRepo1(), getBBCRepo2()),
1,
100);
}
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;
+ }
+
}