From e1bc9bf8ccea679e94098ea79f27483d7f900344 Mon Sep 17 00:00:00 2001 From: Antoine Vigneau Date: Tue, 26 Mar 2024 10:53:59 +0100 Subject: [PATCH] SONAR-21819 DevopsProjectCreator for BitBucket server --- .../almsettings/DevOpsProjectDescriptor.java | 2 +- .../BitbucketCloudProjectCreator.java | 2 +- .../BitbucketServerProjectCreator.java | 139 +++++++++++ .../BitbucketServerProjectCreatorFactory.java | 75 ++++++ .../bitbucketserver/package-info.java | 24 ++ .../github/GithubProjectCreator.java | 6 +- .../github/GithubProjectCreatorFactory.java | 10 +- .../gitlab/GitlabProjectCreator.java | 4 +- .../common/project/ImportProjectRequest.java | 3 +- .../common/project/ImportProjectService.java | 3 +- ...tbucketCloudProjectCreatorFactoryTest.java | 4 +- .../BitbucketCloudProjectCreatorTest.java | 2 +- ...bucketServerProjectCreatorFactoryTest.java | 78 +++++++ .../BitbucketServerProjectCreatorTest.java | 221 ++++++++++++++++++ .../GithubProjectCreatorFactoryTest.java | 15 +- .../github/GithubProjectCreatorTest.java | 10 +- .../gitlab/GitlabProjectCreatorTest.java | 2 +- .../project/ImportProjectServiceTest.java | 13 +- .../DefaultBoundProjectsController.java | 11 +- .../BoundProjectCreateRestRequest.java | 10 +- .../DefaultBoundProjectsControllerTest.java | 9 +- .../ImportBitbucketServerProjectActionIT.java | 31 ++- .../server/ce/queue/ReportSubmitterIT.java | 12 +- .../ImportBitbucketCloudRepoAction.java | 2 +- .../ImportBitbucketServerProjectAction.java | 137 ++--------- .../ws/github/ImportGithubProjectAction.java | 2 +- .../ws/gitlab/ImportGitLabProjectAction.java | 2 +- .../platformlevel/PlatformLevel4.java | 28 +-- 28 files changed, 661 insertions(+), 196 deletions(-) create mode 100644 server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java create mode 100644 server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactory.java create mode 100644 server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/package-info.java create mode 100644 server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactoryTest.java create mode 100644 server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorTest.java diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectDescriptor.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectDescriptor.java index 55ff967e7e5..2b8fa4b5a98 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectDescriptor.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectDescriptor.java @@ -22,5 +22,5 @@ package org.sonar.server.common.almsettings; import javax.annotation.Nullable; 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) { } diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreator.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreator.java index e7221d09565..71facf82592 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreator.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreator.java @@ -76,7 +76,7 @@ public class BitbucketCloudProjectCreator implements DevOpsProjectCreator { 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()); + Repository repo = bitbucketCloudRestClient.getRepo(pat, workspace, devOpsProjectDescriptor.repositoryIdentifier()); ComponentCreationData componentCreationData = projectCreator.createProject( dbSession, diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java new file mode 100644 index 00000000000..ddfc6ee829d --- /dev/null +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java @@ -0,0 +1,139 @@ +/* + * 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 = 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 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()); + } + +} diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactory.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactory.java new file mode 100644 index 00000000000..017a552b0d8 --- /dev/null +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactory.java @@ -0,0 +1,75 @@ +/* + * 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 getDevOpsProjectCreator(DbSession dbSession, Map characteristics) { + return Optional.empty(); + } + + @Override + public Optional 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)); + } +} diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/package-info.java new file mode 100644 index 00000000000..91b9cbc6e86 --- /dev/null +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/package-info.java @@ -0,0 +1,24 @@ +/* + * 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; + diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreator.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreator.java index 47902dd9c98..3d946813b7f 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreator.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreator.java @@ -104,7 +104,7 @@ public class GithubProjectCreator implements DevOpsProjectCreator { public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() { 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 repository = orgaAndRepoTokenified[1]; @@ -161,9 +161,9 @@ public class GithubProjectCreator implements DevOpsProjectCreator { public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, Boolean monorepo, @Nullable String projectKey, @Nullable String projectName) { 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( - 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); } diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java index 2a1821c2165..05fa562ed2e 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java @@ -94,7 +94,7 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory if (githubApiUrl == null || githubRepository == null) { 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() .filter(almSettingDto -> devOpsProjectDescriptor.url().equals(almSettingDto.getUrl())) @@ -107,14 +107,14 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory private Optional findInstallationIdAndCreateDevOpsProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto) { GithubAppConfiguration githubAppConfiguration = githubGlobalSettingsValidator.validate(almSettingDto); - return findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier()) + return findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.repositoryIdentifier()) .map(installationId -> generateAppInstallationToken(githubAppConfiguration, installationId)) .map(appInstallationToken -> createGithubProjectCreator(devOpsProjectDescriptor, almSettingDto, appInstallationToken)); } private GithubProjectCreator createGithubProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto, 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 authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor); GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, appInstallationToken, @@ -151,11 +151,11 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory private Optional getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) { if (gitHubSettings.isProvisioningEnabled()) { 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", 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.", - devOpsProjectDescriptor.projectIdentifier()))); + devOpsProjectDescriptor.repositoryIdentifier()))); return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId)); } return Optional.empty(); diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java index 2c530a0fc89..de267a71606 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java @@ -101,9 +101,9 @@ public class GitlabProjectCreator implements DevOpsProjectCreator { private Long getGitlabProjectId() { try { - return Long.parseLong(devOpsProjectDescriptor.projectIdentifier()); + return Long.parseLong(devOpsProjectDescriptor.repositoryIdentifier()); } 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())); } } diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectRequest.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectRequest.java index 017ed88fc6a..261216fcbf9 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectRequest.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectRequest.java @@ -24,12 +24,13 @@ import javax.annotation.Nullable; public record ImportProjectRequest( @Nullable String projectKey, - @Nullable String projectName, String almSettingId, String repositoryIdentifier, @Nullable + String projectIdentifier, + @Nullable String newCodeDefinitionType, @Nullable String newCodeDefinitionValue, diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectService.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectService.java index feaab35332b..e2b8f841f12 100644 --- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectService.java +++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectService.java @@ -62,7 +62,8 @@ public class ImportProjectService { try (DbSession dbSession = dbClient.openSession(false)) { checkNewCodeDefinitionParam(request.newCodeDefinitionType(), request.newCodeDefinitionValue()); 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) .orElseThrow(() -> new IllegalArgumentException(format("Platform %s not supported", almSetting.getAlm().name()))); diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorFactoryTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorFactoryTest.java index 34e63761ef1..1d623b25902 100644 --- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorFactoryTest.java +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorFactoryTest.java @@ -59,7 +59,7 @@ class BitbucketCloudProjectCreatorFactoryTest { void getDevOpsProjectCreator_whenAlmIsNotBitbucketCloud_shouldReturnEmpty() { AlmSettingDto almSettingDto = mock(AlmSettingDto.class); 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(); } @@ -68,7 +68,7 @@ class BitbucketCloudProjectCreatorFactoryTest { 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"); + DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.BITBUCKET_CLOUD, null, "bitbucket_repo", "bitbucket_project"); DevOpsProjectCreator expectedProjectCreator = new BitbucketCloudProjectCreator(dbClient, almSettingDto, devOpsProjectDescriptor, userSession, bitbucketCloudRestClient, projectCreator, projectKeyGenerator); diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorTest.java index 92d73e8f7ae..61fde22b674 100644 --- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorTest.java +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorTest.java @@ -93,7 +93,7 @@ class BitbucketCloudProjectCreatorTest { 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.repositoryIdentifier()).thenReturn(REPOSITORY_SLUG); lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.BITBUCKET_CLOUD); } diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactoryTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactoryTest.java new file mode 100644 index 00000000000..afe4d816a4f --- /dev/null +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactoryTest.java @@ -0,0 +1,78 @@ +/* + * 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); + } +} diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorTest.java new file mode 100644 index 00000000000..b5888283c02 --- /dev/null +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorTest.java @@ -0,0 +1,221 @@ +/* + * 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 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 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); + } +} diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactoryTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactoryTest.java index ac805ea74ab..29c2ec7d57d 100644 --- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactoryTest.java +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactoryTest.java @@ -43,16 +43,13 @@ 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.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.management.ManagedProjectService; 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.common.project.ProjectCreator; import org.sonar.server.user.UserSession; import static java.lang.String.format; @@ -74,10 +71,10 @@ public class GithubProjectCreatorFactoryTest { 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 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 VALID_GITHUB_PROJECT_COORDINATES = Map.of( 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 String USER_ACCESS_TOKEN = "userPat"; @@ -243,7 +240,7 @@ public class GithubProjectCreatorFactoryTest { } 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; GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken, authAppInstallToken); diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorTest.java index dcff559a270..705e1468349 100644 --- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorTest.java +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorTest.java @@ -87,7 +87,7 @@ class GithubProjectCreatorTest { private static final String REPOSITORY_NAME = "repo1"; 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 USER_LOGIN = "userLogin"; private static final String USER_UUID = "userUuid"; @@ -427,11 +427,11 @@ class GithubProjectCreatorTest { GithubApplicationClient.Repository repository = mock(); when(repository.getDefaultBranch()).thenReturn(MAIN_BRANCH_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); - 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)); - when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier()); + when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier()); return repository; } @@ -468,7 +468,7 @@ class GithubProjectCreatorTest { } 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.getAlmSettingUuid()).isEqualTo(almSettingDto.getUuid()); assertThat(projectAlmSettingDto.getProjectUuid()).isEqualTo(projectDto.getUuid()); diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java index 7e945bc0589..c58fd39e64b 100644 --- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java @@ -108,7 +108,7 @@ class GitlabProjectCreatorTest { lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY); 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.alm()).thenReturn(ALM.GITLAB); } diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/project/ImportProjectServiceTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/project/ImportProjectServiceTest.java index 6fe106801ae..4bf728aee16 100644 --- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/project/ImportProjectServiceTest.java +++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/project/ImportProjectServiceTest.java @@ -52,7 +52,8 @@ class ImportProjectServiceTest { private static final String API_URL = "https://api.com"; 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_NAME = "project-name"; private static final String MAIN_BRANCH_UUID = "main-branch-uuid"; @@ -77,7 +78,7 @@ class ImportProjectServiceTest { DbSession dbSession = mockDbSession(); 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)) .isInstanceOf(IllegalArgumentException.class) @@ -95,7 +96,7 @@ class ImportProjectServiceTest { when(devOpsProjectCreatorFactory.getDevOpsProjectCreator(eq(almSetting), any())) .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)) .isInstanceOf(IllegalArgumentException.class) @@ -117,7 +118,7 @@ class ImportProjectServiceTest { 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); @@ -142,7 +143,7 @@ class ImportProjectServiceTest { 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); @@ -175,7 +176,7 @@ class ImportProjectServiceTest { private DevOpsProjectCreator mockDevOpsProjectCreator(AlmSettingDto almSetting) { 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)) .thenReturn(Optional.of(devOpsProjectCreator)); return devOpsProjectCreator; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsController.java index 1ca3578199f..3a19a48a37e 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsController.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsController.java @@ -48,8 +48,15 @@ public class DefaultBoundProjectsController implements BoundProjectsController { } 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) { diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/request/BoundProjectCreateRestRequest.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/request/BoundProjectCreateRestRequest.java index 340c1674449..74f8bdf2ef6 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/request/BoundProjectCreateRestRequest.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/request/BoundProjectCreateRestRequest.java @@ -42,11 +42,19 @@ public record BoundProjectCreateRestRequest( @Schema( description = """ 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 """) 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 @Schema(description = """ Project New Code Definition Type diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsControllerTest.java index 58af5d025fa..c776ad9db4f 100644 --- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsControllerTest.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsControllerTest.java @@ -44,7 +44,8 @@ class DefaultBoundProjectsControllerTest { private static final String PROJECT_UUID = "project-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_NAME = "project-name"; private static final String ALM_SETTING_ID = "alm-setting-id"; @@ -117,7 +118,8 @@ class DefaultBoundProjectsControllerTest { PROJECT_KEY, PROJECT_NAME, ALM_SETTING_ID, - REPOSITORY_ID, + DOP_REPOSITORY_ID, + DOP_PROJECT_ID, "NUMBER_OF_DAYS", "10", true))) @@ -133,7 +135,8 @@ class DefaultBoundProjectsControllerTest { "projectKey": "project-key", "projectName": "project-name", "devOpsPlatformSettingId": "alm-setting-id", - "repositoryIdentifier": "repository-id", + "repositoryIdentifier": "dop-repository-id", + "projectIdentifier": "dop-project-id", "newCodeDefinitionType": "NUMBER_OF_DAYS", "newCodeDefinitionValue": "10", "monorepo": true diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionIT.java index 4337d8d95ee..b0315679e7e 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionIT.java @@ -48,10 +48,14 @@ import org.sonar.db.project.ProjectDto; import org.sonar.db.user.UserDto; import org.sonar.server.almintegration.ws.ImportHelper; 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.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.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; @@ -68,6 +72,7 @@ import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.Projects; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; @@ -110,8 +115,13 @@ public class ImportBitbucketServerProjectActionIT { private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession); 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; @@ -350,15 +360,18 @@ public class ImportBitbucketServerProjectActionIT { public void check_pat_is_missing() { UserDto user = db.users().insertUser(); 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) - .hasMessage("personal access token for '" + almSetting.getKey() + "' is missing"); + .hasMessage(format("personal access token for '%s' is missing", almSetting.getKey())); } @Test diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java index ef5e940f567..71296d9b482 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java @@ -27,10 +27,10 @@ import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.sonar.auth.github.client.GithubApplicationClient; import org.sonar.alm.client.github.GithubGlobalSettingsValidator; import org.sonar.api.utils.System2; import org.sonar.auth.github.GitHubSettings; +import org.sonar.auth.github.client.GithubApplicationClient; import org.sonar.ce.queue.CeQueue; import org.sonar.ce.queue.CeQueueImpl; import org.sonar.ce.queue.CeTaskSubmit; @@ -57,6 +57,10 @@ import org.sonar.server.common.almsettings.github.GithubProjectCreationParameter import org.sonar.server.common.almsettings.github.GithubProjectCreator; import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory; 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.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; @@ -65,13 +69,9 @@ import org.sonar.server.management.ManagedInstanceService; import org.sonar.server.management.ManagedProjectService; import org.sonar.server.permission.PermissionService; 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.ProjectDefaultVisibility; import org.sonar.server.project.Visibility; -import org.sonar.server.common.project.ProjectCreator; import org.sonar.server.tester.UserSessionRule; import static java.lang.String.format; @@ -393,7 +393,7 @@ public class ReportSubmitterIT { 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); DevOpsProjectCreator devOpsProjectCreator = spy(new GithubProjectCreator(db.getDbClient(), githubApplicationClient, null, projectKeyGenerator, permissionUpdater, permissionService, managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings)); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoAction.java index a639d46f0c2..d8f8ef235e2 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoAction.java @@ -107,7 +107,7 @@ public class ImportBitbucketCloudRepoAction implements AlmIntegrationsWsAction { 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); + return new ImportProjectRequest(null, null, almSettingDto.getUuid(), slug, null, newCodeDefinitionType, newCodeDefinitionValue, false); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java index 68641432c54..3f40c2890d3 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java @@ -19,48 +19,25 @@ */ package org.sonar.server.almintegration.ws.bitbucketserver; -import java.util.Optional; import javax.annotation.Nullable; 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.Request; import org.sonar.api.server.ws.Response; 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.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.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 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.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_VALUE_DESCRIPTION_PROJECT_CREATION; -import static org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; 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_VALUE; @@ -70,40 +47,21 @@ public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsActi private static final String PARAM_PROJECT_KEY = "projectKey"; 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 ProjectKeyGenerator projectKeyGenerator; - - private final NewCodeDefinitionResolver newCodeDefinitionResolver; - - private final DefaultBranchNameResolver defaultBranchNameResolver; + private final ImportProjectService importProjectService; @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.projectKeyGenerator = projectKeyGenerator; - this.newCodeDefinitionResolver = newCodeDefinitionResolver; - this.defaultBranchNameResolver = defaultBranchNameResolver; + this.importProjectService = importProjectService; } @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction("import_bitbucketserver_project") .setDescription("Create a SonarQube project with the information from the provided BitbucketServer project.
" + - "Autoconfigure pull request decoration mechanism.
" + - "Requires the 'Create Projects' permission") + "Autoconfigure pull request decoration mechanism.
" + + "Requires the 'Create Projects' permission") .setPost(true) .setSince("8.2") .setHandler(this) @@ -149,81 +107,18 @@ public class ImportBitbucketServerProjectAction implements AlmIntegrationsWsActi String newCodeDefinitionType = request.param(PARAM_NEW_CODE_DEFINITION_TYPE); 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 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 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); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java index 8869870a2f0..9aebb9f3397 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java @@ -109,6 +109,6 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction { private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String githubRepositoryKey, @Nullable String newCodeDefinitionType, @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); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java index 94672feecdb..8803e7a1b29 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java @@ -102,6 +102,6 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { private static ImportProjectRequest toServiceRequest(AlmSettingDto almSettingDto, String gitlabProjectId, @Nullable String newCodeDefinitionType, @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); } } diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index e7a8b4c24f9..a58f0f90fc8 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -63,14 +63,9 @@ import org.sonar.core.platform.SpringComponentContainer; import org.sonar.server.almintegration.ws.AlmIntegrationsWSModule; import org.sonar.server.almintegration.ws.CredentialsEncoderHelper; 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.almsettings.MultipleAlmFeature; 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.DefaultAdminCredentialsVerifierImpl; import org.sonar.server.authentication.DefaultAdminCredentialsVerifierNotificationHandler; @@ -84,16 +79,28 @@ import org.sonar.server.branch.ws.BranchWsModule; import org.sonar.server.ce.CeModule; import org.sonar.server.ce.projectdump.ProjectExportWsModule; 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.group.service.GroupMembershipService; 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.service.RuleService; import org.sonar.server.common.text.MacroInterpreter; import org.sonar.server.component.ComponentCleanerService; import org.sonar.server.component.ComponentFinder; 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.ComponentIndexDefinition; import org.sonar.server.component.index.EntityDefinitionIndexer; @@ -160,15 +167,9 @@ import org.sonar.server.monitoring.devops.AzureMetricsTask; import org.sonar.server.monitoring.devops.BitbucketMetricsTask; import org.sonar.server.monitoring.devops.GithubMetricsTask; 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.notification.NotificationModule; 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.ws.PermissionsWsModule; import org.sonar.server.platform.ClusterVerification; @@ -574,8 +575,9 @@ public class PlatformLevel4 extends PlatformLevel { BitbucketServerRestClient.class, AzureDevOpsHttpClient.class, new AlmIntegrationsWSModule(), - BitbucketCloudProjectCreatorFactory.class, BitbucketCloudValidator.class, + BitbucketCloudProjectCreatorFactory.class, + BitbucketServerProjectCreatorFactory.class, BitbucketServerSettingsValidator.class, GithubGlobalSettingsValidator.class, GitlabHeaders.class, -- 2.39.5