@@ -102,13 +102,15 @@ public interface GithubApplicationClient { | |||
private final boolean isPrivate; | |||
private final String fullName; | |||
private final String url; | |||
private final String defaultBranch; | |||
public Repository(long id, String name, boolean isPrivate, String fullName, String url) { | |||
public Repository(long id, String name, boolean isPrivate, String fullName, String url, String defaultBranch) { | |||
this.id = id; | |||
this.name = name; | |||
this.isPrivate = isPrivate; | |||
this.fullName = fullName; | |||
this.url = url; | |||
this.defaultBranch = defaultBranch; | |||
} | |||
public long getId() { | |||
@@ -131,6 +133,10 @@ public interface GithubApplicationClient { | |||
return url; | |||
} | |||
public String getDefaultBranch() { | |||
return defaultBranch; | |||
} | |||
@Override | |||
public String toString() { | |||
return "Repository{" + |
@@ -183,7 +183,7 @@ public class GithubApplicationClientImpl implements GithubApplicationClient { | |||
if (gsonRepositories.get().items != null) { | |||
repositories.setRepositories(gsonRepositories.get().items.stream() | |||
.map(gsonRepository -> new Repository(gsonRepository.id, gsonRepository.name, gsonRepository.isPrivate, gsonRepository.fullName, gsonRepository.url)) | |||
.map(GsonGithubRepository::toRepository) | |||
.collect(toList())); | |||
} | |||
@@ -200,7 +200,7 @@ public class GithubApplicationClientImpl implements GithubApplicationClient { | |||
GetResponse response = appHttpClient.get(appUrl, accessToken, String.format("/repos/%s", repositoryKey)); | |||
return response.getContent() | |||
.map(content -> GSON.fromJson(content, GsonGithubRepository.class)) | |||
.map(repository -> new Repository(repository.id, repository.name, repository.isPrivate, repository.fullName, repository.url)); | |||
.map(GsonGithubRepository::toRepository); | |||
} catch (Exception e) { | |||
throw new IllegalStateException(format("Failed to get repository '%s' of '%s' accessible by user access token on '%s'", repositoryKey, organization, appUrl), e); | |||
} |
@@ -22,6 +22,8 @@ package org.sonar.alm.client.github; | |||
import com.google.gson.annotations.SerializedName; | |||
import java.util.List; | |||
import static org.sonar.alm.client.github.GithubApplicationClient.*; | |||
public class GithubBinding { | |||
private GithubBinding() { | |||
@@ -138,11 +140,18 @@ public class GithubBinding { | |||
boolean isPrivate; | |||
@SerializedName("url") | |||
String url; | |||
@SerializedName("default_branch") | |||
String defaultBranch; | |||
public GsonGithubRepository() { | |||
// even if empty constructor is not required for Gson, it is strongly | |||
// recommended: | |||
// http://stackoverflow.com/a/18645370/229031 | |||
} | |||
public Repository toRepository() { | |||
return new Repository(this.id, this.name, this.isPrivate, this.fullName, | |||
this.url, this.defaultBranch); | |||
} | |||
} | |||
} |
@@ -793,8 +793,8 @@ public class GithubApplicationClientImplTest { | |||
.isPresent() | |||
.get() | |||
.extracting(GithubApplicationClient.Repository::getId, GithubApplicationClient.Repository::getName, GithubApplicationClient.Repository::getFullName, | |||
GithubApplicationClient.Repository::getUrl, GithubApplicationClient.Repository::isPrivate) | |||
.containsOnly(1296269L, "Hello-World", "octocat/Hello-World", "https://github.sonarsource.com/api/v3/repos/octocat/Hello-World", false); | |||
GithubApplicationClient.Repository::getUrl, GithubApplicationClient.Repository::isPrivate, GithubApplicationClient.Repository::getDefaultBranch) | |||
.containsOnly(1296269L, "Hello-World", "octocat/Hello-World", "https://github.sonarsource.com/api/v3/repos/octocat/Hello-World", false, "master"); | |||
} | |||
private AppToken mockAppToken() { |
@@ -72,5 +72,4 @@ public class ImportHelper { | |||
.setVisibility(Visibility.getLabel(componentDto.isPrivate()))) | |||
.build(); | |||
} | |||
} |
@@ -61,7 +61,7 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction { | |||
private final ImportHelper importHelper; | |||
public ImportGithubProjectAction(DbClient dbClient, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility, | |||
GithubApplicationClientImpl githubApplicationClient, ComponentUpdater componentUpdater, ImportHelper importHelper) { | |||
GithubApplicationClientImpl githubApplicationClient, ComponentUpdater componentUpdater, ImportHelper importHelper) { | |||
this.dbClient = dbClient; | |||
this.userSession = userSession; | |||
this.projectDefaultVisibility = projectDefaultVisibility; | |||
@@ -121,22 +121,23 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction { | |||
Repository repository = githubApplicationClient.getRepository(url, accessToken, githubOrganization, repositoryKey) | |||
.orElseThrow(() -> new NotFoundException(String.format("GitHub repository '%s' not found", repositoryKey))); | |||
ComponentDto componentDto = createProject(dbSession, repository); | |||
ComponentDto componentDto = createProject(dbSession, repository, repository.getDefaultBranch()); | |||
populatePRSetting(dbSession, repository, componentDto, almSettingDto); | |||
componentUpdater.commitAndIndex(dbSession, componentDto); | |||
return toCreateResponse(componentDto); | |||
} | |||
} | |||
private ComponentDto createProject(DbSession dbSession, Repository repo) { | |||
private ComponentDto createProject(DbSession dbSession, Repository repo, String mainBranchName) { | |||
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate(); | |||
return componentUpdater.create(dbSession, newComponentBuilder() | |||
return componentUpdater.createWithoutCommit(dbSession, newComponentBuilder() | |||
.setKey(getProjectKeyFromRepository(repo)) | |||
.setName(repo.getName()) | |||
.setPrivate(visibility) | |||
.setQualifier(PROJECT) | |||
.build(), | |||
userSession.getUuid()); | |||
userSession.getUuid(), mainBranchName, s -> {}); | |||
} | |||
static String getProjectKeyFromRepository(Repository repo) { | |||
@@ -152,6 +153,5 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction { | |||
.setSummaryCommentEnabled(true) | |||
.setMonorepo(false); | |||
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, projectAlmSettingDto); | |||
dbSession.commit(); | |||
} | |||
} |
@@ -22,6 +22,7 @@ package org.sonar.server.component; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.Date; | |||
import java.util.Locale; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.function.Consumer; | |||
import javax.annotation.Nullable; | |||
@@ -88,11 +89,22 @@ public class ComponentUpdater { | |||
* Create component without committing. | |||
* Don't forget to call commitAndIndex(...) when ready to commit. | |||
*/ | |||
public ComponentDto createWithoutCommit(DbSession dbSession, NewComponent newComponent, @Nullable String userUuid, Consumer<ComponentDto> componentModifier) { | |||
public ComponentDto createWithoutCommit(DbSession dbSession, NewComponent newComponent, | |||
@Nullable String userUuid, Consumer<ComponentDto> componentModifier) { | |||
return createWithoutCommit(dbSession, newComponent, userUuid, null, componentModifier); | |||
} | |||
/** | |||
* Create component without committing. | |||
* Don't forget to call commitAndIndex(...) when ready to commit. | |||
*/ | |||
public ComponentDto createWithoutCommit(DbSession dbSession, NewComponent newComponent, | |||
@Nullable String userUuid, @Nullable String mainBranchName, | |||
Consumer<ComponentDto> componentModifier) { | |||
checkKeyFormat(newComponent.qualifier(), newComponent.key()); | |||
ComponentDto componentDto = createRootComponent(dbSession, newComponent, componentModifier); | |||
if (isRootProject(componentDto)) { | |||
createMainBranch(dbSession, componentDto.uuid()); | |||
createMainBranch(dbSession, componentDto.uuid(), mainBranchName); | |||
} | |||
handlePermissionTemplate(dbSession, componentDto, userUuid); | |||
return componentDto; | |||
@@ -152,11 +164,11 @@ public class ComponentUpdater { | |||
&& MAIN_BRANCH_QUALIFIERS.contains(componentDto.qualifier()); | |||
} | |||
private void createMainBranch(DbSession session, String componentUuid) { | |||
private void createMainBranch(DbSession session, String componentUuid, @Nullable String mainBranch) { | |||
BranchDto branch = new BranchDto() | |||
.setBranchType(BranchType.BRANCH) | |||
.setUuid(componentUuid) | |||
.setKey(BranchDto.DEFAULT_MAIN_BRANCH_NAME) | |||
.setKey(Optional.ofNullable(mainBranch).orElse(BranchDto.DEFAULT_MAIN_BRANCH_NAME)) | |||
.setMergeBranchUuid(null) | |||
.setExcludeFromPurge(true) | |||
.setProjectUuid(componentUuid); |
@@ -0,0 +1,97 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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.almintegration.ws; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.BranchDto; | |||
import org.sonar.db.component.BranchType; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonarqube.ws.Projects.CreateWsResponse; | |||
public class ImportHelperTest { | |||
private final System2 system2 = System2.INSTANCE; | |||
private final ComponentDto componentDto = ComponentTesting.newPublicProjectDto(); | |||
private final BranchDto branchDto = new BranchDto() | |||
.setBranchType(BranchType.BRANCH) | |||
.setKey("main") | |||
.setUuid(componentDto.uuid()) | |||
.setProjectUuid(componentDto.uuid()); | |||
private final Request request = mock(Request.class); | |||
@Rule | |||
public final DbTester db = DbTester.create(system2); | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||
private ImportHelper underTest = new ImportHelper(db.getDbClient(), userSession); | |||
@Test | |||
public void it_throws_exception_when_provisioning_project_without_permission() { | |||
assertThatThrownBy(() -> underTest.checkProvisionProjectPermission()) | |||
.isInstanceOf(UnauthorizedException.class) | |||
.hasMessage("Authentication is required"); | |||
} | |||
@Test | |||
public void it_throws_exception_on_get_alm_setting_when_key_is_empty() { | |||
assertThatThrownBy(() -> underTest.getAlmSetting(request)) | |||
.isInstanceOf(NotFoundException.class); | |||
} | |||
@Test | |||
public void it_throws_exception_on_get_alm_setting_when_key_is_not_found() { | |||
when(request.mandatoryParam("almSetting")).thenReturn("key"); | |||
assertThatThrownBy(() -> underTest.getAlmSetting(request)) | |||
.isInstanceOf(NotFoundException.class) | |||
.hasMessage("ALM Setting 'key' not found"); | |||
} | |||
@Test | |||
public void it_throws_exception_when_user_uuid_is_null() { | |||
assertThatThrownBy(() -> underTest.getUserUuid()) | |||
.isInstanceOf(NullPointerException.class) | |||
.hasMessage("User UUID cannot be null"); | |||
} | |||
@Test | |||
public void it_returns_create_response() { | |||
CreateWsResponse response = ImportHelper.toCreateResponse(componentDto); | |||
CreateWsResponse.Project project = response.getProject(); | |||
assertThat(project).extracting(CreateWsResponse.Project::getKey, CreateWsResponse.Project::getName, | |||
CreateWsResponse.Project::getQualifier) | |||
.containsExactly(componentDto.getDbKey(), componentDto.name(), componentDto.qualifier()); | |||
} | |||
} |
@@ -31,6 +31,7 @@ import org.sonar.core.i18n.I18n; | |||
import org.sonar.core.util.SequenceUuidFactory; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.alm.setting.AlmSettingDto; | |||
import org.sonar.db.component.BranchDto; | |||
import org.sonar.db.permission.GlobalPermission; | |||
import org.sonar.db.project.ProjectDto; | |||
import org.sonar.db.user.UserDto; | |||
@@ -90,7 +91,7 @@ public class ImportGithubProjectActionTest { | |||
db.almPats().insert(p -> p.setAlmSettingUuid(githubAlmSetting.getUuid()).setUserUuid(userSession.getUuid())); | |||
GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, "Hello-World", false, "octocat/Hello-World", | |||
"https://github.sonarsource.com/api/v3/repos/octocat/Hello-World"); | |||
"https://github.sonarsource.com/api/v3/repos/octocat/Hello-World", "default-branch"); | |||
when(appClient.getRepository(any(), any(), any(), any())) | |||
.thenReturn(Optional.of(repository)); | |||
@@ -107,6 +108,9 @@ public class ImportGithubProjectActionTest { | |||
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey()); | |||
assertThat(projectDto).isPresent(); | |||
assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent(); | |||
Optional<BranchDto> mainBranch = db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get()).stream().filter(BranchDto::isMain).findAny(); | |||
assertThat(mainBranch).isPresent(); | |||
assertThat(mainBranch.get().getKey()).isEqualTo("default-branch"); | |||
} | |||
@Test | |||
@@ -116,7 +120,7 @@ public class ImportGithubProjectActionTest { | |||
db.components().insertPublicProject(p -> p.setDbKey("octocat_Hello-World")); | |||
GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, "Hello-World", false, "octocat/Hello-World", | |||
"https://github.sonarsource.com/api/v3/repos/octocat/Hello-World"); | |||
"https://github.sonarsource.com/api/v3/repos/octocat/Hello-World", "main"); | |||
when(appClient.getRepository(any(), any(), any(), any())) | |||
.thenReturn(Optional.of(repository)); | |||
@@ -107,7 +107,8 @@ public class ListGithubRepositoriesActionTest { | |||
.thenReturn(new GithubApplicationClient.Repositories() | |||
.setTotal(2) | |||
.setRepositories(Stream.of("HelloWorld", "HelloUniverse") | |||
.map(name -> new GithubApplicationClient.Repository(name.length(), name, false, "github/" + name, "https://github-enterprise.sonarqube.com/api/v3/github/HelloWorld")) | |||
.map(name -> new GithubApplicationClient.Repository(name.length(), name, false, "github/" + name, | |||
"https://github-enterprise.sonarqube.com/api/v3/github/HelloWorld", "main")) | |||
.collect(Collectors.toList()))); | |||
ProjectDto project = db.components().insertPrivateProjectDto(componentDto -> componentDto.setDbKey("github_HelloWorld")); |