From: Julien Lancelot Date: Wed, 4 Jan 2017 15:18:14 +0000 (+0100) Subject: SONAR-7300 Deprecate and rewrite api/properties/index in Java X-Git-Tag: 6.3-RC1~603 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c54d9160675527e5b95c6af914be0bbaeb657627;p=sonarqube.git SONAR-7300 Deprecate and rewrite api/properties/index in Java --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/config/ws/IndexAction.java b/server/sonar-server/src/main/java/org/sonar/server/config/ws/IndexAction.java new file mode 100644 index 00000000000..fef3bad5035 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/config/ws/IndexAction.java @@ -0,0 +1,257 @@ +/* + * 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.config.ws; + +import com.google.common.base.Splitter; +import com.google.common.collect.Multimap; +import com.google.common.collect.Ordering; +import com.google.common.collect.TreeMultimap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +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.api.utils.text.JsonWriter; +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.db.property.PropertyDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.user.UserSession; +import org.sonar.server.ws.WsAction; + +import static org.apache.commons.lang.StringUtils.isEmpty; +import static org.sonar.api.PropertyType.PROPERTY_SET; +import static org.sonar.api.server.ws.RailsHandler.PARAM_FORMAT; +import static org.sonar.api.server.ws.RailsHandler.addJsonOnlyFormatParam; +import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.server.setting.ws.SettingsPermissionPredicates.DOT_LICENSE; +import static org.sonar.server.setting.ws.SettingsPermissionPredicates.DOT_SECURED; +import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; + +public class IndexAction implements WsAction { + + private static final Splitter DOT_SPLITTER = Splitter.on(".").omitEmptyStrings(); + private static final Splitter COMMA_SPLITTER = Splitter.on(","); + private static final String COMMA_ENCODED_VALUE = "%2C"; + + private static final String PARAM_KEY = "key"; + private static final String PARAM_COMPONENT = "resource"; + + private final DbClient dbClient; + private final ComponentFinder componentFinder; + private final UserSession userSession; + private final PropertyDefinitions propertyDefinitions; + + public IndexAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, PropertyDefinitions propertyDefinitions) { + this.dbClient = dbClient; + this.componentFinder = componentFinder; + this.userSession = userSession; + this.propertyDefinitions = propertyDefinitions; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context.createAction("index") + .setDescription("This web service is deprecated, please use api/settings/values instead.") + .setDeprecatedSince("6.3") + .setResponseExample(getClass().getResource("index-example.json")) + .setSince("2.6") + .setHandler(this); + action.createParam(PARAM_KEY) + .setDescription("Setting key") + .setExampleValue("sonar.technicalDebt.hoursInDay"); + action.createParam(PARAM_COMPONENT) + .setDescription("Component key or database id") + .setExampleValue(KEY_PROJECT_EXAMPLE_001); + addJsonOnlyFormatParam(action); + } + + @Override + public void handle(Request request, Response response) throws Exception { + request.param(PARAM_FORMAT); + JsonWriter json = response.newJsonWriter(); + json.beginArray(); + doHandle(json, request); + json.endArray(); + json.close(); + } + + private void doHandle(JsonWriter json, Request request) { + DbSession dbSession = dbClient.openSession(true); + try { + Optional component = loadComponent(dbSession, request); + String key = request.param(PARAM_KEY); + List propertyDtos = loadProperties(dbSession, component, Optional.ofNullable(key)); + new ResponseBuilder(propertyDtos).build(json); + } finally { + dbClient.closeSession(dbSession); + } + } + + private Optional loadComponent(DbSession dbSession, Request request) { + String component = request.param(PARAM_COMPONENT); + if (component == null) { + return Optional.empty(); + } + return Optional.of(loadComponent(dbSession, component)); + } + + private ComponentDto loadComponent(DbSession dbSession, String component) { + try { + Long componentId = Long.parseLong(component); + return componentFinder.getById(dbSession, componentId); + } catch (NumberFormatException e) { + return componentFinder.getByKey(dbSession, component); + } + } + + private List loadProperties(DbSession dbSession, Optional component, Optional key) { + // List of settings must be kept in the following orders : default -> global -> component + List propertyDtos = new ArrayList<>(); + propertyDtos.addAll(loadDefaultSettings(key)); + propertyDtos.addAll(loadGlobalSettings(dbSession, key)); + component.ifPresent(componentDto -> propertyDtos.addAll(loadComponentSettings(dbSession, key, componentDto).values())); + return propertyDtos.stream().filter(isVisible(component)).collect(Collectors.toList()); + } + + Predicate isVisible(Optional component) { + return propertyDto -> !propertyDto.getKey().endsWith(DOT_SECURED) + || hasAdminPermission(component) + || (propertyDto.getKey().contains(DOT_LICENSE) && userSession.isLoggedIn()); + } + + private boolean hasAdminPermission(Optional component) { + return component.isPresent() ? userSession.hasComponentUuidPermission(ADMIN, component.get().uuid()) : userSession.hasPermission(GlobalPermissions.SYSTEM_ADMIN); + } + + private List loadGlobalSettings(DbSession dbSession, Optional key) { + if (key.isPresent()) { + return dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, Collections.singleton(key.get())); + } + return dbClient.propertiesDao().selectGlobalProperties(dbSession); + } + + /** + * Return list of propertyDto by component uuid, sorted from project to lowest module + */ + private Multimap loadComponentSettings(DbSession dbSession, Optional key, ComponentDto component) { + List componentUuids = DOT_SPLITTER.splitToList(component.moduleUuidPath()); + List componentDtos = dbClient.componentDao().selectByUuids(dbSession, componentUuids); + Set componentIds = componentDtos.stream().map(ComponentDto::getId).collect(Collectors.toSet()); + Map uuidsById = componentDtos.stream().collect(Collectors.toMap(ComponentDto::getId, ComponentDto::uuid)); + List properties = key.isPresent() ? dbClient.propertiesDao().selectPropertiesByKeysAndComponentIds(dbSession, Collections.singleton(key.get()), componentIds) + : dbClient.propertiesDao().selectPropertiesByComponentIds(dbSession, componentIds); + + Multimap propertyDtosByUuid = TreeMultimap.create(Ordering.explicit(componentUuids), Ordering.arbitrary()); + for (PropertyDto propertyDto : properties) { + Long componentId = propertyDto.getResourceId(); + String componentUuid = uuidsById.get(componentId); + propertyDtosByUuid.put(componentUuid, propertyDto); + } + return propertyDtosByUuid; + } + + private List loadDefaultSettings(Optional key) { + return propertyDefinitions.getAll().stream() + .filter(definition -> !key.isPresent() || key.get().equals(definition.key())) + .filter(defaultProperty -> !isEmpty(defaultProperty.defaultValue())) + .map(definition -> new PropertyDto().setKey(definition.key()).setValue(definition.defaultValue())) + .collect(Collectors.toList()); + } + + private class ResponseBuilder { + private final List propertyDtos; + private final Map propertiesByKey = new HashMap<>(); + + ResponseBuilder(List propertyDtos) { + this.propertyDtos = propertyDtos; + } + + void build(JsonWriter json) { + processSettings(); + propertiesByKey.values().forEach(property -> { + json.beginObject() + .prop("key", property.key) + .prop("value", property.value); + if (!property.values.isEmpty()) { + json.name("values").beginArray().values(property.values).endArray(); + } + json.endObject(); + }); + } + + private void processSettings() { + propertyDtos.forEach(setting -> { + Property property = createProperty(setting.getKey()); + setValue(setting, property); + }); + } + + private Property createProperty(String key) { + return propertiesByKey.computeIfAbsent(key, k -> new Property(key)); + } + + private void setValue(PropertyDto propertyDto, Property property) { + String value = propertyDto.getValue(); + property.setValue(value); + PropertyDefinition definition = propertyDefinitions.get(propertyDto.getKey()); + if (definition != null && (definition.multiValues() || definition.type().equals(PROPERTY_SET))) { + property.setValues(createValues(value)); + } + } + + private List createValues(String value) { + return COMMA_SPLITTER.splitToList(value).stream().map(v -> v.replace(COMMA_ENCODED_VALUE, ",")).collect(Collectors.toList()); + } + } + + private static class Property { + private final String key; + private String value; + private List values = new ArrayList<>(); + + public Property(String key) { + this.key = key; + } + + public Property setValue(String value) { + this.value = value; + return this; + } + + public Property setValues(List values) { + this.values = values; + return this; + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java b/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java index 3aa3f58d8a0..3870bff9e62 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java @@ -19,30 +19,24 @@ */ package org.sonar.server.config.ws; -import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; -import static org.sonar.api.server.ws.RailsHandler.addFormatParam; - public class PropertiesWs implements WebService { - @Override - public void define(Context context) { - NewController controller = context.createController("api/properties"); - controller.setDescription("Manage global and project properties."); - controller.setSince("2.6"); + public static final String CONTROLLER_PROPERTIES = "api/properties"; - defineIndexAction(controller); + private final IndexAction indexAction; - controller.done(); + public PropertiesWs(IndexAction indexAction) { + this.indexAction = indexAction; } - private void defineIndexAction(NewController controller) { - NewAction action = controller.createAction("index") - .setDescription("Documentation of this web service is available here") - .setResponseExample(getClass().getResource("index-example.xml")) - .setSince("2.6") - .setHandler(RailsHandler.INSTANCE); - addFormatParam(action); + @Override + public void define(Context context) { + NewController controller = context.createController(CONTROLLER_PROPERTIES) + .setDescription("This web service is deprecated, please use api/settings instead.") + .setSince("2.6"); + indexAction.define(controller); + controller.done(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsPermissionPredicates.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsPermissionPredicates.java index 53eb10147b7..ef4482efb6b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsPermissionPredicates.java +++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsPermissionPredicates.java @@ -32,9 +32,10 @@ import static org.sonar.api.web.UserRole.ADMIN; public class SettingsPermissionPredicates { - private static final String SECURED_SUFFIX = ".secured"; - static final String LICENSE_SUFFIX = ".license.secured"; - static final String LICENSE_HASH_SUFFIX = ".licenseHash.secured"; + public static final String DOT_SECURED = ".secured"; + public static final String DOT_LICENSE = ".license"; + static final String LICENSE_SUFFIX = DOT_LICENSE + DOT_SECURED; + static final String LICENSE_HASH_SUFFIX = ".licenseHash" + DOT_SECURED; private final UserSession userSession; @@ -55,7 +56,7 @@ public class SettingsPermissionPredicates { } private boolean verifySecuredSetting(String key, @Nullable PropertyDefinition definition, Optional component) { - return isLicense(key, definition) || (!key.endsWith(SECURED_SUFFIX) || hasAdminPermission(component)); + return isLicense(key, definition) || (!key.endsWith(DOT_SECURED) || hasAdminPermission(component)); } private boolean hasAdminPermission(Optional component) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/WebServiceFilter.java b/server/sonar-server/src/main/java/org/sonar/server/ws/WebServiceFilter.java index 1c5e10b44d2..028c5c1760f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ws/WebServiceFilter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ws/WebServiceFilter.java @@ -29,8 +29,11 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.ws.RailsHandler; +import org.sonar.api.server.ws.WebService; import org.sonar.api.web.ServletFilter; +import static org.sonar.server.config.ws.PropertiesWs.CONTROLLER_PROPERTIES; + /** * This filter is used to execute Java WS. * @@ -45,11 +48,11 @@ public class WebServiceFilter extends ServletFilter { public WebServiceFilter(WebServiceEngine webServiceEngine) { this.webServiceEngine = webServiceEngine; - webServiceEngine.controllers().stream() - .forEach(controller -> controller.actions().stream() + webServiceEngine.controllers() + .forEach(controller -> controller.actions() .forEach(action -> { - // Rails and servlet filter WS should not be executed by the web service engine - if (!(action.handler() instanceof RailsHandler) && !(action.handler() instanceof ServletFilterHandler)) { + // Rails, Rest and servlet filter WS should not be executed by the web service engine + if (isJavaWs(controller, action)) { includeUrls.add("/" + controller.path() + "/*"); } else { excludeUrls.add("/" + action.path() + "*"); @@ -84,21 +87,10 @@ public class WebServiceFilter extends ServletFilter { // Nothing to do } - private static class WsUrl { - private final String controller; - private final String action; - - WsUrl(String controller, String action) { - this.controller = controller; - this.action = action; - } - - String getController() { - return controller; - } - - String getAction() { - return action; - } + private static boolean isJavaWs(WebService.Controller controller, WebService.Action action) { + return !(action.handler() instanceof RailsHandler) + && !(action.handler() instanceof ServletFilterHandler) + && !controller.path().equals(CONTROLLER_PROPERTIES); } + } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/config/ws/index-example.json b/server/sonar-server/src/main/resources/org/sonar/server/config/ws/index-example.json new file mode 100644 index 00000000000..9574238f574 --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/config/ws/index-example.json @@ -0,0 +1,39 @@ +[ + { + "key": "sonar.test.jira", + "value": "abc" + }, + { + "key": "sonar.autogenerated", + "value": "val1,val2,val3", + "values": [ + "val1", + "val2", + "val3" + ] + }, + { + "key": "sonar.demo", + "value": "1,2", + "values": [ + "1", + "2" + ] + }, + { + "key": "sonar.demo.1.text", + "value": "foo" + }, + { + "key": "sonar.demo.1.boolean", + "value": "true" + }, + { + "key": "sonar.demo.2.text", + "value": "bar" + }, + { + "key": "sonar.demo.2.boolean", + "value": "false" + } +] diff --git a/server/sonar-server/src/main/resources/org/sonar/server/config/ws/index-example.xml b/server/sonar-server/src/main/resources/org/sonar/server/config/ws/index-example.xml deleted file mode 100644 index 33186f28bd9..00000000000 --- a/server/sonar-server/src/main/resources/org/sonar/server/config/ws/index-example.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - sonar.core.treemap.colormetric - - - - sonar.core.treemap.sizemetric - - - - sonar.plsql.suffixes - - - - sonar.build-stability.days - - - - sonar.dbcleaner.monthsBeforeDeletingAllSnapshots - - - - sonar.c.predefinedMacros - - - - sonar.scm.enabled - - - - sonar.cpd.cross_project - - - - sonar.forceAuthentication - - - - sonar.dbcleaner.weeksBeforeDeletingAllSnapshots - - - - sonar.java.coveragePlugin - - - diff --git a/server/sonar-server/src/test/java/org/sonar/server/config/ws/IndexActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/config/ws/IndexActionTest.java new file mode 100644 index 00000000000..1aba7354382 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/config/ws/IndexActionTest.java @@ -0,0 +1,483 @@ +/* + * 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.config.ws; + +import com.google.common.collect.ImmutableMap; +import java.net.URL; +import javax.annotation.Nullable; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.PropertyType; +import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.PropertyFieldDefinition; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.property.PropertyDbTester; +import org.sonar.server.component.ComponentFinder; +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 static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.PropertyType.LICENSE; +import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.api.web.UserRole.CODEVIEWER; +import static org.sonar.api.web.UserRole.USER; +import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; +import static org.sonar.db.component.ComponentTesting.newModuleDto; +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.sonar.test.JsonAssert.assertJson; +import static org.sonarqube.ws.MediaTypes.JSON; + +public class IndexActionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + + DbClient dbClient = db.getDbClient(); + PropertyDbTester propertyDb = new PropertyDbTester(db); + ComponentDbTester componentDb = new ComponentDbTester(db); + PropertyDefinitions definitions = new PropertyDefinitions(); + + ComponentDto project; + + WsActionTester ws = new WsActionTester(new IndexAction(dbClient, new ComponentFinder(dbClient), userSession, definitions)); + + @Before + public void setUp() throws Exception { + project = componentDb.insertComponent(newProjectDto()); + } + + @Test + public void return_simple_value() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").build()); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("foo").setValue("one")); + + executeAndVerify(null, null, "return_simple_value.json"); + } + + @Test + public void return_multi_values() throws Exception { + setAuthenticatedUser(); + // Property never defined, default value is returned + definitions.addComponent(PropertyDefinition.builder("default") + .multiValues(true) + .defaultValue("one,two") + .build()); + // Property defined at global level + definitions.addComponent(PropertyDefinition.builder("global") + .multiValues(true) + .build()); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("global").setValue("three,four")); + + executeAndVerify(null, null, "return_multi_values.json"); + } + + @Test + public void return_multi_value_with_coma() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("global").multiValues(true).build()); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("global").setValue("three,four%2Cfive")); + + executeAndVerify(null, null, "return_multi_value_with_coma.json"); + } + + @Test + public void return_property_set() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition + .builder("foo") + .type(PropertyType.PROPERTY_SET) + .fields(asList( + PropertyFieldDefinition.build("key").name("Key").build(), + PropertyFieldDefinition.build("size").name("Size").build())) + .build()); + propertyDb.insertPropertySet("foo", null, ImmutableMap.of("key", "key1", "size", "size1"), ImmutableMap.of("key", "key2")); + + executeAndVerify(null, null, "return_property_set.json"); + } + + @Test + public void return_default_values() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").defaultValue("default").build()); + + executeAndVerify(null, null, "return_default_values.json"); + } + + @Test + public void return_global_values() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("property").defaultValue("default").build()); + propertyDb.insertProperties( + // The property is overriding default value + newGlobalPropertyDto().setKey("property").setValue("one")); + + executeAndVerify(null, null, "return_global_values.json"); + } + + @Test + public void return_project_values() throws Exception { + setUserWithBrowsePermissionOnProject(); + definitions.addComponent(PropertyDefinition.builder("property").defaultValue("default").build()); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("property").setValue("one"), + // The property is overriding global value + newComponentPropertyDto(project).setKey("property").setValue("two")); + + executeAndVerify(project.key(), null, "return_project_values.json"); + } + + @Test + public void return_values_even_if_no_property_definition() throws Exception { + setAuthenticatedUser(); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("globalPropertyWithoutDefinition").setValue("value")); + + executeAndVerify(null, null, "return_values_even_if_no_property_definition.json"); + } + + @Test + public void return_empty_when_property_def_exists_but_no_value() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").build()); + + executeAndVerify(null, null, "empty.json"); + } + + @Test + public void return_nothing_when_unknown_key() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").defaultValue("default").build()); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("bar").setValue("")); + + executeAndVerify(null, "unknown", "empty.json"); + } + + @Test + public void return_module_values() throws Exception { + setUserWithBrowsePermissionOnProject(); + ComponentDto module = componentDb.insertComponent(newModuleDto(project)); + definitions.addComponent(PropertyDefinition.builder("property").defaultValue("default").build()); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("property").setValue("one"), + // The property is overriding global value + newComponentPropertyDto(module).setKey("property").setValue("two")); + + executeAndVerify(module.key(), "property", "return_module_values.json"); + } + + @Test + public void return_inherited_values_on_module() throws Exception { + setUserWithBrowsePermissionOnProject(); + ComponentDto module = componentDb.insertComponent(newModuleDto(project)); + definitions.addComponents(asList( + PropertyDefinition.builder("defaultProperty").defaultValue("default").build(), + PropertyDefinition.builder("globalProperty").build(), + PropertyDefinition.builder("projectProperty").build(), + PropertyDefinition.builder("moduleProperty").build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("globalProperty").setValue("global"), + newComponentPropertyDto(project).setKey("projectProperty").setValue("project"), + newComponentPropertyDto(module).setKey("moduleProperty").setValue("module")); + + executeAndVerify(module.key(), null, "return_inherited_values_on_module.json"); + } + + @Test + public void return_inherited_values_on_global_setting() throws Exception { + setAuthenticatedUser(); + definitions.addComponents(asList( + PropertyDefinition.builder("defaultProperty").defaultValue("default").build(), + PropertyDefinition.builder("globalProperty").build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("globalProperty").setValue("global")); + + executeAndVerify(null, null, "return_inherited_values_on_global_setting.json"); + } + + @Test + public void does_not_return_value_of_deprecated_key() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").deprecatedKey("deprecated").build()); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("foo").setValue("one")); + + executeAndVerify(null, "deprecated", "empty.json"); + } + + @Test + public void does_not_returned_secured_and_license_settings_when_not_authenticated() throws Exception { + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("secret.secured").build(), + PropertyDefinition.builder("plugin.license.secured").type(LICENSE).build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("foo").setValue("one"), + newGlobalPropertyDto().setKey("secret.secured").setValue("password"), + newGlobalPropertyDto().setKey("plugin.license.secured").setValue("ABCD")); + + executeAndVerify(null, null, "does_not_returned_secured_and_license_settings_when_not_authenticated.json"); + } + + @Test + public void does_not_returned_secured_and_license_settings_in_property_set_when_not_authenticated() throws Exception { + definitions.addComponent(PropertyDefinition + .builder("foo") + .type(PropertyType.PROPERTY_SET) + .fields(asList( + PropertyFieldDefinition.build("key").name("Key").build(), + PropertyFieldDefinition.build("plugin.license.secured").name("License").type(LICENSE).build(), + PropertyFieldDefinition.build("secret.secured").name("Secured").build())) + .build()); + propertyDb.insertPropertySet("foo", null, + ImmutableMap.of("key", "key1", "plugin.license.secured", "ABCD", "secret.secured", "123456")); + + executeAndVerify(null, null, "does_not_returned_secured_and_license_settings_in_property_set_when_not_authenticated.json"); + } + + @Test + public void return_license_with_hash_settings_when_authenticated_but_not_admin() throws Exception { + setUserWithBrowsePermissionOnProject(); + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("secret.secured").build(), + PropertyDefinition.builder("commercial.plugin").type(LICENSE).build(), + PropertyDefinition.builder("plugin.license.secured").type(LICENSE).build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("foo").setValue("one"), + newGlobalPropertyDto().setKey("secret.secured").setValue("password"), + newGlobalPropertyDto().setKey("commercial.plugin").setValue("ABCD"), + newGlobalPropertyDto().setKey("plugin.license.secured").setValue("ABCD"), + newGlobalPropertyDto().setKey("plugin.licenseHash.secured").setValue("987654321")); + + executeAndVerify(null, null, "return_license_with_hash_settings_when_authenticated_but_not_admin.json"); + } + + @Test + public void return_secured_and_license_settings_when_system_admin() throws Exception { + setUserAsSystemAdmin(); + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("secret.secured").build(), + PropertyDefinition.builder("plugin.license.secured").type(LICENSE).build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("foo").setValue("one"), + newGlobalPropertyDto().setKey("secret.secured").setValue("password"), + newGlobalPropertyDto().setKey("plugin.license.secured").setValue("ABCD"), + newGlobalPropertyDto().setKey("plugin.licenseHash.secured").setValue("987654321")); + + executeAndVerify(null, null, "return_secured_and_license_settings_when_system_admin.json"); + } + + @Test + public void return_secured_and_license_settings_when_project_admin() throws Exception { + setUserAsProjectAdmin(); + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("secret.secured").build(), + PropertyDefinition.builder("plugin.license.secured").type(LICENSE).build())); + propertyDb.insertProperties( + newComponentPropertyDto(project).setKey("foo").setValue("one"), + newComponentPropertyDto(project).setKey("secret.secured").setValue("password"), + newComponentPropertyDto(project).setKey("plugin.license.secured").setValue("ABCD"), + newComponentPropertyDto(project).setKey("plugin.licenseHash.secured").setValue("987654321")); + + executeAndVerify(project.key(), null, "return_secured_and_license_settings_when_project_admin.json"); + } + + @Test + public void return_secured_and_license_settings_in_property_set_when_system_admin() throws Exception { + setUserAsSystemAdmin(); + definitions.addComponent(PropertyDefinition + .builder("foo") + .type(PropertyType.PROPERTY_SET) + .fields(asList( + PropertyFieldDefinition.build("key").name("Key").build(), + PropertyFieldDefinition.build("plugin.license.secured").name("License").type(LICENSE).build(), + PropertyFieldDefinition.build("secret.secured").name("Secured").build())) + .build()); + propertyDb.insertPropertySet("foo", null, + ImmutableMap.of("key", "key1", "plugin.license.secured", "ABCD", "secret.secured", "123456")); + + executeAndVerify(null, null, "return_secured_and_license_settings_in_property_set_when_system_admin.json"); + } + + @Test + public void return_all_settings_when_no_component_and_no_key() throws Exception { + setUserAsSystemAdmin(); + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("secret.secured").build(), + PropertyDefinition.builder("plugin.license.secured").type(LICENSE).build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("foo").setValue("one"), + newGlobalPropertyDto().setKey("secret.secured").setValue("password"), + newGlobalPropertyDto().setKey("plugin.license.secured").setValue("ABCD"), + newGlobalPropertyDto().setKey("not_defined").setValue("ABCD")); + + executeAndVerify(null, null, "return_all_settings_when_no_component_and_no_key.json"); + } + + @Test + public void return_all_project_settings_when_component_and_no_key() throws Exception { + setUserAsProjectAdmin(); + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("secret.secured").build(), + PropertyDefinition.builder("plugin.license.secured").type(LICENSE).build())); + propertyDb.insertProperties( + newComponentPropertyDto(project).setKey("foo").setValue("one"), + newComponentPropertyDto(project).setKey("secret.secured").setValue("password"), + newComponentPropertyDto(project).setKey("plugin.license.secured").setValue("ABCD"), + newComponentPropertyDto(project).setKey("not_defined").setValue("ABCD"), + newGlobalPropertyDto().setKey("global_not_defined").setValue("ABCD")); + + executeAndVerify(project.key(), null, "return_all_project_settings_when_component_and_no_key.json"); + } + + @Test + public void return_only_one_setting_when_key_is_provided() throws Exception { + definitions.addComponents(asList( + PropertyDefinition.builder("foo").build(), + PropertyDefinition.builder("bar").build())); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey("foo").setValue("one"), + newGlobalPropertyDto().setKey("bar").setValue("two")); + + executeAndVerify(project.key(), "foo", "return_only_one_setting_when_key_is_provided.json"); + executeAndVerify(project.key(), "unknown", "empty.json"); + } + + @Test + public void does_not_fail_when_user_has_not_project_browse_permission() throws Exception { + userSession.login("project-admin").addProjectUuidPermissions(CODEVIEWER, project.uuid()); + definitions.addComponent(PropertyDefinition.builder("foo").build()); + propertyDb.insertProperties(newComponentPropertyDto(project).setKey("foo").setValue("one")); + + executeAndVerify(project.key(), null, "does_not_fail_when_user_has_not_project_browse_permission.json"); + } + + @Test + public void does_not_fail_when_format_is_set_to_json() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").defaultValue("default").build()); + + ws.newRequest().setParam("format", "json").execute(); + } + + @Test + public void fail_when_format_is_set_to_xml() throws Exception { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition.builder("foo").defaultValue("default").build()); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Value of parameter 'format' (xml) must be one of: [json]"); + ws.newRequest().setParam("format", "xml").execute(); + } + + @Test + public void test_example_json_response() { + setAuthenticatedUser(); + definitions.addComponent(PropertyDefinition + .builder("sonar.test.jira") + .defaultValue("abc") + .build()); + definitions.addComponent(PropertyDefinition + .builder("sonar.autogenerated") + .multiValues(true) + .build()); + propertyDb.insertProperties(newGlobalPropertyDto().setKey("sonar.autogenerated").setValue("val1,val2,val3")); + definitions.addComponent(PropertyDefinition + .builder("sonar.demo") + .type(PropertyType.PROPERTY_SET) + .fields(PropertyFieldDefinition.build("text").name("Text").build(), + PropertyFieldDefinition.build("boolean").name("Boolean").build()) + .build()); + propertyDb.insertPropertySet("sonar.demo", null, ImmutableMap.of("text", "foo", "boolean", "true"), ImmutableMap.of("text", "bar", "boolean", "false")); + + String result = ws.newRequest().setMediaType(JSON).execute().getInput(); + + JsonAssert.assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result); + } + + @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 void executeAndVerify(@Nullable String componentKey, @Nullable String key, String expectedFile) { + TestRequest request = ws.newRequest().setMediaType(MediaTypes.JSON); + if (key != null) { + request.setParam("key", key); + } + if (componentKey != null) { + request.setParam("resource", componentKey); + } + String result = request.execute().getInput(); + assertJson(result).isSimilarTo(resource(expectedFile)); + } + + private void setAuthenticatedUser() { + userSession.login("user"); + } + + private void setUserWithBrowsePermissionOnProject() { + userSession.login("user").addProjectUuidPermissions(USER, project.uuid()); + } + + private void setUserAsSystemAdmin() { + userSession.login("admin").setGlobalPermissions(SYSTEM_ADMIN); + } + + private void setUserAsProjectAdmin() { + userSession.login("project-admin") + .addProjectUuidPermissions(ADMIN, project.uuid()) + .addProjectUuidPermissions(USER, project.uuid()); + } + + protected static URL resource(String s) { + Class clazz = IndexActionTest.class; + return clazz.getResource(clazz.getSimpleName() + "/" + s); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/config/ws/PropertiesWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/config/ws/PropertiesWsTest.java deleted file mode 100644 index c9914b1b94f..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/config/ws/PropertiesWsTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.config.ws; - -import org.junit.Test; -import org.sonar.api.server.ws.WebService; -import org.sonar.server.ws.WsTester; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PropertiesWsTest { - - WsTester tester = new WsTester(new PropertiesWs()); - - @Test - public void define_ws() { - WebService.Controller controller = tester.controller("api/properties"); - assertThat(controller).isNotNull(); - assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(1); - } - -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_fail_when_user_has_not_project_browse_permission.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_fail_when_user_has_not_project_browse_permission.json new file mode 100644 index 00000000000..d98b4c40613 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_fail_when_user_has_not_project_browse_permission.json @@ -0,0 +1,6 @@ +[ + { + "key": "foo", + "value": "one" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_returned_secured_and_license_settings_in_property_set_when_not_authenticated.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_returned_secured_and_license_settings_in_property_set_when_not_authenticated.json new file mode 100644 index 00000000000..1475b5a81e6 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_returned_secured_and_license_settings_in_property_set_when_not_authenticated.json @@ -0,0 +1,13 @@ +[ + { + "key": "foo", + "value": "1", + "values": [ + "1" + ] + }, + { + "key": "foo.1.key", + "value": "key1" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_returned_secured_and_license_settings_when_not_authenticated.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_returned_secured_and_license_settings_when_not_authenticated.json new file mode 100644 index 00000000000..d98b4c40613 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/does_not_returned_secured_and_license_settings_when_not_authenticated.json @@ -0,0 +1,6 @@ +[ + { + "key": "foo", + "value": "one" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/empty.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/empty.json new file mode 100644 index 00000000000..0d4f101c7a3 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/empty.json @@ -0,0 +1,2 @@ +[ +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_all_project_settings_when_component_and_no_key.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_all_project_settings_when_component_and_no_key.json new file mode 100644 index 00000000000..12d2fd7c1a2 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_all_project_settings_when_component_and_no_key.json @@ -0,0 +1,22 @@ +[ + { + "key": "secret.secured", + "value": "password" + }, + { + "key": "foo", + "value": "one" + }, + { + "key": "not_defined", + "value": "ABCD" + }, + { + "key": "plugin.license.secured", + "value": "ABCD" + }, + { + "key": "global_not_defined", + "value": "ABCD" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_all_settings_when_no_component_and_no_key.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_all_settings_when_no_component_and_no_key.json new file mode 100644 index 00000000000..96409e92995 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_all_settings_when_no_component_and_no_key.json @@ -0,0 +1,18 @@ +[ + { + "key": "secret.secured", + "value": "password" + }, + { + "key": "foo", + "value": "one" + }, + { + "key": "not_defined", + "value": "ABCD" + }, + { + "key": "plugin.license.secured", + "value": "ABCD" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_default_values.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_default_values.json new file mode 100644 index 00000000000..4647a4e42ba --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_default_values.json @@ -0,0 +1,6 @@ +[ + { + "key": "foo", + "value": "default" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_global_values.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_global_values.json new file mode 100644 index 00000000000..0869d171522 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_global_values.json @@ -0,0 +1,6 @@ +[ + { + "key": "property", + "value": "one" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_inherited_values_on_global_setting.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_inherited_values_on_global_setting.json new file mode 100644 index 00000000000..815c45955ff --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_inherited_values_on_global_setting.json @@ -0,0 +1,10 @@ +[ + { + "key": "defaultProperty", + "value": "default" + }, + { + "key": "globalProperty", + "value": "global" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_inherited_values_on_module.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_inherited_values_on_module.json new file mode 100644 index 00000000000..15a3ae5fb4c --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_inherited_values_on_module.json @@ -0,0 +1,18 @@ +[ + { + "key": "defaultProperty", + "value": "default" + }, + { + "key": "projectProperty", + "value": "project" + }, + { + "key": "globalProperty", + "value": "global" + }, + { + "key": "moduleProperty", + "value": "module" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_license_with_hash_settings_when_authenticated_but_not_admin.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_license_with_hash_settings_when_authenticated_but_not_admin.json new file mode 100644 index 00000000000..e40076cc580 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_license_with_hash_settings_when_authenticated_but_not_admin.json @@ -0,0 +1,18 @@ +[ + { + "key": "plugin.licenseHash.secured", + "value": "987654321" + }, + { + "key": "foo", + "value": "one" + }, + { + "key": "plugin.license.secured", + "value": "ABCD" + }, + { + "key": "commercial.plugin", + "value": "ABCD" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_module_values.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_module_values.json new file mode 100644 index 00000000000..5486106db4f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_module_values.json @@ -0,0 +1,6 @@ +[ + { + "key": "property", + "value": "two" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_multi_value_with_coma.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_multi_value_with_coma.json new file mode 100644 index 00000000000..8411a36aea3 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_multi_value_with_coma.json @@ -0,0 +1,10 @@ +[ + { + "key": "global", + "value": "three,four%2Cfive", + "values": [ + "three", + "four,five" + ] + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_multi_values.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_multi_values.json new file mode 100644 index 00000000000..ce4fcb99c7f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_multi_values.json @@ -0,0 +1,18 @@ +[ + { + "key": "default", + "value": "one,two", + "values": [ + "one", + "two" + ] + }, + { + "key": "global", + "value": "three,four", + "values": [ + "three", + "four" + ] + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_only_one_setting_when_key_is_provided.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_only_one_setting_when_key_is_provided.json new file mode 100644 index 00000000000..d98b4c40613 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_only_one_setting_when_key_is_provided.json @@ -0,0 +1,6 @@ +[ + { + "key": "foo", + "value": "one" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_project_values.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_project_values.json new file mode 100644 index 00000000000..5486106db4f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_project_values.json @@ -0,0 +1,6 @@ +[ + { + "key": "property", + "value": "two" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_property_set.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_property_set.json new file mode 100644 index 00000000000..327d187331c --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_property_set.json @@ -0,0 +1,22 @@ +[ + { + "key": "foo", + "value": "1,2", + "values": [ + "1", + "2" + ] + }, + { + "key": "foo.1.key", + "value": "key1" + }, + { + "key": "foo.2.key", + "value": "key2" + }, + { + "key": "foo.1.size", + "value": "size1" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_in_property_set_when_system_admin.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_in_property_set_when_system_admin.json new file mode 100644 index 00000000000..3da8aaba675 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_in_property_set_when_system_admin.json @@ -0,0 +1,21 @@ +[ + { + "key": "foo", + "value": "1", + "values": [ + "1" + ] + }, + { + "key": "foo.1.key", + "value": "key1" + }, + { + "key": "foo.1.plugin.license.secured", + "value": "ABCD" + }, + { + "key": "foo.1.secret.secured", + "value": "123456" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_when_project_admin.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_when_project_admin.json new file mode 100644 index 00000000000..29261a0ea24 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_when_project_admin.json @@ -0,0 +1,18 @@ +[ + { + "key": "secret.secured", + "value": "password" + }, + { + "key": "plugin.licenseHash.secured", + "value": "987654321" + }, + { + "key": "foo", + "value": "one" + }, + { + "key": "plugin.license.secured", + "value": "ABCD" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_when_system_admin.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_when_system_admin.json new file mode 100644 index 00000000000..29261a0ea24 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_secured_and_license_settings_when_system_admin.json @@ -0,0 +1,18 @@ +[ + { + "key": "secret.secured", + "value": "password" + }, + { + "key": "plugin.licenseHash.secured", + "value": "987654321" + }, + { + "key": "foo", + "value": "one" + }, + { + "key": "plugin.license.secured", + "value": "ABCD" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_simple_value.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_simple_value.json new file mode 100644 index 00000000000..d98b4c40613 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_simple_value.json @@ -0,0 +1,6 @@ +[ + { + "key": "foo", + "value": "one" + } +] diff --git a/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_values_even_if_no_property_definition.json b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_values_even_if_no_property_definition.json new file mode 100644 index 00000000000..c3d4ba19dd4 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/config/ws/IndexActionTest/return_values_even_if_no_property_definition.json @@ -0,0 +1,6 @@ +[ + { + "key": "globalPropertyWithoutDefinition", + "value": "value" + } +] diff --git a/sonar-db/src/main/java/org/sonar/db/property/PropertiesDao.java b/sonar-db/src/main/java/org/sonar/db/property/PropertiesDao.java index fb7718edcda..ee83f0e5040 100644 --- a/sonar-db/src/main/java/org/sonar/db/property/PropertiesDao.java +++ b/sonar-db/src/main/java/org/sonar/db/property/PropertiesDao.java @@ -162,6 +162,10 @@ public class PropertiesDao implements Dao { partitionComponentIds -> getMapper(session).selectByKeysAndComponentIds(partitionKeys, partitionComponentIds))); } + public List selectPropertiesByComponentIds(DbSession session, Set componentIds) { + return executeLargeInputs(componentIds, getMapper(session)::selectByComponentIds); + } + private List selectByKeys(DbSession session, Set keys, @Nullable Long componentId) { return executeLargeInputs(keys, partitionKeys -> getMapper(session).selectByKeys(partitionKeys, componentId)); } diff --git a/sonar-db/src/main/java/org/sonar/db/property/PropertiesMapper.java b/sonar-db/src/main/java/org/sonar/db/property/PropertiesMapper.java index 05ea17e97fc..8d4d996a9d1 100644 --- a/sonar-db/src/main/java/org/sonar/db/property/PropertiesMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/property/PropertiesMapper.java @@ -39,6 +39,8 @@ public interface PropertiesMapper { List selectByKeysAndComponentIds(@Param("keys") List keys, @Param("componentIds") List componentIds); + List selectByComponentIds(@Param("componentIds") List componentIds); + List selectByQuery(@Param("query") PropertyQuery query); List selectDescendantModuleProperties(@Param("moduleUuid") String moduleUuid, @Param(value = "scope") String scope, diff --git a/sonar-db/src/main/resources/org/sonar/db/property/PropertiesMapper.xml b/sonar-db/src/main/resources/org/sonar/db/property/PropertiesMapper.xml index 6656440da63..517eb1c324e 100644 --- a/sonar-db/src/main/resources/org/sonar/db/property/PropertiesMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/property/PropertiesMapper.xml @@ -150,6 +150,19 @@ and p.user_id is null + +