aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server-common
diff options
context:
space:
mode:
authorMichal Duda <michal.duda@sonarsource.com>2020-12-03 17:32:12 +0100
committersonartech <sonartech@sonarsource.com>2020-12-04 20:06:50 +0000
commit60843a4f5ecce218f0253742177566243739e139 (patch)
tree96946f19294ca628f6992f1867f6b84d02322957 /server/sonar-server-common
parent17439910a22be35c8f4a9d601ab8a4b524df287a (diff)
downloadsonarqube-60843a4f5ecce218f0253742177566243739e139.tar.gz
sonarqube-60843a4f5ecce218f0253742177566243739e139.zip
SONAR-14189 sonar.dbcleaner.branchesToKeepWhenInactive ignored when set on project level
Diffstat (limited to 'server/sonar-server-common')
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java49
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java73
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java193
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);
+ }
+}