diff options
author | Michal Duda <michal.duda@sonarsource.com> | 2020-12-03 17:32:12 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-12-04 20:06:50 +0000 |
commit | 60843a4f5ecce218f0253742177566243739e139 (patch) | |
tree | 96946f19294ca628f6992f1867f6b84d02322957 /server/sonar-server-common | |
parent | 17439910a22be35c8f4a9d601ab8a4b524df287a (diff) | |
download | sonarqube-60843a4f5ecce218f0253742177566243739e139.tar.gz sonarqube-60843a4f5ecce218f0253742177566243739e139.zip |
SONAR-14189 sonar.dbcleaner.branchesToKeepWhenInactive ignored when set on project level
Diffstat (limited to 'server/sonar-server-common')
3 files changed, 315 insertions, 0 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java b/server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java new file mode 100644 index 00000000000..d9a366bf3ba --- /dev/null +++ b/server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.setting; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.sonar.api.config.Configuration; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public interface ProjectConfigurationLoader { + /** + * Loads configuration for the specified components. + * + * <p> + * Returns the applicable component configuration with most specific configuration overriding more global ones + * (eg. global > project > branch). + * + * <p> + * Any component is accepted but SQ only supports specific properties for projects and branches. + */ + Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects); + + default Configuration loadProjectConfiguration(DbSession dbSession, ComponentDto project) { + Map<String, Configuration> configurations = loadProjectConfigurations(dbSession, Collections.singleton(project)); + return requireNonNull(configurations.get(project.uuid()), () -> format("Configuration for project '%s' is not found", project.getKey())); + } +} diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java b/server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java new file mode 100644 index 00000000000..8d81a7937a0 --- /dev/null +++ b/server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.setting; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.sonar.api.config.Configuration; +import org.sonar.api.config.internal.Settings; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.property.PropertyDto; + +import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; + +public class ProjectConfigurationLoaderImpl implements ProjectConfigurationLoader { + private final Settings globalSettings; + private final DbClient dbClient; + + public ProjectConfigurationLoaderImpl(Settings globalSettings, DbClient dbClient) { + this.globalSettings = globalSettings; + this.dbClient = dbClient; + } + + @Override + public Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects) { + Set<String> mainBranchDbKeys = projects.stream().map(ComponentDto::getKey).collect(Collectors.toSet()); + Map<String, ChildSettings> mainBranchSettingsByDbKey = loadMainBranchConfigurations(dbSession, mainBranchDbKeys); + return projects.stream() + .collect(uniqueIndex(ComponentDto::uuid, component -> { + if (component.getDbKey().equals(component.getKey())) { + return mainBranchSettingsByDbKey.get(component.getKey()).asConfiguration(); + } + + ChildSettings settings = new ChildSettings(mainBranchSettingsByDbKey.get(component.getKey())); + dbClient.propertiesDao() + .selectProjectProperties(dbSession, component.getDbKey()) + .forEach(property -> settings.setProperty(property.getKey(), property.getValue())); + return settings.asConfiguration(); + })); + } + + private Map<String, ChildSettings> loadMainBranchConfigurations(DbSession dbSession, Set<String> dbKeys) { + return dbKeys.stream().collect(uniqueIndex(Function.identity(), dbKey -> { + ChildSettings settings = new ChildSettings(globalSettings); + List<PropertyDto> propertyDtos = dbClient.propertiesDao() + .selectProjectProperties(dbSession, dbKey); + propertyDtos + .forEach(property -> settings.setProperty(property.getKey(), property.getValue())); + return settings; + })); + } +} diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java new file mode 100644 index 00000000000..59bd9aff81b --- /dev/null +++ b/server/sonar-server-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java @@ -0,0 +1,193 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.setting; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import java.util.Collections; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Configuration; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.property.PropertiesDao; +import org.sonar.db.property.PropertyDto; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ProjectConfigurationLoaderImplTest { + private final DbClient dbClient = mock(DbClient.class); + private final DbSession dbSession = mock(DbSession.class); + private final PropertiesDao propertiesDao = mock(PropertiesDao.class); + private final MapSettings globalSettings = new MapSettings(); + private final ProjectConfigurationLoaderImpl underTest = new ProjectConfigurationLoaderImpl(globalSettings, dbClient); + + @Before + public void setUp() { + when(dbClient.openSession(anyBoolean())) + .thenThrow(new IllegalStateException("ProjectConfigurationLoaderImpl should not open DB session")); + when(dbClient.propertiesDao()).thenReturn(propertiesDao); + } + + @Test + public void returns_empty_map_when_no_component() { + assertThat(underTest.loadProjectConfigurations(dbSession, Collections.emptySet())) + .isEmpty(); + + verifyNoInteractions(propertiesDao); + } + + @Test + public void return_configuration_with_just_global_settings_when_no_component_settings() { + String key = randomAlphanumeric(3); + String value = randomAlphanumeric(4); + String componentDbKey = randomAlphanumeric(5); + String componentUuid = randomAlphanumeric(6); + globalSettings.setProperty(key, value); + when(propertiesDao.selectProjectProperties(dbSession, componentDbKey)) + .thenReturn(emptyList()); + ComponentDto component = newComponentDto(componentDbKey, componentUuid); + + Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component)); + + assertThat(configurations) + .containsOnlyKeys(componentUuid); + assertThat(configurations.get(componentUuid).get(key)).contains(value); + } + + @Test + public void returns_single_configuration_for_single_project_load() { + String key = randomAlphanumeric(3); + String value = randomAlphanumeric(4); + String componentDbKey = randomAlphanumeric(5); + String componentUuid = randomAlphanumeric(6); + globalSettings.setProperty(key, value); + when(propertiesDao.selectProjectProperties(dbSession, componentDbKey)) + .thenReturn(emptyList()); + ComponentDto component = newComponentDto(componentDbKey, componentUuid); + + Configuration configuration = underTest.loadProjectConfiguration(dbSession, component); + assertThat(configuration.get(key)).hasValue(value); + } + + @Test + public void return_configuration_with_global_settings_and_component_settings() { + String globalKey = randomAlphanumeric(3); + String globalValue = randomAlphanumeric(4); + String componentDbKey = randomAlphanumeric(5); + String componentUuid = randomAlphanumeric(6); + String projectPropKey1 = randomAlphanumeric(7); + String projectPropValue1 = randomAlphanumeric(8); + String projectPropKey2 = randomAlphanumeric(9); + String projectPropValue2 = randomAlphanumeric(10); + globalSettings.setProperty(globalKey, globalValue); + when(propertiesDao.selectProjectProperties(dbSession, componentDbKey)) + .thenReturn(ImmutableList.of(newPropertyDto(projectPropKey1, projectPropValue1), newPropertyDto(projectPropKey2, projectPropValue2))); + ComponentDto component = newComponentDto(componentDbKey, componentUuid); + + Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component)); + + assertThat(configurations) + .containsOnlyKeys(componentUuid); + assertThat(configurations.get(componentUuid).get(globalKey)).contains(globalValue); + assertThat(configurations.get(componentUuid).get(projectPropKey1)).contains(projectPropValue1); + assertThat(configurations.get(componentUuid).get(projectPropKey2)).contains(projectPropValue2); + } + + @Test + public void return_configuration_with_global_settings_main_branch_settings_and_branch_settings() { + String globalKey = randomAlphanumeric(3); + String globalValue = randomAlphanumeric(4); + String mainBranchDbKey = randomAlphanumeric(5); + String branchDbKey = mainBranchDbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5); + String branchUuid = randomAlphanumeric(6); + String mainBranchPropKey = randomAlphanumeric(7); + String mainBranchPropValue = randomAlphanumeric(8); + String branchPropKey = randomAlphanumeric(9); + String branchPropValue = randomAlphanumeric(10); + globalSettings.setProperty(globalKey, globalValue); + when(propertiesDao.selectProjectProperties(dbSession, mainBranchDbKey)) + .thenReturn(ImmutableList.of(newPropertyDto(mainBranchPropKey, mainBranchPropValue))); + when(propertiesDao.selectProjectProperties(dbSession, branchDbKey)) + .thenReturn(ImmutableList.of(newPropertyDto(branchPropKey, branchPropValue))); + ComponentDto component = newComponentDto(branchDbKey, branchUuid); + + Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component)); + + assertThat(configurations) + .containsOnlyKeys(branchUuid); + assertThat(configurations.get(branchUuid).get(globalKey)).contains(globalValue); + assertThat(configurations.get(branchUuid).get(mainBranchPropKey)).contains(mainBranchPropValue); + assertThat(configurations.get(branchUuid).get(branchPropKey)).contains(branchPropValue); + } + + @Test + public void loads_configuration_of_any_given_component_only_once() { + String mainBranch1DbKey = randomAlphanumeric(4); + String mainBranch1Uuid = randomAlphanumeric(5); + String branch1DbKey = mainBranch1DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5); + String branch1Uuid = randomAlphanumeric(6); + String branch2DbKey = mainBranch1DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(7); + String branch2Uuid = randomAlphanumeric(8); + String mainBranch2DbKey = randomAlphanumeric(14); + String mainBranch2Uuid = randomAlphanumeric(15); + String branch3DbKey = mainBranch2DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5); + String branch3Uuid = randomAlphanumeric(16); + + ComponentDto mainBranch1 = newComponentDto(mainBranch1DbKey, mainBranch1Uuid); + ComponentDto branch1 = newComponentDto(branch1DbKey, branch1Uuid); + ComponentDto branch2 = newComponentDto(branch2DbKey, branch2Uuid); + ComponentDto mainBranch2 = newComponentDto(mainBranch2DbKey, mainBranch2Uuid); + ComponentDto branch3 = newComponentDto(branch3DbKey, branch3Uuid); + + underTest.loadProjectConfigurations(dbSession, ImmutableSet.of(mainBranch1, mainBranch2, branch1, branch2, branch3)); + + verify(propertiesDao, times(1)).selectProjectProperties(dbSession, mainBranch1DbKey); + verify(propertiesDao, times(1)).selectProjectProperties(dbSession, mainBranch2DbKey); + verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch1DbKey); + verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch2DbKey); + verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch3DbKey); + verifyNoMoreInteractions(propertiesDao); + } + + private ComponentDto newComponentDto(String componentDbKey, String componentUuid) { + return new ComponentDto().setDbKey(componentDbKey).setUuid(componentUuid); + } + + private PropertyDto newPropertyDto(String projectKey1, String projectValue1) { + return new PropertyDto() + .setKey(projectKey1) + .setValue(projectValue1); + } +} |