aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-common
diff options
context:
space:
mode:
authorAurelien Poscia <aurelien.poscia@sonarsource.com>2024-07-19 13:42:04 +0200
committersonartech <sonartech@sonarsource.com>2024-07-23 20:02:45 +0000
commit04bd7628152bc52451c9ac593a2cd1398b44f91b (patch)
treed8b8ce3bd598527342c954bb7226b93b761be7be /server/sonar-webserver-common
parent3e515e1d04424e962ef4d798c2820013e5facfae (diff)
downloadsonarqube-04bd7628152bc52451c9ac593a2cd1398b44f91b.tar.gz
sonarqube-04bd7628152bc52451c9ac593a2cd1398b44f91b.zip
SONAR-22557 Synchronize projects visiblity upon GitLab onboarding
Diffstat (limited to 'server/sonar-webserver-common')
-rw-r--r--server/sonar-webserver-common/build.gradle1
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreator.java137
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContext.java (renamed from server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreationParameters.java)10
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContextService.java28
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextService.java80
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreator.java141
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java59
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextService.java99
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java143
-rw-r--r--server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactory.java45
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreatorTest.java343
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextServiceTest.java139
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactoryTest.java23
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorTest.java265
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextServiceTest.java167
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactoryTest.java31
-rw-r--r--server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java209
17 files changed, 1127 insertions, 793 deletions
diff --git a/server/sonar-webserver-common/build.gradle b/server/sonar-webserver-common/build.gradle
index 4764b249bc0..38b5f22e1ab 100644
--- a/server/sonar-webserver-common/build.gradle
+++ b/server/sonar-webserver-common/build.gradle
@@ -25,6 +25,7 @@ dependencies {
testImplementation 'junit:junit'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.mockito:mockito-junit-jupiter'
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreator.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreator.java
new file mode 100644
index 00000000000..e6a8015677b
--- /dev/null
+++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreator.java
@@ -0,0 +1,137 @@
+/*
+ * 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;
+
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import org.jetbrains.annotations.Nullable;
+import org.sonar.api.web.UserRole;
+import org.sonar.auth.DevOpsPlatformSettings;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+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.CreationMethod;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.user.UserIdDto;
+import org.sonar.server.common.almintegration.ProjectKeyGenerator;
+import org.sonar.server.common.permission.Operation;
+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.component.ComponentCreationData;
+import org.sonar.server.management.ManagedProjectService;
+import org.sonar.server.permission.PermissionService;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Objects.requireNonNull;
+
+public class DefaultDevOpsProjectCreator implements DevOpsProjectCreator {
+
+ protected final DbClient dbClient;
+ protected final ProjectKeyGenerator projectKeyGenerator;
+ protected final DevOpsPlatformSettings devOpsPlatformSettings;
+ protected final ProjectCreator projectCreator;
+ protected final PermissionService permissionService;
+ protected final PermissionUpdater<UserPermissionChange> permissionUpdater;
+ protected final ManagedProjectService managedProjectService;
+ protected final DevOpsProjectCreationContext devOpsProjectCreationContext;
+
+ public DefaultDevOpsProjectCreator(DbClient dbClient, DevOpsProjectCreationContext devOpsProjectCreationContext, ProjectKeyGenerator projectKeyGenerator,
+ DevOpsPlatformSettings devOpsPlatformSettings, ProjectCreator projectCreator, PermissionService permissionService, PermissionUpdater<UserPermissionChange> permissionUpdater,
+ ManagedProjectService managedProjectService) {
+ this.dbClient = dbClient;
+ this.projectKeyGenerator = projectKeyGenerator;
+ this.devOpsPlatformSettings = devOpsPlatformSettings;
+ this.projectCreator = projectCreator;
+ this.permissionService = permissionService;
+ this.permissionUpdater = permissionUpdater;
+ this.managedProjectService = managedProjectService;
+ this.devOpsProjectCreationContext = devOpsProjectCreationContext;
+ }
+
+ @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 key = Optional.ofNullable(projectKey).orElse(generateUniqueProjectKey());
+ boolean isManaged = devOpsPlatformSettings.isProvisioningEnabled();
+ Boolean shouldProjectBePrivate = shouldProjectBePrivate(devOpsProjectCreationContext.isPublic());
+
+ ComponentCreationData componentCreationData = projectCreator.createProject(dbSession, key, getProjectName(projectName),
+ devOpsProjectCreationContext.defaultBranchName(), creationMethod, shouldProjectBePrivate, isManaged);
+ ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow();
+
+ createProjectAlmSettingDto(dbSession, projectDto, devOpsProjectCreationContext.almSettingDto(), monorepo);
+ addScanPermissionToCurrentUser(dbSession, projectDto);
+
+ BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow();
+ if (isManaged) {
+ syncProjectPermissionsWithDevOpsPlatform(projectDto, mainBranchDto);
+ }
+ return componentCreationData;
+ }
+
+ @CheckForNull
+ private Boolean shouldProjectBePrivate(boolean isPublic) {
+ if (devOpsPlatformSettings.isProvisioningEnabled() && devOpsPlatformSettings.isProjectVisibilitySynchronizationActivated()) {
+ return !isPublic;
+ } else if (devOpsPlatformSettings.isProvisioningEnabled()) {
+ return true;
+ }
+ return null;
+ }
+
+ private String getProjectName(@Nullable String projectName) {
+ return Optional.ofNullable(projectName).orElse(devOpsProjectCreationContext.name());
+ }
+
+ private String generateUniqueProjectKey() {
+ return projectKeyGenerator.generateUniqueProjectKey(devOpsProjectCreationContext.fullName());
+ }
+
+ private void createProjectAlmSettingDto(DbSession dbSession, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) {
+ ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto()
+ .setAlmSettingUuid(almSettingDto.getUuid())
+ .setAlmRepo(devOpsProjectCreationContext.devOpsPlatformIdentifier())
+ .setProjectUuid(projectDto.getUuid())
+ .setSummaryCommentEnabled(true)
+ .setMonorepo(monorepo);
+ dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), projectDto.getName(), projectDto.getKey());
+ }
+
+ private void addScanPermissionToCurrentUser(DbSession dbSession, ProjectDto projectDto) {
+ UserSession userSession = devOpsProjectCreationContext.userSession();
+ UserIdDto userId = new UserIdDto(requireNonNull(userSession.getUuid()), requireNonNull(userSession.getLogin()));
+ UserPermissionChange scanPermission = new UserPermissionChange(Operation.ADD, UserRole.SCAN, projectDto, userId, permissionService);
+ permissionUpdater.apply(dbSession, Set.of(scanPermission));
+ }
+
+ private void syncProjectPermissionsWithDevOpsPlatform(ProjectDto projectDto, BranchDto mainBranchDto) {
+ String userUuid = requireNonNull(devOpsProjectCreationContext.userSession().getUuid());
+ managedProjectService.queuePermissionSyncTask(userUuid, mainBranchDto.getUuid(), projectDto.getUuid());
+ }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreationParameters.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContext.java
index 72b2ca87602..1530eb1e7c0 100644
--- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreationParameters.java
+++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContext.java
@@ -17,16 +17,12 @@
* 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.github;
+package org.sonar.server.common.almsettings;
import javax.annotation.Nullable;
-import org.sonar.auth.github.AppInstallationToken;
-import org.sonar.auth.github.security.AccessToken;
import org.sonar.db.alm.setting.AlmSettingDto;
-import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
import org.sonar.server.user.UserSession;
-public record GithubProjectCreationParameters(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto, UserSession userSession,
- AccessToken devOpsAppInstallationToken,
- @Nullable AppInstallationToken authAppInstallationToken) {
+public record DevOpsProjectCreationContext(String name, String fullName, String devOpsPlatformIdentifier, boolean isPublic, @Nullable String defaultBranchName,
+ AlmSettingDto almSettingDto, UserSession userSession) {
}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContextService.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContextService.java
new file mode 100644
index 00000000000..e86f91df853
--- /dev/null
+++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/DevOpsProjectCreationContextService.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+public interface DevOpsProjectCreationContextService {
+
+ DevOpsProjectCreationContext create(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor);
+
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextService.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextService.java
new file mode 100644
index 00000000000..db83c9b7ae7
--- /dev/null
+++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextService.java
@@ -0,0 +1,80 @@
+/*
+ * 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.github;
+
+import org.sonar.api.server.ServerSide;
+import org.sonar.auth.github.client.GithubApplicationClient;
+import org.sonar.auth.github.security.AccessToken;
+import org.sonar.auth.github.security.UserAccessToken;
+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.server.common.almsettings.DevOpsProjectCreationContext;
+import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContextService;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Objects.requireNonNull;
+
+@ServerSide
+public class GithubDevOpsProjectCreationContextService implements DevOpsProjectCreationContextService {
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final GithubApplicationClient githubApplicationClient;
+
+ public GithubDevOpsProjectCreationContextService(DbClient dbClient, UserSession userSession, GithubApplicationClient githubApplicationClient) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.githubApplicationClient = githubApplicationClient;
+ }
+
+ @Override
+ public DevOpsProjectCreationContext create(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor) {
+ AccessToken accessToken = getAccessToken(almSettingDto);
+ return createDevOpsProject(almSettingDto, devOpsProjectDescriptor, accessToken);
+ }
+
+ private AccessToken getAccessToken(AlmSettingDto almSettingDto) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ String userUuid = requireNonNull(userSession.getUuid(), "User UUID cannot be null.");
+ return dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto)
+ .map(AlmPatDto::getPersonalAccessToken)
+ .map(UserAccessToken::new)
+ .orElseThrow(() -> new IllegalArgumentException("No personal access token found"));
+ }
+ }
+
+ public DevOpsProjectCreationContext createDevOpsProject(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor, AccessToken accessToken) {
+ GithubApplicationClient.Repository repository = fetchRepository(almSettingDto, devOpsProjectDescriptor, accessToken);
+ return new DevOpsProjectCreationContext(repository.getName(), repository.getFullName(), repository.getFullName(),
+ !repository.isPrivate(), repository.getDefaultBranch(), almSettingDto, userSession);
+ }
+
+ private GithubApplicationClient.Repository fetchRepository(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor, AccessToken accessToken) {
+ String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null");
+ return githubApplicationClient.getRepository(url, accessToken, devOpsProjectDescriptor.repositoryIdentifier())
+ .orElseThrow(() -> new IllegalStateException(
+ String.format("Impossible to find the repository '%s' on GitHub, using the devops config %s", devOpsProjectDescriptor.repositoryIdentifier(), almSettingDto.getKey())));
+ }
+
+
+}
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 3d946813b7f..90890b477fc 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
@@ -19,38 +19,25 @@
*/
package org.sonar.server.common.almsettings.github;
-import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
import org.sonar.alm.client.github.GithubPermissionConverter;
import org.sonar.api.web.UserRole;
+import org.sonar.auth.DevOpsPlatformSettings;
import org.sonar.auth.github.AppInstallationToken;
-import org.sonar.auth.github.GitHubSettings;
import org.sonar.auth.github.GsonRepositoryCollaborator;
import org.sonar.auth.github.GsonRepositoryPermissions;
import org.sonar.auth.github.GsonRepositoryTeam;
import org.sonar.auth.github.client.GithubApplicationClient;
-import org.sonar.auth.github.client.GithubApplicationClient.Repository;
-import org.sonar.auth.github.security.AccessToken;
import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-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.CreationMethod;
-import org.sonar.db.project.ProjectDto;
import org.sonar.db.provisioning.GithubPermissionsMappingDto;
import org.sonar.db.user.GroupDto;
-import org.sonar.db.user.UserIdDto;
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.permission.Operation;
+import org.sonar.server.common.almsettings.DefaultDevOpsProjectCreator;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
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.component.ComponentCreationData;
import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.user.UserSession;
@@ -59,52 +46,31 @@ import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
import static org.sonar.api.utils.Preconditions.checkState;
-public class GithubProjectCreator implements DevOpsProjectCreator {
+public class GithubProjectCreator extends DefaultDevOpsProjectCreator {
- private final DbClient dbClient;
private final GithubApplicationClient githubApplicationClient;
private final GithubPermissionConverter githubPermissionConverter;
- private final ProjectKeyGenerator projectKeyGenerator;
- private final PermissionUpdater<UserPermissionChange> permissionUpdater;
- private final PermissionService permissionService;
- private final ManagedProjectService managedProjectService;
- private final ProjectCreator projectCreator;
- private final GithubProjectCreationParameters githubProjectCreationParameters;
- private final DevOpsProjectDescriptor devOpsProjectDescriptor;
- private final UserSession userSession;
- private final AlmSettingDto almSettingDto;
- private final AccessToken devOpsAppInstallationToken;
- private final GitHubSettings gitHubSettings;
@CheckForNull
private final AppInstallationToken authAppInstallationToken;
- public GithubProjectCreator(DbClient dbClient, GithubApplicationClient githubApplicationClient, GithubPermissionConverter githubPermissionConverter,
- ProjectKeyGenerator projectKeyGenerator, PermissionUpdater<UserPermissionChange> permissionUpdater, PermissionService permissionService,
- ManagedProjectService managedProjectService, ProjectCreator projectCreator, GithubProjectCreationParameters githubProjectCreationParameters, GitHubSettings gitHubSettings) {
-
- this.dbClient = dbClient;
+ public GithubProjectCreator(DbClient dbClient, DevOpsProjectCreationContext devOpsProjectCreationContext,
+ ProjectKeyGenerator projectKeyGenerator,
+ DevOpsPlatformSettings devOpsPlatformSettings, ProjectCreator projectCreator, PermissionService permissionService, PermissionUpdater<UserPermissionChange> permissionUpdater,
+ ManagedProjectService managedProjectService, GithubApplicationClient githubApplicationClient, GithubPermissionConverter githubPermissionConverter,
+ @CheckForNull AppInstallationToken authAppInstallationToken) {
+ super(dbClient, devOpsProjectCreationContext, projectKeyGenerator, devOpsPlatformSettings, projectCreator, permissionService, permissionUpdater,
+ managedProjectService);
this.githubApplicationClient = githubApplicationClient;
this.githubPermissionConverter = githubPermissionConverter;
- this.projectKeyGenerator = projectKeyGenerator;
- this.permissionUpdater = permissionUpdater;
- this.permissionService = permissionService;
- this.managedProjectService = managedProjectService;
- this.projectCreator = projectCreator;
- this.githubProjectCreationParameters = githubProjectCreationParameters;
- userSession = githubProjectCreationParameters.userSession();
- almSettingDto = githubProjectCreationParameters.almSettingDto();
- devOpsProjectDescriptor = githubProjectCreationParameters.devOpsProjectDescriptor();
- devOpsAppInstallationToken = githubProjectCreationParameters.devOpsAppInstallationToken();
- authAppInstallationToken = githubProjectCreationParameters.authAppInstallationToken();
- this.gitHubSettings = gitHubSettings;
+ this.authAppInstallationToken = authAppInstallationToken;
}
@Override
public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() {
- checkState(githubProjectCreationParameters.authAppInstallationToken() != null, "An auth app token is required in case repository permissions checking is necessary.");
+ checkState(authAppInstallationToken != null, "An auth app token is required in case repository permissions checking is necessary.");
- String[] orgaAndRepoTokenified = devOpsProjectDescriptor.repositoryIdentifier().split("/");
+ String[] orgaAndRepoTokenified = devOpsProjectCreationContext.fullName().split("/");
String organization = orgaAndRepoTokenified[0];
String repository = orgaAndRepoTokenified[1];
@@ -118,9 +84,10 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
}
private boolean doesUserHaveScanPermission(String organization, String repository, Set<GithubPermissionsMappingDto> permissionsMappingDtos) {
- Set<GsonRepositoryCollaborator> repositoryCollaborators = githubApplicationClient.getRepositoryCollaborators(devOpsProjectDescriptor.url(), authAppInstallationToken,
- organization, repository);
+ String url = requireNonNull(devOpsProjectCreationContext.almSettingDto().getUrl(), "GitHub url not defined");
+ Set<GsonRepositoryCollaborator> repositoryCollaborators = githubApplicationClient.getRepositoryCollaborators(url, authAppInstallationToken, organization, repository);
+ UserSession userSession = devOpsProjectCreationContext.userSession();
String externalLogin = userSession.getExternalIdentity().map(UserSession.ExternalIdentity::login).orElse(null);
if (externalLogin == null) {
return false;
@@ -134,7 +101,8 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
private boolean doesUserBelongToAGroupWithScanPermission(String organization, String repository,
Set<GithubPermissionsMappingDto> permissionsMappingDtos) {
- Set<GsonRepositoryTeam> repositoryTeams = githubApplicationClient.getRepositoryTeams(devOpsProjectDescriptor.url(), authAppInstallationToken, organization, repository);
+ String url = requireNonNull(devOpsProjectCreationContext.almSettingDto().getUrl(), "GitHub url not defined");
+ Set<GsonRepositoryTeam> repositoryTeams = githubApplicationClient.getRepositoryTeams(url, authAppInstallationToken, organization, repository);
Set<String> groupsOfUser = findUserMembershipOnSonarQube(organization);
return repositoryTeams.stream()
@@ -144,7 +112,7 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
}
private Set<String> findUserMembershipOnSonarQube(String organization) {
- return userSession.getGroups().stream()
+ return devOpsProjectCreationContext.userSession().getGroups().stream()
.map(GroupDto::getName)
.filter(groupName -> groupName.contains("/"))
.map(name -> name.replaceFirst(organization + "/", ""))
@@ -157,73 +125,4 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
return sonarqubePermissions.contains(UserRole.SCAN);
}
- @Override
- 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.repositoryIdentifier())
- .orElseThrow(() -> new IllegalStateException(
- 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);
- }
-
- private ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, Boolean monorepo, @Nullable String projectKey, @Nullable String projectName,
- AlmSettingDto almSettingDto,
- Repository repository, CreationMethod creationMethod) {
- String key = Optional.ofNullable(projectKey).orElse(getUniqueProjectKey(repository));
-
- boolean isManaged = gitHubSettings.isProvisioningEnabled();
-
- ComponentCreationData componentCreationData = projectCreator.createProject(dbSession, key, Optional.ofNullable(projectName).orElse(repository.getName()),
- repository.getDefaultBranch(), creationMethod,
- shouldProjectBePrivate(repository), isManaged);
- ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow();
- createProjectAlmSettingDto(dbSession, repository, projectDto, almSettingDto, monorepo);
- addScanPermissionToCurrentUser(dbSession, projectDto);
-
- BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow();
- if (gitHubSettings.isProvisioningEnabled()) {
- syncProjectPermissionsWithGithub(projectDto, mainBranchDto);
- }
- return componentCreationData;
- }
-
- @CheckForNull
- private Boolean shouldProjectBePrivate(Repository repository) {
- if (gitHubSettings.isProvisioningEnabled() && gitHubSettings.isProjectVisibilitySynchronizationActivated()) {
- return repository.isPrivate();
- } else if (gitHubSettings.isProvisioningEnabled()) {
- return true;
- } else {
- return null;
- }
- }
-
- private void addScanPermissionToCurrentUser(DbSession dbSession, ProjectDto projectDto) {
- UserIdDto userId = new UserIdDto(requireNonNull(userSession.getUuid()), requireNonNull(userSession.getLogin()));
- UserPermissionChange scanPermission = new UserPermissionChange(Operation.ADD, UserRole.SCAN, projectDto, userId, permissionService);
- permissionUpdater.apply(dbSession, Set.of(scanPermission));
- }
-
- private void syncProjectPermissionsWithGithub(ProjectDto projectDto, BranchDto mainBranchDto) {
- String userUuid = requireNonNull(userSession.getUuid());
- managedProjectService.queuePermissionSyncTask(userUuid, mainBranchDto.getUuid(), projectDto.getUuid());
- }
-
- private String getUniqueProjectKey(Repository repository) {
- return projectKeyGenerator.generateUniqueProjectKey(repository.getFullName());
- }
-
- private void createProjectAlmSettingDto(DbSession dbSession, Repository repo, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) {
- ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto()
- .setAlmSettingUuid(almSettingDto.getUuid())
- .setAlmRepo(repo.getFullName())
- .setAlmSlug(null)
- .setProjectUuid(projectDto.getUuid())
- .setSummaryCommentEnabled(true)
- .setMonorepo(monorepo);
- 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/github/GithubProjectCreatorFactory.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubProjectCreatorFactory.java
index 05fa562ed2e..59c4c54a8ae 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
@@ -30,14 +30,13 @@ import org.sonar.auth.github.AppInstallationToken;
import org.sonar.auth.github.GitHubSettings;
import org.sonar.auth.github.GithubAppConfiguration;
import org.sonar.auth.github.client.GithubApplicationClient;
-import org.sonar.auth.github.security.AccessToken;
-import org.sonar.auth.github.security.UserAccessToken;
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.server.common.almintegration.ProjectKeyGenerator;
+import org.sonar.server.common.almsettings.DefaultDevOpsProjectCreator;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
import org.sonar.server.common.almsettings.DevOpsProjectCreator;
import org.sonar.server.common.almsettings.DevOpsProjectCreatorFactory;
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
@@ -47,10 +46,8 @@ 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.user.UserSession;
import static java.lang.String.format;
-import static java.util.Objects.requireNonNull;
import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_PROJECT_IDENTIFIER;
import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_URL;
@@ -63,28 +60,29 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory
private final GithubApplicationClient githubApplicationClient;
private final ProjectKeyGenerator projectKeyGenerator;
private final ProjectCreator projectCreator;
- private final UserSession userSession;
private final GitHubSettings gitHubSettings;
private final GithubPermissionConverter githubPermissionConverter;
private final PermissionUpdater<UserPermissionChange> permissionUpdater;
private final PermissionService permissionService;
private final ManagedProjectService managedProjectService;
+ private final GithubDevOpsProjectCreationContextService githubDevOpsProjectService;
public GithubProjectCreatorFactory(DbClient dbClient, GithubGlobalSettingsValidator githubGlobalSettingsValidator,
- GithubApplicationClient githubApplicationClient, ProjectKeyGenerator projectKeyGenerator, UserSession userSession,
+ GithubApplicationClient githubApplicationClient, ProjectKeyGenerator projectKeyGenerator,
ProjectCreator projectCreator, GitHubSettings gitHubSettings, GithubPermissionConverter githubPermissionConverter,
- PermissionUpdater<UserPermissionChange> permissionUpdater, PermissionService permissionService, ManagedProjectService managedProjectService) {
+ PermissionUpdater<UserPermissionChange> permissionUpdater, PermissionService permissionService, ManagedProjectService managedProjectService,
+ GithubDevOpsProjectCreationContextService githubDevOpsProjectService) {
this.dbClient = dbClient;
this.githubGlobalSettingsValidator = githubGlobalSettingsValidator;
this.githubApplicationClient = githubApplicationClient;
this.projectKeyGenerator = projectKeyGenerator;
- this.userSession = userSession;
this.projectCreator = projectCreator;
this.gitHubSettings = gitHubSettings;
this.githubPermissionConverter = githubPermissionConverter;
this.permissionUpdater = permissionUpdater;
this.permissionService = permissionService;
this.managedProjectService = managedProjectService;
+ this.githubDevOpsProjectService = githubDevOpsProjectService;
}
@Override
@@ -112,50 +110,39 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory
.map(appInstallationToken -> createGithubProjectCreator(devOpsProjectDescriptor, almSettingDto, appInstallationToken));
}
- private GithubProjectCreator createGithubProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto,
+ private DevOpsProjectCreator createGithubProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto,
AppInstallationToken appInstallationToken) {
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,
- authAppInstallationToken.orElse(null));
- return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater, permissionService,
- managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings);
+ DevOpsProjectCreationContext devOpsProjectCreationContext = githubDevOpsProjectService.createDevOpsProject(almSettingDto, devOpsProjectDescriptor, appInstallationToken);
+ return createDefaultDevOpsProjectCreator(devOpsProjectCreationContext);
}
@Override
- public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto,
- DevOpsProjectDescriptor devOpsProjectDescriptor) {
+ public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor) {
if (almSettingDto.getAlm() != ALM.GITHUB) {
return Optional.empty();
}
- try (DbSession dbSession = dbClient.openSession(false)) {
- AccessToken accessToken = getAccessToken(dbSession, almSettingDto);
- Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor);
- GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken,
- authAppInstallationToken.orElse(null));
- GithubProjectCreator githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater,
- permissionService, managedProjectService, this.projectCreator, githubProjectCreationParameters, gitHubSettings);
- return Optional.of(githubProjectCreator);
- }
+ DevOpsProjectCreationContext devOpsProjectCreationContext = githubDevOpsProjectService.create(almSettingDto, devOpsProjectDescriptor);
+ return Optional.of(createDefaultDevOpsProjectCreator(devOpsProjectCreationContext));
}
- private AccessToken getAccessToken(DbSession dbSession, AlmSettingDto almSettingDto) {
- String userUuid = requireNonNull(userSession.getUuid(), "User UUID cannot be null.");
- return dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto)
- .map(AlmPatDto::getPersonalAccessToken)
- .map(UserAccessToken::new)
- .orElseThrow(() -> new IllegalArgumentException("No personal access token found"));
+ private DefaultDevOpsProjectCreator createDefaultDevOpsProjectCreator(DevOpsProjectCreationContext devOpsProjectCreationContext) {
+ Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectCreationContext);
+
+ return new GithubProjectCreator(dbClient, devOpsProjectCreationContext, projectKeyGenerator,
+ gitHubSettings, projectCreator, permissionService, permissionUpdater,
+ managedProjectService, githubApplicationClient, githubPermissionConverter, authAppInstallationToken.orElse(null));
}
- private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) {
+ private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectCreationContext devOpsProjectCreationContext) {
if (gitHubSettings.isProvisioningEnabled()) {
GithubAppConfiguration githubAppConfiguration = new GithubAppConfiguration(Long.parseLong(gitHubSettings.appId()), gitHubSettings.privateKey(), gitHubSettings.apiURL());
- long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.repositoryIdentifier())
+ long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectCreationContext.devOpsPlatformIdentifier())
.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.repositoryIdentifier())));
+ + "The permissions can't be checked, and the project can not be created.",
+ devOpsProjectCreationContext.devOpsPlatformIdentifier())));
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/GitlabDevOpsProjectCreationContextService.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextService.java
new file mode 100644
index 00000000000..e1fa269cff1
--- /dev/null
+++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextService.java
@@ -0,0 +1,99 @@
+/*
+ * 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.gitlab;
+
+import java.util.Optional;
+import org.sonar.alm.client.gitlab.GitLabBranch;
+import org.sonar.alm.client.gitlab.GitlabApplicationClient;
+import org.sonar.alm.client.gitlab.GitlabServerException;
+import org.sonar.alm.client.gitlab.Project;
+import org.sonar.api.server.ServerSide;
+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.server.common.almsettings.DevOpsProjectCreationContext;
+import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContextService;
+import org.sonar.server.user.UserSession;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+@ServerSide
+public class GitlabDevOpsProjectCreationContextService implements DevOpsProjectCreationContextService {
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final GitlabApplicationClient gitlabApplicationClient;
+
+ public GitlabDevOpsProjectCreationContextService(DbClient dbClient, UserSession userSession, GitlabApplicationClient gitlabApplicationClient) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.gitlabApplicationClient = gitlabApplicationClient;
+ }
+
+ @Override
+ public DevOpsProjectCreationContext create(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor) {
+ String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null");
+ Long gitlabProjectId = getGitlabProjectId(devOpsProjectDescriptor);
+ String pat = findPersonalAccessTokenOrThrow(almSettingDto);
+ Project gitlabProject = fetchGitlabProject(url, pat, gitlabProjectId);
+ String defaultBranchName = getDefaultBranchOnGitlab(url, pat, gitlabProjectId).orElse(null);
+
+ return new DevOpsProjectCreationContext(gitlabProject.getName(), gitlabProject.getPathWithNamespace(),
+ String.valueOf(gitlabProjectId), gitlabProject.getVisibility().equals("public"), defaultBranchName, almSettingDto, userSession);
+
+ }
+
+ private static Long getGitlabProjectId(DevOpsProjectDescriptor devOpsProjectDescriptor) {
+ try {
+ return Long.parseLong(devOpsProjectDescriptor.repositoryIdentifier());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(format("GitLab project identifier must be a number, was '%s'", devOpsProjectDescriptor.repositoryIdentifier()));
+ }
+ }
+
+ private String findPersonalAccessTokenOrThrow(AlmSettingDto almSettingDto) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ 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 Project fetchGitlabProject(String gitlabUrl, String pat, Long gitlabProjectId) {
+ try {
+ return gitlabApplicationClient.getProject(
+ gitlabUrl,
+ pat,
+ gitlabProjectId);
+ } catch (GitlabServerException e) {
+ throw new IllegalStateException(format("Failed to fetch GitLab project with ID '%s' from '%s'", gitlabProjectId, gitlabUrl), e);
+ }
+ }
+
+ private Optional<String> getDefaultBranchOnGitlab(String gitlabUrl, String pat, long gitlabProjectId) {
+ Optional<GitLabBranch> almMainBranch = gitlabApplicationClient.getBranches(gitlabUrl, pat, gitlabProjectId).stream().filter(GitLabBranch::isDefault).findFirst();
+ return almMainBranch.map(GitLabBranch::getName);
+ }
+
+}
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
deleted file mode 100644
index a5f2f695d51..00000000000
--- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreator.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.gitlab;
-
-import java.util.Optional;
-import org.jetbrains.annotations.Nullable;
-import org.sonar.alm.client.gitlab.GitLabBranch;
-import org.sonar.alm.client.gitlab.GitlabApplicationClient;
-import org.sonar.alm.client.gitlab.GitlabServerException;
-import org.sonar.alm.client.gitlab.Project;
-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 GitlabProjectCreator implements DevOpsProjectCreator {
-
- private final DbClient dbClient;
- private final ProjectKeyGenerator projectKeyGenerator;
- private final ProjectCreator projectCreator;
- private final AlmSettingDto almSettingDto;
- private final DevOpsProjectDescriptor devOpsProjectDescriptor;
- private final GitlabApplicationClient gitlabApplicationClient;
- private final UserSession userSession;
-
- public GitlabProjectCreator(DbClient dbClient, ProjectKeyGenerator projectKeyGenerator, ProjectCreator projectCreator, AlmSettingDto almSettingDto,
- DevOpsProjectDescriptor devOpsProjectDescriptor, GitlabApplicationClient gitlabApplicationClient, UserSession userSession) {
- this.dbClient = dbClient;
- this.projectKeyGenerator = projectKeyGenerator;
- this.projectCreator = projectCreator;
- this.almSettingDto = almSettingDto;
- this.devOpsProjectDescriptor = devOpsProjectDescriptor;
- this.gitlabApplicationClient = gitlabApplicationClient;
- this.userSession = userSession;
- }
-
- @Override
- public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() {
- throw new UnsupportedOperationException("Not Implemented");
- }
-
- @Override
- public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, Boolean monorepo, @Nullable String projectKey,
- @Nullable String projectName) {
-
- String pat = findPersonalAccessTokenOrThrow(dbSession, almSettingDto);
-
- String gitlabUrl = requireNonNull(almSettingDto.getUrl(), "DevOps Platform gitlabUrl cannot be null");
-
- Long gitlabProjectId = getGitlabProjectId();
- Project gitlabProject = fetchGitlabProject(gitlabUrl, pat, gitlabProjectId);
-
- Optional<String> almDefaultBranch = getDefaultBranchOnGitlab(gitlabUrl, pat, gitlabProjectId);
- ComponentCreationData componentCreationData = projectCreator.createProject(
- dbSession,
- getProjectKey(projectKey, gitlabProject),
- getProjectName(projectName, gitlabProject),
- almDefaultBranch.orElse(null),
- creationMethod);
- ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow();
-
- createProjectAlmSettingDto(dbSession, gitlabProjectId.toString(), projectDto, almSettingDto, monorepo);
- return componentCreationData;
- }
-
- private String findPersonalAccessTokenOrThrow(DbSession dbSession, AlmSettingDto almSettingDto) {
- String userUuid = requireNonNull(userSession.getUuid(), "User UUID cannot be null.");
- Optional<AlmPatDto> almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto);
- return almPatDto.map(AlmPatDto::getPersonalAccessToken)
- .orElseThrow(() -> new IllegalArgumentException(format("personal access token for '%s' is missing", almSettingDto.getKey())));
- }
-
- private Long getGitlabProjectId() {
- try {
- return Long.parseLong(devOpsProjectDescriptor.repositoryIdentifier());
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException(format("GitLab project identifier must be a number, was '%s'", devOpsProjectDescriptor.repositoryIdentifier()));
- }
- }
-
- private Project fetchGitlabProject(String gitlabUrl, String pat, Long gitlabProjectId) {
- try {
- return gitlabApplicationClient.getProject(
- gitlabUrl,
- pat,
- gitlabProjectId);
- } catch (GitlabServerException e) {
- throw new IllegalStateException(format("Failed to fetch GitLab project with ID '%s' from '%s'", gitlabProjectId, gitlabUrl), e);
- }
- }
-
- private Optional<String> getDefaultBranchOnGitlab(String gitlabUrl, String pat, long gitlabProjectId) {
- Optional<GitLabBranch> almMainBranch = gitlabApplicationClient.getBranches(gitlabUrl, pat, gitlabProjectId).stream().filter(GitLabBranch::isDefault).findFirst();
- return almMainBranch.map(GitLabBranch::getName);
- }
-
- private String getProjectKey(@Nullable String projectKey, Project gitlabProject) {
- return Optional.ofNullable(projectKey).orElseGet(() -> projectKeyGenerator.generateUniqueProjectKey(gitlabProject.getPathWithNamespace()));
- }
-
- private static String getProjectName(@Nullable String projectName, Project gitlabProject) {
- return Optional.ofNullable(projectName).orElse(gitlabProject.getName());
- }
-
- private void createProjectAlmSettingDto(DbSession dbSession, String gitlabProjectId, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) {
- ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto()
- .setAlmSettingUuid(almSettingDto.getUuid())
- .setAlmRepo(gitlabProjectId)
- .setProjectUuid(projectDto.getUuid())
- .setMonorepo(monorepo);
- 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/gitlab/GitlabProjectCreatorFactory.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactory.java
index 19176e8c41a..8a13df6beb9 100644
--- a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactory.java
+++ b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactory.java
@@ -21,32 +21,44 @@ package org.sonar.server.common.almsettings.gitlab;
import java.util.Map;
import java.util.Optional;
-import org.sonar.alm.client.gitlab.GitlabApplicationClient;
+import org.sonar.auth.gitlab.GitLabSettings;
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.DefaultDevOpsProjectCreator;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
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.permission.PermissionUpdater;
+import org.sonar.server.common.permission.UserPermissionChange;
import org.sonar.server.common.project.ProjectCreator;
-import org.sonar.server.user.UserSession;
+import org.sonar.server.management.ManagedProjectService;
+import org.sonar.server.permission.PermissionService;
public class GitlabProjectCreatorFactory implements DevOpsProjectCreatorFactory {
private final DbClient dbClient;
private final ProjectKeyGenerator projectKeyGenerator;
private final ProjectCreator projectCreator;
- private final GitlabApplicationClient gitlabApplicationClient;
- private final UserSession userSession;
+ private final GitLabSettings gitLabSettings;
+ private final PermissionService permissionService;
+ private final PermissionUpdater<UserPermissionChange> permissionUpdater;
+ private final ManagedProjectService managedProjectService;
+ private final GitlabDevOpsProjectCreationContextService gitlabDevOpsProjectService;
- public GitlabProjectCreatorFactory(DbClient dbClient, ProjectKeyGenerator projectKeyGenerator, ProjectCreator projectCreator, GitlabApplicationClient gitlabApplicationClient,
- UserSession userSession) {
+ public GitlabProjectCreatorFactory(DbClient dbClient, ProjectKeyGenerator projectKeyGenerator, ProjectCreator projectCreator,
+ GitLabSettings gitLabSettings, PermissionService permissionService, PermissionUpdater<UserPermissionChange> permissionUpdater,
+ ManagedProjectService managedProjectService, GitlabDevOpsProjectCreationContextService gitlabDevOpsProjectService) {
this.dbClient = dbClient;
this.projectKeyGenerator = projectKeyGenerator;
this.projectCreator = projectCreator;
- this.gitlabApplicationClient = gitlabApplicationClient;
- this.userSession = userSession;
+ this.gitLabSettings = gitLabSettings;
+ this.permissionService = permissionService;
+ this.permissionUpdater = permissionUpdater;
+ this.managedProjectService = managedProjectService;
+ this.gitlabDevOpsProjectService = gitlabDevOpsProjectService;
}
@Override
@@ -59,14 +71,21 @@ public class GitlabProjectCreatorFactory implements DevOpsProjectCreatorFactory
if (almSettingDto.getAlm() != ALM.GITLAB) {
return Optional.empty();
}
+
+ DevOpsProjectCreationContext devOpsProjectCreationContext = gitlabDevOpsProjectService.create(almSettingDto, devOpsProjectDescriptor);
+
return Optional.of(
- new GitlabProjectCreator(
+ new DefaultDevOpsProjectCreator(
dbClient,
+ devOpsProjectCreationContext,
projectKeyGenerator,
+ gitLabSettings,
projectCreator,
- almSettingDto,
- devOpsProjectDescriptor,
- gitlabApplicationClient,
- userSession));
+ permissionService,
+ permissionUpdater,
+ managedProjectService
+ )
+ );
}
+
}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreatorTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreatorTest.java
new file mode 100644
index 00000000000..cc4fb7ec19b
--- /dev/null
+++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/DefaultDevOpsProjectCreatorTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.web.UserRole;
+import org.sonar.auth.DevOpsPlatformSettings;
+import org.sonar.db.DbClient;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.db.alm.setting.ProjectAlmSettingDao;
+import org.sonar.db.alm.setting.ProjectAlmSettingDto;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.project.CreationMethod;
+import org.sonar.db.project.ProjectDto;
+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.permission.PermissionUpdater;
+import org.sonar.server.common.permission.UserPermissionChange;
+import org.sonar.server.common.project.ProjectCreator;
+import org.sonar.server.component.ComponentCreationData;
+import org.sonar.server.management.ManagedProjectService;
+import org.sonar.server.permission.PermissionService;
+import org.sonar.server.permission.PermissionServiceImpl;
+import org.sonar.server.project.ProjectDefaultVisibility;
+import org.sonar.server.project.Visibility;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Objects.requireNonNull;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatException;
+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;
+import static org.sonar.db.project.CreationMethod.ALM_IMPORT_API;
+import static org.sonar.db.project.CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG;
+
+@ExtendWith(MockitoExtension.class)
+class DefaultDevOpsProjectCreatorTest {
+
+ private static final String ORGANIZATION_NAME = "orga2";
+ 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,
+ 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";
+
+ @Mock
+ private DbClient dbClient;
+ @Mock
+ private ProjectKeyGenerator projectKeyGenerator;
+ @Mock
+ private DevOpsPlatformSettings devOpsPlatformSettings;
+ @Mock
+ private PermissionUpdater<UserPermissionChange> permissionUpdater;
+ @Mock
+ private ManagedProjectService managedProjectService;
+ @Mock
+ private DevOpsProjectCreationContext devOpsProjectCreationContext;
+
+ @InjectMocks
+ private DefaultDevOpsProjectCreator defaultDevOpsProjectCreator;
+
+ @Captor
+ ArgumentCaptor<ComponentCreationParameters> componentCreationParametersCaptor;
+ @Captor
+ ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingDtoCaptor;
+
+ @Mock
+ private AlmSettingDto almSettingDto;
+ @Mock
+ private UserSession userSession;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private ProjectDefaultVisibility projectDefaultVisibility;
+ @Mock
+ private ComponentUpdater componentUpdater;
+
+ private final PermissionService permissionService = new PermissionServiceImpl(mock());
+
+ @BeforeEach
+ void setup() {
+ lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN);
+ lenient().when(userSession.getUuid()).thenReturn(USER_UUID);
+ lenient().when(devOpsProjectCreationContext.userSession()).thenReturn(userSession);
+
+ lenient().when(almSettingDto.getUrl()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.url());
+ lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY);
+ lenient().when(devOpsProjectCreationContext.almSettingDto()).thenReturn(almSettingDto);
+
+ lenient().when(devOpsProjectCreationContext.name()).thenReturn(REPOSITORY_NAME);
+ lenient().when(devOpsProjectCreationContext.devOpsPlatformIdentifier()).thenReturn(ORGANIZATION_NAME + "/" + REPOSITORY_NAME);
+ lenient().when(devOpsProjectCreationContext.fullName()).thenReturn(ORGANIZATION_NAME + "/" + REPOSITORY_NAME);
+ lenient().when(devOpsProjectCreationContext.defaultBranchName()).thenReturn(MAIN_BRANCH_NAME);
+
+ ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater);
+ defaultDevOpsProjectCreator = new DefaultDevOpsProjectCreator(dbClient, devOpsProjectCreationContext, projectKeyGenerator, devOpsPlatformSettings, projectCreator,
+ permissionService, permissionUpdater,
+ managedProjectService);
+
+ }
+
+ @Test
+ void isScanAllowedUsingPermissionsFromDevopsPlatform_throws() {
+ assertThatException()
+ .isThrownBy(() -> defaultDevOpsProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform())
+ .isInstanceOf(UnsupportedOperationException.class)
+ .withMessage("Not Implemented");
+ }
+
+ @Test
+ void createProjectAndBindToDevOpsPlatformFromScanner_whenVisibilitySyncDeactivated_successfullyCreatesProjectAndUseDefaultProjectVisibility() {
+ // given
+ mockGeneratedProjectKey();
+
+ ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
+ ProjectAlmSettingDao projectAlmSettingDao = mock();
+ when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
+ when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
+
+ // when
+ ComponentCreationData actualComponentCreationData = defaultDevOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
+ SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null);
+
+ // then
+ assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
+
+ ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
+ assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, "generated_orga2/repo1", SCANNER_API_DEVOPS_AUTO_CONFIG);
+ assertThat(componentCreationParameters.isManaged()).isFalse();
+ assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
+
+ verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq("generated_orga2/repo1"));
+ ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
+ assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
+ }
+
+ @Test
+ void createProjectAndBindToDevOpsPlatformFromScanner_whenVisibilitySynchronizationEnabled_successfullyCreatesProjectAndSetsVisibility() {
+ // given
+ mockGeneratedProjectKey();
+ when(devOpsProjectCreationContext.isPublic()).thenReturn(true);
+
+ ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
+ ProjectAlmSettingDao projectAlmSettingDao = mock();
+ when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
+ when(devOpsPlatformSettings.isProvisioningEnabled()).thenReturn(true);
+ when(devOpsPlatformSettings.isProjectVisibilitySynchronizationActivated()).thenReturn(true);
+
+ // when
+ ComponentCreationData actualComponentCreationData = defaultDevOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
+ SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null);
+
+ // then
+ assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
+
+ ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
+ assertThat(componentCreationParameters.newComponent().isPrivate()).isFalse();
+ }
+
+ @Test
+ void createProjectAndBindToDevOpsPlatformFromScanner_whenVisibilitySynchronizationDisabled_successfullyCreatesProjectAndMakesProjectPrivate() {
+ // given
+ mockGeneratedProjectKey();
+
+ ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
+ ProjectAlmSettingDao projectAlmSettingDao = mock();
+ when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
+ when(devOpsPlatformSettings.isProvisioningEnabled()).thenReturn(true);
+ when(devOpsPlatformSettings.isProjectVisibilitySynchronizationActivated()).thenReturn(false);
+
+ // when
+ ComponentCreationData actualComponentCreationData = defaultDevOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
+ SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null);
+
+ // then
+ assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
+
+ ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
+ assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
+ }
+
+ @Test
+ void createProjectAndBindToDevOpsPlatformFromApi_whenRepoFoundOnGitHub_successfullyCreatesProject() {
+ // given
+ String projectKey = "customProjectKey";
+ mockGeneratedProjectKey();
+
+ ComponentCreationData componentCreationData = mockProjectCreation(projectKey);
+ ProjectAlmSettingDao projectAlmSettingDao = mock();
+ when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
+ when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
+
+ // when
+ ComponentCreationData actualComponentCreationData = defaultDevOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, false,
+ projectKey,
+ null);
+
+ // then
+ assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
+
+ ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
+ assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, projectKey, ALM_IMPORT_API);
+ assertThat(componentCreationParameters.isManaged()).isFalse();
+ assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
+
+ verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(projectKey));
+ ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
+ assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
+ }
+
+ @Captor
+ private ArgumentCaptor<Collection<UserPermissionChange>> permissionChangesCaptor;
+
+ @Test
+ void createProjectAndBindToDevOpsPlatformFromApi_whenAutoProvisioningOnAndRepoPrivate_successfullyCreatesProject() {
+ // given
+ String projectKey = "customProjectKey";
+ mockGeneratedProjectKey();
+
+ ComponentCreationData componentCreationData = mockProjectCreation(projectKey);
+ ProjectAlmSettingDao projectAlmSettingDao = mock();
+ when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
+ when(devOpsPlatformSettings.isProvisioningEnabled()).thenReturn(true);
+
+ // when
+ ComponentCreationData actualComponentCreationData = defaultDevOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, false,
+ projectKey,
+ null);
+
+ // then
+ assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
+
+ ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
+ assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, projectKey, ALM_IMPORT_API);
+ assertThat(componentCreationParameters.isManaged()).isTrue();
+ assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
+
+ verifyScanPermissionWasAddedToUser(actualComponentCreationData);
+ verifyProjectSyncTaskWasCreated(actualComponentCreationData);
+
+ verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(projectKey));
+ ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
+ assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
+ }
+
+ private void verifyProjectSyncTaskWasCreated(ComponentCreationData componentCreationData) {
+ String projectUuid = requireNonNull(componentCreationData.projectDto()).getUuid();
+ String mainBranchUuid = requireNonNull(componentCreationData.mainBranchDto()).getUuid();
+ verify(managedProjectService).queuePermissionSyncTask(USER_UUID, mainBranchUuid, projectUuid);
+ }
+
+ private void verifyScanPermissionWasAddedToUser(ComponentCreationData actualComponentCreationData) {
+ verify(permissionUpdater).apply(any(), permissionChangesCaptor.capture());
+ UserPermissionChange permissionChange = permissionChangesCaptor.getValue().iterator().next();
+ assertThat(permissionChange.getUserId().getUuid()).isEqualTo(userSession.getUuid());
+ assertThat(permissionChange.getUserId().getLogin()).isEqualTo(userSession.getLogin());
+ assertThat(permissionChange.getPermission()).isEqualTo(UserRole.SCAN);
+ assertThat(permissionChange.getProjectUuid()).isEqualTo(actualComponentCreationData.projectDto().getUuid());
+ }
+
+ private void mockGeneratedProjectKey() {
+ String generatedKey = "generated_" + devOpsProjectCreationContext.devOpsPlatformIdentifier();
+ when(projectKeyGenerator.generateUniqueProjectKey(devOpsProjectCreationContext.fullName())).thenReturn(generatedKey);
+ }
+
+ private ComponentCreationData mockProjectCreation(String projectKey) {
+ ComponentCreationData componentCreationData = mock();
+ ProjectDto projectDto = mockProjectDto(projectKey);
+ when(componentCreationData.projectDto()).thenReturn(projectDto);
+ BranchDto branchDto = mock();
+ when(componentCreationData.mainBranchDto()).thenReturn(branchDto);
+ when(componentUpdater.createWithoutCommit(any(), componentCreationParametersCaptor.capture())).thenReturn(componentCreationData);
+ return componentCreationData;
+ }
+
+ private static ProjectDto mockProjectDto(String projectKey) {
+ ProjectDto projectDto = mock();
+ when(projectDto.getName()).thenReturn(REPOSITORY_NAME);
+ when(projectDto.getKey()).thenReturn(projectKey);
+ when(projectDto.getUuid()).thenReturn("project-uuid-1");
+ return projectDto;
+ }
+
+ private static void assertComponentCreationParametersContainsCorrectInformation(ComponentCreationParameters componentCreationParameters, String expectedKey,
+ CreationMethod expectedCreationMethod) {
+ assertThat(componentCreationParameters.creationMethod()).isEqualTo(expectedCreationMethod);
+ assertThat(componentCreationParameters.mainBranchName()).isEqualTo(MAIN_BRANCH_NAME);
+ assertThat(componentCreationParameters.userLogin()).isEqualTo(USER_LOGIN);
+ assertThat(componentCreationParameters.userUuid()).isEqualTo(USER_UUID);
+
+ NewComponent newComponent = componentCreationParameters.newComponent();
+ assertThat(newComponent.isProject()).isTrue();
+ assertThat(newComponent.qualifier()).isEqualTo(Qualifiers.PROJECT);
+ assertThat(newComponent.key()).isEqualTo(expectedKey);
+ assertThat(newComponent.name()).isEqualTo(REPOSITORY_NAME);
+ }
+
+ private static void assertAlmSettingsDtoContainsCorrectInformation(AlmSettingDto almSettingDto, ProjectDto projectDto, ProjectAlmSettingDto projectAlmSettingDto) {
+ assertThat(projectAlmSettingDto.getAlmRepo()).isEqualTo(DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier());
+ assertThat(projectAlmSettingDto.getAlmSlug()).isNull();
+ assertThat(projectAlmSettingDto.getAlmSettingUuid()).isEqualTo(almSettingDto.getUuid());
+ assertThat(projectAlmSettingDto.getProjectUuid()).isEqualTo(projectDto.getUuid());
+ assertThat(projectAlmSettingDto.getMonorepo()).isFalse();
+ assertThat(projectAlmSettingDto.getSummaryCommentEnabled()).isTrue();
+ }
+
+}
+
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextServiceTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextServiceTest.java
new file mode 100644
index 00000000000..b37660ae95f
--- /dev/null
+++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/github/GithubDevOpsProjectCreationContextServiceTest.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.github;
+
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.sonar.auth.github.client.GithubApplicationClient;
+import org.sonar.auth.github.client.GithubApplicationClient.Repository;
+import org.sonar.auth.github.security.UserAccessToken;
+import org.sonar.db.DbClient;
+import org.sonar.db.alm.pat.AlmPatDto;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
+import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
+import org.sonar.server.user.UserSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.assertj.core.api.Assertions.assertThatNullPointerException;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class GithubDevOpsProjectCreationContextServiceTest {
+
+ private static final DevOpsProjectDescriptor DEV_OPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, "project-key", "repository-identifier", null);
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private DbClient dbClient;
+ @Mock
+ private UserSession userSession;
+ @Mock
+ private GithubApplicationClient githubApplicationClient;
+
+ @InjectMocks
+ private GithubDevOpsProjectCreationContextService githubDevOpsProjectService;
+
+ @Test
+ void create_whenUserUuidIsNull_shouldThrow() {
+ AlmSettingDto almSettingDto = mock();
+
+ assertThatNullPointerException()
+ .isThrownBy(() -> githubDevOpsProjectService.create(almSettingDto, DEV_OPS_PROJECT_DESCRIPTOR))
+ .withMessage("User UUID cannot be null.");
+ }
+
+ @Test
+ void create_whenNoPat_shouldThrow() {
+ AlmSettingDto almSettingDto = mock();
+
+ when(userSession.getUuid()).thenReturn("user-uuid");
+ when(dbClient.almPatDao().selectByUserAndAlmSetting(dbClient.openSession(false), userSession.getUuid(), almSettingDto)).thenReturn(Optional.empty());
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> githubDevOpsProjectService.create(almSettingDto, DEV_OPS_PROJECT_DESCRIPTOR))
+ .withMessage("No personal access token found");
+ }
+
+ @Test
+ void create_whenRepoNotFound_throws() {
+ AlmSettingDto almSettingDto = mockAlmSettingDto();
+
+ mockValidAccessToken(almSettingDto);
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> githubDevOpsProjectService.create(almSettingDto, DEV_OPS_PROJECT_DESCRIPTOR))
+ .withMessage("Impossible to find the repository 'repository-identifier' on GitHub, using the devops config alm-config-key");
+ }
+
+ @Test
+ void create_whenRepoFound_createsDevOpsProject() {
+ AlmSettingDto almSettingDto = mockAlmSettingDto();
+
+ AlmPatDto almPatDto = mockValidAccessToken(almSettingDto);
+
+ Repository repository = mockGitHubRepository(almPatDto, almSettingDto);
+
+ DevOpsProjectCreationContext devOpsProjectCreationContext = githubDevOpsProjectService.create(almSettingDto, DEV_OPS_PROJECT_DESCRIPTOR);
+
+ assertThat(devOpsProjectCreationContext.name()).isEqualTo(repository.getName());
+ assertThat(devOpsProjectCreationContext.fullName()).isEqualTo(repository.getFullName());
+ assertThat(devOpsProjectCreationContext.devOpsPlatformIdentifier()).isEqualTo(repository.getFullName());
+ assertThat(devOpsProjectCreationContext.isPublic()).isTrue();
+ assertThat(devOpsProjectCreationContext.defaultBranchName()).isEqualTo(repository.getDefaultBranch());
+ }
+
+ private static AlmSettingDto mockAlmSettingDto() {
+ AlmSettingDto almSettingDto = mock();
+ when(almSettingDto.getUrl()).thenReturn("http://www.url.com");
+ lenient().when(almSettingDto.getKey()).thenReturn("alm-config-key");
+ return almSettingDto;
+ }
+
+ private AlmPatDto mockValidAccessToken(AlmSettingDto almSettingDto) {
+ when(userSession.getUuid()).thenReturn("user-uuid");
+ AlmPatDto almPatDto = mock();
+ when(dbClient.almPatDao().selectByUserAndAlmSetting(dbClient.openSession(false), userSession.getUuid(), almSettingDto)).thenReturn(Optional.of(almPatDto));
+ when(almPatDto.getPersonalAccessToken()).thenReturn("token");
+ return almPatDto;
+ }
+
+ private Repository mockGitHubRepository(AlmPatDto almPatDto, AlmSettingDto almSettingDto) {
+ Repository repository = mock();
+ when(repository.getName()).thenReturn("name");
+ when(repository.getFullName()).thenReturn("full-name");
+ when(repository.isPrivate()).thenReturn(false);
+ when(repository.getDefaultBranch()).thenReturn("default-branch");
+ UserAccessToken accessToken = new UserAccessToken(almPatDto.getPersonalAccessToken());
+ when(githubApplicationClient.getRepository(almSettingDto.getUrl(), accessToken,
+ DEV_OPS_PROJECT_DESCRIPTOR.repositoryIdentifier())).thenReturn(Optional.of(repository));
+ return repository;
+ }
+
+}
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 29c2ec7d57d..eebe1c535a0 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
@@ -41,6 +41,7 @@ import org.sonar.db.alm.pat.AlmPatDto;
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.DevOpsProjectCreationContext;
import org.sonar.server.common.almsettings.DevOpsProjectCreator;
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
import org.sonar.server.common.permission.PermissionUpdater;
@@ -50,7 +51,6 @@ import org.sonar.server.exceptions.BadConfigurationException;
import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.project.ProjectDefaultVisibility;
-import org.sonar.server.user.UserSession;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
@@ -77,6 +77,7 @@ public class GithubProjectCreatorFactoryTest {
DEVOPS_PLATFORM_PROJECT_IDENTIFIER, GITHUB_PROJECT_DESCRIPTOR.repositoryIdentifier());
private static final long APP_INSTALLATION_ID = 534534534543L;
private static final String USER_ACCESS_TOKEN = "userPat";
+ private static final DevOpsProjectCreationContext DEV_OPS_PROJECT = mock();
@Mock
private DbSession dbSession;
@@ -86,8 +87,6 @@ public class GithubProjectCreatorFactoryTest {
private GithubApplicationClient githubApplicationClient;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private DbClient dbClient;
- @Mock
- private UserSession userSession;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ProjectDefaultVisibility projectDefaultVisibility;
@Mock
@@ -108,6 +107,8 @@ public class GithubProjectCreatorFactoryTest {
private ManagedProjectService managedProjectService;
@Mock
private ProjectCreator projectCreator;
+ @Mock
+ private GithubDevOpsProjectCreationContextService devOpsProjectService;
@InjectMocks
private GithubProjectCreatorFactory githubProjectCreatorFactory;
@@ -160,6 +161,7 @@ public class GithubProjectCreatorFactoryTest {
AlmSettingDto almSettingDto = mockAlmSettingDto(true);
mockSuccessfulGithubInteraction();
+ when(devOpsProjectService.createDevOpsProject(almSettingDto, GITHUB_PROJECT_DESCRIPTOR, appInstallationToken)).thenReturn(DEV_OPS_PROJECT);
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow();
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(almSettingDto, false, appInstallationToken);
@@ -177,7 +179,9 @@ public class GithubProjectCreatorFactoryTest {
long authAppInstallationId = 32;
when(githubApplicationClient.getInstallationId(any(), eq(GITHUB_REPO_FULL_NAME))).thenReturn(Optional.of(authAppInstallationId));
when(githubApplicationClient.createAppInstallationToken(any(), eq(authAppInstallationId))).thenReturn(Optional.of(authAppInstallationToken));
+ when(DEV_OPS_PROJECT.devOpsPlatformIdentifier()).thenReturn(GITHUB_REPO_FULL_NAME);
+ when(devOpsProjectService.createDevOpsProject(almSettingDto, GITHUB_PROJECT_DESCRIPTOR, authAppInstallationToken)).thenReturn(DEV_OPS_PROJECT);
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow();
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(almSettingDto, true, appInstallationToken);
@@ -192,6 +196,7 @@ public class GithubProjectCreatorFactoryTest {
mockSuccessfulGithubInteraction();
+ when(devOpsProjectService.createDevOpsProject(matchingAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR, appInstallationToken)).thenReturn(DEV_OPS_PROJECT);
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow();
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(matchingAlmSettingDto, false, appInstallationToken);
@@ -205,6 +210,7 @@ public class GithubProjectCreatorFactoryTest {
mockSuccessfulGithubInteraction();
+ when(devOpsProjectService.create(mockAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR)).thenReturn(DEV_OPS_PROJECT);
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(mockAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR).orElseThrow();
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(mockAlmSettingDto, false, new UserAccessToken(USER_ACCESS_TOKEN));
@@ -219,6 +225,8 @@ public class GithubProjectCreatorFactoryTest {
mockValidGitHubSettings();
when(githubApplicationClient.getInstallationId(any(), eq(GITHUB_REPO_FULL_NAME))).thenReturn(Optional.empty());
+ when(devOpsProjectService.create(mockAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR)).thenReturn(DEV_OPS_PROJECT);
+ when(DEV_OPS_PROJECT.devOpsPlatformIdentifier()).thenReturn(GITHUB_PROJECT_DESCRIPTOR.repositoryIdentifier());
assertThatThrownBy(() -> githubProjectCreatorFactory.getDevOpsProjectCreator(mockAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR))
.isInstanceOf(BadConfigurationException.class)
@@ -240,12 +248,10 @@ public class GithubProjectCreatorFactoryTest {
}
private GithubProjectCreator getExpectedGithubProjectCreator(AlmSettingDto almSettingDto, boolean isInstanceManaged, AccessToken accessToken) {
- 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);
- return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater, permissionService,
- managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings);
+ return new GithubProjectCreator(dbClient, DEV_OPS_PROJECT,
+ projectKeyGenerator, gitHubSettings, projectCreator, permissionService, permissionUpdater, managedProjectService, githubApplicationClient,
+ githubPermissionConverter, authAppInstallToken);
}
private AlmSettingDto mockAlmSettingDto(boolean repoAccess) {
@@ -258,7 +264,6 @@ public class GithubProjectCreatorFactoryTest {
}
private void mockAlmPatDto(AlmSettingDto almSettingDto) {
- when(userSession.getUuid()).thenReturn("userUuid");
when(dbClient.almPatDao().selectByUserAndAlmSetting(any(), eq("userUuid"), eq(almSettingDto)))
.thenReturn(Optional.of(new AlmPatDto().setPersonalAccessToken(USER_ACCESS_TOKEN)));
}
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 705e1468349..bdf3bf51c7e 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
@@ -20,19 +20,15 @@
package org.sonar.server.common.almsettings.github;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.sonar.alm.client.github.GithubPermissionConverter;
-import org.sonar.api.resources.Qualifiers;
import org.sonar.api.web.UserRole;
import org.sonar.auth.github.AppInstallationToken;
import org.sonar.auth.github.GitHubSettings;
@@ -40,45 +36,31 @@ import org.sonar.auth.github.GsonRepositoryCollaborator;
import org.sonar.auth.github.GsonRepositoryPermissions;
import org.sonar.auth.github.GsonRepositoryTeam;
import org.sonar.auth.github.client.GithubApplicationClient;
-import org.sonar.auth.github.security.AccessToken;
import org.sonar.db.DbClient;
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
-import org.sonar.db.alm.setting.ProjectAlmSettingDao;
-import org.sonar.db.alm.setting.ProjectAlmSettingDto;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.project.CreationMethod;
-import org.sonar.db.project.ProjectDto;
import org.sonar.db.provisioning.GithubPermissionsMappingDto;
import org.sonar.db.user.GroupDto;
import org.sonar.server.common.almintegration.ProjectKeyGenerator;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
-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.permission.PermissionUpdater;
import org.sonar.server.common.permission.UserPermissionChange;
import org.sonar.server.common.project.ProjectCreator;
-import org.sonar.server.component.ComponentCreationData;
import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionServiceImpl;
import org.sonar.server.project.ProjectDefaultVisibility;
-import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession;
-import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
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;
-import static org.sonar.db.project.CreationMethod.ALM_IMPORT_API;
-import static org.sonar.db.project.CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG;
@ExtendWith(MockitoExtension.class)
class GithubProjectCreatorTest {
@@ -87,7 +69,8 @@ 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, null);
+ 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";
@@ -103,10 +86,6 @@ class GithubProjectCreatorTest {
@Mock
private ComponentUpdater componentUpdater;
@Mock
- private GithubProjectCreationParameters githubProjectCreationParameters;
- @Mock
- private AccessToken devOpsAppInstallationToken;
- @Mock
private AppInstallationToken authAppInstallationToken;
@Mock
private UserSession userSession;
@@ -119,38 +98,38 @@ class GithubProjectCreatorTest {
private ManagedProjectService managedProjectService;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ProjectDefaultVisibility projectDefaultVisibility;
+ @Mock
+ private DevOpsProjectCreationContext devOpsProjectCreationContext;
+
private final GitHubSettings gitHubSettings = mock();
private GithubProjectCreator githubProjectCreator;
- @Captor
- ArgumentCaptor<ComponentCreationParameters> componentCreationParametersCaptor;
- @Captor
- ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingDtoCaptor;
-
@BeforeEach
void setup() {
lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN);
lenient().when(userSession.getUuid()).thenReturn(USER_UUID);
+ lenient().when(devOpsProjectCreationContext.userSession()).thenReturn(userSession);
lenient().when(almSettingDto.getUrl()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.url());
lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY);
+ lenient().when(devOpsProjectCreationContext.almSettingDto()).thenReturn(almSettingDto);
- when(githubProjectCreationParameters.devOpsProjectDescriptor()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR);
- when(githubProjectCreationParameters.userSession()).thenReturn(userSession);
- when(githubProjectCreationParameters.devOpsAppInstallationToken()).thenReturn(devOpsAppInstallationToken);
- when(githubProjectCreationParameters.authAppInstallationToken()).thenReturn(authAppInstallationToken);
- when(githubProjectCreationParameters.almSettingDto()).thenReturn(almSettingDto);
+ lenient().when(devOpsProjectCreationContext.name()).thenReturn(REPOSITORY_NAME);
+ lenient().when(devOpsProjectCreationContext.devOpsPlatformIdentifier()).thenReturn(ORGANIZATION_NAME + "/" + REPOSITORY_NAME);
+ lenient().when(devOpsProjectCreationContext.fullName()).thenReturn(ORGANIZATION_NAME + "/" + REPOSITORY_NAME);
+ lenient().when(devOpsProjectCreationContext.defaultBranchName()).thenReturn(MAIN_BRANCH_NAME);
ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater);
- githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator,
- permissionUpdater, permissionService, managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings);
+ githubProjectCreator = new GithubProjectCreator(dbClient, devOpsProjectCreationContext, projectKeyGenerator, gitHubSettings, projectCreator, permissionService, permissionUpdater,
+ managedProjectService, githubApplicationClient, githubPermissionConverter, authAppInstallationToken);
}
@Test
void isScanAllowedUsingPermissionsFromDevopsPlatform_whenNoAuthToken_throws() {
- when(githubProjectCreationParameters.authAppInstallationToken()).thenReturn(null);
+ githubProjectCreator = new GithubProjectCreator(dbClient, devOpsProjectCreationContext, projectKeyGenerator, gitHubSettings, null, permissionService, permissionUpdater,
+ managedProjectService, githubApplicationClient, githubPermissionConverter, null);
assertThatIllegalStateException().isThrownBy(() -> githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform())
.withMessage("An auth app token is required in case repository permissions checking is necessary.");
@@ -263,216 +242,4 @@ class GithubProjectCreatorTest {
when(userSession.getGroups()).thenReturn(groupDtos);
}
- @Test
- void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() {
- assertThatIllegalStateException().isThrownBy(
- () -> githubProjectCreator.createProjectAndBindToDevOpsPlatform(mock(), SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null))
- .withMessage("Impossible to find the repository 'orga2/repo1' on GitHub, using the devops config " + ALM_SETTING_KEY);
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatformFromScanner_whenRepoFoundOnGitHub_successfullyCreatesProject() {
- // given
- mockGitHubRepository();
-
- ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
- ProjectAlmSettingDao projectAlmSettingDao = mock();
- when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
- when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
-
- // when
- ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
- SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null);
-
- // then
- assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
-
- ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
- assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, "generated_orga2/repo1", SCANNER_API_DEVOPS_AUTO_CONFIG);
- assertThat(componentCreationParameters.isManaged()).isFalse();
- assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
-
- verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq("generated_orga2/repo1"));
- ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
- assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatformFromScanner_whenRepoFoundOnGitHubAndVisibilitySynchronizationEnabled_successfullyCreatesProjectAndSetsVisibility() {
- // given
- mockPublicGithubRepository();
-
- ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
- ProjectAlmSettingDao projectAlmSettingDao = mock();
- when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
- when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
- when(gitHubSettings.isProjectVisibilitySynchronizationActivated()).thenReturn(true);
-
- // when
- ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
- SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null);
-
- // then
- assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
-
- ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
- assertThat(componentCreationParameters.newComponent().isPrivate()).isFalse();
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatformFromScanner_whenRepoFoundOnGitHubAndVisibilitySynchronizationDisabled_successfullyCreatesProjectAndMakesProjectPrivate() {
- // given
- mockGitHubRepository();
-
- ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
- ProjectAlmSettingDao projectAlmSettingDao = mock();
- when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
- when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
- when(gitHubSettings.isProjectVisibilitySynchronizationActivated()).thenReturn(false);
-
- // when
- ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
- SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null);
-
- // then
- assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
-
- ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
- assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatformFromApi_whenRepoFoundOnGitHub_successfullyCreatesProject() {
- // given
- String projectKey = "customProjectKey";
- mockGitHubRepository();
-
- ComponentCreationData componentCreationData = mockProjectCreation(projectKey);
- ProjectAlmSettingDao projectAlmSettingDao = mock();
- when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
- when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
-
- // when
- ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, false, projectKey,
- null);
-
- // then
- assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
-
- ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
- assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, projectKey, ALM_IMPORT_API);
- assertThat(componentCreationParameters.isManaged()).isFalse();
- assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
-
- verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(projectKey));
- ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
- assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
- }
-
- @Captor
- private ArgumentCaptor<Collection<UserPermissionChange>> permissionChangesCaptor;
-
- @Test
- void createProjectAndBindToDevOpsPlatformFromApi_whenRepoFoundOnGitHubAutoProvisioningOnAndRepoPrivate_successfullyCreatesProject() {
- // given
- String projectKey = "customProjectKey";
- mockGitHubRepository();
-
- ComponentCreationData componentCreationData = mockProjectCreation(projectKey);
- ProjectAlmSettingDao projectAlmSettingDao = mock();
- when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
- when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
-
- // when
- ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, false, projectKey,
- null);
-
- // then
- assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
-
- ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
- assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, projectKey, ALM_IMPORT_API);
- assertThat(componentCreationParameters.isManaged()).isTrue();
- assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
-
- verifyScanPermissionWasAddedToUser(actualComponentCreationData);
- verifyProjectSyncTaskWasCreated(actualComponentCreationData);
-
- verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(projectKey));
- ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
- assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
- }
-
- private void verifyProjectSyncTaskWasCreated(ComponentCreationData componentCreationData) {
- String projectUuid = requireNonNull(componentCreationData.projectDto()).getUuid();
- String mainBranchUuid = requireNonNull(componentCreationData.mainBranchDto()).getUuid();
- verify(managedProjectService).queuePermissionSyncTask(USER_UUID, mainBranchUuid, projectUuid);
- }
-
- private void verifyScanPermissionWasAddedToUser(ComponentCreationData actualComponentCreationData) {
- verify(permissionUpdater).apply(any(), permissionChangesCaptor.capture());
- UserPermissionChange permissionChange = permissionChangesCaptor.getValue().iterator().next();
- assertThat(permissionChange.getUserId().getUuid()).isEqualTo(userSession.getUuid());
- assertThat(permissionChange.getUserId().getLogin()).isEqualTo(userSession.getLogin());
- assertThat(permissionChange.getPermission()).isEqualTo(UserRole.SCAN);
- assertThat(permissionChange.getProjectUuid()).isEqualTo(actualComponentCreationData.projectDto().getUuid());
- }
-
- private void mockPublicGithubRepository() {
- GithubApplicationClient.Repository repository = mockGitHubRepository();
- when(repository.isPrivate()).thenReturn(false);
- }
-
- private GithubApplicationClient.Repository mockGitHubRepository() {
- GithubApplicationClient.Repository repository = mock();
- when(repository.getDefaultBranch()).thenReturn(MAIN_BRANCH_NAME);
- when(repository.getName()).thenReturn(REPOSITORY_NAME);
- 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.repositoryIdentifier())).thenReturn(
- Optional.of(repository));
- when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier());
- return repository;
- }
-
- private ComponentCreationData mockProjectCreation(String projectKey) {
- ComponentCreationData componentCreationData = mock();
- ProjectDto projectDto = mockProjectDto(projectKey);
- when(componentCreationData.projectDto()).thenReturn(projectDto);
- BranchDto branchDto = mock();
- when(componentCreationData.mainBranchDto()).thenReturn(branchDto);
- when(componentUpdater.createWithoutCommit(any(), componentCreationParametersCaptor.capture())).thenReturn(componentCreationData);
- return componentCreationData;
- }
-
- private static ProjectDto mockProjectDto(String projectKey) {
- ProjectDto projectDto = mock();
- when(projectDto.getName()).thenReturn(REPOSITORY_NAME);
- when(projectDto.getKey()).thenReturn(projectKey);
- when(projectDto.getUuid()).thenReturn("project-uuid-1");
- return projectDto;
- }
-
- private static void assertComponentCreationParametersContainsCorrectInformation(ComponentCreationParameters componentCreationParameters, String expectedKey,
- CreationMethod expectedCreationMethod) {
- assertThat(componentCreationParameters.creationMethod()).isEqualTo(expectedCreationMethod);
- assertThat(componentCreationParameters.mainBranchName()).isEqualTo(MAIN_BRANCH_NAME);
- assertThat(componentCreationParameters.userLogin()).isEqualTo(USER_LOGIN);
- assertThat(componentCreationParameters.userUuid()).isEqualTo(USER_UUID);
-
- NewComponent newComponent = componentCreationParameters.newComponent();
- assertThat(newComponent.isProject()).isTrue();
- assertThat(newComponent.qualifier()).isEqualTo(Qualifiers.PROJECT);
- assertThat(newComponent.key()).isEqualTo(expectedKey);
- assertThat(newComponent.name()).isEqualTo(REPOSITORY_NAME);
- }
-
- private static void assertAlmSettingsDtoContainsCorrectInformation(AlmSettingDto almSettingDto, ProjectDto projectDto, ProjectAlmSettingDto projectAlmSettingDto) {
- assertThat(projectAlmSettingDto.getAlmRepo()).isEqualTo(DEVOPS_PROJECT_DESCRIPTOR.repositoryIdentifier());
- assertThat(projectAlmSettingDto.getAlmSlug()).isNull();
- assertThat(projectAlmSettingDto.getAlmSettingUuid()).isEqualTo(almSettingDto.getUuid());
- assertThat(projectAlmSettingDto.getProjectUuid()).isEqualTo(projectDto.getUuid());
- assertThat(projectAlmSettingDto.getMonorepo()).isFalse();
- assertThat(projectAlmSettingDto.getSummaryCommentEnabled()).isTrue();
- }
}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextServiceTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextServiceTest.java
new file mode 100644
index 00000000000..da6c57d8874
--- /dev/null
+++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabDevOpsProjectCreationContextServiceTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.gitlab;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Answers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.sonar.alm.client.gitlab.GitLabBranch;
+import org.sonar.alm.client.gitlab.GitlabApplicationClient;
+import org.sonar.alm.client.gitlab.Project;
+import org.sonar.db.DbClient;
+import org.sonar.db.alm.pat.AlmPatDto;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
+import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
+import org.sonar.server.user.UserSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.assertj.core.api.Assertions.assertThatNullPointerException;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class GitlabDevOpsProjectCreationContextServiceTest {
+
+ private static final AlmSettingDto ALM_SETTING_DTO = mock();
+ private static final long GITLAB_PROJECT_ID = 123L;
+
+ private static final DevOpsProjectDescriptor DEV_OPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, "project-key", String.valueOf(GITLAB_PROJECT_ID), null);
+ private static final String GITLAB_COM = "https://gitlab.com";
+ private static final String DEFAULT_BRANCH_NAME = "default-branch";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private DbClient dbClient;
+ @Mock
+ private UserSession userSession;
+ @Mock
+ private GitlabApplicationClient gitlabApplicationClient;
+
+ @InjectMocks
+ private GitlabDevOpsProjectCreationContextService gitlabDevOpsProjectService;
+
+ @BeforeEach
+ public void setUp() {
+ when(ALM_SETTING_DTO.getUrl()).thenReturn(GITLAB_COM);
+ lenient().when(ALM_SETTING_DTO.getKey()).thenReturn("almKey");
+ }
+
+ @Test
+ void create_whenGitlabProjectIdIsInvalid_throws() {
+ DevOpsProjectDescriptor devOpsProjectDescriptor = mock();
+ when(devOpsProjectDescriptor.repositoryIdentifier()).thenReturn("invalid");
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> gitlabDevOpsProjectService.create(ALM_SETTING_DTO, devOpsProjectDescriptor))
+ .withMessage("GitLab project identifier must be a number, was 'invalid'");
+ }
+
+ @Test
+ void createDevOpsProject_whenUserUuidIsNull_shouldThrow() {
+ assertThatNullPointerException()
+ .isThrownBy(() -> gitlabDevOpsProjectService.create(ALM_SETTING_DTO, DEV_OPS_PROJECT_DESCRIPTOR))
+ .withMessage("User UUID cannot be null.");
+ }
+
+ @Test
+ void createDevOpsProject_whenNoPat_shouldThrow() {
+ when(userSession.getUuid()).thenReturn("user-uuid");
+ when(dbClient.almPatDao().selectByUserAndAlmSetting(dbClient.openSession(false), userSession.getUuid(), ALM_SETTING_DTO)).thenReturn(Optional.empty());
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> gitlabDevOpsProjectService.create(ALM_SETTING_DTO, DEV_OPS_PROJECT_DESCRIPTOR))
+ .withMessage("Personal access token for 'almKey' is missing");
+ }
+
+ @Test
+ void create_whenProjectNotFoundOnGitlab_throws() {
+ AlmPatDto almPatDto = mockPatExistence();
+
+ when(gitlabApplicationClient.getProject(GITLAB_COM, almPatDto.getPersonalAccessToken(), GITLAB_PROJECT_ID)).thenThrow(new IllegalStateException("error"));
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> gitlabDevOpsProjectService.create(ALM_SETTING_DTO, DEV_OPS_PROJECT_DESCRIPTOR))
+ .withMessage("error");
+ }
+
+ private static Stream<Arguments> visibilitiesAndExpectedResults() {
+ return Stream.of(
+ Arguments.of("public", true),
+ Arguments.of("private", false),
+ Arguments.of("internal", false),
+ Arguments.of("other", false)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("visibilitiesAndExpectedResults")
+ void create_whenProjectFoundOnGitLab_createCorrectDevOpsProject(String gitlabVisibility, boolean isPublic) {
+ AlmPatDto almPatDto = mockPatExistence();
+
+ Project project = mockGitlabProjectAndBranches(gitlabVisibility, almPatDto);
+
+ DevOpsProjectCreationContext devOpsProjectCreationContext = gitlabDevOpsProjectService.create(ALM_SETTING_DTO, DEV_OPS_PROJECT_DESCRIPTOR);
+ assertThat(devOpsProjectCreationContext.name()).isEqualTo(project.getName());
+ assertThat(devOpsProjectCreationContext.fullName()).isEqualTo(project.getPathWithNamespace());
+ assertThat(devOpsProjectCreationContext.devOpsPlatformIdentifier()).isEqualTo(String.valueOf(project.getId()));
+ assertThat(devOpsProjectCreationContext.isPublic()).isEqualTo(isPublic);
+ assertThat(devOpsProjectCreationContext.defaultBranchName()).isEqualTo(DEFAULT_BRANCH_NAME);
+ }
+
+ private AlmPatDto mockPatExistence() {
+ when(userSession.getUuid()).thenReturn("user-uuid");
+
+ AlmPatDto almPatDto = mock(AlmPatDto.class);
+ when(almPatDto.getPersonalAccessToken()).thenReturn("token");
+ when(dbClient.almPatDao().selectByUserAndAlmSetting(dbClient.openSession(false), userSession.getUuid(), ALM_SETTING_DTO)).thenReturn(Optional.of(almPatDto));
+ return almPatDto;
+ }
+
+ private Project mockGitlabProjectAndBranches(String gitlabVisibility, AlmPatDto almPatDto) {
+ Project project = mock(Project.class);
+ when(project.getId()).thenReturn(GITLAB_PROJECT_ID);
+ when(project.getName()).thenReturn("project-name");
+ when(project.getPathWithNamespace()).thenReturn("project-path");
+ when(project.getVisibility()).thenReturn(gitlabVisibility);
+ when(gitlabApplicationClient.getProject(GITLAB_COM, almPatDto.getPersonalAccessToken(), GITLAB_PROJECT_ID)).thenReturn(project);
+
+ GitLabBranch gitLabBranch = mock();
+ GitLabBranch defaultGitlabBranch = mock();
+ when(defaultGitlabBranch.getName()).thenReturn(DEFAULT_BRANCH_NAME);
+ when(defaultGitlabBranch.isDefault()).thenReturn(true);
+ when(gitlabApplicationClient.getBranches(GITLAB_COM, almPatDto.getPersonalAccessToken(), GITLAB_PROJECT_ID)).thenReturn(List.of(gitLabBranch, defaultGitlabBranch));
+ return project;
+ }
+
+}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactoryTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactoryTest.java
index 2abdd276753..ed040708218 100644
--- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactoryTest.java
+++ b/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorFactoryTest.java
@@ -20,24 +20,32 @@
package org.sonar.server.common.almsettings.gitlab;
import java.util.Map;
+import java.util.Optional;
import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
+import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.sonar.db.DbSession;
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.common.almsettings.DevOpsProjectCreator;
import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GitlabProjectCreatorFactoryTest {
+ @Mock
+ private GitlabDevOpsProjectCreationContextService gitlabDevOpsProjectService;
+
@InjectMocks
private GitlabProjectCreatorFactory underTest;
@@ -50,17 +58,28 @@ class GitlabProjectCreatorFactoryTest {
@Test
void getDevOpsProjectCreator_whenDevOpsPlatformIsNotGitlab_returnsEmpty() {
- AlmSettingDto almSetting = mock();
- when(almSetting.getAlm()).thenReturn(ALM.AZURE_DEVOPS);
+ AlmSettingDto almSetting = mockAlm(ALM.GITHUB);
AssertionsForClassTypes.assertThat(underTest.getDevOpsProjectCreator(almSetting, Mockito.mock(DevOpsProjectDescriptor.class))).isEmpty();
}
@Test
- void getDevOpsProjectCreator_whenDevOpsPlatformIsNotGitlab_returnsProjectCreator() {
- AlmSettingDto almSetting = mock();
- when(almSetting.getAlm()).thenReturn(ALM.GITLAB);
- assertThat(underTest.getDevOpsProjectCreator(almSetting, mock(DevOpsProjectDescriptor.class))).isNotEmpty();
+ void getDevOpsProjectCreator_whenDevOpsPlatformIsGitlab_returnsProjectCreator() {
+ when(gitlabDevOpsProjectService.create(any(), any())).thenReturn(mock());
+
+ AlmSettingDto almSetting = mockAlm(ALM.GITLAB);
+ DevOpsProjectDescriptor devOpsProjectDescriptor = mock();
+
+ Optional<DevOpsProjectCreator> devOpsProjectCreator = underTest.getDevOpsProjectCreator(almSetting, devOpsProjectDescriptor);
+
+ assertThat(devOpsProjectCreator).isNotEmpty();
+ verify(gitlabDevOpsProjectService).create(almSetting, devOpsProjectDescriptor);
+ }
+
+ private static AlmSettingDto mockAlm(ALM alm) {
+ AlmSettingDto almSettingDto = mock(AlmSettingDto.class);
+ when(almSettingDto.getAlm()).thenReturn(alm);
+ return almSettingDto;
}
}
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
deleted file mode 100644
index 1e67da2ff7c..00000000000
--- a/server/sonar-webserver-common/src/test/java/org/sonar/server/common/almsettings/gitlab/GitlabProjectCreatorTest.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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.gitlab;
-
-import java.util.List;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.sonar.alm.client.gitlab.GitLabBranch;
-import org.sonar.alm.client.gitlab.GitlabApplicationClient;
-import org.sonar.alm.client.gitlab.GitlabServerException;
-import org.sonar.alm.client.gitlab.Project;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.alm.pat.AlmPatDto;
-import org.sonar.db.alm.setting.ALM;
-import org.sonar.db.alm.setting.AlmSettingDto;
-import org.sonar.db.alm.setting.ProjectAlmSettingDto;
-import org.sonar.db.project.CreationMethod;
-import org.sonar.db.project.ProjectDto;
-import org.sonar.server.common.almintegration.ProjectKeyGenerator;
-import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
-import org.sonar.server.common.project.ProjectCreator;
-import org.sonar.server.component.ComponentCreationData;
-import org.sonar.server.user.UserSession;
-
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class GitlabProjectCreatorTest {
-
- private static final String PROJECT_UUID = "projectUuid";
- private static final String USER_LOGIN = "userLogin";
- private static final String USER_UUID = "userUuid";
- private static final String REPOSITORY_PATH_WITH_NAMESPACE = "pathWith/namespace";
- private static final String GITLAB_PROJECT_NAME = "gitlabProjectName";
- private static final String REPOSITORY_ID = "1234";
- private static final String MAIN_BRANCH_NAME = "defaultBranch";
- private static final String ALM_SETTING_KEY = "gitlab_config_1";
- private static final String ALM_SETTING_UUID = "almSettingUuid";
- private static final String USER_PAT = "1234";
- public static final String GITLAB_URL = "http://api.com";
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private DbClient dbClient;
-
- @Mock
- private ProjectKeyGenerator projectKeyGenerator;
-
- @Mock
- private ProjectCreator projectCreator;
-
- @Mock
- private AlmSettingDto almSettingDto;
- @Mock
- private DevOpsProjectDescriptor devOpsProjectDescriptor;
- @Mock
- private GitlabApplicationClient gitlabApplicationClient;
- @Mock
- private UserSession userSession;
-
- @InjectMocks
- private GitlabProjectCreator underTest;
-
- @BeforeEach
- void setup() {
- lenient().when(userSession.getLogin()).thenReturn(USER_LOGIN);
- lenient().when(userSession.getUuid()).thenReturn(USER_UUID);
-
- lenient().when(almSettingDto.getUrl()).thenReturn(GITLAB_URL);
- lenient().when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY);
- lenient().when(almSettingDto.getUuid()).thenReturn(ALM_SETTING_UUID);
-
- lenient().when(devOpsProjectDescriptor.repositoryIdentifier()).thenReturn(REPOSITORY_ID);
- lenient().when(devOpsProjectDescriptor.url()).thenReturn(GITLAB_URL);
- lenient().when(devOpsProjectDescriptor.alm()).thenReturn(ALM.GITLAB);
- }
-
- @Test
- void isScanAllowedUsingPermissionsFromDevopsPlatform_shouldThrowUnsupportedOperationException() {
- assertThatExceptionOfType(UnsupportedOperationException.class)
- .isThrownBy(() -> underTest.isScanAllowedUsingPermissionsFromDevopsPlatform())
- .withMessage("Not Implemented");
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatform_whenUserHasNoPat_throws() {
- assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null))
- .withMessage("personal access token for 'gitlab_config_1' is missing");
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() {
- mockPatForUser();
- when(gitlabApplicationClient.getProject(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))).thenThrow(new GitlabServerException(404, "Not found"));
- assertThatExceptionOfType(IllegalStateException.class)
- .isThrownBy(() -> underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, false, null, null))
- .withMessage("Failed to fetch GitLab project with ID '1234' from 'http://api.com'");
-
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatform_whenRepoFoundOnGitlab_successfullyCreatesProject() {
- mockPatForUser();
- mockGitlabProject();
- mockMainBranch();
- mockProjectCreation("projectKey", "projectName");
-
- underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, "projectKey", "projectName");
-
- ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class);
-
- verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq("projectName"), eq("projectKey"));
-
- ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue();
-
- assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID);
- assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_ID);
- assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID);
- assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue();
- }
-
- @Test
- void createProjectAndBindToDevOpsPlatform_whenNoKeyAndNameSpecified_generatesKeyAndUsesGitlabProjectName() {
- mockPatForUser();
- mockGitlabProject();
- mockMainBranch();
-
- String generatedProjectKey = "generatedProjectKey";
- when(projectKeyGenerator.generateUniqueProjectKey(REPOSITORY_PATH_WITH_NAMESPACE)).thenReturn(generatedProjectKey);
-
- mockProjectCreation(generatedProjectKey, GITLAB_PROJECT_NAME);
-
- underTest.createProjectAndBindToDevOpsPlatform(mock(DbSession.class), CreationMethod.ALM_IMPORT_API, true, null, null);
-
- ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingCaptor = ArgumentCaptor.forClass(ProjectAlmSettingDto.class);
-
- verify(dbClient.projectAlmSettingDao()).insertOrUpdate(any(), projectAlmSettingCaptor.capture(), eq(ALM_SETTING_KEY), eq(GITLAB_PROJECT_NAME), eq(generatedProjectKey));
-
- ProjectAlmSettingDto createdProjectAlmSettingDto = projectAlmSettingCaptor.getValue();
-
- assertThat(createdProjectAlmSettingDto.getAlmSettingUuid()).isEqualTo(ALM_SETTING_UUID);
- assertThat(createdProjectAlmSettingDto.getAlmRepo()).isEqualTo(REPOSITORY_ID);
- assertThat(createdProjectAlmSettingDto.getProjectUuid()).isEqualTo(PROJECT_UUID);
- assertThat(createdProjectAlmSettingDto.getMonorepo()).isTrue();
- }
-
- private void mockPatForUser() {
- AlmPatDto almPatDto = mock();
- when(almPatDto.getPersonalAccessToken()).thenReturn(USER_PAT);
- when(dbClient.almPatDao().selectByUserAndAlmSetting(any(), eq(USER_UUID), eq(almSettingDto))).thenReturn(Optional.of(almPatDto));
- }
-
- private void mockGitlabProject() {
- Project project = mock(Project.class);
- lenient().when(project.getPathWithNamespace()).thenReturn(REPOSITORY_PATH_WITH_NAMESPACE);
- when(project.getName()).thenReturn(GITLAB_PROJECT_NAME);
- when(gitlabApplicationClient.getProject(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID))).thenReturn(project);
-
- }
-
- private void mockMainBranch() {
- when(gitlabApplicationClient.getBranches(GITLAB_URL, USER_PAT, Long.valueOf(REPOSITORY_ID)))
- .thenReturn(List.of(new GitLabBranch("notMain", false), new GitLabBranch(MAIN_BRANCH_NAME, true)));
- }
-
- 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);
- }
-
-}