import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Settings;
import org.sonarqube.ws.Settings.ListDefinitionsWsResponse;
import static org.elasticsearch.common.Strings.isNullOrEmpty;
-import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.settings.ws.SettingsWsComponentParameters.PARAM_COMPONENT_ID;
+import static org.sonar.server.settings.ws.SettingsWsComponentParameters.PARAM_COMPONENT_KEY;
+import static org.sonar.server.settings.ws.SettingsWsComponentParameters.addComponentParameters;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class ListDefinitionsAction implements SettingsWsAction {
- private static final String PARAM_COMPONENT_ID = "componentId";
- private static final String PARAM_COMPONENT_KEY = "componentKey";
-
private final DbClient dbClient;
- private final ComponentFinder componentFinder;
- private final UserSession userSession;
+ private final SettingsWsComponentParameters settingsWsComponentParameters;
private final PropertyDefinitions propertyDefinitions;
- public ListDefinitionsAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, PropertyDefinitions propertyDefinitions) {
+ public ListDefinitionsAction(DbClient dbClient, SettingsWsComponentParameters settingsWsComponentParameters, PropertyDefinitions propertyDefinitions) {
this.dbClient = dbClient;
- this.componentFinder = componentFinder;
- this.userSession = userSession;
+ this.settingsWsComponentParameters = settingsWsComponentParameters;
this.propertyDefinitions = propertyDefinitions;
}
.setSince("6.1")
.setHandler(this);
- action.createParam(PARAM_COMPONENT_ID)
- .setDescription("Component id")
- .setExampleValue(UUID_EXAMPLE_01);
-
- action.createParam(PARAM_COMPONENT_KEY)
- .setDescription("Component key")
- .setExampleValue("my_component_key");
+ addComponentParameters(action);
}
@Override
private String getQualifier(Request request) {
DbSession dbSession = dbClient.openSession(false);
try {
- if (request.hasParam(PARAM_COMPONENT_ID) || request.hasParam(PARAM_COMPONENT_KEY)) {
- ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.param(PARAM_COMPONENT_ID), request.param(PARAM_COMPONENT_KEY),
- ComponentFinder.ParamNames.ID_AND_KEY);
- userSession.checkComponentUuidPermission(UserRole.ADMIN, component.uuid());
- return component.qualifier();
- } else {
- userSession.checkPermission(GlobalPermissions.SYSTEM_ADMIN);
- return null;
- }
+ ComponentDto component = settingsWsComponentParameters.getComponent(dbSession, request);
+ settingsWsComponentParameters.checkAdminPermission(component);
+ return component == null ? null : component.qualifier();
} finally {
dbClient.closeSession(dbSession);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.settings.ws;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.component.ComponentFinder.ParamNames.ID_AND_KEY;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+
+public class SettingsWsComponentParameters {
+
+ static final String PARAM_COMPONENT_ID = "componentId";
+ static final String PARAM_COMPONENT_KEY = "componentKey";
+
+ private final ComponentFinder componentFinder;
+ private final UserSession userSession;
+
+ public SettingsWsComponentParameters(ComponentFinder componentFinder, UserSession userSession) {
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ }
+
+ static void addComponentParameters(WebService.NewAction action) {
+ action.createParam(PARAM_COMPONENT_ID)
+ .setDescription("Component id")
+ .setExampleValue(UUID_EXAMPLE_01);
+
+ action.createParam(PARAM_COMPONENT_KEY)
+ .setDescription("Component key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+ }
+
+ @CheckForNull
+ ComponentDto getComponent(DbSession dbSession, Request request) {
+ if (request.hasParam(PARAM_COMPONENT_ID) || request.hasParam(PARAM_COMPONENT_KEY)) {
+ return componentFinder.getByUuidOrKey(dbSession, request.param(PARAM_COMPONENT_ID), request.param(PARAM_COMPONENT_KEY), ID_AND_KEY);
+ }
+ return null;
+ }
+
+ void checkAdminPermission(@Nullable ComponentDto component) {
+ if (component == null) {
+ userSession.checkPermission(GlobalPermissions.SYSTEM_ADMIN);
+ } else {
+ userSession.checkComponentUuidPermission(UserRole.ADMIN, component.uuid());
+ }
+ }
+
+}
add(
SettingsWs.class,
SetAction.class,
- ListDefinitionsAction.class);
+ SettingsWsComponentParameters.class,
+ ListDefinitionsAction.class,
+ ValuesAction.class);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.settings.ws;
+
+import com.google.common.base.Splitter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.property.PropertyDto;
+import org.sonarqube.ws.Settings;
+import org.sonarqube.ws.Settings.ValuesWsResponse;
+
+import static org.elasticsearch.common.Strings.isNullOrEmpty;
+import static org.sonar.server.settings.ws.SettingsWsComponentParameters.PARAM_COMPONENT_ID;
+import static org.sonar.server.settings.ws.SettingsWsComponentParameters.PARAM_COMPONENT_KEY;
+import static org.sonar.server.settings.ws.SettingsWsComponentParameters.addComponentParameters;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
+public class ValuesAction implements SettingsWsAction {
+
+ private static final Splitter MULTI_VALUE_SPLITTER = Splitter.on(",");
+
+ static final String PARAM_KEYS = "keys";
+
+ private final DbClient dbClient;
+ private final SettingsWsComponentParameters settingsWsComponentParameters;
+ private final PropertyDefinitions propertyDefinitions;
+
+ public ValuesAction(DbClient dbClient, SettingsWsComponentParameters settingsWsComponentParameters, PropertyDefinitions propertyDefinitions) {
+ this.dbClient = dbClient;
+ this.settingsWsComponentParameters = settingsWsComponentParameters;
+ this.propertyDefinitions = propertyDefinitions;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction("values")
+ .setDescription(String.format("Returns values of given properties.<br>" +
+ "If no value have been set for a property, then the default value is returned.<br>" +
+ "Either '%s' or '%s' could be provided, not both.<br> " +
+ "Requires one of the following permissions: " +
+ "<ul>" +
+ "<li>'Administer System'</li>" +
+ "<li>'Administer' rights on the specified component</li>" +
+ "</ul>", PARAM_COMPONENT_ID, PARAM_COMPONENT_KEY))
+ .setResponseExample(getClass().getResource("values-example.json"))
+ .setSince("6.1")
+ .setHandler(this);
+ addComponentParameters(action);
+ action.createParam(PARAM_KEYS)
+ .setDescription("List of property keys")
+ .setRequired(true)
+ .setExampleValue("sonar.technicalDebt.hoursInDay,sonar.dbcleaner.cleanDirectory");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ writeProtobuf(doHandle(request), request, response);
+ }
+
+ private ValuesWsResponse doHandle(Request request) {
+ DbSession dbSession = dbClient.openSession(true);
+ try {
+ ComponentDto component = settingsWsComponentParameters.getComponent(dbSession, request);
+ settingsWsComponentParameters.checkAdminPermission(component);
+ Set<String> keys = new HashSet<>(request.mandatoryParamAsStrings(PARAM_KEYS));
+
+ List<PropertyDefinition> definitions = getDefinitions(keys);
+ Map<String, PropertyDefinition> definitionsByKey = definitions.stream()
+ .collect(Collectors.toMap(PropertyDefinition::key, Function.identity()));
+
+ ValuesWsResponse.Builder valuesBuilder = ValuesWsResponse.newBuilder();
+ new ValuesBuilder(dbSession, valuesBuilder, definitionsByKey, keys, component).build();
+ return valuesBuilder.build();
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private class ValuesBuilder {
+ private final DbSession dbSession;
+ private final ValuesWsResponse.Builder valuesWsBuilder;
+ private final Map<String, PropertyDefinition> definitionsByKey;
+ private final Set<String> keys;
+ private final ComponentDto component;
+
+ private final Map<String, Settings.Value.Builder> valueBuilderByKey = new HashMap<>();
+
+ ValuesBuilder(DbSession dbSession, ValuesWsResponse.Builder valuesWsBuilder, Map<String, PropertyDefinition> definitionsByKey,
+ Set<String> keys, @Nullable ComponentDto component) {
+ this.dbSession = dbSession;
+ this.valuesWsBuilder = valuesWsBuilder;
+ this.definitionsByKey = definitionsByKey;
+ this.keys = keys;
+ this.component = component;
+ }
+
+ void build() {
+ processDefinitions();
+ processPropertyDtos(dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, keys));
+ if (component != null) {
+ processPropertyDtos(dbClient.propertiesDao().selectComponentPropertiesByKeys(dbSession, keys, component.getId()));
+ }
+ valueBuilderByKey.values().forEach(Settings.Value.Builder::build);
+ }
+
+ private void processDefinitions() {
+ definitionsByKey.values().stream()
+ .filter(defaultProperty -> !isNullOrEmpty(defaultProperty.defaultValue()))
+ .forEach(this::processDefaultValue);
+ }
+
+ private void processDefaultValue(PropertyDefinition definition) {
+ Settings.Value.Builder valueBuilder = valuesWsBuilder.addValuesBuilder()
+ .setKey(definition.key())
+ .setIsDefault(true);
+ setValue(valueBuilder, definition.defaultValue());
+ valueBuilderByKey.put(definition.key(), valueBuilder);
+ }
+
+ private void processPropertyDtos(List<PropertyDto> properties) {
+ properties.stream()
+ .filter(propertyDto -> !isNullOrEmpty(propertyDto.getValue()))
+ .forEach(this::processDtoValue);
+ }
+
+ private void processDtoValue(PropertyDto property) {
+ Settings.Value.Builder valueBuilder = valueBuilderByKey.get(property.getKey());
+ if (valueBuilder == null) {
+ valueBuilder = valuesWsBuilder.addValuesBuilder().setKey(property.getKey());
+ valueBuilderByKey.put(property.getKey(), valueBuilder);
+ }
+ valueBuilder.setIsInherited(component != null && property.getResourceId() == null);
+ valueBuilder.setIsDefault(false);
+ setValue(valueBuilder, property.getValue());
+ }
+
+ private void setValue(Settings.Value.Builder valueBuilder, String value) {
+ PropertyDefinition definition = definitionsByKey.get(valueBuilder.getKey());
+ if (definition != null && definition.multiValues()) {
+ valueBuilder.addAllValues(MULTI_VALUE_SPLITTER.split(value));
+ } else {
+ valueBuilder.setValue(value);
+ }
+ }
+ }
+
+ private List<PropertyDefinition> getDefinitions(Set<String> keys) {
+ return propertyDefinitions.getAll().stream()
+ .filter(def -> keys.contains(def.key()))
+ .collect(Collectors.toList());
+ }
+
+}
--- /dev/null
+{
+ "values": [
+ {
+ "key": "sonar.test.jira",
+ "value": "abc",
+ "isDefault": true
+ },
+ {
+ "key": "sonar.autogenerated",
+ "values": [
+ "val1",
+ "val2",
+ "val3"
+ ],
+ "isDefault": false
+ },
+ {
+ "key": "sonar.demo",
+ "setValues": {
+ "text": {
+ "values": [
+ "foo",
+ "bar"
+ ]
+ },
+ "boolean": {
+ "values": [
+ "true",
+ "false"
+ ]
+ }
+ },
+ "isDefault": false
+ }
+ ]
+}
import static org.sonar.core.permission.GlobalPermissions.DASHBOARD_SHARING;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonarqube.ws.MediaTypes.JSON;
import static org.sonarqube.ws.Settings.Type.BOOLEAN;
import static org.sonarqube.ws.Settings.Type.PROPERTY_SET;
import static org.sonarqube.ws.Settings.Type.SINGLE_SELECT_LIST;
ComponentDto project;
PropertyDefinitions propertyDefinitions = new PropertyDefinitions();
+ SettingsWsComponentParameters settingsWsComponentParameters = new SettingsWsComponentParameters(new ComponentFinder(dbClient), userSession);
- WsActionTester ws = new WsActionTester(new ListDefinitionsAction(dbClient, new ComponentFinder(dbClient), userSession, propertyDefinitions));
+ WsActionTester ws = new WsActionTester(new ListDefinitionsAction(dbClient, settingsWsComponentParameters, propertyDefinitions));
@Before
public void setUp() throws Exception {
- project = insertProject();
+ project = componentDb.insertComponent(newProjectDto());
}
@Test
.build())
.build()));
- String result = ws.newRequest().setMediaType(MediaTypes.JSON).execute().getInput();
+ String result = ws.newRequest().setMediaType(JSON).execute().getInput();
JsonAssert.assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result);
}
- private ComponentDto insertProject() {
- return componentDb.insertComponent(newProjectDto());
- }
-
private ListDefinitionsWsResponse newRequest() {
return newRequest(null, null);
}
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
-import static org.sonar.db.property.PropertyTesting.newGlobalProperty;
-import static org.sonar.db.property.PropertyTesting.newProjectProperty;
+import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto;
+import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto;
public class SetActionTest {
@Test
public void update_existing_global_property() {
- propertyDb.insertProperty(newGlobalProperty("my.key", "my value"));
+ propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my value"));
assertGlobalProperty("my.key", "my value");
callForGlobalProperty("my.key", "my new value");
@Test
public void persist_new_project_property() {
- propertyDb.insertProperty(newGlobalProperty("my.key", "my global value"));
+ propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my global value"));
ComponentDto project = componentDb.insertProject();
callForProjectPropertyByUuid("my.key", "my project value", project.uuid());
@Test
public void update_existing_project_property() {
- propertyDb.insertProperty(newGlobalProperty("my.key", "my global value"));
+ propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my global value"));
ComponentDto project = componentDb.insertProject();
- propertyDb.insertProperty(newProjectProperty("my.key", "my project value", project.getId()));
+ propertyDb.insertProperty(newComponentPropertyDto("my.key", "my project value", project));
assertProjectProperty("my.key", "my project value", project.getId());
callForProjectPropertyByKey("my.key", "my new project value", project.key());
@Test
public void user_property_is_not_updated() {
- propertyDb.insertProperty(newGlobalProperty("my.key", "my user value").setUserId(42L));
- propertyDb.insertProperty(newGlobalProperty("my.key", "my global value"));
+ propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my user value").setUserId(42L));
+ propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my global value"));
callForGlobalProperty("my.key", "my new global value");
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new SettingsWsModule().configure(container);
- assertThat(container.size()).isEqualTo(3 + 2);
+ assertThat(container.size()).isEqualTo(5 + 2);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.settings.ws;
+
+import com.google.common.base.Joiner;
+import java.io.IOException;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.property.PropertyDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonar.test.JsonAssert;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.Settings;
+import org.sonarqube.ws.Settings.ValuesWsResponse;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.sonar.api.web.UserRole.ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.core.permission.GlobalPermissions.DASHBOARD_SHARING;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto;
+import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto;
+import static org.sonarqube.ws.MediaTypes.JSON;
+
+public class ValuesActionTest {
+
+ static Joiner COMMA_JOINER = Joiner.on(",");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = db.getDbClient();
+ DbSession dbSession = db.getSession();
+ ComponentDbTester componentDb = new ComponentDbTester(db);
+ SettingsWsComponentParameters settingsWsComponentParameters = new SettingsWsComponentParameters(new ComponentFinder(dbClient), userSession);
+ PropertyDefinitions propertyDefinitions = new PropertyDefinitions();
+
+ ComponentDto project;
+
+ WsActionTester ws = new WsActionTester(new ValuesAction(dbClient, settingsWsComponentParameters, propertyDefinitions));
+
+ @Before
+ public void setUp() throws Exception {
+ project = componentDb.insertComponent(newProjectDto());
+ }
+
+ @Test
+ public void return_simple_value() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition
+ .builder("foo")
+ .build());
+ insertProperties(newGlobalPropertyDto().setKey("foo").setValue("one"));
+
+ ValuesWsResponse result = newRequestForGlobalProperties("foo");
+ assertThat(result.getValuesList()).hasSize(1);
+
+ Settings.Value value = result.getValues(0);
+ assertThat(value.getKey()).isEqualTo("foo");
+ assertThat(value.getValue()).isEqualTo("one");
+ assertThat(value.getValuesCount()).isZero();
+ assertThat(value.getSetValues()).isEmpty();
+ assertThat(value.getIsDefault()).isFalse();
+ }
+
+ @Test
+ public void return_multi_values() throws Exception {
+ setUserAsSystemAdmin();
+
+ // Property never defined, default value is returned
+ propertyDefinitions.addComponent(PropertyDefinition.builder("default")
+ .multiValues(true)
+ .defaultValue("one,two")
+ .build());
+
+ // Property defined at global level
+ propertyDefinitions.addComponent(PropertyDefinition.builder("global")
+ .multiValues(true)
+ .build());
+ insertProperties(newGlobalPropertyDto().setKey("global").setValue("three,four"));
+
+ ValuesWsResponse result = newRequestForGlobalProperties("default", "global");
+ assertThat(result.getValuesList()).hasSize(2);
+
+ Settings.Value foo = result.getValues(0);
+ assertThat(foo.getKey()).isEqualTo("default");
+ assertThat(foo.hasValue()).isFalse();
+ assertThat(foo.getValuesList()).containsOnly("one", "two");
+ assertThat(foo.getSetValues()).isEmpty();
+
+ Settings.Value bar = result.getValues(1);
+ assertThat(bar.getKey()).isEqualTo("global");
+ assertThat(bar.hasValue()).isFalse();
+ assertThat(bar.getValuesList()).containsOnly("three", "four");
+ assertThat(bar.getSetValues()).isEmpty();
+ }
+
+ @Test
+ public void return_default_values() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition
+ .builder("foo")
+ .defaultValue("default")
+ .build());
+
+ ValuesWsResponse result = newRequestForGlobalProperties("foo");
+ assertThat(result.getValuesList()).hasSize(1);
+
+ Settings.Value value = result.getValues(0);
+ assertThat(value.getKey()).isEqualTo("foo");
+ assertThat(value.getValue()).isEqualTo("default");
+ assertThat(value.getIsDefault()).isTrue();
+ assertThat(value.getIsInherited()).isFalse();
+ }
+
+ @Test
+ public void return_global_values() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition.builder("property").defaultValue("default").build());
+ insertProperties(
+ // The property is overriding default value
+ newGlobalPropertyDto().setKey("property").setValue("one"));
+
+ ValuesWsResponse result = newRequestForGlobalProperties("property");
+ assertThat(result.getValuesList()).hasSize(1);
+
+ Settings.Value globalPropertyValue = result.getValues(0);
+ assertThat(globalPropertyValue.getKey()).isEqualTo("property");
+ assertThat(globalPropertyValue.getValue()).isEqualTo("one");
+ assertThat(globalPropertyValue.getIsDefault()).isFalse();
+ assertThat(globalPropertyValue.getIsInherited()).isFalse();
+ }
+
+ @Test
+ public void return_component_values() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition.builder("property").defaultValue("default").build());
+ insertProperties(
+ newGlobalPropertyDto().setKey("property").setValue("one"),
+ // The property is overriding global value
+ newComponentPropertyDto(project).setKey("property").setValue("two"));
+
+ ValuesWsResponse result = newRequestForProjectProperties("property");
+ assertThat(result.getValuesList()).hasSize(1);
+
+ Settings.Value globalPropertyValue = result.getValues(0);
+ assertThat(globalPropertyValue.getKey()).isEqualTo("property");
+ assertThat(globalPropertyValue.getValue()).isEqualTo("two");
+ assertThat(globalPropertyValue.getIsDefault()).isFalse();
+ assertThat(globalPropertyValue.getIsInherited()).isFalse();
+ }
+
+ @Test
+ public void return_is_inherited_to_true_when_property_is_defined_only_at_global_level() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition.builder("property").defaultValue("default").build());
+ // The property is not defined on project
+ insertProperties(newGlobalPropertyDto().setKey("property").setValue("one"));
+
+ ValuesWsResponse result = newRequestForProjectProperties("property");
+ assertThat(result.getValuesList()).hasSize(1);
+
+ Settings.Value globalPropertyValue = result.getValues(0);
+ assertThat(globalPropertyValue.getKey()).isEqualTo("property");
+ assertThat(globalPropertyValue.getValue()).isEqualTo("one");
+ assertThat(globalPropertyValue.getIsDefault()).isFalse();
+ assertThat(globalPropertyValue.getIsInherited()).isTrue();
+ }
+
+ @Test
+ public void return_values_even_if_no_property_definition() throws Exception {
+ setUserAsSystemAdmin();
+ insertProperties(newGlobalPropertyDto().setKey("globalPropertyWithoutDefinition").setValue("value"));
+
+ ValuesWsResponse result = newRequestForGlobalProperties("globalPropertyWithoutDefinition");
+ Settings.Value globalPropertyWithoutDefinitionValue = result.getValues(0);
+ assertThat(globalPropertyWithoutDefinitionValue.getKey()).isEqualTo("globalPropertyWithoutDefinition");
+ assertThat(globalPropertyWithoutDefinitionValue.getValue()).isEqualTo("value");
+ assertThat(globalPropertyWithoutDefinitionValue.getIsDefault()).isFalse();
+ }
+
+ @Test
+ public void return_empty_when_property_def_exists_but_no_value() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition
+ .builder("foo")
+ .build());
+ insertProperties(newGlobalPropertyDto().setKey("bar").setValue(""));
+
+ ValuesWsResponse result = newRequestForGlobalProperties("foo", "bar");
+ assertThat(result.getValuesList()).isEmpty();
+ }
+
+ @Test
+ public void does_return_nothing_when_unknown_keys() throws Exception {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition
+ .builder("foo")
+ .defaultValue("default")
+ .build());
+ insertProperties(newGlobalPropertyDto().setKey("bar").setValue(""));
+
+ ValuesWsResponse result = newRequestForGlobalProperties("unknown");
+ assertThat(result.getValuesList()).isEmpty();
+ }
+
+ @Test
+ @Ignore
+ public void test_example_json_response() {
+ setUserAsSystemAdmin();
+ propertyDefinitions.addComponent(PropertyDefinition
+ .builder("sonar.test.jira")
+ .defaultValue("abc")
+ .build());
+
+ String result = ws.newRequest()
+ .setParam("keys", "sonar.test.jira,sonar.autogenerated,sonar.demo")
+ .setMediaType(JSON)
+ .execute()
+ .getInput();
+ JsonAssert.assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result);
+ }
+
+ @Test
+ public void fail_when_id_and_key_are_set() throws Exception {
+ setUserAsProjectAdmin();
+
+ expectedException.expect(IllegalArgumentException.class);
+ newRequest(project.uuid(), project.key());
+ }
+
+ @Test
+ public void fail_when_not_system_admin() throws Exception {
+ userSession.login("not-admin").setGlobalPermissions(DASHBOARD_SHARING);
+ propertyDefinitions.addComponent(PropertyDefinition.builder("foo").build());
+
+ expectedException.expect(ForbiddenException.class);
+ newRequestForGlobalProperties();
+ }
+
+ @Test
+ public void fail_when_not_project_admin() throws Exception {
+ userSession.login("project-admin").addProjectUuidPermissions(USER, project.uuid());
+ propertyDefinitions.addComponent(PropertyDefinition.builder("foo").build());
+
+ expectedException.expect(ForbiddenException.class);
+ newRequest(project.uuid(), null);
+ }
+
+ @Test
+ public void test_ws_definition() {
+ WebService.Action action = ws.getDef();
+ assertThat(action).isNotNull();
+ assertThat(action.isInternal()).isFalse();
+ assertThat(action.isPost()).isFalse();
+ assertThat(action.responseExampleAsString()).isNotEmpty();
+ assertThat(action.params()).hasSize(3);
+ }
+
+ private ValuesWsResponse newRequestForProjectProperties(String... keys) {
+ return newRequest(project.uuid(), null, keys);
+ }
+
+ private ValuesWsResponse newRequestForGlobalProperties(String... keys) {
+ return newRequest(null, null, keys);
+ }
+
+ private ValuesWsResponse newRequest(@Nullable String id, @Nullable String key, String... keys) {
+ TestRequest request = ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF)
+ .setParam("keys", COMMA_JOINER.join(keys));
+ if (id != null) {
+ request.setParam("componentId", id);
+ }
+ if (key != null) {
+ request.setParam("componentKey", key);
+ }
+ try {
+ return ValuesWsResponse.parseFrom(request.execute().getInputStream());
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private void setUserAsSystemAdmin() {
+ userSession.login("admin").setGlobalPermissions(SYSTEM_ADMIN);
+ }
+
+ private void setUserAsProjectAdmin() {
+ userSession.login("project-admin").addProjectUuidPermissions(ADMIN, project.uuid());
+ }
+
+ private void insertProperties(PropertyDto... properties) {
+ for (PropertyDto propertyDto : properties) {
+ dbClient.propertiesDao().insertProperty(dbSession, propertyDto);
+ }
+ dbSession.commit();
+ }
+
+}
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.dbutils.DbUtils;
import org.sonar.db.DbSession;
import org.sonar.db.MyBatis;
+import static org.sonar.db.DatabaseUtils.executeLargeInputs;
+
public class PropertiesDao implements Dao {
private static final String NOTIFICATION_PREFIX = "notification.";
return session.getMapper(PropertiesMapper.class).selectByQuery(query);
}
+ public List<PropertyDto> selectGlobalPropertiesByKeys(DbSession session, Set<String> keys) {
+ return selectByKeys(session, keys, null);
+ }
+
+ public List<PropertyDto> selectComponentPropertiesByKeys(DbSession session, Set<String> keys, long componentId) {
+ return selectByKeys(session, keys, componentId);
+ }
+
+ private List<PropertyDto> selectByKeys(DbSession session, Set<String> keys, @Nullable Long componentId) {
+ return executeLargeInputs(keys, propertyKeys -> session.getMapper(PropertiesMapper.class).selectByKeys(propertyKeys, componentId));
+ }
+
public void insertProperty(DbSession session, PropertyDto property) {
PropertiesMapper mapper = session.getMapper(PropertiesMapper.class);
PropertyDto persistedProperty = mapper.selectByKey(property);
PropertyDto selectByKey(PropertyDto key);
+ List<PropertyDto> selectByKeys(@Param("keys") List<String> keys, @Nullable @Param("componentId") Long componentId);
+
List<PropertyDto> selectByQuery(@Param("query") PropertyQuery query);
List<PropertyDto> selectDescendantModuleProperties(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.property;
+
+import javax.annotation.Nullable;
+import org.apache.commons.lang.math.RandomUtils;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.user.UserDto;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class PropertyTesting {
+
+ private static int cursor = RandomUtils.nextInt(100);
+
+ private PropertyTesting() {
+ // static methods only
+ }
+
+ public static PropertyDto newGlobalPropertyDto(String key, String value) {
+ return newPropertyDto(key, value, (Long) null, null);
+ }
+
+ public static PropertyDto newGlobalPropertyDto() {
+ return newPropertyDto((Long) null, null);
+ }
+
+ public static PropertyDto newComponentPropertyDto(String key, String value, ComponentDto component) {
+ checkNotNull(component.getId());
+ return newPropertyDto(key, value, component.getId(), null);
+ }
+
+ public static PropertyDto newComponentPropertyDto(ComponentDto component) {
+ checkNotNull(component.getId());
+ return newPropertyDto(component.getId(), null);
+ }
+
+ public static PropertyDto newUserPropertyDto(String key, String value, UserDto user) {
+ checkNotNull(user.getId());
+ return newPropertyDto(key, value, null, user.getId());
+ }
+
+ public static PropertyDto newUserPropertyDto(UserDto user) {
+ checkNotNull(user.getId());
+ return newPropertyDto(null, user.getId());
+ }
+
+ public static PropertyDto newPropertyDto(String key, String value, ComponentDto component, UserDto user) {
+ checkNotNull(component.getId());
+ checkNotNull(user.getId());
+ return newPropertyDto(key, value, component.getId(), user.getId());
+ }
+
+ public static PropertyDto newPropertyDto(ComponentDto component, UserDto user) {
+ checkNotNull(component.getId());
+ checkNotNull(user.getId());
+ return newPropertyDto(component.getId(), user.getId());
+ }
+
+ private static PropertyDto newPropertyDto(@Nullable Long componentId, @Nullable Long userId) {
+ String key = String.valueOf(cursor);
+ cursor++;
+ String value = String.valueOf(cursor);
+ cursor++;
+ return newPropertyDto(key, value, componentId, userId);
+ }
+
+ private static PropertyDto newPropertyDto(String key, String value, @Nullable Long componentId, @Nullable Long userId) {
+ PropertyDto propertyDto = new PropertyDto()
+ .setKey(key)
+ .setValue(value);
+ if (componentId != null) {
+ propertyDto.setResourceId(componentId);
+ }
+ if (userId != null) {
+ propertyDto.setUserId(userId);
+ }
+ return propertyDto;
+ }
+
+}
</if>
</select>
+ <select id="selectByKeys" parameterType="map" resultType="Property">
+ SELECT p.id as id, p.prop_key as "key", p.text_value as value, p.resource_id as resourceId, p.user_id as userId
+ FROM properties p
+ <where>
+ AND p.prop_key in
+ <foreach collection="keys" open="(" close=")" item="key" separator=",">
+ #{key}
+ </foreach>
+ <if test="componentId == null">
+ AND p.resource_id is null
+ </if>
+ <if test="componentId != null">
+ AND p.resource_id=#{componentId}
+ </if>
+ AND p.user_id is null
+ </where>
+ </select>
+
<select id="selectByQuery" parameterType="map" resultType="Property">
select p.id as id, p.prop_key as "key", p.text_value as value, p.resource_id as resourceId, p.user_id as userId
from properties p
package org.sonar.db.property;
import com.google.common.collect.ImmutableMap;
-import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserTesting;
+import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
-
+import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto;
+import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto;
+import static org.sonar.db.property.PropertyTesting.newUserPropertyDto;
public class PropertiesDaoTest {
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ DbClient dbClient = dbTester.getDbClient();
+ DbSession session = dbTester.getSession();
+
PropertiesDao dao = dbTester.getDbClient().propertiesDao();
@Test
dbTester.prepareDbUnit(getClass(), "findNotificationSubscribers.xml");
// Nobody is subscribed
- assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("NotSexyDispatcher"))).isFalse();
+ assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", asList("NotSexyDispatcher"))).isFalse();
// Global subscribers
- assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("DispatcherWithGlobalSubscribers"))).isTrue();
+ assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", asList("DispatcherWithGlobalSubscribers"))).isTrue();
// Project subscribers
- assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("DispatcherWithProjectSubscribers"))).isTrue();
- assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", Arrays.asList("DispatcherWithProjectSubscribers"))).isFalse();
+ assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", asList("DispatcherWithProjectSubscribers"))).isTrue();
+ assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", asList("DispatcherWithProjectSubscribers"))).isFalse();
// Global + Project subscribers
- assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", Arrays.asList("DispatcherWithGlobalAndProjectSubscribers"))).isTrue();
- assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", Arrays.asList("DispatcherWithGlobalAndProjectSubscribers"))).isTrue();
+ assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", asList("DispatcherWithGlobalAndProjectSubscribers"))).isTrue();
+ assertThat(dao.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", asList("DispatcherWithGlobalAndProjectSubscribers"))).isTrue();
}
@Test
assertThat(results.get(0).getValue()).isEqualTo("one");
}
+ @Test
+ public void select_global_properties_by_keys() throws Exception {
+ ComponentDto project = ComponentTesting.newProjectDto();
+ dbClient.componentDao().insert(session, project);
+ UserDto user = UserTesting.newUserDto();
+ dbClient.userDao().insert(session, user);
+
+ String key = "key";
+ String anotherKey = "anotherKey";
+ insertProperties(
+ newGlobalPropertyDto().setKey(key),
+ newComponentPropertyDto(project).setKey(key),
+ newUserPropertyDto(user).setKey(key),
+ newGlobalPropertyDto().setKey(anotherKey));
+
+ assertThat(dao.selectGlobalPropertiesByKeys(session, newHashSet(key))).extracting("key").containsOnly(key);
+ assertThat(dao.selectGlobalPropertiesByKeys(session, newHashSet(key, anotherKey))).extracting("key").containsOnly(key, anotherKey);
+ assertThat(dao.selectGlobalPropertiesByKeys(session, newHashSet(key, anotherKey, "unknown"))).extracting("key").containsOnly(key, anotherKey);
+
+ assertThat(dao.selectGlobalPropertiesByKeys(session, newHashSet("unknown"))).isEmpty();
+ }
+
+ @Test
+ public void select_component_properties_by_keys() throws Exception {
+ ComponentDto project = ComponentTesting.newProjectDto();
+ dbClient.componentDao().insert(session, project);
+ UserDto user = UserTesting.newUserDto();
+ dbClient.userDao().insert(session, user);
+
+ String key = "key";
+ String anotherKey = "anotherKey";
+ insertProperties(
+ newGlobalPropertyDto().setKey(key),
+ newComponentPropertyDto(project).setKey(key),
+ newUserPropertyDto(user).setKey(key),
+ newComponentPropertyDto(project).setKey(anotherKey));
+
+ assertThat(dao.selectComponentPropertiesByKeys(session, newHashSet(key), project.getId())).extracting("key").containsOnly(key);
+ assertThat(dao.selectComponentPropertiesByKeys(session, newHashSet(key, anotherKey), project.getId())).extracting("key").containsOnly(key, anotherKey);
+ assertThat(dao.selectComponentPropertiesByKeys(session, newHashSet(key, anotherKey, "unknown"), project.getId())).extracting("key").containsOnly(key, anotherKey);
+
+ assertThat(dao.selectComponentPropertiesByKeys(session, newHashSet("unknown"), project.getId())).isEmpty();
+ assertThat(dao.selectComponentPropertiesByKeys(session, newHashSet(key), 123456789L)).isEmpty();
+ }
+
@Test
public void setProperty_update() {
dbTester.prepareDbUnit(getClass(), "update.xml");
}
return null;
}
+
+ private void insertProperties(PropertyDto... properties) {
+ for (PropertyDto propertyDto : properties) {
+ dao.insertProperty(session, propertyDto);
+ }
+ session.commit();
+ }
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.db.property;
-
-public class PropertyTesting {
-
- public static PropertyDto newGlobalProperty(String key, String value) {
- return new PropertyDto().setKey(key).setValue(value);
- }
-
- public static PropertyDto newProjectProperty(String key, String value, long componentId) {
- return new PropertyDto().setKey(key).setValue(value).setResourceId(componentId);
- }
-}
PROPERTY_SET = 12;
}
+// Response of GET api/settings/values
+message ValuesWsResponse {
+ repeated Value values = 1;
+}
+
+message Value {
+ optional string key = 1;
+ optional string value = 2;
+ repeated string values = 3;
+ map<string, SetValue> setValues = 4;
+ optional bool isDefault = 5;
+ optional bool isInherited = 6;
+}
+
+message SetValue {
+ repeated string values = 1;
+}
+