--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 com.google.common.annotations.VisibleForTesting;
+import java.util.List;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.core.util.UuidFactory;
+
+import static com.google.common.collect.Lists.asList;
+import static org.sonar.core.component.ComponentKeys.sanitizeProjectKey;
+
+public class ProjectKeyGenerator {
+
+ @VisibleForTesting
+ static final int MAX_PROJECT_KEY_SIZE = 250;
+ @VisibleForTesting
+ static final Character PROJECT_KEY_SEPARATOR = '_';
+
+ private final UuidFactory uuidFactory;
+
+ public ProjectKeyGenerator(UuidFactory uuidFactory) {
+ this.uuidFactory = uuidFactory;
+ }
+
+ public String generateUniqueProjectKey(String projectName, String... extraProjectKeyItems) {
+ String sqProjectKey = generateCompleteProjectKey(projectName, extraProjectKeyItems);
+ sqProjectKey = truncateProjectKeyIfNecessary(sqProjectKey);
+ return sanitizeProjectKey(sqProjectKey);
+ }
+
+ private String generateCompleteProjectKey(String projectName, String[] extraProjectKeyItems) {
+ List<String> projectKeyItems = asList(projectName, extraProjectKeyItems);
+ String projectKey = StringUtils.join(projectKeyItems, PROJECT_KEY_SEPARATOR);
+ String uuid = uuidFactory.create();
+ return projectKey + PROJECT_KEY_SEPARATOR + uuid;
+ }
+
+ private static String truncateProjectKeyIfNecessary(String sqProjectKey) {
+ if (sqProjectKey.length() > MAX_PROJECT_KEY_SIZE) {
+ return sqProjectKey.substring(sqProjectKey.length() - MAX_PROJECT_KEY_SIZE);
+ }
+ return sqProjectKey;
+ }
+
+}
*/
package org.sonar.server.almintegration.ws.azure;
-import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
import org.sonar.alm.client.azure.GsonAzureRepo;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.project.ProjectDefaultVisibility;
import org.sonar.server.user.UserSession;
private final ProjectDefaultVisibility projectDefaultVisibility;
private final ComponentUpdater componentUpdater;
private final ImportHelper importHelper;
+ private final ProjectKeyGenerator projectKeyGenerator;
public ImportAzureProjectAction(DbClient dbClient, UserSession userSession, AzureDevOpsHttpClient azureDevOpsHttpClient,
ProjectDefaultVisibility projectDefaultVisibility, ComponentUpdater componentUpdater,
- ImportHelper importHelper) {
+ ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator) {
this.dbClient = dbClient;
this.userSession = userSession;
this.azureDevOpsHttpClient = azureDevOpsHttpClient;
this.projectDefaultVisibility = projectDefaultVisibility;
this.componentUpdater = componentUpdater;
this.importHelper = importHelper;
+ this.projectKeyGenerator = projectKeyGenerator;
}
@Override
private ComponentDto createProject(DbSession dbSession, GsonAzureRepo repo) {
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
+ String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(repo.getProject().getName(), repo.getName());
return componentUpdater.createWithoutCommit(dbSession, newComponentBuilder()
- .setKey(generateProjectKey(repo.getProject().getName(), repo.getName()))
+ .setKey(uniqueProjectKey)
.setName(repo.getName())
.setPrivate(visibility)
.setQualifier(PROJECT)
componentDto.name(), componentDto.getKey());
}
- @VisibleForTesting
- String generateProjectKey(String projectName, String repoName) {
- String sqProjectKey = projectName + "_" + repoName;
-
- if (sqProjectKey.length() > 250) {
- sqProjectKey = sqProjectKey.substring(sqProjectKey.length() - 250);
- }
-
- return sqProjectKey.replace(" ", "_");
- }
-
}
import org.sonar.db.component.ComponentDto;
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.component.NewComponent;
import org.sonar.server.project.ProjectDefaultVisibility;
private final ProjectDefaultVisibility projectDefaultVisibility;
private final ComponentUpdater componentUpdater;
private final ImportHelper importHelper;
+ private final ProjectKeyGenerator projectKeyGenerator;
public ImportBitbucketCloudRepoAction(DbClient dbClient, UserSession userSession, BitbucketCloudRestClient bitbucketCloudRestClient,
- ProjectDefaultVisibility projectDefaultVisibility, ComponentUpdater componentUpdater, ImportHelper importHelper) {
+ ProjectDefaultVisibility projectDefaultVisibility, ComponentUpdater componentUpdater, ImportHelper importHelper,
+ ProjectKeyGenerator projectKeyGenerator) {
this.dbClient = dbClient;
this.userSession = userSession;
this.bitbucketCloudRestClient = bitbucketCloudRestClient;
this.projectDefaultVisibility = projectDefaultVisibility;
this.componentUpdater = componentUpdater;
this.importHelper = importHelper;
+ this.projectKeyGenerator = projectKeyGenerator;
}
@Override
private ComponentDto createProject(DbSession dbSession, String workspace, Repository repo, @Nullable String defaultBranchName) {
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
+ String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(workspace, repo.getSlug());
NewComponent newProject = newComponentBuilder()
- .setKey(workspace + "_" + repo.getSlug())
+ .setKey(uniqueProjectKey)
.setName(repo.getName())
.setPrivate(visibility)
.setQualifier(PROJECT)
import org.sonar.db.component.ComponentDto;
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.component.NewComponent;
import org.sonar.server.project.ProjectDefaultVisibility;
private final ProjectDefaultVisibility projectDefaultVisibility;
private final ComponentUpdater componentUpdater;
private final ImportHelper importHelper;
+ private final ProjectKeyGenerator projectKeyGenerator;
public ImportBitbucketServerProjectAction(DbClient dbClient, UserSession userSession, BitbucketServerRestClient bitbucketServerRestClient,
ProjectDefaultVisibility projectDefaultVisibility, ComponentUpdater componentUpdater,
- ImportHelper importHelper) {
+ ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator) {
this.dbClient = dbClient;
this.userSession = userSession;
this.bitbucketServerRestClient = bitbucketServerRestClient;
this.projectDefaultVisibility = projectDefaultVisibility;
this.componentUpdater = componentUpdater;
this.importHelper = importHelper;
+ this.projectKeyGenerator = projectKeyGenerator;
}
@Override
private ComponentDto createProject(DbSession dbSession, Repository repo, @Nullable String defaultBranchName) {
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
+ String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(repo.getProject().getKey(), repo.getSlug());
NewComponent newProject = newComponentBuilder()
- .setKey(getProjectKey(repo))
+ .setKey(uniqueProjectKey)
.setName(repo.getName())
.setPrivate(visibility)
.setQualifier(PROJECT)
componentDto.name(), componentDto.getKey());
}
- private static String getProjectKey(Repository repo) {
- String key = repo.getProject().getKey() + "_" + repo.getSlug();
- return key.replace("~", "");
- }
-
}
import org.sonar.db.component.ComponentDto;
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.project.ProjectDefaultVisibility;
private final GithubApplicationClient githubApplicationClient;
private final ComponentUpdater componentUpdater;
private final ImportHelper importHelper;
+ private final ProjectKeyGenerator projectKeyGenerator;
public ImportGithubProjectAction(DbClient dbClient, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility,
- GithubApplicationClientImpl githubApplicationClient, ComponentUpdater componentUpdater, ImportHelper importHelper) {
+ GithubApplicationClientImpl githubApplicationClient, ComponentUpdater componentUpdater, ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator) {
this.dbClient = dbClient;
this.userSession = userSession;
this.projectDefaultVisibility = projectDefaultVisibility;
this.githubApplicationClient = githubApplicationClient;
this.componentUpdater = componentUpdater;
this.importHelper = importHelper;
+ this.projectKeyGenerator = projectKeyGenerator;
}
@Override
private ComponentDto createProject(DbSession dbSession, Repository repo, String mainBranchName) {
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
+ String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(repo.getFullName());
return componentUpdater.createWithoutCommit(dbSession, newComponentBuilder()
- .setKey(getProjectKeyFromRepository(repo))
- .setName(repo.getName())
- .setPrivate(visibility)
- .setQualifier(PROJECT)
- .build(),
+ .setKey(uniqueProjectKey)
+ .setName(repo.getName())
+ .setPrivate(visibility)
+ .setQualifier(PROJECT)
+ .build(),
userSession.getUuid(), userSession.getLogin(), mainBranchName, s -> {});
}
- static String getProjectKeyFromRepository(Repository repo) {
- return repo.getFullName().replace("/", "_");
- }
-
private void populatePRSetting(DbSession dbSession, Repository repo, ComponentDto componentDto, AlmSettingDto almSettingDto) {
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto()
.setAlmSettingUuid(almSettingDto.getUuid())
*/
package org.sonar.server.almintegration.ws.gitlab;
-import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import javax.annotation.Nullable;
import org.sonar.alm.client.gitlab.GitLabBranch;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.alm.pat.AlmPatDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.almintegration.ws.AlmIntegrationsWsAction;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.project.ProjectDefaultVisibility;
import org.sonar.server.user.UserSession;
private final ProjectDefaultVisibility projectDefaultVisibility;
private final GitlabHttpClient gitlabHttpClient;
private final ComponentUpdater componentUpdater;
- private final UuidFactory uuidFactory;
private final ImportHelper importHelper;
+ private final ProjectKeyGenerator projectKeyGenerator;
public ImportGitLabProjectAction(DbClient dbClient, UserSession userSession,
ProjectDefaultVisibility projectDefaultVisibility, GitlabHttpClient gitlabHttpClient,
- ComponentUpdater componentUpdater, UuidFactory uuidFactory, ImportHelper importHelper) {
+ ComponentUpdater componentUpdater, ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator) {
this.dbClient = dbClient;
this.userSession = userSession;
this.projectDefaultVisibility = projectDefaultVisibility;
this.gitlabHttpClient = gitlabHttpClient;
this.componentUpdater = componentUpdater;
- this.uuidFactory = uuidFactory;
this.importHelper = importHelper;
+ this.projectKeyGenerator = projectKeyGenerator;
}
@Override
private ComponentDto createProject(DbSession dbSession, Project gitlabProject, @Nullable String mainBranchName) {
boolean visibility = projectDefaultVisibility.get(dbSession).isPrivate();
- String sqProjectKey = generateProjectKey(gitlabProject.getPathWithNamespace(), uuidFactory.create());
+ String uniqueProjectKey = projectKeyGenerator.generateUniqueProjectKey(gitlabProject.getPathWithNamespace());
return componentUpdater.createWithoutCommit(dbSession, newComponentBuilder()
- .setKey(sqProjectKey)
+ .setKey(uniqueProjectKey)
.setName(gitlabProject.getName())
.setPrivate(visibility)
.setQualifier(PROJECT)
});
}
- @VisibleForTesting
- String generateProjectKey(String pathWithNamespace, String uuid) {
- String sqProjectKey = pathWithNamespace + "_" + uuid;
-
- if (sqProjectKey.length() > 250) {
- sqProjectKey = sqProjectKey.substring(sqProjectKey.length() - 250);
- }
-
- return sqProjectKey.replace("/", "_");
- }
}
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;
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
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
- private ImportHelper underTest = new ImportHelper(db.getDbClient(), userSession);
+ private final ImportHelper underTest = new ImportHelper(db.getDbClient(), userSession);
@Test
public void it_throws_exception_when_provisioning_project_without_permission() {
CreateWsResponse.Project project = response.getProject();
assertThat(project).extracting(CreateWsResponse.Project::getKey, CreateWsResponse.Project::getName,
- CreateWsResponse.Project::getQualifier)
+ CreateWsResponse.Project::getQualifier)
.containsExactly(componentDto.getDbKey(), componentDto.name(), componentDto.qualifier());
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.apache.commons.lang.RandomStringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.core.util.UuidFactory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.almintegration.ws.ProjectKeyGenerator.MAX_PROJECT_KEY_SIZE;
+import static org.sonar.server.almintegration.ws.ProjectKeyGenerator.PROJECT_KEY_SEPARATOR;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ProjectKeyGeneratorTest {
+
+ private static final int MAX_UUID_SIZE = 40;
+ private static final String UUID_STRING = RandomStringUtils.randomAlphanumeric(MAX_UUID_SIZE);
+
+ @Mock
+ private UuidFactory uuidFactory;
+
+ @InjectMocks
+ private ProjectKeyGenerator projectKeyGenerator;
+
+ @Before
+ public void setUp() {
+ when(uuidFactory.create()).thenReturn(UUID_STRING);
+ }
+
+ @Test
+ public void generateUniqueProjectKey_shortProjectName_shouldAppendUuid() {
+ String fullProjectName = RandomStringUtils.randomAlphanumeric(10);
+
+ assertThat(projectKeyGenerator.generateUniqueProjectKey(fullProjectName))
+ .isEqualTo(generateExpectedKeyName(fullProjectName));
+ }
+
+ @Test
+ public void generateUniqueProjectKey_projectNameEqualsToMaximumSize_shouldTruncateProjectNameAndPreserveUUID() {
+ String fullProjectName = RandomStringUtils.randomAlphanumeric(MAX_PROJECT_KEY_SIZE);
+
+ String projectKey = projectKeyGenerator.generateUniqueProjectKey(fullProjectName);
+ assertThat(projectKey)
+ .hasSize(MAX_PROJECT_KEY_SIZE)
+ .isEqualTo(generateExpectedKeyName(fullProjectName.substring(fullProjectName.length() + UUID_STRING.length() + 1 - MAX_PROJECT_KEY_SIZE)));
+ }
+
+ @Test
+ public void generateUniqueProjectKey_projectNameBiggerThanMaximumSize_shouldTruncateProjectNameAndPreserveUUID() {
+ String fullProjectName = RandomStringUtils.randomAlphanumeric(MAX_PROJECT_KEY_SIZE + 50);
+
+ String projectKey = projectKeyGenerator.generateUniqueProjectKey(fullProjectName);
+ assertThat(projectKey)
+ .hasSize(MAX_PROJECT_KEY_SIZE)
+ .isEqualTo(generateExpectedKeyName(fullProjectName.substring(fullProjectName.length() + UUID_STRING.length() + 1 - MAX_PROJECT_KEY_SIZE)));
+ }
+
+ @Test
+ public void generateUniqueProjectKey_projectNameContainsSlashes_shouldBeEscaped() {
+ String fullProjectName = "a/b/c";
+
+ assertThat(projectKeyGenerator.generateUniqueProjectKey(fullProjectName))
+ .isEqualTo(generateExpectedKeyName(fullProjectName.replace("/", "_")));
+ }
+
+ private String generateExpectedKeyName(String truncatedProjectName) {
+ return truncatedProjectName + PROJECT_KEY_SEPARATOR + UUID_STRING;
+ }
+}
package org.sonar.server.almintegration.ws.azure;
import java.util.Optional;
-import java.util.stream.IntStream;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Projects;
-import static java.util.stream.Collectors.joining;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.db.alm.integration.pat.AlmPatsTesting.newAlmPatDto;
import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
public class ImportAzureProjectActionTest {
+ private static final String GENERATED_PROJECT_KEY = "TEST_PROJECT_KEY";
+
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
private final Encryption encryption = mock(Encryption.class);
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
+ private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
private final ImportAzureProjectAction importAzureProjectAction = new ImportAzureProjectAction(db.getDbClient(), userSession,
- azureDevOpsHttpClient, projectDefaultVisibility, componentUpdater, importHelper);
+ azureDevOpsHttpClient, projectDefaultVisibility, componentUpdater, importHelper, projectKeyGenerator);
private final WsActionTester ws = new WsActionTester(importAzureProjectAction);
@Before
public void before() {
when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
+ when(projectKeyGenerator.generateUniqueProjectKey(any(), any())).thenReturn(GENERATED_PROJECT_KEY);
}
@Test
.executeProtobuf(Projects.CreateWsResponse.class);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(repo.getProject().getName() + "_" + repo.getName());
+ assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
assertThat(result.getName()).isEqualTo(repo.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
.findFirst();
assertThat(mainBranch).isPresent();
assertThat(mainBranch.get().getKey()).hasToString("repo-default-branch");
+
+ verify(projectKeyGenerator).generateUniqueProjectKey(repo.getProject().getName(), repo.getName());
}
@Test
dto.setUserUuid(user.getUuid());
});
GsonAzureRepo repo = getGsonAzureRepo();
- String projectKey = repo.getProject().getName() + "_" + repo.getName();
- db.components().insertPublicProject(p -> p.setDbKey(projectKey));
+ db.components().insertPublicProject(p -> p.setDbKey(GENERATED_PROJECT_KEY));
when(azureDevOpsHttpClient.getRepo(almSetting.getUrl(), almSetting.getDecryptedPersonalAccessToken(encryption),
"project-name", "repo-name")).thenReturn(repo);
assertThatThrownBy(request::execute)
.isInstanceOf(BadRequestException.class)
- .hasMessage("Could not create null, key already exists: " + projectKey);
- }
-
- @Test
- public void sanitize_project_and_repo_names_with_invalid_characters() {
- assertThat(importAzureProjectAction.generateProjectKey("project name", "repo name"))
- .isEqualTo("project_name_repo_name");
- }
-
- @Test
- public void sanitize_long_project_and_repo_names() {
- String projectName = IntStream.range(0, 260).mapToObj(i -> "a").collect(joining());
-
- assertThat(importAzureProjectAction.generateProjectKey(projectName, "repo name"))
- .hasSize(250);
+ .hasMessage("Could not create null, key already exists: " + GENERATED_PROJECT_KEY);
}
@Test
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Projects;
+import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.db.alm.integration.pat.AlmPatsTesting.newAlmPatDto;
import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
import static org.sonar.db.permission.GlobalPermission.SCAN;
public class ImportBitbucketCloudRepoActionTest {
+
+ private static final String GENERATED_PROJECT_KEY = "TEST_PROJECT_KEY";
+
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory());
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
+ private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
private final WsActionTester ws = new WsActionTester(new ImportBitbucketCloudRepoAction(db.getDbClient(), userSession,
- bitbucketCloudRestClient, projectDefaultVisibility, componentUpdater, importHelper));
+ bitbucketCloudRestClient, projectDefaultVisibility, componentUpdater, importHelper, projectKeyGenerator));
@Before
public void before() {
when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
+ when(projectKeyGenerator.generateUniqueProjectKey(any(), any())).thenReturn(GENERATED_PROJECT_KEY);
}
@Test
.executeProtobuf(Projects.CreateWsResponse.class);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(almSetting.getAppId() + "_" + repo.getSlug());
+ assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
assertThat(result.getName()).isEqualTo(repo.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
Optional<BranchDto> branchDto = db.getDbClient().branchDao().selectByBranchKey(db.getSession(), projectDto.get().getUuid(), "develop");
assertThat(branchDto).isPresent();
assertThat(branchDto.get().isMain()).isTrue();
+ verify(projectKeyGenerator).generateUniqueProjectKey(requireNonNull(almSetting.getAppId()), repo.getSlug());
}
@Test
dto.setUserUuid(user.getUuid());
});
Repository repo = getGsonBBCRepo();
- String projectKey = almSetting.getAppId() + "_" + repo.getSlug();
- db.components().insertPublicProject(p -> p.setDbKey(projectKey));
+ db.components().insertPublicProject(p -> p.setDbKey(GENERATED_PROJECT_KEY));
when(bitbucketCloudRestClient.getRepo(any(), any(), any())).thenReturn(repo);
assertThatThrownBy(request::execute)
.isInstanceOf(BadRequestException.class)
- .hasMessageContaining("Could not create null, key already exists: " + projectKey);
+ .hasMessageContaining("Could not create null, key already exists: " + GENERATED_PROJECT_KEY);
}
@Test
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Projects;
+import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.apache.commons.lang.math.JVMRandom.nextLong;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.db.alm.integration.pat.AlmPatsTesting.newAlmPatDto;
import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
import static org.sonar.db.permission.GlobalPermission.SCAN;
public class ImportBitbucketServerProjectActionTest {
+ private static final String GENERATED_PROJECT_KEY = "TEST_PROJECT_KEY";
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory());
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
+ private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
private final WsActionTester ws = new WsActionTester(new ImportBitbucketServerProjectAction(db.getDbClient(), userSession,
- bitbucketServerRestClient, projectDefaultVisibility, componentUpdater, importHelper));
+ bitbucketServerRestClient, projectDefaultVisibility, componentUpdater, importHelper, projectKeyGenerator));
private static BranchesList defaultBranchesList;
@Before
public void before() {
when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PRIVATE);
+ when(projectKeyGenerator.generateUniqueProjectKey(any(), any())).thenReturn(GENERATED_PROJECT_KEY);
}
@Test
.executeProtobuf(Projects.CreateWsResponse.class);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(project.getKey() + "_" + repo.getSlug());
+ assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
assertThat(result.getName()).isEqualTo(repo.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
assertThat(projectDto).isPresent();
assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
- }
-
- @Test
- public void import_project_with_tilda() {
- UserDto user = db.users().insertUser();
- userSession.logIn(user).addPermission(PROVISION_PROJECTS);
- AlmSettingDto almSetting = db.almSettings().insertGitHubAlmSetting();
- db.almPats().insert(dto -> {
- dto.setAlmSettingUuid(almSetting.getUuid());
- dto.setUserUuid(user.getUuid());
- });
- Project project = getGsonBBSProject();
- project.setKey("~" + project.getKey());
- Repository repo = getGsonBBSRepo(project);
- when(bitbucketServerRestClient.getRepo(any(), any(), any(), any())).thenReturn(repo);
- when(bitbucketServerRestClient.getBranches(any(), any(), any(), any())).thenReturn(defaultBranchesList);
-
- Projects.CreateWsResponse response = ws.newRequest()
- .setParam("almSetting", almSetting.getKey())
- .setParam("projectKey", "~projectKey")
- .setParam("repositorySlug", "repo-slug")
- .executeProtobuf(Projects.CreateWsResponse.class);
-
- Projects.CreateWsResponse.Project result = response.getProject();
-
- String key = project.getKey() + "_" + repo.getSlug();
- assertThat(result.getKey()).isNotEqualTo(key);
- assertThat(result.getKey()).isEqualTo(key.substring(1));
+ verify(projectKeyGenerator).generateUniqueProjectKey(requireNonNull(project.getKey()), repo.getSlug());
}
@Test
});
Project project = getGsonBBSProject();
Repository repo = getGsonBBSRepo(project);
- String projectKey = project.getKey() + "_" + repo.getSlug();
- db.components().insertPublicProject(p -> p.setDbKey(projectKey));
+ db.components().insertPublicProject(p -> p.setDbKey(GENERATED_PROJECT_KEY));
assertThatThrownBy(() -> {
when(bitbucketServerRestClient.getRepo(any(), any(), any(), any())).thenReturn(repo);
.execute();
})
.isInstanceOf(BadRequestException.class)
- .hasMessage("Could not create null, key already exists: " + projectKey);
+ .hasMessage("Could not create null, key already exists: " + GENERATED_PROJECT_KEY);
}
@Test
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
-import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.favorite.FavoriteUpdater;
public class ImportGithubProjectActionTest {
+ private static final String PROJECT_KEY_NAME = "PROJECT_NAME";
+
@Rule
public UserSessionRule userSession = standalone();
mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory());
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
+ private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), userSession,
- projectDefaultVisibility, appClient, componentUpdater, importHelper));
+ projectDefaultVisibility, appClient, componentUpdater, importHelper, projectKeyGenerator));
@Before
public void before() {
}
@Test
- public void import_project() {
+ public void importProject_ifProjectWithSameNameDoesNotExist_importSucceed() {
AlmSettingDto githubAlmSetting = setupAlm();
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", "default-branch");
- when(appClient.getRepository(any(), any(), any(), any()))
- .thenReturn(Optional.of(repository));
+ GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, PROJECT_KEY_NAME, false, "octocat/" + PROJECT_KEY_NAME,
+ "https://github.sonarsource.com/api/v3/repos/octocat/" + PROJECT_KEY_NAME, "default-branch");
+ when(appClient.getRepository(any(), any(), any(), any())).thenReturn(Optional.of(repository));
+ when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn(PROJECT_KEY_NAME);
Projects.CreateWsResponse response = ws.newRequest()
.setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
.setParam(PARAM_ORGANIZATION, "octocat")
- .setParam(PARAM_REPOSITORY_KEY, "octocat/Hello-World")
+ .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
.executeProtobuf(Projects.CreateWsResponse.class);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(repository.getFullName().replace("/", "_"));
+ assertThat(result.getKey()).isEqualTo(PROJECT_KEY_NAME);
assertThat(result.getName()).isEqualTo(repository.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
}
@Test
- public void fail_project_already_exist() {
+ public void importProject_ifProjectWithSameNameAlreadyExists_importSucceed() {
AlmSettingDto githubAlmSetting = setupAlm();
db.almPats().insert(p -> p.setAlmSettingUuid(githubAlmSetting.getUuid()).setUserUuid(userSession.getUuid()));
- db.components().insertPublicProject(p -> p.setDbKey("octocat_Hello-World"));
+ db.components().insertPublicProject(p -> p.setDbKey("Hello-World"));
- GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, "Hello-World", false, "octocat/Hello-World",
+ GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, "Hello-World", false, "Hello-World",
"https://github.sonarsource.com/api/v3/repos/octocat/Hello-World", "main");
- when(appClient.getRepository(any(), any(), any(), any()))
- .thenReturn(Optional.of(repository));
+ when(appClient.getRepository(any(), any(), any(), any())).thenReturn(Optional.of(repository));
+ when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn(PROJECT_KEY_NAME);
- TestRequest request = ws.newRequest()
- .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
- .setParam(PARAM_ORGANIZATION, "octocat")
- .setParam(PARAM_REPOSITORY_KEY, "octocat/Hello-World");
- assertThatThrownBy(request::execute)
- .isInstanceOf(BadRequestException.class)
- .hasMessage("Could not create null, key already exists: octocat_Hello-World");
+ Projects.CreateWsResponse response = ws.newRequest()
+ .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
+ .setParam(PARAM_ORGANIZATION, "octocat")
+ .setParam(PARAM_REPOSITORY_KEY, "Hello-World")
+ .executeProtobuf(Projects.CreateWsResponse.class);
+
+ Projects.CreateWsResponse.Project result = response.getProject();
+ assertThat(result.getKey()).isEqualTo(PROJECT_KEY_NAME);
+ assertThat(result.getName()).isEqualTo(repository.getName());
}
@Test
public void fail_when_not_logged_in() {
TestRequest request = ws.newRequest()
- .setParam(PARAM_ALM_SETTING, "asdfghjkl")
- .setParam(PARAM_ORGANIZATION, "test")
- .setParam(PARAM_REPOSITORY_KEY, "test/repo");
+ .setParam(PARAM_ALM_SETTING, "asdfghjkl")
+ .setParam(PARAM_ORGANIZATION, "test")
+ .setParam(PARAM_REPOSITORY_KEY, "test/repo");
assertThatThrownBy(request::execute)
- .isInstanceOf(UnauthorizedException.class);
+ .isInstanceOf(UnauthorizedException.class);
}
@Test
userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
TestRequest request = ws.newRequest()
- .setParam(PARAM_ALM_SETTING, "unknown")
- .setParam(PARAM_ORGANIZATION, "test")
- .setParam(PARAM_REPOSITORY_KEY, "test/repo");
+ .setParam(PARAM_ALM_SETTING, "unknown")
+ .setParam(PARAM_ORGANIZATION, "test")
+ .setParam(PARAM_REPOSITORY_KEY, "test/repo");
assertThatThrownBy(request::execute)
- .isInstanceOf(NotFoundException.class)
- .hasMessage("ALM Setting 'unknown' not found");
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("ALM Setting 'unknown' not found");
}
@Test
AlmSettingDto githubAlmSetting = setupAlm();
TestRequest request = ws.newRequest()
- .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
- .setParam(PARAM_ORGANIZATION, "test")
- .setParam(PARAM_REPOSITORY_KEY, "test/repo");
+ .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
+ .setParam(PARAM_ORGANIZATION, "test")
+ .setParam(PARAM_REPOSITORY_KEY, "test/repo");
assertThatThrownBy(request::execute)
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("No personal access token found");
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("No personal access token found");
}
@Test
package org.sonar.server.almintegration.ws.gitlab;
import java.util.Optional;
-import java.util.stream.IntStream;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
import org.sonar.api.utils.System2;
import org.sonar.core.i18n.I18n;
import org.sonar.core.util.SequenceUuidFactory;
-import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbTester;
import org.sonar.db.alm.setting.AlmSettingDto;
import org.sonar.db.component.BranchDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
import org.sonar.server.favorite.FavoriteUpdater;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
-import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
public class ImportGitLabProjectActionTest {
+ private static final String PROJECT_KEY_NAME = "PROJECT_NAME";
+
private final System2 system2 = mock(System2.class);
@Rule
mock(PermissionTemplateService.class), new FavoriteUpdater(db.getDbClient()), new TestProjectIndexers(), new SequenceUuidFactory());
private final GitlabHttpClient gitlabHttpClient = mock(GitlabHttpClient.class);
- private final UuidFactory uuidFactory = mock(UuidFactory.class);
private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
+ private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
private final ImportGitLabProjectAction importGitLabProjectAction = new ImportGitLabProjectAction(
- db.getDbClient(), userSession, projectDefaultVisibility, gitlabHttpClient, componentUpdater, uuidFactory, importHelper);
+ db.getDbClient(), userSession, projectDefaultVisibility, gitlabHttpClient, componentUpdater, importHelper, projectKeyGenerator);
private final WsActionTester ws = new WsActionTester(importGitLabProjectAction);
@Before
Project project = getGitlabProject();
when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project);
when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(singletonList(new GitLabBranch("master", true)));
- when(uuidFactory.create()).thenReturn("uuid");
+ when(projectKeyGenerator.generateUniqueProjectKey(project.getPathWithNamespace())).thenReturn(PROJECT_KEY_NAME);
Projects.CreateWsResponse response = ws.newRequest()
.setParam("almSetting", almSetting.getKey())
verify(gitlabHttpClient).getProject(almSetting.getUrl(), "PAT", 12345L);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(project.getPathWithNamespace() + "_uuid");
+ assertThat(result.getKey()).isEqualTo(PROJECT_KEY_NAME);
assertThat(result.getName()).isEqualTo(project.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
Project project = getGitlabProject();
when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project);
when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(singletonList(new GitLabBranch("main", true)));
- when(uuidFactory.create()).thenReturn("uuid");
+ when(projectKeyGenerator.generateUniqueProjectKey(project.getPathWithNamespace())).thenReturn(PROJECT_KEY_NAME);
Projects.CreateWsResponse response = ws.newRequest()
.setParam("almSetting", almSetting.getKey())
verify(gitlabHttpClient).getBranches(almSetting.getUrl(), "PAT", 12345L);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(project.getPathWithNamespace() + "_uuid");
+ assertThat(result.getKey()).isEqualTo(PROJECT_KEY_NAME);
assertThat(result.getName()).isEqualTo(project.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
Project project = getGitlabProject();
when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project);
when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(emptyList());
- when(uuidFactory.create()).thenReturn("uuid");
+ when(projectKeyGenerator.generateUniqueProjectKey(project.getPathWithNamespace())).thenReturn(PROJECT_KEY_NAME);
Projects.CreateWsResponse response = ws.newRequest()
.setParam("almSetting", almSetting.getKey())
verify(gitlabHttpClient).getBranches(almSetting.getUrl(), "PAT", 12345L);
Projects.CreateWsResponse.Project result = response.getProject();
- assertThat(result.getKey()).isEqualTo(project.getPathWithNamespace() + "_uuid");
+ assertThat(result.getKey()).isEqualTo(PROJECT_KEY_NAME);
assertThat(result.getName()).isEqualTo(project.getName());
Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
.containsExactlyInAnyOrder(tuple("master", true));
}
- @Test
- public void generate_project_key_less_than_250() {
- String name = "abcdeert";
- assertThat(importGitLabProjectAction.generateProjectKey(name, "uuid")).isEqualTo("abcdeert_uuid");
- }
-
- @Test
- public void generate_project_key_equal_250() {
- String name = IntStream.range(0, 245).mapToObj(i -> "a").collect(joining());
- String projectKey = importGitLabProjectAction.generateProjectKey(name, "uuid");
- assertThat(projectKey)
- .hasSize(250)
- .isEqualTo(name + "_uuid");
-
- }
-
- @Test
- public void generate_project_key_more_than_250() {
- String name = IntStream.range(0, 250).mapToObj(i -> "a").collect(joining());
- String projectKey = importGitLabProjectAction.generateProjectKey(name, "uuid");
- assertThat(projectKey)
- .hasSize(250)
- .isEqualTo(name.substring(5) + "_uuid");
- }
-
- @Test
- public void generate_project_key_containing_slash() {
- String name = "a/b/c";
- assertThat(importGitLabProjectAction.generateProjectKey(name, "uuid")).isEqualTo("a_b_c_uuid");
- }
private Project getGitlabProject() {
return new Project(randomAlphanumeric(5), randomAlphanumeric(5));
import org.sonar.server.almintegration.ws.AlmIntegrationsWSModule;
import org.sonar.server.almintegration.ws.CredentialsEncoderHelper;
import org.sonar.server.almintegration.ws.ImportHelper;
+import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
import org.sonar.server.almsettings.MultipleAlmFeatureProvider;
import org.sonar.server.almsettings.ws.AlmSettingsWsModule;
import org.sonar.server.authentication.AuthenticationModule;
TimeoutConfigurationImpl.class,
CredentialsEncoderHelper.class,
ImportHelper.class,
+ ProjectKeyGenerator.class,
GithubAppSecurityImpl.class,
GithubApplicationClientImpl.class,
GithubApplicationHttpClientImpl.class,
public static final String MALFORMED_KEY_MESSAGE = "Malformed key for '%s'. %s.";
/**
- * Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit
+ * Allowed characters are alphanumeric, '-', '_', '.' and ':'
*/
- private static final Pattern VALID_PROJECT_KEY_REGEXP = Pattern.compile("[\\p{Alnum}\\-_.:]*[\\p{Alpha}\\-_.:]+[\\p{Alnum}\\-_.:]*");
+ private static final String VALID_PROJECT_KEY_CHARS = "\\p{Alnum}-_.:";
+
+ private static final Pattern INVALID_PROJECT_KEY_REGEXP = Pattern.compile("[^" + VALID_PROJECT_KEY_CHARS + "]");
+
+ /**
+ * At least one non-digit is necessary
+ */
+ private static final Pattern VALID_PROJECT_KEY_REGEXP = Pattern.compile("[" + VALID_PROJECT_KEY_CHARS + "]*[\\p{Alpha}\\-_.:]+[" + VALID_PROJECT_KEY_CHARS + "]*");
private static final String KEY_WITH_BRANCH_FORMAT = "%s:%s";
+ private static final String REPLACEMENT_CHARACTER = "_";
private ComponentKeys() {
// only static stuff
checkArgument(isValidProjectKey(keyCandidate), MALFORMED_KEY_MESSAGE, keyCandidate, ALLOWED_CHARACTERS_MESSAGE);
}
+ public static String sanitizeProjectKey(String rawProjectKey) {
+ return INVALID_PROJECT_KEY_REGEXP.matcher(rawProjectKey).replaceAll(REPLACEMENT_CHARACTER);
+ }
+
/**
* Return the project key with potential branch
*/
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.core.component;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(Parameterized.class)
+public class ComponentKeysSanitizationTest {
+
+ @Parameterized.Parameters(name = "{index}: input {0}, expected output {1}")
+ public static Collection<String[]> data() {
+ return Arrays.asList(new String[][] {
+ {"/a/b/c/", "_a_b_c_"},
+ {".a.b:c:", ".a.b:c:"},
+ {"_1_2_3_", "_1_2_3_"},
+ {"fully_valid_-name2", "fully_valid_-name2"},
+ {"°+\"*ç%&\\/()=?`^“#Ç[]|{}≠¿ ~", "___________________________"},
+ });
+ }
+
+ private final String inputString;
+ private final String expectedOutputString;
+
+ public ComponentKeysSanitizationTest(String inputString, String expectedOutputString) {
+ this.inputString = inputString;
+ this.expectedOutputString = expectedOutputString;
+ }
+
+ @Test
+ public void sanitizeProjectKey() {
+ assertThat(ComponentKeys.sanitizeProjectKey(inputString)).isEqualTo(expectedOutputString);
+ }
+
+}