import org.sonar.db.user.UserDto; | import org.sonar.db.user.UserDto; | ||||
import org.sonar.server.almintegration.ws.ImportHelper; | import org.sonar.server.almintegration.ws.ImportHelper; | ||||
import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | ||||
import org.sonar.server.almsettings.ws.gitlab.GitlabProjectCreatorFactory; | |||||
import org.sonar.server.component.ComponentUpdater; | import org.sonar.server.component.ComponentUpdater; | ||||
import org.sonar.server.es.TestIndexers; | import org.sonar.server.es.TestIndexers; | ||||
import org.sonar.server.exceptions.NotFoundException; | import org.sonar.server.exceptions.NotFoundException; | ||||
import org.sonar.server.project.DefaultBranchNameResolver; | import org.sonar.server.project.DefaultBranchNameResolver; | ||||
import org.sonar.server.project.ProjectDefaultVisibility; | import org.sonar.server.project.ProjectDefaultVisibility; | ||||
import org.sonar.server.project.Visibility; | import org.sonar.server.project.Visibility; | ||||
import org.sonar.server.project.ws.ProjectCreator; | |||||
import org.sonar.server.tester.UserSessionRule; | import org.sonar.server.tester.UserSessionRule; | ||||
import org.sonar.server.ws.TestRequest; | import org.sonar.server.ws.TestRequest; | ||||
import org.sonar.server.ws.WsActionTester; | import org.sonar.server.ws.WsActionTester; | ||||
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession); | private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession); | ||||
private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class); | private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class); | ||||
private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class); | private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class); | ||||
private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); | |||||
private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); | |||||
private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); | |||||
private final NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); | |||||
private final ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater); | |||||
private final GitlabProjectCreatorFactory gitlabProjectCreatorFactory = new GitlabProjectCreatorFactory(db.getDbClient(), projectKeyGenerator, projectCreator, | |||||
gitlabApplicationClient, userSession); | |||||
private final ImportGitLabProjectAction importGitLabProjectAction = new ImportGitLabProjectAction( | private final ImportGitLabProjectAction importGitLabProjectAction = new ImportGitLabProjectAction( | ||||
db.getDbClient(), userSession, projectDefaultVisibility, gitlabApplicationClient, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver, | |||||
defaultBranchNameResolver); | |||||
db.getDbClient(), userSession, componentUpdater, importHelper, newCodeDefinitionResolver, gitlabProjectCreatorFactory); | |||||
private final WsActionTester ws = new WsActionTester(importGitLabProjectAction); | private final WsActionTester ws = new WsActionTester(importGitLabProjectAction); | ||||
@Before | @Before | ||||
assertThatNoException().isThrownBy(request::execute); | assertThatNoException().isThrownBy(request::execute); | ||||
} | } | ||||
private AlmSettingDto configureUserAndPatAndAlmSettings() { | private AlmSettingDto configureUserAndPatAndAlmSettings() { | ||||
UserDto user = db.users().insertUser(); | UserDto user = db.users().insertUser(); | ||||
userSession.logIn(user).addPermission(PROVISION_PROJECTS); | userSession.logIn(user).addPermission(PROVISION_PROJECTS); |
private void assertProjectWasCreatedWithBinding(boolean isPrivate) { | private void assertProjectWasCreatedWithBinding(boolean isPrivate) { | ||||
ProjectDto projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), PROJECT_KEY).orElseThrow(); | ProjectDto projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), PROJECT_KEY).orElseThrow(); | ||||
assertThat(projectDto.getCreationMethod()).isEqualTo(CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG); | assertThat(projectDto.getCreationMethod()).isEqualTo(CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG); | ||||
assertThat(projectDto.getName()).isEqualTo("repoName"); | |||||
assertThat(projectDto.getName()).isEqualTo(PROJECT_NAME); | |||||
assertThat(projectDto.isPrivate()).isEqualTo(isPrivate); | assertThat(projectDto.isPrivate()).isEqualTo(isPrivate); | ||||
BranchDto branchDto = db.getDbClient().branchDao().selectByBranchKey(db.getSession(), projectDto.getUuid(), "defaultBranch").orElseThrow(); | BranchDto branchDto = db.getDbClient().branchDao().selectByBranchKey(db.getSession(), projectDto.getUuid(), "defaultBranch").orElseThrow(); |
import java.util.Optional; | import java.util.Optional; | ||||
import javax.inject.Inject; | import javax.inject.Inject; | ||||
import org.sonar.auth.github.security.AccessToken; | |||||
import org.sonar.auth.github.security.UserAccessToken; | |||||
import org.sonar.api.server.ws.Change; | import org.sonar.api.server.ws.Change; | ||||
import org.sonar.api.server.ws.Request; | import org.sonar.api.server.ws.Request; | ||||
import org.sonar.api.server.ws.Response; | import org.sonar.api.server.ws.Response; | ||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.alm.pat.AlmPatDto; | |||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | import org.sonar.db.alm.setting.AlmSettingDto; | ||||
import org.sonar.db.component.BranchDto; | import org.sonar.db.component.BranchDto; | ||||
import org.sonar.server.almsettings.ws.GithubProjectCreatorFactory; | import org.sonar.server.almsettings.ws.GithubProjectCreatorFactory; | ||||
import org.sonar.server.component.ComponentCreationData; | import org.sonar.server.component.ComponentCreationData; | ||||
import org.sonar.server.component.ComponentUpdater; | import org.sonar.server.component.ComponentUpdater; | ||||
import org.sonar.server.exceptions.BadRequestException; | |||||
import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; | import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; | ||||
import org.sonar.server.project.DefaultBranchNameResolver; | import org.sonar.server.project.DefaultBranchNameResolver; | ||||
import org.sonar.server.user.UserSession; | import org.sonar.server.user.UserSession; | ||||
import org.sonarqube.ws.Projects; | import org.sonarqube.ws.Projects; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | |||||
import static org.sonar.db.project.CreationMethod.getCreationMethod; | import static org.sonar.db.project.CreationMethod.getCreationMethod; | ||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | |||||
import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING; | import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING; | ||||
import static org.sonar.server.almintegration.ws.ImportHelper.toCreateResponse; | import static org.sonar.server.almintegration.ws.ImportHelper.toCreateResponse; | ||||
import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; | import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; | ||||
public void define(WebService.NewController context) { | public void define(WebService.NewController context) { | ||||
WebService.NewAction action = context.createAction("import_github_project") | WebService.NewAction action = context.createAction("import_github_project") | ||||
.setDescription("Create a SonarQube project with the information from the provided GitHub repository.<br/>" + | .setDescription("Create a SonarQube project with the information from the provided GitHub repository.<br/>" + | ||||
"Autoconfigure pull request decoration mechanism. If Automatic Provisioning is enable for GitHub, " + | |||||
"it will also synchronize permissions from the repository.<br/>" + | |||||
"Requires the 'Create Projects' permission") | |||||
"Autoconfigure pull request decoration mechanism. If Automatic Provisioning is enable for GitHub, " + | |||||
"it will also synchronize permissions from the repository.<br/>" + | |||||
"Requires the 'Create Projects' permission") | |||||
.setPost(true) | .setPost(true) | ||||
.setSince("8.4") | .setSince("8.4") | ||||
.setHandler(this) | .setHandler(this) | ||||
String newCodeDefinitionValue = request.param(PARAM_NEW_CODE_DEFINITION_VALUE); | String newCodeDefinitionValue = request.param(PARAM_NEW_CODE_DEFINITION_VALUE); | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
AccessToken accessToken = getAccessToken(dbSession, almSettingDto); | |||||
String repositoryKey = request.mandatoryParam(PARAM_REPOSITORY_KEY); | String repositoryKey = request.mandatoryParam(PARAM_REPOSITORY_KEY); | ||||
String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | ||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, url, repositoryKey); | DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, url, repositoryKey); | ||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(almSettingDto, accessToken, devOpsProjectDescriptor); | |||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor) | |||||
.orElseThrow(() -> BadRequestException.create("GitHub DevOps platform configuration not found.")); | |||||
CreationMethod creationMethod = getCreationMethod(ALM_IMPORT, userSession.isAuthenticatedBrowserSession()); | CreationMethod creationMethod = getCreationMethod(ALM_IMPORT, userSession.isAuthenticatedBrowserSession()); | ||||
ComponentCreationData componentCreationData = devOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbSession, creationMethod, null); | |||||
ComponentCreationData componentCreationData = devOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbSession, creationMethod, false, null, null); | |||||
checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); | checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); | ||||
return toCreateResponse(projectDto); | return toCreateResponse(projectDto); | ||||
} | } | ||||
} | } | ||||
private AccessToken getAccessToken(DbSession dbSession, AlmSettingDto almSettingDto) { | |||||
String userUuid = importHelper.getUserUuid(); | |||||
return dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto) | |||||
.map(AlmPatDto::getPersonalAccessToken) | |||||
.map(UserAccessToken::new) | |||||
.orElseThrow(() -> new IllegalArgumentException("No personal access token found")); | |||||
} | |||||
} | } |
package org.sonar.server.almintegration.ws.gitlab; | package org.sonar.server.almintegration.ws.gitlab; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import javax.annotation.Nullable; | |||||
import javax.inject.Inject; | import javax.inject.Inject; | ||||
import org.sonar.alm.client.gitlab.GitLabBranch; | |||||
import org.sonar.alm.client.gitlab.GitlabApplicationClient; | |||||
import org.sonar.alm.client.gitlab.Project; | |||||
import org.sonar.api.server.ws.Change; | import org.sonar.api.server.ws.Change; | ||||
import org.sonar.api.server.ws.Request; | import org.sonar.api.server.ws.Request; | ||||
import org.sonar.api.server.ws.Response; | import org.sonar.api.server.ws.Response; | ||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.alm.pat.AlmPatDto; | |||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | import org.sonar.db.alm.setting.AlmSettingDto; | ||||
import org.sonar.db.alm.setting.ProjectAlmSettingDto; | |||||
import org.sonar.db.component.BranchDto; | import org.sonar.db.component.BranchDto; | ||||
import org.sonar.db.project.ProjectDto; | import org.sonar.db.project.ProjectDto; | ||||
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction; | import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction; | ||||
import org.sonar.server.almintegration.ws.ImportHelper; | import org.sonar.server.almintegration.ws.ImportHelper; | ||||
import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectCreator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectDescriptor; | |||||
import org.sonar.server.almsettings.ws.gitlab.GitlabProjectCreatorFactory; | |||||
import org.sonar.server.component.ComponentCreationData; | import org.sonar.server.component.ComponentCreationData; | ||||
import org.sonar.server.component.ComponentCreationParameters; | |||||
import org.sonar.server.component.ComponentUpdater; | import org.sonar.server.component.ComponentUpdater; | ||||
import org.sonar.server.component.NewComponent; | |||||
import org.sonar.server.exceptions.BadRequestException; | |||||
import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; | import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; | ||||
import org.sonar.server.project.DefaultBranchNameResolver; | |||||
import org.sonar.server.project.ProjectDefaultVisibility; | |||||
import org.sonar.server.user.UserSession; | import org.sonar.server.user.UserSession; | ||||
import org.sonarqube.ws.Projects.CreateWsResponse; | import org.sonarqube.ws.Projects.CreateWsResponse; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
import static org.sonar.api.resources.Qualifiers.PROJECT; | |||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | |||||
import static org.sonar.db.project.CreationMethod.getCreationMethod; | import static org.sonar.db.project.CreationMethod.getCreationMethod; | ||||
import static org.sonar.db.project.CreationMethod.Category.ALM_IMPORT; | |||||
import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING; | import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING; | ||||
import static org.sonar.server.component.NewComponent.newComponentBuilder; | |||||
import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; | import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; | ||||
import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; | import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; | ||||
import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; | import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; | ||||
private final DbClient dbClient; | private final DbClient dbClient; | ||||
private final UserSession userSession; | private final UserSession userSession; | ||||
private final ProjectDefaultVisibility projectDefaultVisibility; | |||||
private final GitlabApplicationClient gitlabApplicationClient; | |||||
private final ComponentUpdater componentUpdater; | private final ComponentUpdater componentUpdater; | ||||
private final ImportHelper importHelper; | private final ImportHelper importHelper; | ||||
private final ProjectKeyGenerator projectKeyGenerator; | |||||
private final NewCodeDefinitionResolver newCodeDefinitionResolver; | private final NewCodeDefinitionResolver newCodeDefinitionResolver; | ||||
private final DefaultBranchNameResolver defaultBranchNameResolver; | |||||
private final GitlabProjectCreatorFactory projectCreatorFactory; | |||||
@Inject | @Inject | ||||
public ImportGitLabProjectAction(DbClient dbClient, UserSession userSession, | public ImportGitLabProjectAction(DbClient dbClient, UserSession userSession, | ||||
ProjectDefaultVisibility projectDefaultVisibility, GitlabApplicationClient gitlabApplicationClient, | |||||
ComponentUpdater componentUpdater, ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator, NewCodeDefinitionResolver newCodeDefinitionResolver, | |||||
DefaultBranchNameResolver defaultBranchNameResolver) { | |||||
ComponentUpdater componentUpdater, ImportHelper importHelper, NewCodeDefinitionResolver newCodeDefinitionResolver, | |||||
GitlabProjectCreatorFactory projectCreatorFactory) { | |||||
this.dbClient = dbClient; | this.dbClient = dbClient; | ||||
this.userSession = userSession; | this.userSession = userSession; | ||||
this.projectDefaultVisibility = projectDefaultVisibility; | |||||
this.gitlabApplicationClient = gitlabApplicationClient; | |||||
this.componentUpdater = componentUpdater; | this.componentUpdater = componentUpdater; | ||||
this.importHelper = importHelper; | this.importHelper = importHelper; | ||||
this.projectKeyGenerator = projectKeyGenerator; | |||||
this.newCodeDefinitionResolver = newCodeDefinitionResolver; | this.newCodeDefinitionResolver = newCodeDefinitionResolver; | ||||
this.defaultBranchNameResolver = defaultBranchNameResolver; | |||||
this.projectCreatorFactory = projectCreatorFactory; | |||||
} | } | ||||
@Override | @Override | ||||
public void define(WebService.NewController context) { | public void define(WebService.NewController context) { | ||||
WebService.NewAction action = context.createAction("import_gitlab_project") | WebService.NewAction action = context.createAction("import_gitlab_project") | ||||
.setDescription("Import a GitLab project to SonarQube, creating a new project and configuring MR decoration<br/>" + | .setDescription("Import a GitLab project to SonarQube, creating a new project and configuring MR decoration<br/>" + | ||||
"Requires the 'Create Projects' permission") | |||||
"Requires the 'Create Projects' permission") | |||||
.setPost(true) | .setPost(true) | ||||
.setSince("8.5") | .setSince("8.5") | ||||
.setHandler(this) | .setHandler(this) | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
AlmSettingDto almSettingDto = importHelper.getAlmSettingDtoForAlm(request, ALM.GITLAB); | AlmSettingDto almSettingDto = importHelper.getAlmSettingDtoForAlm(request, ALM.GITLAB); | ||||
String pat = getPat(dbSession, almSettingDto); | |||||
long gitlabProjectId = request.mandatoryParamAsLong(PARAM_GITLAB_PROJECT_ID); | |||||
String gitlabProjectId = request.mandatoryParam(PARAM_GITLAB_PROJECT_ID); | |||||
String gitlabUrl = requireNonNull(almSettingDto.getUrl(), "DevOps Platform gitlabUrl cannot be null"); | String gitlabUrl = requireNonNull(almSettingDto.getUrl(), "DevOps Platform gitlabUrl cannot be null"); | ||||
Project gitlabProject = gitlabApplicationClient.getProject(gitlabUrl, pat, gitlabProjectId); | |||||
Optional<String> almMainBranchName = getAlmDefaultBranch(pat, gitlabProjectId, gitlabUrl); | |||||
DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(ALM.GITLAB, gitlabUrl, gitlabProjectId); | |||||
DevOpsProjectCreator projectCreator = projectCreatorFactory.getDevOpsProjectCreator(almSettingDto, projectDescriptor) | |||||
.orElseThrow(() -> BadRequestException.create("Gitlab DevOps platform configuration not found")); | |||||
ComponentCreationData componentCreationData = projectCreator.createProjectAndBindToDevOpsPlatform(dbSession, | |||||
getCreationMethod(ALM_IMPORT, userSession.isAuthenticatedBrowserSession()), false, null, null); | |||||
ComponentCreationData componentCreationData = createProject(dbSession, gitlabProject, almMainBranchName.orElse(null)); | |||||
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | ||||
BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow(); | BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow(); | ||||
populateMRSetting(dbSession, gitlabProjectId, projectDto, almSettingDto); | |||||
checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); | checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); | ||||
if (newCodeDefinitionType != null) { | if (newCodeDefinitionType != null) { | ||||
newCodeDefinitionResolver.createNewCodeDefinition(dbSession, projectDto.getUuid(), mainBranchDto.getUuid(), | newCodeDefinitionResolver.createNewCodeDefinition(dbSession, projectDto.getUuid(), mainBranchDto.getUuid(), | ||||
almMainBranchName.orElse(defaultBranchNameResolver.getEffectiveMainBranchName()), newCodeDefinitionType, newCodeDefinitionValue); | |||||
mainBranchDto.getKey(), newCodeDefinitionType, newCodeDefinitionValue); | |||||
} | } | ||||
componentUpdater.commitAndIndex(dbSession, componentCreationData); | componentUpdater.commitAndIndex(dbSession, componentCreationData); | ||||
return ImportHelper.toCreateResponse(projectDto); | return ImportHelper.toCreateResponse(projectDto); | ||||
} | } | ||||
} | } | ||||
private String getPat(DbSession dbSession, AlmSettingDto almSettingDto) { | |||||
String userUuid = importHelper.getUserUuid(); | |||||
Optional<AlmPatDto> almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto); | |||||
return almPatDto.map(AlmPatDto::getPersonalAccessToken) | |||||
.orElseThrow(() -> new IllegalArgumentException(String.format("personal access token for '%s' is missing", almSettingDto.getKey()))); | |||||
} | |||||
private Optional<String> getAlmDefaultBranch(String pat, long gitlabProjectId, String gitlabUrl) { | |||||
Optional<GitLabBranch> almMainBranch = gitlabApplicationClient.getBranches(gitlabUrl, pat, gitlabProjectId).stream().filter(GitLabBranch::isDefault).findFirst(); | |||||
return almMainBranch.map(GitLabBranch::getName); | |||||
} | |||||
private void populateMRSetting(DbSession dbSession, Long gitlabProjectId, ProjectDto projectDto, AlmSettingDto almSettingDto) { | |||||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, new ProjectAlmSettingDto() | |||||
.setProjectUuid(projectDto.getUuid()) | |||||
.setAlmSettingUuid(almSettingDto.getUuid()) | |||||
.setAlmRepo(gitlabProjectId.toString()) | |||||
.setAlmSlug(null) | |||||
.setMonorepo(false), | |||||
almSettingDto.getKey(), | |||||
projectDto.getName(), projectDto.getKey()); | |||||
} | |||||
private ComponentCreationData createProject(DbSession dbSession, Project gitlabProject, @Nullable String mainBranchName) { | |||||
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate(); | |||||
String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(gitlabProject.getPathWithNamespace()); | |||||
NewComponent newProject = newComponentBuilder() | |||||
.setKey(uniqueProjectKey) | |||||
.setName(gitlabProject.getName()) | |||||
.setPrivate(visibility) | |||||
.setQualifier(PROJECT) | |||||
.build(); | |||||
ComponentCreationParameters componentCreationParameters = ComponentCreationParameters.builder() | |||||
.newComponent(newProject) | |||||
.userUuid(userSession.getUuid()) | |||||
.userLogin(userSession.getLogin()) | |||||
.mainBranchName(mainBranchName) | |||||
.creationMethod(getCreationMethod(ALM_IMPORT, userSession.isAuthenticatedBrowserSession())) | |||||
.build(); | |||||
return componentUpdater.createWithoutCommit(dbSession, componentCreationParameters); | |||||
} | |||||
} | } |
import javax.annotation.Priority; | import javax.annotation.Priority; | ||||
import org.sonar.api.server.ServerSide; | import org.sonar.api.server.ServerSide; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | |||||
@ServerSide | @ServerSide | ||||
@Priority(1) | @Priority(1) | ||||
.findFirst(); | .findFirst(); | ||||
} | } | ||||
@Override | |||||
public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor) { | |||||
return delegates.stream() | |||||
.flatMap(delegate -> delegate.getDevOpsProjectCreator(almSettingDto, devOpsProjectDescriptor).stream()) | |||||
.findFirst(); | |||||
} | |||||
} | } |
boolean isScanAllowedUsingPermissionsFromDevopsPlatform(); | boolean isScanAllowedUsingPermissionsFromDevopsPlatform(); | ||||
ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, @Nullable String projectKey); | |||||
ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, Boolean monorepo, @Nullable String projectKey, | |||||
@Nullable String projectName); | |||||
} | } |
import java.util.Map; | import java.util.Map; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | |||||
public interface DevOpsProjectCreatorFactory { | public interface DevOpsProjectCreatorFactory { | ||||
Optional<DevOpsProjectCreator> getDevOpsProjectCreator(DbSession dbSession, Map<String, String> characteristics); | Optional<DevOpsProjectCreator> getDevOpsProjectCreator(DbSession dbSession, Map<String, String> characteristics); | ||||
Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor); | |||||
} | } |
} | } | ||||
@Override | @Override | ||||
public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, @Nullable String projectKey) { | |||||
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"); | String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null"); | ||||
Repository repository = githubApplicationClient.getRepository(url, devOpsAppInstallationToken, devOpsProjectDescriptor.projectIdentifier()) | Repository repository = githubApplicationClient.getRepository(url, devOpsAppInstallationToken, devOpsProjectDescriptor.projectIdentifier()) | ||||
.orElseThrow(() -> new IllegalStateException( | .orElseThrow(() -> new IllegalStateException( | ||||
String.format("Impossible to find the repository '%s' on GitHub, using the devops config %s", devOpsProjectDescriptor.projectIdentifier(), almSettingDto.getKey()))); | String.format("Impossible to find the repository '%s' on GitHub, using the devops config %s", devOpsProjectDescriptor.projectIdentifier(), almSettingDto.getKey()))); | ||||
return createProjectAndBindToDevOpsPlatform(dbSession, projectKey, almSettingDto, repository, creationMethod); | |||||
return createProjectAndBindToDevOpsPlatform(dbSession, monorepo, projectKey, projectName, almSettingDto, repository, creationMethod); | |||||
} | } | ||||
private ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, @Nullable String projectKey, AlmSettingDto almSettingDto, | |||||
private ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, Boolean monorepo, @Nullable String projectKey, @Nullable String projectName, | |||||
AlmSettingDto almSettingDto, | |||||
Repository repository, CreationMethod creationMethod) { | Repository repository, CreationMethod creationMethod) { | ||||
String key = Optional.ofNullable(projectKey).orElse(getUniqueProjectKey(repository)); | String key = Optional.ofNullable(projectKey).orElse(getUniqueProjectKey(repository)); | ||||
boolean isManaged = gitHubSettings.isProvisioningEnabled(); | boolean isManaged = gitHubSettings.isProvisioningEnabled(); | ||||
ComponentCreationData componentCreationData = projectCreator.createProject(dbSession, key, repository.getName(), repository.getDefaultBranch(), creationMethod, | |||||
ComponentCreationData componentCreationData = projectCreator.createProject(dbSession, key, Optional.ofNullable(projectName).orElse(repository.getName()), | |||||
repository.getDefaultBranch(), creationMethod, | |||||
shouldProjectBePrivate(repository), isManaged); | shouldProjectBePrivate(repository), isManaged); | ||||
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); | ||||
createProjectAlmSettingDto(dbSession, repository, projectDto, almSettingDto); | |||||
createProjectAlmSettingDto(dbSession, repository, projectDto, almSettingDto, monorepo); | |||||
addScanPermissionToCurrentUser(dbSession, projectDto); | addScanPermissionToCurrentUser(dbSession, projectDto); | ||||
BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow(); | BranchDto mainBranchDto = Optional.ofNullable(componentCreationData.mainBranchDto()).orElseThrow(); | ||||
return projectKeyGenerator.generateUniqueProjectKey(repository.getFullName()); | return projectKeyGenerator.generateUniqueProjectKey(repository.getFullName()); | ||||
} | } | ||||
private void createProjectAlmSettingDto(DbSession dbSession, Repository repo, ProjectDto projectDto, AlmSettingDto almSettingDto) { | |||||
private void createProjectAlmSettingDto(DbSession dbSession, Repository repo, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) { | |||||
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto() | ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto() | ||||
.setAlmSettingUuid(almSettingDto.getUuid()) | .setAlmSettingUuid(almSettingDto.getUuid()) | ||||
.setAlmRepo(repo.getFullName()) | .setAlmRepo(repo.getFullName()) | ||||
.setAlmSlug(null) | .setAlmSlug(null) | ||||
.setProjectUuid(projectDto.getUuid()) | .setProjectUuid(projectDto.getUuid()) | ||||
.setSummaryCommentEnabled(true) | .setSummaryCommentEnabled(true) | ||||
.setMonorepo(false); | |||||
.setMonorepo(monorepo); | |||||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), projectDto.getName(), projectDto.getKey()); | dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), projectDto.getName(), projectDto.getKey()); | ||||
} | } | ||||
import org.sonar.auth.github.GithubAppConfiguration; | import org.sonar.auth.github.GithubAppConfiguration; | ||||
import org.sonar.auth.github.client.GithubApplicationClient; | import org.sonar.auth.github.client.GithubApplicationClient; | ||||
import org.sonar.auth.github.security.AccessToken; | import org.sonar.auth.github.security.AccessToken; | ||||
import org.sonar.auth.github.security.UserAccessToken; | |||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.alm.pat.AlmPatDto; | |||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | import org.sonar.db.alm.setting.AlmSettingDto; | ||||
import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | ||||
import org.sonar.server.user.UserSession; | import org.sonar.server.user.UserSession; | ||||
import static java.lang.String.format; | import static java.lang.String.format; | ||||
import static java.util.Objects.requireNonNull; | |||||
import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_PROJECT_IDENTIFIER; | import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_PROJECT_IDENTIFIER; | ||||
import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_URL; | import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_URL; | ||||
managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings); | managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings); | ||||
} | } | ||||
public DevOpsProjectCreator getDevOpsProjectCreator(AlmSettingDto almSettingDto, AccessToken accessToken, | |||||
@Override | |||||
public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto, | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor) { | 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); | |||||
} | |||||
} | |||||
Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor); | |||||
GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken, | |||||
authAppInstallationToken.orElse(null)); | |||||
return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater, permissionService, | |||||
managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings | |||||
); | |||||
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 Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) { | private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) { | ||||
long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier()) | long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier()) | ||||
.orElseThrow(() -> new BadConfigurationException("PROJECT", | .orElseThrow(() -> new BadConfigurationException("PROJECT", | ||||
format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. " | format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. " | ||||
+ "The permissions can't be checked, and the project can not be created.", | |||||
devOpsProjectDescriptor.projectIdentifier()))); | |||||
+ "The permissions can't be checked, and the project can not be created.", | |||||
devOpsProjectDescriptor.projectIdentifier()))); | |||||
return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId)); | return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId)); | ||||
} | } | ||||
return Optional.empty(); | return Optional.empty(); |
/* | |||||
* 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.almsettings.ws.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.almintegration.ws.ProjectKeyGenerator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectCreator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectDescriptor; | |||||
import org.sonar.server.component.ComponentCreationData; | |||||
import org.sonar.server.project.ws.ProjectCreator; | |||||
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.projectIdentifier()); | |||||
} catch (NumberFormatException e) { | |||||
throw new IllegalArgumentException(format("GitLab project identifier must be a number, was '%s'", devOpsProjectDescriptor.projectIdentifier())); | |||||
} | |||||
} | |||||
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) | |||||
.setAlmSlug(null) | |||||
.setProjectUuid(projectDto.getUuid()) | |||||
.setSummaryCommentEnabled(true) | |||||
.setMonorepo(monorepo); | |||||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto, almSettingDto.getKey(), projectDto.getName(), projectDto.getKey()); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
package org.sonar.server.almsettings.ws.gitlab; | |||||
import java.util.Map; | |||||
import java.util.Optional; | |||||
import org.sonar.alm.client.gitlab.GitlabApplicationClient; | |||||
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.almintegration.ws.ProjectKeyGenerator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectCreator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectCreatorFactory; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectDescriptor; | |||||
import org.sonar.server.project.ws.ProjectCreator; | |||||
import org.sonar.server.user.UserSession; | |||||
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; | |||||
public GitlabProjectCreatorFactory(DbClient dbClient, ProjectKeyGenerator projectKeyGenerator, ProjectCreator projectCreator, GitlabApplicationClient gitlabApplicationClient, | |||||
UserSession userSession) { | |||||
this.dbClient = dbClient; | |||||
this.projectKeyGenerator = projectKeyGenerator; | |||||
this.projectCreator = projectCreator; | |||||
this.gitlabApplicationClient = gitlabApplicationClient; | |||||
this.userSession = userSession; | |||||
} | |||||
@Override | |||||
public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(DbSession dbSession, Map<String, String> characteristics) { | |||||
return Optional.empty(); | |||||
} | |||||
@Override | |||||
public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto, DevOpsProjectDescriptor devOpsProjectDescriptor) { | |||||
if (almSettingDto.getAlm() != ALM.GITLAB) { | |||||
return Optional.empty(); | |||||
} | |||||
return Optional.of( | |||||
new GitlabProjectCreator( | |||||
dbClient, | |||||
projectKeyGenerator, | |||||
projectCreator, | |||||
almSettingDto, | |||||
devOpsProjectDescriptor, | |||||
gitlabApplicationClient, | |||||
userSession)); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2024 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
@ParametersAreNonnullByDefault | |||||
package org.sonar.server.almsettings.ws.gitlab; | |||||
import javax.annotation.ParametersAreNonnullByDefault; |
// Project key is already used as a module of another project | // Project key is already used as a module of another project | ||||
ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, component.branchUuid()); | ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, component.branchUuid()); | ||||
errors.add(format("The project '%s' is already defined in SonarQube but as a module of project '%s'. " | errors.add(format("The project '%s' is already defined in SonarQube but as a module of project '%s'. " | ||||
+ "If you really want to stop directly analysing project '%s', please first delete it from SonarQube and then relaunch the analysis of project '%s'.", | |||||
+ "If you really want to stop directly analysing project '%s', please first delete it from SonarQube and then relaunch the analysis of project '%s'.", | |||||
rawProjectKey, anotherBaseProject.getKey(), anotherBaseProject.getKey(), rawProjectKey)); | rawProjectKey, anotherBaseProject.getKey(), anotherBaseProject.getKey(), rawProjectKey)); | ||||
} | } | ||||
if (!errors.isEmpty()) { | if (!errors.isEmpty()) { | ||||
throwIfCurrentUserWouldNotHaveScanPermission(projectKey, dbSession, devOpsProjectCreator); | throwIfCurrentUserWouldNotHaveScanPermission(projectKey, dbSession, devOpsProjectCreator); | ||||
if (devOpsProjectCreator != null) { | if (devOpsProjectCreator != null) { | ||||
return devOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbSession, SCANNER_API_DEVOPS_AUTO_CONFIG, projectKey); | |||||
return devOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbSession, SCANNER_API_DEVOPS_AUTO_CONFIG, false, projectKey, projectName); | |||||
} | } | ||||
return projectCreator.createProject(dbSession, componentKey.getKey(), defaultIfBlank(projectName, projectKey), null, SCANNER_API); | return projectCreator.createProject(dbSession, componentKey.getKey(), defaultIfBlank(projectName, projectKey), null, SCANNER_API); | ||||
} | } |
import org.sonar.auth.github.AppInstallationToken; | import org.sonar.auth.github.AppInstallationToken; | ||||
import org.sonar.auth.github.GitHubSettings; | import org.sonar.auth.github.GitHubSettings; | ||||
import org.sonar.auth.github.client.GithubApplicationClient; | 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.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.alm.pat.AlmPatDto; | |||||
import org.sonar.db.alm.setting.ALM; | import org.sonar.db.alm.setting.ALM; | ||||
import org.sonar.db.alm.setting.AlmSettingDto; | import org.sonar.db.alm.setting.AlmSettingDto; | ||||
import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | import org.sonar.server.almintegration.ws.ProjectKeyGenerator; | ||||
DEVOPS_PLATFORM_URL, GITHUB_PROJECT_DESCRIPTOR.url(), | DEVOPS_PLATFORM_URL, GITHUB_PROJECT_DESCRIPTOR.url(), | ||||
DEVOPS_PLATFORM_PROJECT_IDENTIFIER, GITHUB_PROJECT_DESCRIPTOR.projectIdentifier()); | DEVOPS_PLATFORM_PROJECT_IDENTIFIER, GITHUB_PROJECT_DESCRIPTOR.projectIdentifier()); | ||||
private static final long APP_INSTALLATION_ID = 534534534543L; | private static final long APP_INSTALLATION_ID = 534534534543L; | ||||
private static final String USER_ACCESS_TOKEN = "userPat"; | |||||
@Mock | @Mock | ||||
private DbSession dbSession; | private DbSession dbSession; | ||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow(); | DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow(); | ||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(almSettingDto, false); | |||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(almSettingDto, false, appInstallationToken); | |||||
assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | ||||
} | } | ||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow(); | DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow(); | ||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(almSettingDto, true); | |||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(almSettingDto, true, appInstallationToken); | |||||
assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | ||||
} | } | ||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow(); | DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow(); | ||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(matchingAlmSettingDto, false); | |||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(matchingAlmSettingDto, false, appInstallationToken); | |||||
assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | ||||
} | } | ||||
@Test | @Test | ||||
public void getDevOpsProjectCreatorFromImport_shouldInstantiateDevOpsProjectCreator() { | public void getDevOpsProjectCreatorFromImport_shouldInstantiateDevOpsProjectCreator() { | ||||
AlmSettingDto mockAlmSettingDto = mockAlmSettingDto(true); | AlmSettingDto mockAlmSettingDto = mockAlmSettingDto(true); | ||||
mockAlmPatDto(mockAlmSettingDto); | |||||
mockSuccessfulGithubInteraction(); | mockSuccessfulGithubInteraction(); | ||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(mockAlmSettingDto, appInstallationToken, GITHUB_PROJECT_DESCRIPTOR); | |||||
DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(mockAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR).orElseThrow(); | |||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(mockAlmSettingDto, false); | |||||
GithubProjectCreator expectedGithubProjectCreator = getExpectedGithubProjectCreator(mockAlmSettingDto, false, new UserAccessToken(USER_ACCESS_TOKEN)); | |||||
assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator); | ||||
} | } | ||||
@Test | @Test | ||||
public void getDevOpsProjectCreatorFromImport_whenGitHubConfigDoesNotAllowAccessToRepo_shouldThrow() { | public void getDevOpsProjectCreatorFromImport_whenGitHubConfigDoesNotAllowAccessToRepo_shouldThrow() { | ||||
AlmSettingDto mockAlmSettingDto = mockAlmSettingDto(false); | AlmSettingDto mockAlmSettingDto = mockAlmSettingDto(false); | ||||
mockAlmPatDto(mockAlmSettingDto); | |||||
mockValidGitHubSettings(); | mockValidGitHubSettings(); | ||||
when(githubApplicationClient.getInstallationId(any(), eq(GITHUB_REPO_FULL_NAME))).thenReturn(Optional.empty()); | when(githubApplicationClient.getInstallationId(any(), eq(GITHUB_REPO_FULL_NAME))).thenReturn(Optional.empty()); | ||||
assertThatThrownBy(() -> githubProjectCreatorFactory.getDevOpsProjectCreator(mockAlmSettingDto, appInstallationToken, GITHUB_PROJECT_DESCRIPTOR)) | |||||
assertThatThrownBy(() -> githubProjectCreatorFactory.getDevOpsProjectCreator(mockAlmSettingDto, GITHUB_PROJECT_DESCRIPTOR)) | |||||
.isInstanceOf(BadConfigurationException.class) | .isInstanceOf(BadConfigurationException.class) | ||||
.hasMessage(format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. " | .hasMessage(format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. " | ||||
+ "The permissions can't be checked, and the project can not be created.", | |||||
+ "The permissions can't be checked, and the project can not be created.", | |||||
GITHUB_REPO_FULL_NAME)); | GITHUB_REPO_FULL_NAME)); | ||||
} | } | ||||
when(githubApplicationClient.createAppInstallationToken(any(), eq(APP_INSTALLATION_ID))).thenReturn(Optional.of(appInstallationToken)); | when(githubApplicationClient.createAppInstallationToken(any(), eq(APP_INSTALLATION_ID))).thenReturn(Optional.of(appInstallationToken)); | ||||
} | } | ||||
private GithubProjectCreator getExpectedGithubProjectCreator(AlmSettingDto almSettingDto, boolean isInstanceManaged) { | |||||
private GithubProjectCreator getExpectedGithubProjectCreator(AlmSettingDto almSettingDto, boolean isInstanceManaged, AccessToken accessToken) { | |||||
DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, almSettingDto.getUrl(), GITHUB_REPO_FULL_NAME); | DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, almSettingDto.getUrl(), GITHUB_REPO_FULL_NAME); | ||||
AppInstallationToken authAppInstallToken = isInstanceManaged ? authAppInstallationToken : null; | AppInstallationToken authAppInstallToken = isInstanceManaged ? authAppInstallationToken : null; | ||||
GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, appInstallationToken, | |||||
GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken, | |||||
authAppInstallToken); | authAppInstallToken); | ||||
return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater, permissionService, | return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater, permissionService, | ||||
managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings); | managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings); | ||||
private AlmSettingDto mockAlmSettingDto(boolean repoAccess) { | private AlmSettingDto mockAlmSettingDto(boolean repoAccess) { | ||||
AlmSettingDto almSettingDto = mock(); | AlmSettingDto almSettingDto = mock(); | ||||
when(almSettingDto.getUrl()).thenReturn(repoAccess ? GITHUB_PROJECT_DESCRIPTOR.url() : "anotherUrl"); | when(almSettingDto.getUrl()).thenReturn(repoAccess ? GITHUB_PROJECT_DESCRIPTOR.url() : "anotherUrl"); | ||||
when(almSettingDto.getAlm()).thenReturn(ALM.GITHUB); | |||||
when(dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB)).thenReturn(List.of(almSettingDto)); | when(dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB)).thenReturn(List.of(almSettingDto)); | ||||
return almSettingDto; | return almSettingDto; | ||||
} | } | ||||
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))); | |||||
} | |||||
} | } |
ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater); | ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater); | ||||
githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, | githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, | ||||
permissionUpdater, permissionService, managedProjectService, projectCreator, githubProjectCreationParameters,gitHubSettings); | |||||
permissionUpdater, permissionService, managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings); | |||||
} | } | ||||
@Test | @Test | ||||
void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() { | void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() { | ||||
assertThatIllegalStateException().isThrownBy( | assertThatIllegalStateException().isThrownBy( | ||||
() -> githubProjectCreator.createProjectAndBindToDevOpsPlatform(mock(), SCANNER_API_DEVOPS_AUTO_CONFIG, null)) | |||||
() -> 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); | .withMessage("Impossible to find the repository 'orga2/repo1' on GitHub, using the devops config " + ALM_SETTING_KEY); | ||||
} | } | ||||
// when | // when | ||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), | ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), | ||||
SCANNER_API_DEVOPS_AUTO_CONFIG, null); | |||||
SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null); | |||||
// then | // then | ||||
assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | ||||
// when | // when | ||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), | ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), | ||||
SCANNER_API_DEVOPS_AUTO_CONFIG, null); | |||||
SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null); | |||||
// then | // then | ||||
assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | ||||
// when | // when | ||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), | ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), | ||||
SCANNER_API_DEVOPS_AUTO_CONFIG, null); | |||||
SCANNER_API_DEVOPS_AUTO_CONFIG, false, null, null); | |||||
// then | // then | ||||
assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | ||||
when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE); | when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE); | ||||
// when | // when | ||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, projectKey); | |||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, false, projectKey, | |||||
null); | |||||
// then | // then | ||||
assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | ||||
when(gitHubSettings.isProvisioningEnabled()).thenReturn(true); | when(gitHubSettings.isProvisioningEnabled()).thenReturn(true); | ||||
// when | // when | ||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, projectKey); | |||||
ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, false, projectKey, | |||||
null); | |||||
// then | // then | ||||
assertThat(actualComponentCreationData).isEqualTo(componentCreationData); | assertThat(actualComponentCreationData).isEqualTo(componentCreationData); |
/* | |||||
* 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.almsettings.ws.gitlab; | |||||
import java.util.Map; | |||||
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.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.almsettings.ws.DevOpsProjectDescriptor; | |||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
@ExtendWith(MockitoExtension.class) | |||||
class GitlabProjectCreatorFactoryTest { | |||||
@InjectMocks | |||||
private GitlabProjectCreatorFactory underTest; | |||||
@Test | |||||
void getDevOpsProjectCreator_withCharacteristics_returnsEmpty() { | |||||
assertThat(underTest.getDevOpsProjectCreator(mock(DbSession.class), Map.of())).isEmpty(); | |||||
} | |||||
@Test | |||||
void getDevOpsProjectCreator_whenDevOpsPlatformIsNotGitlab_returnsEmpty() { | |||||
AlmSettingDto almSetting = mock(); | |||||
when(almSetting.getAlm()).thenReturn(ALM.AZURE_DEVOPS); | |||||
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(); | |||||
} | |||||
} |
/* | |||||
* 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.almsettings.ws.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.almintegration.ws.ProjectKeyGenerator; | |||||
import org.sonar.server.almsettings.ws.DevOpsProjectDescriptor; | |||||
import org.sonar.server.component.ComponentCreationData; | |||||
import org.sonar.server.project.ws.ProjectCreator; | |||||
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"; | |||||
@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; | |||||
private static final String USER_LOGIN = "userLogin"; | |||||
private static final String USER_UUID = "userUuid"; | |||||
private static final String GROUP_NAME = "group1"; | |||||
private static final String REPOSITORY_PATH_WITH_NAMESPACE = "pathWith/namespace"; | |||||
private static final String GITLAB_PROJECT_NAME = "gitlabProjectName"; | |||||
private static final String REPOSITORY_ID = "1234"; | |||||
private static final String MAIN_BRANCH_NAME = "defaultBranch"; | |||||
private static final String ALM_SETTING_KEY = "gitlab_config_1"; | |||||
private static final String ALM_SETTING_UUID = "almSettingUuid"; | |||||
private static final String USER_PAT = "1234"; | |||||
public static final String GITLAB_URL = "http://api.com"; | |||||
private static final DevOpsProjectDescriptor DEVOPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITLAB, GITLAB_URL, REPOSITORY_ID); | |||||
@BeforeEach | |||||
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.projectIdentifier()).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(DEVOPS_PROJECT_DESCRIPTOR.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_generatesOneKeyAndUsersGitlabProjectName() { | |||||
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(DEVOPS_PROJECT_DESCRIPTOR.url(), USER_PAT, Long.valueOf(REPOSITORY_ID))).thenReturn(project); | |||||
} | |||||
private void mockMainBranch() { | |||||
when(gitlabApplicationClient.getBranches(DEVOPS_PROJECT_DESCRIPTOR.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); | |||||
} | |||||
} |
package org.sonar.server.platform.platformlevel; | package org.sonar.server.platform.platformlevel; | ||||
import java.util.List; | import java.util.List; | ||||
import org.sonar.alm.client.RatioBasedRateLimitChecker; | |||||
import org.sonar.alm.client.TimeoutConfigurationImpl; | import org.sonar.alm.client.TimeoutConfigurationImpl; | ||||
import org.sonar.alm.client.azure.AzureDevOpsHttpClient; | import org.sonar.alm.client.azure.AzureDevOpsHttpClient; | ||||
import org.sonar.alm.client.azure.AzureDevOpsValidator; | import org.sonar.alm.client.azure.AzureDevOpsValidator; | ||||
import org.sonar.alm.client.github.GithubHeaders; | import org.sonar.alm.client.github.GithubHeaders; | ||||
import org.sonar.alm.client.github.GithubPaginatedHttpClient; | import org.sonar.alm.client.github.GithubPaginatedHttpClient; | ||||
import org.sonar.alm.client.github.GithubPermissionConverter; | import org.sonar.alm.client.github.GithubPermissionConverter; | ||||
import org.sonar.alm.client.RatioBasedRateLimitChecker; | |||||
import org.sonar.alm.client.github.config.GithubProvisioningConfigValidator; | import org.sonar.alm.client.github.config.GithubProvisioningConfigValidator; | ||||
import org.sonar.alm.client.github.security.GithubAppSecurityImpl; | import org.sonar.alm.client.github.security.GithubAppSecurityImpl; | ||||
import org.sonar.alm.client.gitlab.GitlabApplicationClient; | |||||
import org.sonar.alm.client.gitlab.GitlabApplicationHttpClient; | import org.sonar.alm.client.gitlab.GitlabApplicationHttpClient; | ||||
import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; | import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; | ||||
import org.sonar.alm.client.gitlab.GitlabHeaders; | import org.sonar.alm.client.gitlab.GitlabHeaders; | ||||
import org.sonar.alm.client.gitlab.GitlabApplicationClient; | |||||
import org.sonar.alm.client.gitlab.GitlabPaginatedHttpClient; | import org.sonar.alm.client.gitlab.GitlabPaginatedHttpClient; | ||||
import org.sonar.api.resources.ResourceTypes; | import org.sonar.api.resources.ResourceTypes; | ||||
import org.sonar.api.server.rule.RulesDefinitionXmlLoader; | import org.sonar.api.server.rule.RulesDefinitionXmlLoader; | ||||
import org.sonar.server.almsettings.ws.AlmSettingsWsModule; | import org.sonar.server.almsettings.ws.AlmSettingsWsModule; | ||||
import org.sonar.server.almsettings.ws.DelegatingDevOpsProjectCreatorFactory; | import org.sonar.server.almsettings.ws.DelegatingDevOpsProjectCreatorFactory; | ||||
import org.sonar.server.almsettings.ws.GithubProjectCreatorFactory; | import org.sonar.server.almsettings.ws.GithubProjectCreatorFactory; | ||||
import org.sonar.server.almsettings.ws.gitlab.GitlabProjectCreatorFactory; | |||||
import org.sonar.server.authentication.AuthenticationModule; | import org.sonar.server.authentication.AuthenticationModule; | ||||
import org.sonar.server.authentication.DefaultAdminCredentialsVerifierImpl; | import org.sonar.server.authentication.DefaultAdminCredentialsVerifierImpl; | ||||
import org.sonar.server.authentication.DefaultAdminCredentialsVerifierNotificationHandler; | import org.sonar.server.authentication.DefaultAdminCredentialsVerifierNotificationHandler; | ||||
import org.sonar.server.common.gitlab.config.GitlabConfigurationService; | import org.sonar.server.common.gitlab.config.GitlabConfigurationService; | ||||
import org.sonar.server.common.group.service.GroupMembershipService; | import org.sonar.server.common.group.service.GroupMembershipService; | ||||
import org.sonar.server.common.group.service.GroupService; | import org.sonar.server.common.group.service.GroupService; | ||||
import org.sonar.server.common.rule.RuleCreator; | |||||
import org.sonar.server.common.rule.service.RuleService; | import org.sonar.server.common.rule.service.RuleService; | ||||
import org.sonar.server.common.text.MacroInterpreter; | import org.sonar.server.common.text.MacroInterpreter; | ||||
import org.sonar.server.component.ComponentCleanerService; | import org.sonar.server.component.ComponentCleanerService; | ||||
import org.sonar.server.qualityprofile.builtin.RuleActivator; | import org.sonar.server.qualityprofile.builtin.RuleActivator; | ||||
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; | import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; | ||||
import org.sonar.server.qualityprofile.ws.QProfilesWsModule; | import org.sonar.server.qualityprofile.ws.QProfilesWsModule; | ||||
import org.sonar.server.common.rule.RuleCreator; | |||||
import org.sonar.server.rule.RuleDefinitionsLoader; | import org.sonar.server.rule.RuleDefinitionsLoader; | ||||
import org.sonar.server.rule.RuleDescriptionFormatter; | import org.sonar.server.rule.RuleDescriptionFormatter; | ||||
import org.sonar.server.rule.RuleUpdater; | import org.sonar.server.rule.RuleUpdater; | ||||
GitlabPaginatedHttpClient.class, | GitlabPaginatedHttpClient.class, | ||||
GitlabApplicationClient.class, | GitlabApplicationClient.class, | ||||
GitlabGlobalSettingsValidator.class, | GitlabGlobalSettingsValidator.class, | ||||
GitlabProjectCreatorFactory.class, | |||||
AzureDevOpsValidator.class, | AzureDevOpsValidator.class, | ||||
// ALM settings | // ALM settings |