import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
public record DevOpsProjectDescriptor(ALM alm, @Nullable String url, String projectIdentifier) { | |||||
public record DevOpsProjectDescriptor(ALM alm, @Nullable String url, String repositoryIdentifier, @Nullable String projectIdentifier) { | |||||
} | } |
String workspace = ofNullable(almSettingDto.getAppId()) | String workspace = ofNullable(almSettingDto.getAppId()) | ||||
.orElseThrow(() -> new IllegalArgumentException(String.format("workspace for alm setting %s is missing", almSettingDto.getKey()))); | .orElseThrow(() -> new IllegalArgumentException(String.format("workspace for alm setting %s is missing", almSettingDto.getKey()))); | ||||
Repository repo = bitbucketCloudRestClient.getRepo(pat, workspace, devOpsProjectDescriptor.projectIdentifier()); | |||||
Repository repo = bitbucketCloudRestClient.getRepo(pat, workspace, devOpsProjectDescriptor.repositoryIdentifier()); | |||||
ComponentCreationData componentCreationData = projectCreator.createProject( | ComponentCreationData componentCreationData = projectCreator.createProject( | ||||
dbSession, | dbSession, |
/* | |||||
* 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.bitbucketserver; | |||||
import java.util.Optional; | |||||
import org.jetbrains.annotations.Nullable; | |||||
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.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; | |||||
public class BitbucketServerProjectCreator implements DevOpsProjectCreator { | |||||
private final AlmSettingDto almSettingDto; | |||||
private final BitbucketServerRestClient bitbucketServerRestClient; | |||||
private final DbClient dbClient; | |||||
private final DevOpsProjectDescriptor devOpsProjectDescriptor; | |||||
private final UserSession userSession; | |||||
private final ProjectCreator projectCreator; | |||||
private final ProjectKeyGenerator projectKeyGenerator; | |||||
public BitbucketServerProjectCreator(AlmSettingDto almSettingDto, BitbucketServerRestClient bitbucketServerRestClient, | |||||
DbClient dbClient, DevOpsProjectDescriptor devOpsProjectDescriptor, UserSession userSession, ProjectCreator projectCreator, | |||||
ProjectKeyGenerator projectKeyGenerator) { | |||||
this.almSettingDto = almSettingDto; | |||||
this.bitbucketServerRestClient = bitbucketServerRestClient; | |||||
this.dbClient = dbClient; | |||||
this.devOpsProjectDescriptor = devOpsProjectDescriptor; | |||||
this.userSession = userSession; | |||||
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); | |||||
String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | |||||
String bitbucketRepo = devOpsProjectDescriptor.repositoryIdentifier(); | |||||
String bitbucketProject = getBitbucketProjectOrThrow(); | |||||
Repository repository = bitbucketServerRestClient.getRepo(url, pat, bitbucketProject, bitbucketRepo); | |||||
String defaultBranchName = getDefaultBranchName(url, pat, bitbucketProject, bitbucketRepo); | |||||
ComponentCreationData componentCreationData = projectCreator.createProject( | |||||
dbSession, | |||||
getProjectKey(projectKey, repository), | |||||
getProjectName(projectName, repository), | |||||
defaultBranchName, | |||||
creationMethod); | |||||
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | |||||
createProjectAlmSettingDto(dbSession, repository.getSlug(), projectDto, almSettingDto, monorepo); | |||||
return componentCreationData; | |||||
} | |||||
private String findPersonalAccessTokenOrThrow(DbSession dbSession) { | |||||
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 getBitbucketProjectOrThrow() { | |||||
if (devOpsProjectDescriptor.projectIdentifier() == null) { | |||||
throw new IllegalArgumentException(String.format("The BitBucket project, in which the repository %s is located, is mandatory", | |||||
devOpsProjectDescriptor.repositoryIdentifier())); | |||||
} | |||||
return devOpsProjectDescriptor.projectIdentifier(); | |||||
} | |||||
private String getDefaultBranchName(String url, String pat, String project, String repo) { | |||||
BranchesList branches = bitbucketServerRestClient.getBranches(url, pat, project, repo); | |||||
Optional<Branch> defaultBranch = branches.findDefaultBranch(); | |||||
return defaultBranch.map(Branch::getName).orElse(null); | |||||
} | |||||
private String getProjectKey(@Nullable String projectKey, Repository repo) { | |||||
return Optional.ofNullable(projectKey).orElseGet(() -> projectKeyGenerator.generateUniqueProjectKey(repo.getProject().getKey(), repo.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 isMonorepo) { | |||||
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto() | |||||
.setAlmSettingUuid(almSettingDto.getUuid()) | |||||
.setAlmRepo(repoSlug) | |||||
.setAlmSlug(null) | |||||
.setProjectUuid(projectDto.getUuid()) | |||||
.setSummaryCommentEnabled(true) | |||||
.setMonorepo(isMonorepo); | |||||
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.bitbucketserver; | |||||
import java.util.Map; | |||||
import java.util.Optional; | |||||
import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient; | |||||
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 BitbucketServerProjectCreatorFactory implements DevOpsProjectCreatorFactory { | |||||
private final DbClient dbClient; | |||||
private final UserSession userSession; | |||||
private final BitbucketServerRestClient bitbucketServerRestClient; | |||||
private final ProjectCreator projectCreator; | |||||
private final ProjectKeyGenerator projectKeyGenerator; | |||||
public BitbucketServerProjectCreatorFactory(DbClient dbClient, UserSession userSession, | |||||
BitbucketServerRestClient bitbucketServerRestClient, | |||||
ProjectCreator projectCreator, ProjectKeyGenerator projectKeyGenerator) { | |||||
this.dbClient = dbClient; | |||||
this.userSession = userSession; | |||||
this.bitbucketServerRestClient = bitbucketServerRestClient; | |||||
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) { | |||||
return Optional.empty(); | |||||
} | |||||
return Optional.of( | |||||
new BitbucketServerProjectCreator( | |||||
almSettingDto, | |||||
bitbucketServerRestClient, | |||||
dbClient, | |||||
devOpsProjectDescriptor, | |||||
userSession, | |||||
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.bitbucketserver; | |||||
import javax.annotation.ParametersAreNonnullByDefault; | |||||
public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() { | public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() { | ||||
checkState(githubProjectCreationParameters.authAppInstallationToken() != null, "An auth app token is required in case repository permissions checking is necessary."); | checkState(githubProjectCreationParameters.authAppInstallationToken() != null, "An auth app token is required in case repository permissions checking is necessary."); | ||||
String[] orgaAndRepoTokenified = devOpsProjectDescriptor.projectIdentifier().split("/"); | |||||
String[] orgaAndRepoTokenified = devOpsProjectDescriptor.repositoryIdentifier().split("/"); | |||||
String organization = orgaAndRepoTokenified[0]; | String organization = orgaAndRepoTokenified[0]; | ||||
String repository = orgaAndRepoTokenified[1]; | String repository = orgaAndRepoTokenified[1]; | ||||
public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, Boolean monorepo, @Nullable String projectKey, | public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, Boolean monorepo, @Nullable String projectKey, | ||||
@Nullable String projectName) { | @Nullable String projectName) { | ||||
String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | ||||
Repository repository = githubApplicationClient.getRepository(url, devOpsAppInstallationToken, devOpsProjectDescriptor.projectIdentifier()) | |||||
Repository repository = githubApplicationClient.getRepository(url, devOpsAppInstallationToken, devOpsProjectDescriptor.repositoryIdentifier()) | |||||
.orElseThrow(() -> new IllegalStateException( | .orElseThrow(() -> new IllegalStateException( | ||||
String.format("Impossible to find the repository '%s' on GitHub, using the devops config %s", devOpsProjectDescriptor.projectIdentifier(), almSettingDto.getKey()))); | |||||
String.format("Impossible to find the repository '%s' on GitHub, using the devops config %s", devOpsProjectDescriptor.repositoryIdentifier(), almSettingDto.getKey()))); | |||||
return createProjectAndBindToDevOpsPlatform(dbSession, monorepo, projectKey, projectName, almSettingDto, repository, creationMethod); | return createProjectAndBindToDevOpsPlatform(dbSession, monorepo, projectKey, projectName, almSettingDto, repository, creationMethod); | ||||
} | } |
if (githubApiUrl == null || githubRepository == null) { | if (githubApiUrl == null || githubRepository == null) { | ||||
return Optional.empty(); | return Optional.empty(); | ||||
} | } | ||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, githubApiUrl, githubRepository); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, githubApiUrl, githubRepository, null); | |||||
return dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB).stream() | return dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB).stream() | ||||
.filter(almSettingDto -> devOpsProjectDescriptor.url().equals(almSettingDto.getUrl())) | .filter(almSettingDto -> devOpsProjectDescriptor.url().equals(almSettingDto.getUrl())) | ||||
private Optional<DevOpsProjectCreator> findInstallationIdAndCreateDevOpsProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, | private Optional<DevOpsProjectCreator> findInstallationIdAndCreateDevOpsProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, | ||||
AlmSettingDto almSettingDto) { | AlmSettingDto almSettingDto) { | ||||
GithubAppConfiguration githubAppConfiguration = githubGlobalSettingsValidator.validate(almSettingDto); | GithubAppConfiguration githubAppConfiguration = githubGlobalSettingsValidator.validate(almSettingDto); | ||||
return findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier()) | |||||
return findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.repositoryIdentifier()) | |||||
.map(installationId -> generateAppInstallationToken(githubAppConfiguration, installationId)) | .map(installationId -> generateAppInstallationToken(githubAppConfiguration, installationId)) | ||||
.map(appInstallationToken -> createGithubProjectCreator(devOpsProjectDescriptor, almSettingDto, appInstallationToken)); | .map(appInstallationToken -> createGithubProjectCreator(devOpsProjectDescriptor, almSettingDto, appInstallationToken)); | ||||
} | } | ||||
private GithubProjectCreator createGithubProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto, | private GithubProjectCreator createGithubProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto, | ||||
AppInstallationToken appInstallationToken) { | AppInstallationToken appInstallationToken) { | ||||
LOG.info("DevOps configuration {} auto-detected for project {}", almSettingDto.getKey(), devOpsProjectDescriptor.projectIdentifier()); | |||||
LOG.info("DevOps configuration {} auto-detected for project {}", almSettingDto.getKey(), devOpsProjectDescriptor.repositoryIdentifier()); | |||||
Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor); | Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor); | ||||
GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, appInstallationToken, | GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, appInstallationToken, | ||||
private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) { | private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) { | ||||
if (gitHubSettings.isProvisioningEnabled()) { | if (gitHubSettings.isProvisioningEnabled()) { | ||||
GithubAppConfiguration githubAppConfiguration = new GithubAppConfiguration(Long.parseLong(gitHubSettings.appId()), gitHubSettings.privateKey(), gitHubSettings.apiURL()); | GithubAppConfiguration githubAppConfiguration = new GithubAppConfiguration(Long.parseLong(gitHubSettings.appId()), gitHubSettings.privateKey(), gitHubSettings.apiURL()); | ||||
long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier()) | |||||
long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.repositoryIdentifier()) | |||||
.orElseThrow(() -> new BadConfigurationException("PROJECT", | .orElseThrow(() -> new BadConfigurationException("PROJECT", | ||||
format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. " | format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. " | ||||
+ "The permissions can't be checked, and the project can not be created.", | + "The permissions can't be checked, and the project can not be created.", | ||||
devOpsProjectDescriptor.projectIdentifier()))); | |||||
devOpsProjectDescriptor.repositoryIdentifier()))); | |||||
return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId)); | return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId)); | ||||
} | } | ||||
return Optional.empty(); | return Optional.empty(); |
private Long getGitlabProjectId() { | private Long getGitlabProjectId() { | ||||
try { | try { | ||||
return Long.parseLong(devOpsProjectDescriptor.projectIdentifier()); | |||||
return Long.parseLong(devOpsProjectDescriptor.repositoryIdentifier()); | |||||
} catch (NumberFormatException e) { | } catch (NumberFormatException e) { | ||||
throw new IllegalArgumentException(format("GitLab project identifier must be a number, was '%s'", devOpsProjectDescriptor.projectIdentifier())); | |||||
throw new IllegalArgumentException(format("GitLab project identifier must be a number, was '%s'", devOpsProjectDescriptor.repositoryIdentifier())); | |||||
} | } | ||||
} | } | ||||
public record ImportProjectRequest( | public record ImportProjectRequest( | ||||
@Nullable | @Nullable | ||||
String projectKey, | String projectKey, | ||||
@Nullable | @Nullable | ||||
String projectName, | String projectName, | ||||
String almSettingId, | String almSettingId, | ||||
String repositoryIdentifier, | String repositoryIdentifier, | ||||
@Nullable | @Nullable | ||||
String projectIdentifier, | |||||
@Nullable | |||||
String newCodeDefinitionType, | String newCodeDefinitionType, | ||||
@Nullable | @Nullable | ||||
String newCodeDefinitionValue, | String newCodeDefinitionValue, |
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")); | ||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(almSetting.getAlm(), almSetting.getUrl(), request.repositoryIdentifier()); | |||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(almSetting.getAlm(), almSetting.getUrl(), request.repositoryIdentifier(), | |||||
request.projectIdentifier()); | |||||
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()))); |
void getDevOpsProjectCreator_whenAlmIsNotBitbucketCloud_shouldReturnEmpty() { | void getDevOpsProjectCreator_whenAlmIsNotBitbucketCloud_shouldReturnEmpty() { | ||||
AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | ||||
when(almSettingDto.getAlm()).thenReturn(ALM.GITLAB); | when(almSettingDto.getAlm()).thenReturn(ALM.GITLAB); | ||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITLAB, null, "bitbucket_project"); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITLAB, null, "bitbucket_repo", "bitbucket_project"); | |||||
assertThat(underTest.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor)).isEmpty(); | assertThat(underTest.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor)).isEmpty(); | ||||
} | } | ||||
void getDevOpsProjectCreator_whenAlmItBitbucketCloud_shouldReturnProjectCreator() { | void getDevOpsProjectCreator_whenAlmItBitbucketCloud_shouldReturnProjectCreator() { | ||||
AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | ||||
when(almSettingDto.getAlm()).thenReturn(ALM.BITBUCKET_CLOUD); | when(almSettingDto.getAlm()).thenReturn(ALM.BITBUCKET_CLOUD); | ||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.BITBUCKET_CLOUD, null, "bitbucket_project"); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.BITBUCKET_CLOUD, null, "bitbucket_repo", "bitbucket_project"); | |||||
DevOpsProjectCreator expectedProjectCreator = new BitbucketCloudProjectCreator(dbClient, almSettingDto, devOpsProjectDescriptor, userSession, bitbucketCloudRestClient, | DevOpsProjectCreator expectedProjectCreator = new BitbucketCloudProjectCreator(dbClient, almSettingDto, devOpsProjectDescriptor, userSession, bitbucketCloudRestClient, | ||||
projectCreator, projectKeyGenerator); | projectCreator, projectKeyGenerator); |
lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); | lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); | ||||
lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID); | lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID); | ||||
lenient().when(devOpsProjectDescriptor.projectIdentifier()).thenReturn(REPOSITORY_SLUG); | |||||
lenient().when(devOpsProjectDescriptor.repositoryIdentifier()).thenReturn(REPOSITORY_SLUG); | |||||
lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.BITBUCKET_CLOUD); | lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.BITBUCKET_CLOUD); | ||||
} | } | ||||
/* | |||||
* 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.bitbucketserver; | |||||
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.bitbucketserver.BitbucketServerRestClient; | |||||
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 BitbucketServerProjectCreatorFactoryTest { | |||||
@Mock | |||||
private DbClient dbClient; | |||||
@Mock | |||||
private UserSession userSession; | |||||
@Mock | |||||
private BitbucketServerRestClient bitbucketServerRestClient; | |||||
@Mock | |||||
private ProjectCreator projectCreator; | |||||
@Mock | |||||
private ProjectKeyGenerator projectKeyGenerator; | |||||
@InjectMocks | |||||
private BitbucketServerProjectCreatorFactory underTest; | |||||
@Test | |||||
void getDevOpsProjectCreator_whenAlmIsNotBitbucketServer_shouldReturnEmpty() { | |||||
AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | |||||
when(almSettingDto.getAlm()).thenReturn(ALM.GITLAB); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITLAB, null, "bitbucket_repo", "bitbucket_project"); | |||||
assertThat(underTest.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor)).isEmpty(); | |||||
} | |||||
@Test | |||||
void testGetDevOpsProjectCreator_whenAlmIsBitbucketServer_shouldReturnProjectCreator() { | |||||
AlmSettingDto almSettingDto = mock(AlmSettingDto.class); | |||||
when(almSettingDto.getAlm()).thenReturn(ALM.BITBUCKET); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.BITBUCKET, null, "bitbucket_repo", "bitbucket_project"); | |||||
DevOpsProjectCreator expectedProjectCreator = new BitbucketServerProjectCreator(almSettingDto, bitbucketServerRestClient, dbClient, | |||||
devOpsProjectDescriptor, userSession, 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.bitbucketserver; | |||||
import java.util.List; | |||||
import java.util.Optional; | |||||
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.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.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.assertThatThrownBy; | |||||
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 BitbucketServerProjectCreatorTest { | |||||
private static final String USER_LOGIN = "userLogin"; | |||||
private static final String USER_UUID = "userUuid"; | |||||
private static final String DOP_REPOSITORY_ID = "repository"; | |||||
private static final String DOP_PROJECT_ID = "project"; | |||||
private static final String URL = "http://rest/api/1.0/projects/projectKey/repos/repoName"; | |||||
private static final String ALM_SETTING_KEY = "bitbucketserver_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 MAIN_BRANCH_NAME = "main"; | |||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS) | |||||
private DbClient dbClient; | |||||
@Mock | |||||
private AlmSettingDto almSettingDto; | |||||
@Mock | |||||
private DevOpsProjectDescriptor devOpsProjectDescriptor; | |||||
@Mock | |||||
private UserSession userSession; | |||||
@Mock | |||||
private BitbucketServerRestClient bitbucketServerRestClient; | |||||
@Mock | |||||
private ProjectCreator projectCreator; | |||||
@Mock | |||||
private ProjectKeyGenerator projectKeyGenerator; | |||||
@InjectMocks | |||||
private BitbucketServerProjectCreator underTest; | |||||
@Test | |||||
void isScanAllowedUsingPermissionsFromDevopsPlatform_shouldThrowUnsupportedOperationException() { | |||||
assertThatThrownBy(() -> underTest.isScanAllowedUsingPermissionsFromDevopsPlatform()) | |||||
.isInstanceOf(UnsupportedOperationException.class) | |||||
.hasMessage("Not Implemented"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenPatIsMissing_shouldThrow() { | |||||
mockValidUserSession(); | |||||
mockValidAlmSettingsDto(); | |||||
assertThatThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null)) | |||||
.isInstanceOf(IllegalArgumentException.class) | |||||
.hasMessage("personal access token for 'bitbucketserver_config_1' is missing"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenBitBucketProjectNotProvided_shouldThrow() { | |||||
mockValidUserSession(); | |||||
mockValidAlmSettingsDto(); | |||||
mockValidPatForUser(); | |||||
assertThatThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, "projectKey", null)) | |||||
.isInstanceOf(IllegalArgumentException.class) | |||||
.hasMessage("The BitBucket project, in which the repository null is located, is mandatory"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenRepositoryNotFound_shouldThrow() { | |||||
mockValidUserSession(); | |||||
mockValidAlmSettingsDto(); | |||||
mockValidPatForUser(); | |||||
mockValidProjectDescriptor(); | |||||
when(bitbucketServerRestClient.getRepo(URL, USER_PAT, DOP_PROJECT_ID, DOP_REPOSITORY_ID)).thenThrow(new IllegalArgumentException("Problem")); | |||||
assertThatThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, "projectKey", null)) | |||||
.isInstanceOf(IllegalArgumentException.class) | |||||
.hasMessage("Problem"); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenRepoFoundOnBitbucket_successfullyCreatesProject() { | |||||
mockValidUserSession(); | |||||
mockValidAlmSettingsDto(); | |||||
mockValidPatForUser(); | |||||
mockValidProjectDescriptor(); | |||||
mockValidBitBucketRepository(); | |||||
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(DOP_REPOSITORY_ID); | |||||
assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue(); | |||||
} | |||||
@Test | |||||
void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesKeyAndUsersBitbucketRepositoryName() { | |||||
mockValidUserSession(); | |||||
mockValidAlmSettingsDto(); | |||||
mockValidPatForUser(); | |||||
mockValidProjectDescriptor(); | |||||
Repository repository = mockValidBitBucketRepository(); | |||||
String generatedProjectKey = "generatedProjectKey"; | |||||
when(projectKeyGenerator.generateUniqueProjectKey(repository.getProject().getKey(), repository.getSlug())).thenReturn(generatedProjectKey); | |||||
mockProjectCreation(generatedProjectKey, repository.getName()); | |||||
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.getName()), eq(generatedProjectKey)); | |||||
ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue(); | |||||
assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(DOP_REPOSITORY_ID); | |||||
assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | |||||
assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue(); | |||||
} | |||||
private void mockValidUserSession() { | |||||
lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN); | |||||
lenient().when(userSession.getUuid()).thenReturn(USER_UUID); | |||||
} | |||||
private void mockValidPatForUser() { | |||||
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 mockValidAlmSettingsDto() { | |||||
lenient().when(almSettingDto.getUrl()).thenReturn(URL); | |||||
lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); | |||||
lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID); | |||||
} | |||||
private void mockValidProjectDescriptor() { | |||||
lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.BITBUCKET); | |||||
lenient().when(devOpsProjectDescriptor.url()).thenReturn(URL); | |||||
lenient().when(devOpsProjectDescriptor.repositoryIdentifier()).thenReturn(DOP_REPOSITORY_ID); | |||||
lenient().when(devOpsProjectDescriptor.projectIdentifier()).thenReturn(DOP_PROJECT_ID); | |||||
} | |||||
private Repository mockValidBitBucketRepository() { | |||||
Repository repository = new Repository(DOP_REPOSITORY_ID, "Repository name", 12L, new Project(DOP_PROJECT_ID, "Project name", 42L)); | |||||
when(bitbucketServerRestClient.getRepo(URL, USER_PAT, DOP_PROJECT_ID, DOP_REPOSITORY_ID)).thenReturn(repository); | |||||
BranchesList branches = new BranchesList(List.of( | |||||
new Branch(MAIN_BRANCH_NAME, true), | |||||
new Branch("feature", false))); | |||||
when(bitbucketServerRestClient.getBranches(URL, USER_PAT, DOP_PROJECT_ID, DOP_REPOSITORY_ID)).thenReturn(branches); | |||||
return 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); | |||||
} | |||||
} |
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | import org.sonar.server.common.almintegration.ProjectKeyGenerator; | ||||
import org.sonar.server.common.almsettings.DevOpsProjectCreator; | import org.sonar.server.common.almsettings.DevOpsProjectCreator; | ||||
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor; | import org.sonar.server.common.almsettings.DevOpsProjectDescriptor; | ||||
import org.sonar.server.common.almsettings.github.GithubProjectCreationParameters; | |||||
import org.sonar.server.common.almsettings.github.GithubProjectCreator; | |||||
import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.permission.UserPermissionChange; | |||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.exceptions.BadConfigurationException; | import org.sonar.server.exceptions.BadConfigurationException; | ||||
import org.sonar.server.management.ManagedProjectService; | import org.sonar.server.management.ManagedProjectService; | ||||
import org.sonar.server.permission.PermissionService; | import org.sonar.server.permission.PermissionService; | ||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.permission.UserPermissionChange; | |||||
import org.sonar.server.project.ProjectDefaultVisibility; | import org.sonar.server.project.ProjectDefaultVisibility; | ||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.user.UserSession; | import org.sonar.server.user.UserSession; | ||||
import static java.lang.String.format; | import static java.lang.String.format; | ||||
private static final String GITHUB_REPO_FULL_NAME = ORGANIZATION_NAME + "/" + PROJECT_NAME; | private static final String GITHUB_REPO_FULL_NAME = ORGANIZATION_NAME + "/" + PROJECT_NAME; | ||||
private static final String GITHUB_API_URL = "https://api.toto.com"; | private static final String GITHUB_API_URL = "https://api.toto.com"; | ||||
private static final DevOpsProjectDescriptor GITHUB_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, GITHUB_API_URL, GITHUB_REPO_FULL_NAME); | |||||
private static final DevOpsProjectDescriptor GITHUB_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, GITHUB_API_URL, GITHUB_REPO_FULL_NAME, null); | |||||
private static final Map<String, String> VALID_GITHUB_PROJECT_COORDINATES = Map.of( | private static final Map<String, String> VALID_GITHUB_PROJECT_COORDINATES = Map.of( | ||||
DEVOPS_PLATFORM_URL, GITHUB_PROJECT_DESCRIPTOR.url(), | DEVOPS_PLATFORM_URL, GITHUB_PROJECT_DESCRIPTOR.url(), | ||||
DEVOPS_PLATFORM_PROJECT_IDENTIFIER, GITHUB_PROJECT_DESCRIPTOR.projectIdentifier()); | |||||
DEVOPS_PLATFORM_PROJECT_IDENTIFIER, GITHUB_PROJECT_DESCRIPTOR.repositoryIdentifier()); | |||||
private static final long APP_INSTALLATION_ID = 534534534543L; | private static final long APP_INSTALLATION_ID = 534534534543L; | ||||
private static final String USER_ACCESS_TOKEN = "userPat"; | private static final String USER_ACCESS_TOKEN = "userPat"; | ||||
} | } | ||||
private GithubProjectCreator getExpectedGithubProjectCreator(AlmSettingDto almSettingDto, boolean isInstanceManaged, AccessToken accessToken) { | private GithubProjectCreator getExpectedGithubProjectCreator(AlmSettingDto almSettingDto, boolean isInstanceManaged, AccessToken accessToken) { | ||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, almSettingDto.getUrl(), GITHUB_REPO_FULL_NAME); | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, almSettingDto.getUrl(), GITHUB_REPO_FULL_NAME, null); | |||||
AppInstallationToken authAppInstallToken = isInstanceManaged ? authAppInstallationToken : null; | AppInstallationToken authAppInstallToken = isInstanceManaged ? authAppInstallationToken : null; | ||||
GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken, | GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken, | ||||
authAppInstallToken); | authAppInstallToken); |
private static final String REPOSITORY_NAME = "repo1"; | private static final String REPOSITORY_NAME = "repo1"; | ||||
private static final String MAIN_BRANCH_NAME = "defaultBranch"; | private static final String MAIN_BRANCH_NAME = "defaultBranch"; | ||||
private static final DevOpsProjectDescriptor DEVOPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, "http://api.com", ORGANIZATION_NAME + "/" + REPOSITORY_NAME); | |||||
private static final DevOpsProjectDescriptor DEVOPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, "http://api.com", ORGANIZATION_NAME + "/" + REPOSITORY_NAME, null); | |||||
private static final String ALM_SETTING_KEY = "github_config_1"; | private static final String ALM_SETTING_KEY = "github_config_1"; | ||||
private static final String USER_LOGIN = "userLogin"; | private static final String USER_LOGIN = "userLogin"; | ||||
private static final String USER_UUID = "userUuid"; | private static final String USER_UUID = "userUuid"; | ||||
GithubApplicationClient.Repository repository = mock(); | GithubApplicationClient.Repository repository = mock(); | ||||
when(repository.getDefaultBranch()).thenReturn(MAIN_BRANCH_NAME); | when(repository.getDefaultBranch()).thenReturn(MAIN_BRANCH_NAME); | ||||
when(repository.getName()).thenReturn(REPOSITORY_NAME); | when(repository.getName()).thenReturn(REPOSITORY_NAME); | ||||
when(repository.getFullName()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier()); | |||||
when(repository.getFullName()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier()); | |||||
lenient().when(repository.isPrivate()).thenReturn(true); | lenient().when(repository.isPrivate()).thenReturn(true); | ||||
when(githubApplicationClient.getRepository(DEVOPS_PROJECT_DESCRIPTOR.url(), devOpsAppInstallationToken, DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier())).thenReturn( | |||||
when(githubApplicationClient.getRepository(DEVOPS_PROJECT_DESCRIPTOR.url(), devOpsAppInstallationToken, DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier())).thenReturn( | |||||
Optional.of(repository)); | Optional.of(repository)); | ||||
when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier()); | |||||
when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier()); | |||||
return repository; | return repository; | ||||
} | } | ||||
} | } | ||||
private static void assertAlmSettingsDtoContainsCorrectInformation(AlmSettingDto almSettingDto, ProjectDto projectDto, ProjectAlmSettingDto projectAlmSettingDto) { | private static void assertAlmSettingsDtoContainsCorrectInformation(AlmSettingDto almSettingDto, ProjectDto projectDto, ProjectAlmSettingDto projectAlmSettingDto) { | ||||
assertThat(projectAlmSettingDto.getAlmRepo()).isEqualTo(DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier()); | |||||
assertThat(projectAlmSettingDto.getAlmRepo()).isEqualTo(DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier()); | |||||
assertThat(projectAlmSettingDto.getAlmSlug()).isNull(); | assertThat(projectAlmSettingDto.getAlmSlug()).isNull(); | ||||
assertThat(projectAlmSettingDto.getAlmSettingUuid()).isEqualTo(almSettingDto.getUuid()); | assertThat(projectAlmSettingDto.getAlmSettingUuid()).isEqualTo(almSettingDto.getUuid()); | ||||
assertThat(projectAlmSettingDto.getProjectUuid()).isEqualTo(projectDto.getUuid()); | assertThat(projectAlmSettingDto.getProjectUuid()).isEqualTo(projectDto.getUuid()); |
lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); | lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); | ||||
lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID); | lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID); | ||||
lenient().when(devOpsProjectDescriptor.projectIdentifier()).thenReturn(REPOSITORY_ID); | |||||
lenient().when(devOpsProjectDescriptor.repositoryIdentifier()).thenReturn(REPOSITORY_ID); | |||||
lenient().when(devOpsProjectDescriptor.url()).thenReturn(GITLAB_URL); | lenient().when(devOpsProjectDescriptor.url()).thenReturn(GITLAB_URL); | ||||
lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.GITLAB); | lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.GITLAB); | ||||
} | } |
private static final String API_URL = "https://api.com"; | private static final String API_URL = "https://api.com"; | ||||
private static final String PROJECT_UUID = "project-uuid"; | private static final String PROJECT_UUID = "project-uuid"; | ||||
private static final String REPOSITORY_ID = "repository-id"; | |||||
private static final String DOP_REPOSITORY_ID = "repository-id"; | |||||
private static final String DOP_PROJECT_ID = "project-id"; | |||||
private static final String PROJECT_KEY = "project-key"; | private static final String PROJECT_KEY = "project-key"; | ||||
private static final String PROJECT_NAME = "project-name"; | private static final String PROJECT_NAME = "project-name"; | ||||
private static final String MAIN_BRANCH_UUID = "main-branch-uuid"; | private static final String MAIN_BRANCH_UUID = "main-branch-uuid"; | ||||
DbSession dbSession = mockDbSession(); | DbSession dbSession = mockDbSession(); | ||||
when(dbClient.almSettingDao().selectByUuid(dbSession, ALM_SETTING_ID)).thenReturn(Optional.empty()); | when(dbClient.almSettingDao().selectByUuid(dbSession, ALM_SETTING_ID)).thenReturn(Optional.empty()); | ||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, REPOSITORY_ID, null, null, true); | |||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, DOP_REPOSITORY_ID, DOP_PROJECT_ID, null, null, true); | |||||
assertThatThrownBy(() -> importProjectService.importProject(request)) | assertThatThrownBy(() -> importProjectService.importProject(request)) | ||||
.isInstanceOf(IllegalArgumentException.class) | .isInstanceOf(IllegalArgumentException.class) | ||||
when(devOpsProjectCreatorFactory.getDevOpsProjectCreator(eq(almSetting), any())) | when(devOpsProjectCreatorFactory.getDevOpsProjectCreator(eq(almSetting), any())) | ||||
.thenReturn(Optional.empty()); | .thenReturn(Optional.empty()); | ||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, REPOSITORY_ID, null, null, true); | |||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, DOP_REPOSITORY_ID, DOP_PROJECT_ID, null, null, true); | |||||
assertThatThrownBy(() -> importProjectService.importProject(request)) | assertThatThrownBy(() -> importProjectService.importProject(request)) | ||||
.isInstanceOf(IllegalArgumentException.class) | .isInstanceOf(IllegalArgumentException.class) | ||||
ProjectAlmSettingDto projectAlmSettingDto = mockProjectAlmSetting(dbSession, projectDto); | ProjectAlmSettingDto projectAlmSettingDto = mockProjectAlmSetting(dbSession, projectDto); | ||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, REPOSITORY_ID, null, null, true); | |||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, DOP_REPOSITORY_ID, DOP_PROJECT_ID, null, null, true); | |||||
ImportedProject importedProject = importProjectService.importProject(request); | ImportedProject importedProject = importProjectService.importProject(request); | ||||
ProjectAlmSettingDto projectAlmSettingDto = mockProjectAlmSetting(dbSession, projectDto); | ProjectAlmSettingDto projectAlmSettingDto = mockProjectAlmSetting(dbSession, projectDto); | ||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, REPOSITORY_ID, "NUMBER_OF_DAYS", "10", true); | |||||
ImportProjectRequest request = new ImportProjectRequest(PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, DOP_REPOSITORY_ID, DOP_PROJECT_ID, "NUMBER_OF_DAYS", "10", true); | |||||
ImportedProject importedProject = importProjectService.importProject(request); | ImportedProject importedProject = importProjectService.importProject(request); | ||||
private DevOpsProjectCreator mockDevOpsProjectCreator(AlmSettingDto almSetting) { | private DevOpsProjectCreator mockDevOpsProjectCreator(AlmSettingDto almSetting) { | ||||
DevOpsProjectCreator devOpsProjectCreator = mock(DevOpsProjectCreator.class); | DevOpsProjectCreator devOpsProjectCreator = mock(DevOpsProjectCreator.class); | ||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, API_URL, REPOSITORY_ID); | |||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, API_URL, DOP_REPOSITORY_ID, DOP_PROJECT_ID); | |||||
when(devOpsProjectCreatorFactory.getDevOpsProjectCreator(almSetting, projectDescriptor)) | when(devOpsProjectCreatorFactory.getDevOpsProjectCreator(almSetting, projectDescriptor)) | ||||
.thenReturn(Optional.of(devOpsProjectCreator)); | .thenReturn(Optional.of(devOpsProjectCreator)); | ||||
return devOpsProjectCreator; | return devOpsProjectCreator; |
} | } | ||||
private static ImportProjectRequest restRequestToServiceRequest(BoundProjectCreateRestRequest request) { | private static ImportProjectRequest restRequestToServiceRequest(BoundProjectCreateRestRequest request) { | ||||
return new ImportProjectRequest(request.projectKey(), request.projectName(), request.devOpsPlatformSettingId(), request.repositoryIdentifier(), | |||||
request.newCodeDefinitionType(), request.newCodeDefinitionValue(), request.monorepo()); | |||||
return new ImportProjectRequest( | |||||
request.projectKey(), | |||||
request.projectName(), | |||||
request.devOpsPlatformSettingId(), | |||||
request.repositoryIdentifier(), | |||||
request.projectIdentifier(), | |||||
request.newCodeDefinitionType(), | |||||
request.newCodeDefinitionValue(), | |||||
request.monorepo()); | |||||
} | } | ||||
private static BoundProjectCreateRestResponse toRestResponse(ImportedProject importedProject) { | private static BoundProjectCreateRestResponse toRestResponse(ImportedProject importedProject) { |
@Schema( | @Schema( | ||||
description = """ | description = """ | ||||
Identifier of the DevOps platform repository to import: | Identifier of the DevOps platform repository to import: | ||||
- repository slug for GitHub and Bitbucket Cloud | |||||
- repository slug for GitHub and Bitbucket (Cloud and Server) | |||||
- repository id for GitLab | - repository id for GitLab | ||||
""") | """) | ||||
String repositoryIdentifier, | String repositoryIdentifier, | ||||
@Nullable | |||||
@Schema( | |||||
description = """ | |||||
Identifier of the DevOps platform project in which the repository is located. | |||||
This is only needed for Azure and BitBucket Server platforms | |||||
""") | |||||
String projectIdentifier, | |||||
@Nullable | @Nullable | ||||
@Schema(description = """ | @Schema(description = """ | ||||
Project New Code Definition Type | Project New Code Definition Type |
private static final String PROJECT_UUID = "project-uuid"; | private static final String PROJECT_UUID = "project-uuid"; | ||||
private static final String PROJECT_ALM_SETTING_UUID = "project-alm-setting-uuid"; | private static final String PROJECT_ALM_SETTING_UUID = "project-alm-setting-uuid"; | ||||
private static final String REPOSITORY_ID = "repository-id"; | |||||
private static final String DOP_REPOSITORY_ID = "dop-repository-id"; | |||||
private static final String DOP_PROJECT_ID = "dop-project-id"; | |||||
private static final String PROJECT_KEY = "project-key"; | private static final String PROJECT_KEY = "project-key"; | ||||
private static final String PROJECT_NAME = "project-name"; | private static final String PROJECT_NAME = "project-name"; | ||||
private static final String ALM_SETTING_ID = "alm-setting-id"; | private static final String ALM_SETTING_ID = "alm-setting-id"; | ||||
PROJECT_KEY, | PROJECT_KEY, | ||||
PROJECT_NAME, | PROJECT_NAME, | ||||
ALM_SETTING_ID, | ALM_SETTING_ID, | ||||
REPOSITORY_ID, | |||||
DOP_REPOSITORY_ID, | |||||
DOP_PROJECT_ID, | |||||
"NUMBER_OF_DAYS", | "NUMBER_OF_DAYS", | ||||
"10", | "10", | ||||
true))) | true))) | ||||
"projectKey": "project-key", | "projectKey": "project-key", | ||||
"projectName": "project-name", | "projectName": "project-name", | ||||
"devOpsPlatformSettingId": "alm-setting-id", | "devOpsPlatformSettingId": "alm-setting-id", | ||||
"repositoryIdentifier": "repository-id", | |||||
"repositoryIdentifier": "dop-repository-id", | |||||
"projectIdentifier": "dop-project-id", | |||||
"newCodeDefinitionType": "NUMBER_OF_DAYS", | "newCodeDefinitionType": "NUMBER_OF_DAYS", | ||||
"newCodeDefinitionValue": "10", | "newCodeDefinitionValue": "10", | ||||
"monorepo": true | "monorepo": true |
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.bitbucketserver.BitbucketServerProjectCreatorFactory; | |||||
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.newcodeperiod.NewCodeDefinitionResolver; | ||||
import org.sonar.server.common.permission.PermissionTemplateService; | import org.sonar.server.common.permission.PermissionTemplateService; | ||||
import org.sonar.server.common.permission.PermissionUpdater; | 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.ws.WsActionTester; | import org.sonar.server.ws.WsActionTester; | ||||
import org.sonarqube.ws.Projects; | import org.sonarqube.ws.Projects; | ||||
import static java.lang.String.format; | |||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; | import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
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 final WsActionTester ws = new WsActionTester(new ImportBitbucketServerProjectAction(db.getDbClient(), userSession, | |||||
bitbucketServerRestClient, projectDefaultVisibility, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver, defaultBranchNameResolver)); | |||||
private final ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater); | |||||
private final DevOpsProjectCreatorFactory devOpsProjectCreatorFactory = new BitbucketServerProjectCreatorFactory(db.getDbClient(), userSession, bitbucketServerRestClient, | |||||
projectCreator, projectKeyGenerator); | |||||
private final ImportProjectService importProjectService = new ImportProjectService(db.getDbClient(), devOpsProjectCreatorFactory, userSession, componentUpdater, | |||||
newCodeDefinitionResolver); | |||||
private final WsActionTester ws = new WsActionTester(new ImportBitbucketServerProjectAction(importHelper, importProjectService)); | |||||
private static BranchesList defaultBranchesList; | private static BranchesList defaultBranchesList; | ||||
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().insertBitbucketAlmSetting(); | |||||
Project project = getGsonBBSProject(); | |||||
mockBitbucketServerRepo(project, new BranchesList()); | |||||
assertThatThrownBy(() -> { | |||||
ws.newRequest() | |||||
.setParam("almSetting", almSetting.getKey()) | |||||
.execute(); | |||||
}) | |||||
TestRequest request = ws.newRequest() | |||||
.setParam("almSetting", almSetting.getKey()) | |||||
.setParam("projectKey", "projectKey") | |||||
.setParam("repositorySlug", "repo-slug"); | |||||
assertThatThrownBy(request::execute) | |||||
.isInstanceOf(IllegalArgumentException.class) | .isInstanceOf(IllegalArgumentException.class) | ||||
.hasMessage("personal access token for '" + almSetting.getKey() + "' is missing"); | |||||
.hasMessage(format("personal access token for '%s' is missing", almSetting.getKey())); | |||||
} | } | ||||
@Test | @Test |
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.auth.github.client.GithubApplicationClient; | |||||
import org.sonar.alm.client.github.GithubGlobalSettingsValidator; | import org.sonar.alm.client.github.GithubGlobalSettingsValidator; | ||||
import org.sonar.api.utils.System2; | import org.sonar.api.utils.System2; | ||||
import org.sonar.auth.github.GitHubSettings; | import org.sonar.auth.github.GitHubSettings; | ||||
import org.sonar.auth.github.client.GithubApplicationClient; | |||||
import org.sonar.ce.queue.CeQueue; | import org.sonar.ce.queue.CeQueue; | ||||
import org.sonar.ce.queue.CeQueueImpl; | import org.sonar.ce.queue.CeQueueImpl; | ||||
import org.sonar.ce.queue.CeTaskSubmit; | import org.sonar.ce.queue.CeTaskSubmit; | ||||
import org.sonar.server.common.almsettings.github.GithubProjectCreator; | import org.sonar.server.common.almsettings.github.GithubProjectCreator; | ||||
import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | ||||
import org.sonar.server.common.component.ComponentUpdater; | import org.sonar.server.common.component.ComponentUpdater; | ||||
import org.sonar.server.common.permission.PermissionTemplateService; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.permission.UserPermissionChange; | |||||
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.management.ManagedProjectService; | import org.sonar.server.management.ManagedProjectService; | ||||
import org.sonar.server.permission.PermissionService; | import org.sonar.server.permission.PermissionService; | ||||
import org.sonar.server.permission.PermissionServiceImpl; | import org.sonar.server.permission.PermissionServiceImpl; | ||||
import org.sonar.server.common.permission.PermissionTemplateService; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.permission.UserPermissionChange; | |||||
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; | ||||
import org.sonar.server.common.project.ProjectCreator; | |||||
import org.sonar.server.tester.UserSessionRule; | import org.sonar.server.tester.UserSessionRule; | ||||
import static java.lang.String.format; | import static java.lang.String.format; | ||||
mockGithubRepository(isPrivate); | mockGithubRepository(isPrivate); | ||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, "apiUrl", "orga/repo"); | |||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, "apiUrl", "orga/repo", null); | |||||
GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(projectDescriptor, almSettingDto, userSession, mock(), null); | GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(projectDescriptor, almSettingDto, userSession, mock(), null); | ||||
DevOpsProjectCreator devOpsProjectCreator = spy(new GithubProjectCreator(db.getDbClient(), githubApplicationClient, null, projectKeyGenerator, | DevOpsProjectCreator devOpsProjectCreator = spy(new GithubProjectCreator(db.getDbClient(), githubApplicationClient, null, projectKeyGenerator, | ||||
permissionUpdater, permissionService, managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings)); | permissionUpdater, permissionService, managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings)); |
private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String slug, @Nullable String newCodeDefinitionType, | private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String slug, @Nullable String newCodeDefinitionType, | ||||
@Nullable String newCodeDefinitionValue) { | @Nullable String newCodeDefinitionValue) { | ||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), slug, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), slug, null, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.server.almintegration.ws.bitbucketserver; | package org.sonar.server.almintegration.ws.bitbucketserver; | ||||
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.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.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.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.component.ComponentCreationData; | |||||
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.Objects.requireNonNull; | |||||
import static org.sonar.api.resources.Qualifiers.PROJECT; | |||||
import static org.sonar.db.project.CreationMethod.getCreationMethod; | |||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | |||||
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_PROJECT_KEY = "projectKey"; | private static final String PARAM_PROJECT_KEY = "projectKey"; | ||||
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 BitbucketServerRestClient bitbucketServerRestClient; | |||||
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 ImportBitbucketServerProjectAction(DbClient dbClient, UserSession userSession, BitbucketServerRestClient bitbucketServerRestClient, | |||||
ProjectDefaultVisibility projectDefaultVisibility, ComponentUpdater componentUpdater, | |||||
ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator, NewCodeDefinitionResolver newCodeDefinitionResolver, | |||||
DefaultBranchNameResolver defaultBranchNameResolver) { | |||||
this.dbClient = dbClient; | |||||
this.userSession = userSession; | |||||
this.bitbucketServerRestClient = bitbucketServerRestClient; | |||||
this.projectDefaultVisibility = projectDefaultVisibility; | |||||
this.componentUpdater = componentUpdater; | |||||
public ImportBitbucketServerProjectAction(ImportHelper importHelper, ImportProjectService importProjectService) { | |||||
this.importHelper = importHelper; | this.importHelper = importHelper; | ||||
this.projectKeyGenerator = projectKeyGenerator; | |||||
this.newCodeDefinitionResolver = newCodeDefinitionResolver; | |||||
this.defaultBranchNameResolver = defaultBranchNameResolver; | |||||
this.importProjectService = importProjectService; | |||||
} | } | ||||
@Override | @Override | ||||
public void define(WebService.NewController context) { | public void define(WebService.NewController context) { | ||||
WebService.NewAction action = context.createAction("import_bitbucketserver_project") | WebService.NewAction action = context.createAction("import_bitbucketserver_project") | ||||
.setDescription("Create a SonarQube project with the information from the provided BitbucketServer project.<br/>" + | .setDescription("Create a SonarQube project with the information from the provided BitbucketServer project.<br/>" + | ||||
"Autoconfigure pull request decoration mechanism.<br/>" + | |||||
"Requires the 'Create Projects' permission") | |||||
"Autoconfigure pull request decoration mechanism.<br/>" + | |||||
"Requires the 'Create Projects' permission") | |||||
.setPost(true) | .setPost(true) | ||||
.setSince("8.2") | .setSince("8.2") | ||||
.setHandler(this) | .setHandler(this) | ||||
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); | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | |||||
String pat = getPat(dbSession, almSettingDto); | |||||
String projectKey = request.mandatoryParam(PARAM_PROJECT_KEY); | |||||
String repoSlug = request.mandatoryParam(PARAM_REPO_SLUG); | |||||
String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | |||||
Repository repo = bitbucketServerRestClient.getRepo(url, pat, projectKey, repoSlug); | |||||
String defaultBranchName = getDefaultBranchName(pat, projectKey, repoSlug, url); | |||||
ComponentCreationData componentCreationData = createProject(dbSession, repo, defaultBranchName); | |||||
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | |||||
BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow(); | |||||
populatePRSetting(dbSession, repo, projectDto, almSettingDto); | |||||
checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); | |||||
String bitbucketProjectKey = request.mandatoryParam(PARAM_PROJECT_KEY); | |||||
String bitbucketRepoSlug = request.mandatoryParam(PARAM_REPO_SLUG); | |||||
if (newCodeDefinitionType != null) { | |||||
newCodeDefinitionResolver.createNewCodeDefinition(dbSession, projectDto.getUuid(), mainBranchDto.getUuid(), | |||||
Optional.ofNullable(defaultBranchName).orElse(defaultBranchNameResolver.getEffectiveMainBranchName()), | |||||
newCodeDefinitionType, newCodeDefinitionValue); | |||||
} | |||||
componentUpdater.commitAndIndex(dbSession, componentCreationData); | |||||
return toCreateResponse(projectDto); | |||||
} | |||||
} | |||||
private String getPat(DbSession dbSession, AlmSettingDto almSettingDto) { | |||||
String userUuid = importHelper.getUserUuid(); | |||||
Optional<AlmPatDto> almPatDot = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto); | |||||
return almPatDot.map(AlmPatDto::getPersonalAccessToken) | |||||
.orElseThrow(() -> new IllegalArgumentException(String.format("personal access token for '%s' is missing", | |||||
almSettingDto.getKey()))); | |||||
} | |||||
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); | |||||
} | |||||
ImportedProject importedProject = importProjectService.importProject(toServiceRequest(almSettingDto, bitbucketRepoSlug, bitbucketProjectKey, newCodeDefinitionType, | |||||
newCodeDefinitionValue)); | |||||
private ComponentCreationData createProject(DbSession dbSession, Repository repo, @Nullable String defaultBranchName) { | |||||
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate(); | |||||
String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(repo.getProject().getKey(), 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); | |||||
return toCreateResponse(importedProject.projectDto()); | |||||
} | } | ||||
private void populatePRSetting(DbSession dbSession, Repository repo, ProjectDto componentDto, AlmSettingDto almSettingDto) { | |||||
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto() | |||||
.setAlmSettingUuid(almSettingDto.getUuid()) | |||||
.setAlmRepo(repo.getProject().getKey()) | |||||
.setAlmSlug(repo.getSlug()) | |||||
.setProjectUuid(componentDto.getUuid()) | |||||
.setMonorepo(false); | |||||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), | |||||
componentDto.getName(), componentDto.getKey()); | |||||
private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String slug, String project, @Nullable String newCodeDefinitionType, | |||||
@Nullable String newCodeDefinitionValue) { | |||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), slug, project, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
} | } | ||||
} | } |
private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String githubRepositoryKey, @Nullable String newCodeDefinitionType, | private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String githubRepositoryKey, @Nullable String newCodeDefinitionType, | ||||
@Nullable String newCodeDefinitionValue) { | @Nullable String newCodeDefinitionValue) { | ||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), githubRepositoryKey, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), githubRepositoryKey, null, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
} | } | ||||
} | } |
private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String gitlabProjectId, @Nullable String newCodeDefinitionType, | private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String gitlabProjectId, @Nullable String newCodeDefinitionType, | ||||
@Nullable String newCodeDefinitionValue) { | @Nullable String newCodeDefinitionValue) { | ||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), gitlabProjectId, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
return new ImportProjectRequest(null, null, almSettingDto.getUuid(), gitlabProjectId, null, newCodeDefinitionType, newCodeDefinitionValue, false); | |||||
} | } | ||||
} | } |
import org.sonar.server.almintegration.ws.AlmIntegrationsWSModule; | import org.sonar.server.almintegration.ws.AlmIntegrationsWSModule; | ||||
import org.sonar.server.almintegration.ws.CredentialsEncoderHelper; | import org.sonar.server.almintegration.ws.CredentialsEncoderHelper; | ||||
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.almintegration.ws.github.GithubProvisioningWs; | import org.sonar.server.almintegration.ws.github.GithubProvisioningWs; | ||||
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.bitbucketcloud.BitbucketCloudProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.gitlab.GitlabProjectCreatorFactory; | |||||
import org.sonar.server.authentication.AuthenticationModule; | import org.sonar.server.authentication.AuthenticationModule; | ||||
import org.sonar.server.authentication.DefaultAdminCredentialsVerifierImpl; | import org.sonar.server.authentication.DefaultAdminCredentialsVerifierImpl; | ||||
import org.sonar.server.authentication.DefaultAdminCredentialsVerifierNotificationHandler; | import org.sonar.server.authentication.DefaultAdminCredentialsVerifierNotificationHandler; | ||||
import org.sonar.server.ce.CeModule; | import org.sonar.server.ce.CeModule; | ||||
import org.sonar.server.ce.projectdump.ProjectExportWsModule; | import org.sonar.server.ce.projectdump.ProjectExportWsModule; | ||||
import org.sonar.server.ce.ws.CeWsModule; | import org.sonar.server.ce.ws.CeWsModule; | ||||
import org.sonar.server.common.almintegration.ProjectKeyGenerator; | |||||
import org.sonar.server.common.almsettings.DelegatingDevOpsProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.bitbucketcloud.BitbucketCloudProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.bitbucketserver.BitbucketServerProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; | |||||
import org.sonar.server.common.almsettings.gitlab.GitlabProjectCreatorFactory; | |||||
import org.sonar.server.common.component.ComponentUpdater; | |||||
import org.sonar.server.common.gitlab.config.GitlabConfigurationService; | import org.sonar.server.common.gitlab.config.GitlabConfigurationService; | ||||
import org.sonar.server.common.group.service.GroupMembershipService; | import org.sonar.server.common.group.service.GroupMembershipService; | ||||
import org.sonar.server.common.group.service.GroupService; | import org.sonar.server.common.group.service.GroupService; | ||||
import org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver; | |||||
import org.sonar.server.common.permission.DefaultTemplatesResolverImpl; | |||||
import org.sonar.server.common.permission.GroupPermissionChanger; | |||||
import org.sonar.server.common.permission.PermissionTemplateService; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.permission.UserPermissionChanger; | |||||
import org.sonar.server.common.rule.RuleCreator; | import org.sonar.server.common.rule.RuleCreator; | ||||
import org.sonar.server.common.rule.service.RuleService; | import org.sonar.server.common.rule.service.RuleService; | ||||
import org.sonar.server.common.text.MacroInterpreter; | import org.sonar.server.common.text.MacroInterpreter; | ||||
import org.sonar.server.component.ComponentCleanerService; | import org.sonar.server.component.ComponentCleanerService; | ||||
import org.sonar.server.component.ComponentFinder; | import org.sonar.server.component.ComponentFinder; | ||||
import org.sonar.server.component.ComponentService; | import org.sonar.server.component.ComponentService; | ||||
import org.sonar.server.common.component.ComponentUpdater; | |||||
import org.sonar.server.component.index.ComponentIndex; | import org.sonar.server.component.index.ComponentIndex; | ||||
import org.sonar.server.component.index.ComponentIndexDefinition; | import org.sonar.server.component.index.ComponentIndexDefinition; | ||||
import org.sonar.server.component.index.EntityDefinitionIndexer; | import org.sonar.server.component.index.EntityDefinitionIndexer; | ||||
import org.sonar.server.monitoring.devops.BitbucketMetricsTask; | import org.sonar.server.monitoring.devops.BitbucketMetricsTask; | ||||
import org.sonar.server.monitoring.devops.GithubMetricsTask; | import org.sonar.server.monitoring.devops.GithubMetricsTask; | ||||
import org.sonar.server.monitoring.devops.GitlabMetricsTask; | import org.sonar.server.monitoring.devops.GitlabMetricsTask; | ||||
import org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver; | |||||
import org.sonar.server.newcodeperiod.ws.NewCodePeriodsWsModule; | import org.sonar.server.newcodeperiod.ws.NewCodePeriodsWsModule; | ||||
import org.sonar.server.notification.NotificationModule; | import org.sonar.server.notification.NotificationModule; | ||||
import org.sonar.server.notification.ws.NotificationWsModule; | import org.sonar.server.notification.ws.NotificationWsModule; | ||||
import org.sonar.server.common.permission.DefaultTemplatesResolverImpl; | |||||
import org.sonar.server.common.permission.GroupPermissionChanger; | |||||
import org.sonar.server.common.permission.PermissionTemplateService; | |||||
import org.sonar.server.common.permission.PermissionUpdater; | |||||
import org.sonar.server.common.permission.UserPermissionChanger; | |||||
import org.sonar.server.permission.index.PermissionIndexer; | import org.sonar.server.permission.index.PermissionIndexer; | ||||
import org.sonar.server.permission.ws.PermissionsWsModule; | import org.sonar.server.permission.ws.PermissionsWsModule; | ||||
import org.sonar.server.platform.ClusterVerification; | import org.sonar.server.platform.ClusterVerification; | ||||
BitbucketServerRestClient.class, | BitbucketServerRestClient.class, | ||||
AzureDevOpsHttpClient.class, | AzureDevOpsHttpClient.class, | ||||
new AlmIntegrationsWSModule(), | new AlmIntegrationsWSModule(), | ||||
BitbucketCloudProjectCreatorFactory.class, | |||||
BitbucketCloudValidator.class, | BitbucketCloudValidator.class, | ||||
BitbucketCloudProjectCreatorFactory.class, | |||||
BitbucketServerProjectCreatorFactory.class, | |||||
BitbucketServerSettingsValidator.class, | BitbucketServerSettingsValidator.class, | ||||
GithubGlobalSettingsValidator.class, | GithubGlobalSettingsValidator.class, | ||||
GitlabHeaders.class, | GitlabHeaders.class, |