]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21819 DevopsProjectCreator for BitBucket server
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>
Tue, 26 Mar 2024 09:53:59 +0000 (10:53 +0100)
committersonartech <sonartech@sonarsource.com>
Thu, 28 Mar 2024 20:02:50 +0000 (20:02 +0000)
28 files changed:
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectDescriptor.java
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreator.java
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactory.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/package-info.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreator.java
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java
server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java
server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectRequest.java
server/sonar-webserver-common/src/main/java/org/sonar/server/common/project/ImportProjectService.java
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorFactoryTest.java
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketcloud/BitbucketCloudProjectCreatorTest.java
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorFactoryTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreatorTest.java [new file with mode: 0644]
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactoryTest.java
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorTest.java
server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java
server/sonar-webserver-common/src/test/java/org/sonar/server/common/project/ImportProjectServiceTest.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsController.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/projects/request/BoundProjectCreateRestRequest.java
server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/projects/controller/DefaultBoundProjectsControllerTest.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketcloud/ImportBitbucketCloudRepoAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/bitbucketserver/ImportBitbucketServerProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java

index 55ff967e7e5615e1c08a3463303f9c925c72bce5..2b8fa4b5a9887cd50e9fbe38a742baf3b05d04dd 100644 (file)
@@ -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) {
 }
index e7221d09565485e911a3502a5f217c8cb7453bb6..71facf8259279fb3efcb2b0e16e79ddcfaad4bf0 100644 (file)
@@ -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 (file)
index 0000000..ddfc6ee
--- /dev/null
@@ -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> 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());
+  }
+
+}
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 (file)
index 0000000..017a552
--- /dev/null
@@ -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<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));
+  }
+}
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 (file)
index 0000000..91b9cbc
--- /dev/null
@@ -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;
+
index 47902dd9c985547cbb96e70cc12e236103537d2f..3d946813b7fee303e8a3db7afa4240daf249f462 100644 (file)
@@ -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);
   }
index 2a1821c2165b0efad780e9df486d60f8b38f8c84..05fa562ed2eac94820ef96f7b6780f261725ab8f 100644 (file)
@@ -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<DevOpsProjectCreator> 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<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor);
 
     GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, appInstallationToken,
@@ -151,11 +151,11 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory
   private Optional<AppInstallationToken> 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();
index 2c530a0fc89cb69edfacc88838d16a5a4b999ca8..de267a716065952609b0512420ed05884ac0b699 100644 (file)
@@ -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()));
     }
   }
 
index 017ed88fc6a752eb59461a3892a504e44c8bae30..261216fcbf97fadc3e7e09151699b09c7cadc96a 100644 (file)
@@ -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,
index feaab35332b7ccf742ff9bd3932f6b69bbed2de0..e2b8f841f1290f3a12b21194a2dab015369a628e 100644 (file)
@@ -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())));
index 34e63761ef104c4530d4eb976f89fa08d6ea2a8b..1d623b2590291c1b3ecb28d1d1a78bf8927879f6 100644 (file)
@@ -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);
index 92d73e8f7ae7a7649d71fdf52019060d15c7ece2..61fde22b674cf7699d1f9bf7bdd0f0da4315cb3f 100644 (file)
@@ -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 (file)
index 0000000..afe4d81
--- /dev/null
@@ -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 (file)
index 0000000..b588828
--- /dev/null
@@ -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<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);
+  }
+}
index ac805ea74ab3a0beea52571dd7829df29a3062a4..29c2ec7d57db672a109b555a7ebdc21dfcdb1189 100644 (file)
@@ -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<String, String> 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);
index dcff559a270125997f7f1271f494b26939118362..705e1468349ceab38c357bd4581b63783614da94 100644 (file)
@@ -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());
index 7e945bc0589d502c323c3bc4f038956b24744ef6..c58fd39e64bebf3b262d57da128c4cd66be8bff6 100644 (file)
@@ -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);
   }
index 6fe106801ae8517613746456a3b066a968235de1..4bf728aee16a4972206529067114f20d9526a563 100644 (file)
@@ -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;
index 1ca3578199f339d4151d74eb3a53a00c8ab90a8e..3a19a48a37e554b3dcc687ee9258d66f8f78285d 100644 (file)
@@ -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) {
index 340c16744499133c5cb3425ed3302be18ecc3c96..74f8bdf2ef6725129786314282015b887217a55b 100644 (file)
@@ -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
index 58af5d025fa7da2c952c76564498c2f86b51fb1d..c776ad9db4ff6390e8036fb121e6edb851775f75 100644 (file)
@@ -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
index 4337d8d95eebb052bb1d4b04d3ae838b07c5d3ce..b0315679e7e487201061386bf7c4a9da435a3c63 100644 (file)
@@ -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
index ef5e940f5676235fb7d374d6507d5491b192fd67..71296d9b482bb05d7139f5729d4c96df41b038ea 100644 (file)
@@ -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));
index a639d46f0c2d3990584ac7bda8929186ae357004..d8f8ef235e2963817424dcd4a6ce1994a58be833 100644 (file)
@@ -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);
   }
 
 }
index 68641432c544f981f19b639762263e7814842ae9..3f40c2890d3eef30e6c311c5ce95daf7d71af181 100644 (file)
  */
 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.<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)
       .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<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);
   }
 
 }
index 8869870a2f04c1f201ca775b1fc46413311ac821..9aebb9f3397f9af917299213b311b834b731b943 100644 (file)
@@ -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);
   }
 }
index 94672feecdbdddce6b3c43102fd3edc949f581b8..8803e7a1b2940e565c04ecf7f189acba762068a3 100644 (file)
@@ -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);
   }
 }
index e7a8b4c24f9c9a22eed3e8da94f3065523d4bfff..a58f0f90fc8fe48f776931f7197b3f088e0c21cf 100644 (file)
@@ -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,