*/ | */ | ||||
package org.sonar.server.common.almsettings; | package org.sonar.server.common.almsettings; | ||||
import javax.annotation.Nullable; | |||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
public record DevOpsProjectDescriptor(ALM alm, String url, String projectIdentifier) { | |||||
public record DevOpsProjectDescriptor(ALM alm, @Nullable String url, String projectIdentifier) { | |||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 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.server.common.almsettings.bitbucketcloud; | |||||
import java.util.Optional; | |||||
import org.jetbrains.annotations.Nullable; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.Repository; | |||||
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.CreationMethod; | |||||
import org.sonar.db.project.ProjectDto; | |||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectCreator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor; | |||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.component.ComponentCreationData; | |||||
import org.sonar.server.user.UserSession; | |||||
import static java.lang.String.format; | |||||
import static java.util.Objects.requireNonNull; | |||||
import static java.util.Optional.ofNullable; | |||||
public class BitbucketCloudProjectCreator implements DevOpsProjectCreator { | |||||
private final DbClient dbClient; | |||||
private final AlmSettingDto almSettingDto; | |||||
private final DevOpsProjectDescriptor devOpsProjectDescriptor; | |||||
private final UserSession userSession; | |||||
private final BitbucketCloudRestClient bitbucketCloudRestClient; | |||||
private final ProjectCreator projectCreator; | |||||
private final ProjectKeyGenerator projectKeyGenerator; | |||||
public BitbucketCloudProjectCreator(DbClient dbClient, AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor, UserSession userSession, | |||||
BitbucketCloudRestClient bitbucketCloudRestClient, ProjectCreator projectCreator, ProjectKeyGenerator projectKeyGenerator) { | |||||
this.dbClient = dbClient; | |||||
this.almSettingDto = almSettingDto; | |||||
this.devOpsProjectDescriptor = devOpsProjectDescriptor; | |||||
this.userSession = userSession; | |||||
this.bitbucketCloudRestClient = bitbucketCloudRestClient; | |||||
this.projectCreator = projectCreator; | |||||
this.projectKeyGenerator = projectKeyGenerator; | |||||
} | |||||
@Override | |||||
public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() { | |||||
throw new UnsupportedOperationException("Not Implemented"); | |||||
} | |||||
@Override | |||||
public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, Boolean monorepo, @Nullable String projectKey, | |||||
@Nullable String projectName) { | |||||
String pat = findPersonalAccessTokenOrThrow(dbSession, almSettingDto); | |||||
String workspace = ofNullable(almSettingDto.getAppId()) | |||||
.orElseThrow(() -> new IllegalArgumentException(String.format("workspace for alm setting %s is missing", almSettingDto.getKey()))); | |||||
Repository repo = bitbucketCloudRestClient.getRepo(pat, workspace, devOpsProjectDescriptor.projectIdentifier()); | |||||
ComponentCreationData componentCreationData = projectCreator.createProject( | |||||
dbSession, | |||||
getProjectKey(workspace, projectKey, repo), | |||||
getProjectName(projectName, repo), | |||||
repo.getMainBranch().getName(), | |||||
creationMethod); | |||||
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | |||||
createProjectAlmSettingDto(dbSession, repo.getSlug(), projectDto, almSettingDto, monorepo); | |||||
return componentCreationData; | |||||
} | |||||
private String findPersonalAccessTokenOrThrow(DbSession dbSession, AlmSettingDto almSettingDto) { | |||||
String userUuid = requireNonNull(userSession.getUuid(), "User UUID cannot be null."); | |||||
Optional<AlmPatDto> almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto); | |||||
return almPatDto.map(AlmPatDto::getPersonalAccessToken) | |||||
.orElseThrow(() -> new IllegalArgumentException(format("personal access token for '%s' is missing", almSettingDto.getKey()))); | |||||
} | |||||
private String getProjectKey(String workspace, @Nullable String projectKey, Repository repository) { | |||||
return Optional.ofNullable(projectKey).orElseGet(() -> projectKeyGenerator.generateUniqueProjectKey(workspace, repository.getSlug())); | |||||
} | |||||
private static String getProjectName(@Nullable String projectName, Repository repository) { | |||||
return Optional.ofNullable(projectName).orElse(repository.getName()); | |||||
} | |||||
private void createProjectAlmSettingDto(DbSession dbSession, String repoSlug, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) { | |||||
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto() | |||||
.setAlmSettingUuid(almSettingDto.getUuid()) | |||||
.setAlmRepo(repoSlug) | |||||
.setAlmSlug(null) | |||||
.setProjectUuid(projectDto.getUuid()) | |||||
.setSummaryCommentEnabled(true) | |||||
.setMonorepo(monorepo); | |||||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), projectDto.getName(), projectDto.getKey()); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 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.server.common.almsettings.bitbucketcloud; | |||||
import java.util.Map; | |||||
import java.util.Optional; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.alm.setting.ALM; | |||||
import org.sonar.db.alm.setting.AlmSettingDto; | |||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectCreator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor; | |||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.user.UserSession; | |||||
public class BitbucketCloudProjectCreatorFactory implements DevOpsProjectCreatorFactory { | |||||
private final DbClient dbClient; | |||||
private final UserSession userSession; | |||||
private final BitbucketCloudRestClient bitbucketCloudRestClient; | |||||
private final ProjectCreator projectCreator; | |||||
private final ProjectKeyGenerator projectKeyGenerator; | |||||
public BitbucketCloudProjectCreatorFactory(DbClient dbClient, UserSession userSession, BitbucketCloudRestClient bitbucketCloudRestClient, ProjectCreator projectCreator, | |||||
ProjectKeyGenerator projectKeyGenerator) { | |||||
this.dbClient = dbClient; | |||||
this.userSession = userSession; | |||||
this.bitbucketCloudRestClient = bitbucketCloudRestClient; | |||||
this.projectCreator = projectCreator; | |||||
this.projectKeyGenerator = projectKeyGenerator; | |||||
} | |||||
@Override | |||||
public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(DbSession dbSession, Map<String, String> characteristics) { | |||||
return Optional.empty(); | |||||
} | |||||
@Override | |||||
public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor) { | |||||
if (almSettingDto.getAlm() != ALM.BITBUCKET_CLOUD) { | |||||
return Optional.empty(); | |||||
} | |||||
return Optional.of( | |||||
new BitbucketCloudProjectCreator( | |||||
dbClient, | |||||
almSettingDto, | |||||
devOpsProjectDescriptor, | |||||
userSession, | |||||
bitbucketCloudRestClient, | |||||
projectCreator, | |||||
projectKeyGenerator)); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 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. | |||||
*/ | |||||
@ParametersAreNonnullByDefault | |||||
package org.sonar.server.common.almsettings.bitbucketcloud; | |||||
import javax.annotation.ParametersAreNonnullByDefault; |
import org.sonar.server.user.UserSession; | import org.sonar.server.user.UserSession; | ||||
import static java.lang.String.format; | import static java.lang.String.format; | ||||
import static java.util.Objects.requireNonNull; | |||||
import static org.sonar.db.project.CreationMethod.getCreationMethod; | import static org.sonar.db.project.CreationMethod.getCreationMethod; | ||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | ||||
import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; | import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
checkNewCodeDefinitionParam(request.newCodeDefinitionType(), request.newCodeDefinitionValue()); | checkNewCodeDefinitionParam(request.newCodeDefinitionType(), request.newCodeDefinitionValue()); | ||||
AlmSettingDto almSetting = dbClient.almSettingDao().selectByUuid(dbSession, request.almSettingId()).orElseThrow(() -> new IllegalArgumentException("ALM setting not found")); | AlmSettingDto almSetting = dbClient.almSettingDao().selectByUuid(dbSession, request.almSettingId()).orElseThrow(() -> new IllegalArgumentException("ALM setting not found")); | ||||
String almUrl = requireNonNull(almSetting.getUrl()); | |||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(almSetting.getAlm(), almUrl, request.repositoryIdentifier()); | |||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(almSetting.getAlm(), almSetting.getUrl(), request.repositoryIdentifier()); | |||||
DevOpsProjectCreator projectCreator = devOpsProjectCreatorFactory.getDevOpsProjectCreator(almSetting, projectDescriptor) | DevOpsProjectCreator projectCreator = devOpsProjectCreatorFactory.getDevOpsProjectCreator(almSetting, projectDescriptor) | ||||
.orElseThrow(() -> new IllegalArgumentException(format("Platform %s not supported", almSetting.getAlm().name()))); | .orElseThrow(() -> new IllegalArgumentException(format("Platform %s not supported", almSetting.getAlm().name()))); |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 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.server.common.almsettings.bitbucketcloud; | |||||
import org.junit.jupiter.api.Test; | |||||
import org.junit.jupiter.api.extension.ExtendWith; | |||||
import org.mockito.InjectMocks; | |||||
import org.mockito.Mock; | |||||
import org.mockito.junit.jupiter.MockitoExtension; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.alm.setting.ALM; | |||||
import org.sonar.db.alm.setting.AlmSettingDto; | |||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectCreator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor; | |||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.user.UserSession; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
@ExtendWith(MockitoExtension.class) | |||||
class BitbucketCloudProjectCreatorFactoryTest { | |||||
@Mock | |||||
private DbClient dbClient; | |||||
@Mock | |||||
private UserSession userSession; | |||||
@Mock | |||||
private BitbucketCloudRestClient bitbucketCloudRestClient; | |||||
@Mock | |||||
private ProjectCreator projectCreator; | |||||
@Mock | |||||
private ProjectKeyGenerator projectKeyGenerator; | |||||
@InjectMocks | |||||
private BitbucketCloudProjectCreatorFactory underTest; | |||||
@Test | |||||
void getDevOpsProjectCreator_whenAlmIsNotBitbucketCloud_shouldReturnEmpty() { | |||||
AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | |||||
when(almSettingDto.getAlm()).thenReturn(ALM.GITLAB); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITLAB, null, "bitbucket_project"); | |||||
assertThat(underTest.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor)).isEmpty(); | |||||
} | |||||
@Test | |||||
void getDevOpsProjectCreator_whenAlmItBitbucketCloud_shouldReturnProjectCreator() { | |||||
AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | |||||
when(almSettingDto.getAlm()).thenReturn(ALM.BITBUCKET_CLOUD); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.BITBUCKET_CLOUD, null, "bitbucket_project"); | |||||
DevOpsProjectCreator expectedProjectCreator = new BitbucketCloudProjectCreator(dbClient, almSettingDto, devOpsProjectDescriptor, userSession, bitbucketCloudRestClient, | |||||
projectCreator, projectKeyGenerator); | |||||
DevOpsProjectCreator devOpsProjectCreator = underTest.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor).orElseThrow(); | |||||
assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedProjectCreator); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 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.server.common.almsettings.bitbucketcloud; | |||||
import java.util.Optional; | |||||
import org.junit.jupiter.api.BeforeEach; | |||||
import org.junit.jupiter.api.Test; | |||||
import org.junit.jupiter.api.extension.ExtendWith; | |||||
import org.mockito.Answers; | |||||
import org.mockito.ArgumentCaptor; | |||||
import org.mockito.InjectMocks; | |||||
import org.mockito.Mock; | |||||
import org.mockito.junit.jupiter.MockitoExtension; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.Repository; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.alm.pat.AlmPatDto; | |||||
import org.sonar.db.alm.setting.ALM; | |||||
import org.sonar.db.alm.setting.AlmSettingDto; | |||||
import org.sonar.db.alm.setting.ProjectAlmSettingDto; | |||||
import org.sonar.db.project.CreationMethod; | |||||
import org.sonar.db.project.ProjectDto; | |||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | |||||
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor; | |||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.component.ComponentCreationData; | |||||
import org.sonar.server.user.UserSession; | |||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | |||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; | |||||
import static org.mockito.ArgumentMatchers.any; | |||||
import static org.mockito.ArgumentMatchers.eq; | |||||
import static org.mockito.Mockito.lenient; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.verify; | |||||
import static org.mockito.Mockito.when; | |||||
@ExtendWith(MockitoExtension.class) | |||||
class BitbucketCloudProjectCreatorTest { | |||||
private static final String USER_LOGIN = "userLogin"; | |||||
private static final String USER_UUID = "userUuid"; | |||||
private static final String REPOSITORY_SLUG = "projectSlug"; | |||||
private static final String REPOSITORY_NAME = "repositoryName"; | |||||
private static final String ALM_SETTING_KEY = "bitbucketcloud_config_1"; | |||||
private static final String ALM_SETTING_UUID = "almSettingUuid"; | |||||
private static final String USER_PAT = "1234"; | |||||
private static final String PROJECT_UUID = "projectUuid"; | |||||
private static final String WORKSPACE = "workspace"; | |||||
private static final String MAIN_BRANCH_NAME = "defaultBranch"; | |||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS) | |||||
private DbClient dbClient; | |||||
@Mock | |||||
private AlmSettingDto almSettingDto; | |||||
@Mock | |||||
private DevOpsProjectDescriptor devOpsProjectDescriptor; | |||||
@Mock | |||||
private UserSession userSession; | |||||
@Mock | |||||
private BitbucketCloudRestClient bitbucketCloudRestClient; | |||||
@Mock | |||||
private ProjectCreator projectCreator; | |||||
@Mock | |||||
private ProjectKeyGenerator projectKeyGenerator; | |||||
@InjectMocks | |||||
private BitbucketCloudProjectCreator underTest; | |||||
@BeforeEach | |||||
void setup() { | |||||
lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN); | |||||
lenient().when(userSession.getUuid()).thenReturn(USER_UUID); | |||||
lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); | |||||
lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID); | |||||
lenient().when(devOpsProjectDescriptor.projectIdentifier()).thenReturn(REPOSITORY_SLUG); | |||||
lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.BITBUCKET_CLOUD); | |||||
} | |||||
@Test | |||||
void isScanAllowedUsingPermissionsFromDevopsPlatform_shouldThrowUnsupportedOperationException() { | |||||
assertThatExceptionOfType(UnsupportedOperationException.class) | |||||
.isThrownBy(() -> underTest.isScanAllowedUsingPermissionsFromDevopsPlatform()) | |||||
.withMessage("Not Implemented"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenPatIsMissing_shouldThrow() { | |||||
assertThatExceptionOfType(IllegalArgumentException.class) | |||||
.isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null)) | |||||
.withMessage("personal access token for 'bitbucketcloud_config_1' is missing"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenWorkspaceIsNotDefined_shouldThrow() { | |||||
mockPatForUser(); | |||||
assertThatExceptionOfType(IllegalArgumentException.class) | |||||
.isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null)) | |||||
.withMessage("workspace for alm setting bitbucketcloud_config_1 is missing"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenRepositoryNotFound_shouldThrow() { | |||||
mockPatForUser(); | |||||
when(almSettingDto.getAppId()).thenReturn("workspace"); | |||||
when(bitbucketCloudRestClient.getRepo(USER_PAT, "workspace", REPOSITORY_SLUG)).thenThrow(new IllegalStateException("Problem fetching repository from Bitbucket Cloud")); | |||||
assertThatExceptionOfType(IllegalStateException.class) | |||||
.isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null)) | |||||
.withMessage("Problem fetching repository from Bitbucket Cloud"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenRepoFoundOnBitbucket_successfullyCreatesProject() { | |||||
mockPatForUser(); | |||||
when(almSettingDto.getAppId()).thenReturn(WORKSPACE); | |||||
mockBitbucketCloudRepository(); | |||||
mockProjectCreation("projectKey", "projectName"); | |||||
underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, "projectKey", "projectName"); | |||||
ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class); | |||||
verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq("projectName"), eq("projectKey")); | |||||
ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue(); | |||||
assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_SLUG); | |||||
assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue(); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesKeyAndUsersBitbucketRepositoryName() { | |||||
mockPatForUser(); | |||||
when(almSettingDto.getAppId()).thenReturn(WORKSPACE); | |||||
mockBitbucketCloudRepository(); | |||||
String generatedProjectKey = "generatedProjectKey"; | |||||
when(projectKeyGenerator.generateUniqueProjectKey(WORKSPACE, REPOSITORY_SLUG)).thenReturn(generatedProjectKey); | |||||
mockProjectCreation(generatedProjectKey, REPOSITORY_NAME); | |||||
underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, null, null); | |||||
ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class); | |||||
verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(generatedProjectKey)); | |||||
ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue(); | |||||
assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_SLUG); | |||||
assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue(); | |||||
} | |||||
private void mockPatForUser() { | |||||
AlmPatDto almPatDto = mock(); | |||||
when(almPatDto.getPersonalAccessToken()).thenReturn(USER_PAT); | |||||
when(dbClient.almPatDao().selectByUserAndAlmSetting(any(), eq(USER_UUID), eq(almSettingDto))).thenReturn(Optional.of(almPatDto)); | |||||
} | |||||
private void mockBitbucketCloudRepository() { | |||||
Repository repository = mock(Repository.class, Answers.RETURNS_DEEP_STUBS); | |||||
when(repository.getMainBranch().getName()).thenReturn(MAIN_BRANCH_NAME); | |||||
when(repository.getSlug()).thenReturn(REPOSITORY_SLUG); | |||||
when(repository.getName()).thenReturn(REPOSITORY_NAME); | |||||
when(bitbucketCloudRestClient.getRepo(USER_PAT, WORKSPACE, REPOSITORY_SLUG)).thenReturn(repository); | |||||
} | |||||
private void mockProjectCreation(String projectKey, String projectName) { | |||||
ComponentCreationData componentCreationData = mock(); | |||||
ProjectDto projectDto = mock(); | |||||
when(componentCreationData.projectDto()).thenReturn(projectDto); | |||||
when(projectDto.getUuid()).thenReturn(PROJECT_UUID); | |||||
when(projectDto.getKey()).thenReturn(projectKey); | |||||
when(projectDto.getName()).thenReturn(projectName); | |||||
when(projectCreator.createProject(any(), eq(projectKey), eq(projectName), eq(MAIN_BRANCH_NAME), eq(CreationMethod.ALM_IMPORT_API))) | |||||
.thenReturn(componentCreationData); | |||||
} | |||||
} |
class GitlabProjectCreatorTest { | class GitlabProjectCreatorTest { | ||||
private static final String PROJECT_UUID = "projectUuid"; | private static final String PROJECT_UUID = "projectUuid"; | ||||
private static final String USER_LOGIN = "userLogin"; | |||||
private static final String USER_UUID = "userUuid"; | |||||
private static final String REPOSITORY_PATH_WITH_NAMESPACE = "pathWith/namespace"; | |||||
private static final String GITLAB_PROJECT_NAME = "gitlabProjectName"; | |||||
private static final String REPOSITORY_ID = "1234"; | |||||
private static final String MAIN_BRANCH_NAME = "defaultBranch"; | |||||
private static final String ALM_SETTING_KEY = "gitlab_config_1"; | |||||
private static final String ALM_SETTING_UUID = "almSettingUuid"; | |||||
private static final String USER_PAT = "1234"; | |||||
public static final String GITLAB_URL = "http://api.com"; | |||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS) | @Mock(answer = Answers.RETURNS_DEEP_STUBS) | ||||
private DbClient dbClient; | private DbClient dbClient; | ||||
@InjectMocks | @InjectMocks | ||||
private GitlabProjectCreator underTest; | private GitlabProjectCreator underTest; | ||||
private static final String USER_LOGIN = "userLogin"; | |||||
private static final String USER_UUID = "userUuid"; | |||||
private static final String GROUP_NAME = "group1"; | |||||
private static final String REPOSITORY_PATH_WITH_NAMESPACE = "pathWith/namespace"; | |||||
private static final String GITLAB_PROJECT_NAME = "gitlabProjectName"; | |||||
private static final String REPOSITORY_ID = "1234"; | |||||
private static final String MAIN_BRANCH_NAME = "defaultBranch"; | |||||
private static final String ALM_SETTING_KEY = "gitlab_config_1"; | |||||
private static final String ALM_SETTING_UUID = "almSettingUuid"; | |||||
private static final String USER_PAT = "1234"; | |||||
public static final String GITLAB_URL = "http://api.com"; | |||||
private static final DevOpsProjectDescriptor DEVOPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITLAB, GITLAB_URL, REPOSITORY_ID); | |||||
@BeforeEach | @BeforeEach | ||||
void setup() { | void setup() { | ||||
lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN); | lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN); | ||||
@Test | @Test | ||||
void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() { | void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() { | ||||
mockPatForUser(); | mockPatForUser(); | ||||
when(gitlabApplicationClient.getProject(DEVOPS_PROJECT_DESCRIPTOR.url(), USER_PAT, Long.valueOf(REPOSITORY_ID))).thenThrow(new GitlabServerException(404, "Not found")); | |||||
when(gitlabApplicationClient.getProject(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))).thenThrow(new GitlabServerException(404, "Not found")); | |||||
assertThatExceptionOfType(IllegalStateException.class) | assertThatExceptionOfType(IllegalStateException.class) | ||||
.isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null)) | .isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null)) | ||||
.withMessage("Failed to fetch GitLab project with ID '1234' from 'http://api.com'"); | .withMessage("Failed to fetch GitLab project with ID '1234' from 'http://api.com'"); | ||||
assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_ID); | assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_ID); | ||||
assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | ||||
assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue(); | assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue(); | ||||
} | } | ||||
@Test | @Test | ||||
void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesOneKeyAndUsersGitlabProjectName() { | |||||
void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesKeyAndUsersGitlabProjectName() { | |||||
mockPatForUser(); | mockPatForUser(); | ||||
mockGitlabProject(); | mockGitlabProject(); | ||||
mockMainBranch(); | mockMainBranch(); | ||||
Project project = mock(Project.class); | Project project = mock(Project.class); | ||||
lenient().when(project.getPathWithNamespace()).thenReturn(REPOSITORY_PATH_WITH_NAMESPACE); | lenient().when(project.getPathWithNamespace()).thenReturn(REPOSITORY_PATH_WITH_NAMESPACE); | ||||
when(project.getName()).thenReturn(GITLAB_PROJECT_NAME); | when(project.getName()).thenReturn(GITLAB_PROJECT_NAME); | ||||
when(gitlabApplicationClient.getProject(DEVOPS_PROJECT_DESCRIPTOR.url(), USER_PAT, Long.valueOf(REPOSITORY_ID))).thenReturn(project); | |||||
when(gitlabApplicationClient.getProject(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))).thenReturn(project); | |||||
} | } | ||||
private void mockMainBranch() { | private void mockMainBranch() { | ||||
when(gitlabApplicationClient.getBranches(DEVOPS_PROJECT_DESCRIPTOR.url(), USER_PAT, Long.valueOf(REPOSITORY_ID))) | |||||
when(gitlabApplicationClient.getBranches(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))) | |||||
.thenReturn(List.of(new GitLabBranch("notMain", false), new GitLabBranch(MAIN_BRANCH_NAME, true))); | .thenReturn(List.of(new GitLabBranch("notMain", false), new GitLabBranch(MAIN_BRANCH_NAME, true))); | ||||
} | } | ||||
String devOpsPlatformSettingId, | String devOpsPlatformSettingId, | ||||
@NotEmpty | @NotEmpty | ||||
@Schema(description = "Identifier of the DevOps platform repository to import. Repository slug for GitHub, repository id for GitLab.") | |||||
@Schema( | |||||
description = """ | |||||
Identifier of the DevOps platform repository to import: | |||||
- repository slug for GitHub and Bitbucket Cloud | |||||
- repository id for GitLab | |||||
""") | |||||
String repositoryIdentifier, | String repositoryIdentifier, | ||||
@Nullable | @Nullable |
import org.sonar.db.user.UserDto; | import org.sonar.db.user.UserDto; | ||||
import org.sonar.server.almintegration.ws.ImportHelper; | import org.sonar.server.almintegration.ws.ImportHelper; | ||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | import org.sonar.server.common.almintegration.ProjectKeyGenerator; | ||||
import org.sonar.server.common.almsettings.DevOpsProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.bitbucketcloud.BitbucketCloudProjectCreatorFactory; | |||||
import org.sonar.server.common.component.ComponentUpdater; | import org.sonar.server.common.component.ComponentUpdater; | ||||
import org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver; | |||||
import org.sonar.server.common.permission.PermissionTemplateService; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.project.ImportProjectService; | |||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.es.TestIndexers; | import org.sonar.server.es.TestIndexers; | ||||
import org.sonar.server.exceptions.BadRequestException; | import org.sonar.server.exceptions.BadRequestException; | ||||
import org.sonar.server.exceptions.ForbiddenException; | import org.sonar.server.exceptions.ForbiddenException; | ||||
import org.sonar.server.exceptions.UnauthorizedException; | import org.sonar.server.exceptions.UnauthorizedException; | ||||
import org.sonar.server.favorite.FavoriteUpdater; | import org.sonar.server.favorite.FavoriteUpdater; | ||||
import org.sonar.server.l18n.I18nRule; | import org.sonar.server.l18n.I18nRule; | ||||
import org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver; | |||||
import org.sonar.server.permission.PermissionService; | import org.sonar.server.permission.PermissionService; | ||||
import org.sonar.server.common.permission.PermissionTemplateService; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.project.DefaultBranchNameResolver; | import org.sonar.server.project.DefaultBranchNameResolver; | ||||
import org.sonar.server.project.ProjectDefaultVisibility; | import org.sonar.server.project.ProjectDefaultVisibility; | ||||
import org.sonar.server.project.Visibility; | import org.sonar.server.project.Visibility; | ||||
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession); | private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession); | ||||
private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class); | private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class); | ||||
private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); | |||||
private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); | |||||
private final WsActionTester ws = new WsActionTester(new ImportBitbucketCloudRepoAction(db.getDbClient(), userSession, | |||||
bitbucketCloudRestClient, projectDefaultVisibility, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver, defaultBranchNameResolver)); | |||||
private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); | |||||
private final NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); | |||||
private final ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater); | |||||
private final DevOpsProjectCreatorFactory devOpsProjectCreatorFactory = new BitbucketCloudProjectCreatorFactory(db.getDbClient(), userSession, bitbucketCloudRestClient, | |||||
projectCreator, projectKeyGenerator); | |||||
private final ImportProjectService importProjectService = new ImportProjectService(db.getDbClient(), devOpsProjectCreatorFactory, userSession, componentUpdater, | |||||
newCodeDefinitionResolver); | |||||
private final WsActionTester ws = new WsActionTester(new ImportBitbucketCloudRepoAction(importHelper, importProjectService)); | |||||
@Before | @Before | ||||
public void before() { | public void before() { | ||||
public void fail_project_already_exist() { | public void fail_project_already_exist() { | ||||
UserDto user = db.users().insertUser(); | UserDto user = db.users().insertUser(); | ||||
userSession.logIn(user).addPermission(PROVISION_PROJECTS); | userSession.logIn(user).addPermission(PROVISION_PROJECTS); | ||||
AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting(); | |||||
AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting(); | |||||
db.almPats().insert(dto -> { | db.almPats().insert(dto -> { | ||||
dto.setAlmSettingUuid(almSetting.getUuid()); | dto.setAlmSettingUuid(almSetting.getUuid()); | ||||
dto.setUserUuid(user.getUuid()); | dto.setUserUuid(user.getUuid()); | ||||
public void check_pat_is_missing() { | public void check_pat_is_missing() { | ||||
UserDto user = db.users().insertUser(); | UserDto user = db.users().insertUser(); | ||||
userSession.logIn(user).addPermission(PROVISION_PROJECTS); | userSession.logIn(user).addPermission(PROVISION_PROJECTS); | ||||
AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting(); | |||||
AlmSettingDto almSetting = db.almSettings().insertBitbucketCloudAlmSetting(); | |||||
TestRequest request = ws.newRequest() | TestRequest request = ws.newRequest() | ||||
.setParam("almSetting", almSetting.getKey()) | .setParam("almSetting", almSetting.getKey()) | ||||
assertThatThrownBy(request::execute) | assertThatThrownBy(request::execute) | ||||
.isInstanceOf(IllegalArgumentException.class) | .isInstanceOf(IllegalArgumentException.class) | ||||
.hasMessageContaining("Username and App Password for '" + almSetting.getKey() + "' is missing"); | |||||
.hasMessageContaining("personal access token for '" + almSetting.getKey() + "' is missing"); | |||||
} | } | ||||
@Test | @Test |
*/ | */ | ||||
package org.sonar.server.almintegration.ws.bitbucketcloud; | package org.sonar.server.almintegration.ws.bitbucketcloud; | ||||
import java.util.Optional; | |||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import javax.inject.Inject; | import javax.inject.Inject; | ||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient; | |||||
import org.sonar.alm.client.bitbucket.bitbucketcloud.Repository; | |||||
import org.sonar.api.server.ws.Change; | import org.sonar.api.server.ws.Change; | ||||
import org.sonar.api.server.ws.Request; | import org.sonar.api.server.ws.Request; | ||||
import org.sonar.api.server.ws.Response; | import org.sonar.api.server.ws.Response; | ||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.alm.pat.AlmPatDto; | |||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | import org.sonar.db.alm.setting.AlmSettingDto; | ||||
import org.sonar.db.alm.setting.ProjectAlmSettingDto; | |||||
import org.sonar.db.component.BranchDto; | |||||
import org.sonar.db.project.ProjectDto; | |||||
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction; | import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction; | ||||
import org.sonar.server.almintegration.ws.ImportHelper; | import org.sonar.server.almintegration.ws.ImportHelper; | ||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | |||||
import org.sonar.server.component.ComponentCreationData; | |||||
import org.sonar.server.common.component.ComponentCreationParameters; | |||||
import org.sonar.server.common.component.ComponentUpdater; | |||||
import org.sonar.server.common.component.NewComponent; | |||||
import org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver; | |||||
import org.sonar.server.project.DefaultBranchNameResolver; | |||||
import org.sonar.server.project.ProjectDefaultVisibility; | |||||
import org.sonar.server.user.UserSession; | |||||
import org.sonar.server.common.project.ImportProjectRequest; | |||||
import org.sonar.server.common.project.ImportProjectService; | |||||
import org.sonar.server.common.project.ImportedProject; | |||||
import org.sonarqube.ws.Projects; | import org.sonarqube.ws.Projects; | ||||
import static java.util.Optional.ofNullable; | |||||
import static org.sonar.api.resources.Qualifiers.PROJECT; | |||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | |||||
import static org.sonar.db.project.CreationMethod.getCreationMethod; | |||||
import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING; | 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.almintegration.ws.ImportHelper.toCreateResponse; | ||||
import static org.sonar.server.common.component.NewComponent.newComponentBuilder; | |||||
import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; | import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; | ||||
import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; | import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; | ||||
import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; | |||||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | import static org.sonar.server.ws.WsUtils.writeProtobuf; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE; | ||||
private static final String PARAM_REPO_SLUG = "repositorySlug"; | private static final String PARAM_REPO_SLUG = "repositorySlug"; | ||||
private final DbClient dbClient; | |||||
private final UserSession userSession; | |||||
private final BitbucketCloudRestClient bitbucketCloudRestClient; | |||||
private final ProjectDefaultVisibility projectDefaultVisibility; | |||||
private final ComponentUpdater componentUpdater; | |||||
private final ImportHelper importHelper; | private final ImportHelper importHelper; | ||||
private final ProjectKeyGenerator projectKeyGenerator; | |||||
private final NewCodeDefinitionResolver newCodeDefinitionResolver; | |||||
private final DefaultBranchNameResolver defaultBranchNameResolver; | |||||
private final ImportProjectService importProjectService; | |||||
@Inject | @Inject | ||||
public ImportBitbucketCloudRepoAction(DbClient dbClient, UserSession userSession, BitbucketCloudRestClient bitbucketCloudRestClient, | |||||
ProjectDefaultVisibility projectDefaultVisibility, ComponentUpdater componentUpdater, ImportHelper importHelper, | |||||
ProjectKeyGenerator projectKeyGenerator, NewCodeDefinitionResolver newCodeDefinitionResolver, | |||||
DefaultBranchNameResolver defaultBranchNameResolver) { | |||||
this.dbClient = dbClient; | |||||
this.userSession = userSession; | |||||
this.bitbucketCloudRestClient = bitbucketCloudRestClient; | |||||
this.projectDefaultVisibility = projectDefaultVisibility; | |||||
this.componentUpdater = componentUpdater; | |||||
public ImportBitbucketCloudRepoAction(ImportHelper importHelper, ImportProjectService importProjectService) { | |||||
this.importHelper = importHelper; | this.importHelper = importHelper; | ||||
this.projectKeyGenerator = projectKeyGenerator; | |||||
this.newCodeDefinitionResolver = newCodeDefinitionResolver; | |||||
this.defaultBranchNameResolver = defaultBranchNameResolver; | |||||
this.importProjectService = importProjectService; | |||||
} | } | ||||
@Override | @Override | ||||
private Projects.CreateWsResponse doHandle(Request request) { | private Projects.CreateWsResponse doHandle(Request request) { | ||||
importHelper.checkProvisionProjectPermission(); | importHelper.checkProvisionProjectPermission(); | ||||
AlmSettingDto almSettingDto = importHelper.getAlmSettingDtoForAlm(request, ALM.BITBUCKET_CLOUD); | |||||
String newCodeDefinitionType = request.param(PARAM_NEW_CODE_DEFINITION_TYPE); | String newCodeDefinitionType = request.param(PARAM_NEW_CODE_DEFINITION_TYPE); | ||||
String newCodeDefinitionValue = request.param(PARAM_NEW_CODE_DEFINITION_VALUE); | String newCodeDefinitionValue = request.param(PARAM_NEW_CODE_DEFINITION_VALUE); | ||||
String repoSlug = request.mandatoryParam(PARAM_REPO_SLUG); | String repoSlug = request.mandatoryParam(PARAM_REPO_SLUG); | ||||
AlmSettingDto almSettingDto = importHelper.getAlmSettingDtoForAlm(request, ALM.BITBUCKET_CLOUD); | |||||
String workspace = ofNullable(almSettingDto.getAppId()) | |||||
.orElseThrow(() -> new IllegalArgumentException(String.format("workspace for alm setting %s is missing", almSettingDto.getKey()))); | |||||
try (DbSession dbSession = dbClient.openSession(false)) { | |||||
String pat = getPat(dbSession, almSettingDto); | |||||
Repository repo = bitbucketCloudRestClient.getRepo(pat, workspace, repoSlug); | |||||
ComponentCreationData componentCreationData = createProject(dbSession, workspace, repo, repo.getMainBranch().getName()); | |||||
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | |||||
BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow(); | |||||
populatePRSetting(dbSession, repo, projectDto, almSettingDto); | |||||
checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); | |||||
if (newCodeDefinitionType != null) { | |||||
newCodeDefinitionResolver.createNewCodeDefinition(dbSession, projectDto.getUuid(), mainBranchDto.getUuid(), | |||||
Optional.ofNullable(repo.getMainBranch().getName()).orElse(defaultBranchNameResolver.getEffectiveMainBranchName()), | |||||
newCodeDefinitionType, newCodeDefinitionValue); | |||||
} | |||||
componentUpdater.commitAndIndex(dbSession, componentCreationData); | |||||
return toCreateResponse(projectDto); | |||||
} | |||||
} | |||||
private String getPat(DbSession dbSession, AlmSettingDto almSettingDto) { | |||||
String userUuid = importHelper.getUserUuid(); | |||||
return dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto) | |||||
.map(AlmPatDto::getPersonalAccessToken) | |||||
.orElseThrow(() -> new IllegalArgumentException(String.format("Username and App Password for '%s' is missing", | |||||
almSettingDto.getKey()))); | |||||
} | |||||
private ComponentCreationData createProject(DbSession dbSession, String workspace, Repository repo, @Nullable String defaultBranchName) { | |||||
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate(); | |||||
String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(workspace, repo.getSlug()); | |||||
NewComponent newProject = newComponentBuilder() | |||||
.setKey(uniqueProjectKey) | |||||
.setName(repo.getName()) | |||||
.setPrivate(visibility) | |||||
.setQualifier(PROJECT) | |||||
.build(); | |||||
ComponentCreationParameters componentCreationParameters = ComponentCreationParameters.builder() | |||||
.newComponent(newProject) | |||||
.userUuid(userSession.getUuid()) | |||||
.userLogin(userSession.getLogin()) | |||||
.mainBranchName(defaultBranchName) | |||||
.creationMethod(getCreationMethod(ALM_IMPORT, userSession.isAuthenticatedBrowserSession())) | |||||
.build(); | |||||
return componentUpdater.createWithoutCommit(dbSession, componentCreationParameters); | |||||
ImportedProject importedProject = importProjectService.importProject(toServiceRequest(almSettingDto, repoSlug, newCodeDefinitionType, newCodeDefinitionValue)); | |||||
return toCreateResponse(importedProject.projectDto()); | |||||
} | } | ||||
private void populatePRSetting(DbSession dbSession, Repository repo, ProjectDto projectDto, AlmSettingDto almSettingDto) { | |||||
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto() | |||||
.setAlmSettingUuid(almSettingDto.getUuid()) | |||||
// Bitbucket Cloud PR decoration reads almRepo | |||||
.setAlmRepo(repo.getSlug()) | |||||
.setProjectUuid(projectDto.getUuid()) | |||||
.setMonorepo(false); | |||||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), | |||||
projectDto.getName(), projectDto.getKey()); | |||||
private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String slug, @Nullable String newCodeDefinitionType, | |||||
@Nullable String newCodeDefinitionValue) { | |||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), slug, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
} | } | ||||
} | } |
import org.sonar.server.almsettings.MultipleAlmFeature; | import org.sonar.server.almsettings.MultipleAlmFeature; | ||||
import org.sonar.server.almsettings.ws.AlmSettingsWsModule; | import org.sonar.server.almsettings.ws.AlmSettingsWsModule; | ||||
import org.sonar.server.common.almsettings.DelegatingDevOpsProjectCreatorFactory; | import org.sonar.server.common.almsettings.DelegatingDevOpsProjectCreatorFactory; | ||||
import org.sonar.server.common.almsettings.bitbucketcloud.BitbucketCloudProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | ||||
import org.sonar.server.common.almsettings.gitlab.GitlabProjectCreatorFactory; | import org.sonar.server.common.almsettings.gitlab.GitlabProjectCreatorFactory; | ||||
import org.sonar.server.authentication.AuthenticationModule; | import org.sonar.server.authentication.AuthenticationModule; | ||||
BitbucketServerRestClient.class, | BitbucketServerRestClient.class, | ||||
AzureDevOpsHttpClient.class, | AzureDevOpsHttpClient.class, | ||||
new AlmIntegrationsWSModule(), | new AlmIntegrationsWSModule(), | ||||
BitbucketCloudProjectCreatorFactory.class, | |||||
BitbucketCloudValidator.class, | BitbucketCloudValidator.class, | ||||
BitbucketServerSettingsValidator.class, | BitbucketServerSettingsValidator.class, | ||||
GithubGlobalSettingsValidator.class, | GithubGlobalSettingsValidator.class, |