3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.setting.ws;
22 import javax.annotation.Nullable;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.sonar.api.PropertyType;
27 import org.sonar.api.config.PropertyDefinition;
28 import org.sonar.api.config.PropertyDefinitions;
29 import org.sonar.api.config.PropertyFieldDefinition;
30 import org.sonar.api.server.ws.WebService;
31 import org.sonar.api.server.ws.WebService.Param;
32 import org.sonar.api.utils.System2;
33 import org.sonar.db.DbClient;
34 import org.sonar.db.DbTester;
35 import org.sonar.db.component.ComponentDbTester;
36 import org.sonar.db.component.ComponentDto;
37 import org.sonar.db.component.ComponentTesting;
38 import org.sonar.server.component.TestComponentFinder;
39 import org.sonar.server.exceptions.ForbiddenException;
40 import org.sonar.server.exceptions.NotFoundException;
41 import org.sonar.server.tester.UserSessionRule;
42 import org.sonar.server.ws.TestRequest;
43 import org.sonar.server.ws.WsActionTester;
44 import org.sonar.test.JsonAssert;
45 import org.sonarqube.ws.Settings;
46 import org.sonarqube.ws.Settings.Definition;
47 import org.sonarqube.ws.Settings.ListDefinitionsWsResponse;
49 import static java.util.Arrays.asList;
50 import static org.assertj.core.api.Assertions.assertThat;
51 import static org.assertj.core.api.Assertions.assertThatThrownBy;
52 import static org.assertj.core.groups.Tuple.tuple;
53 import static org.sonar.api.resources.Qualifiers.PROJECT;
54 import static org.sonar.api.web.UserRole.ADMIN;
55 import static org.sonar.api.web.UserRole.CODEVIEWER;
56 import static org.sonar.api.web.UserRole.USER;
57 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
58 import static org.sonar.db.permission.GlobalPermission.SCAN;
59 import static org.sonarqube.ws.MediaTypes.JSON;
60 import static org.sonarqube.ws.Settings.Definition.CategoryOneOfCase.CATEGORYONEOF_NOT_SET;
61 import static org.sonarqube.ws.Settings.Definition.DefaultValueOneOfCase.DEFAULTVALUEONEOF_NOT_SET;
62 import static org.sonarqube.ws.Settings.Definition.DeprecatedKeyOneOfCase.DEPRECATEDKEYONEOF_NOT_SET;
63 import static org.sonarqube.ws.Settings.Definition.NameOneOfCase.NAMEONEOF_NOT_SET;
64 import static org.sonarqube.ws.Settings.Definition.SubCategoryOneOfCase.SUBCATEGORYONEOF_NOT_SET;
65 import static org.sonarqube.ws.Settings.Type.BOOLEAN;
66 import static org.sonarqube.ws.Settings.Type.LICENSE;
67 import static org.sonarqube.ws.Settings.Type.PROPERTY_SET;
68 import static org.sonarqube.ws.Settings.Type.SINGLE_SELECT_LIST;
69 import static org.sonarqube.ws.Settings.Type.STRING;
70 import static org.sonarqube.ws.Settings.Type.TEXT;
72 public class ListDefinitionsActionTest {
75 public UserSessionRule userSession = UserSessionRule.standalone();
77 public DbTester db = DbTester.create(System2.INSTANCE);
79 private DbClient dbClient = db.getDbClient();
80 private ComponentDbTester componentDb = new ComponentDbTester(db);
81 private ComponentDto project;
82 private PropertyDefinitions propertyDefinitions = new PropertyDefinitions(System2.INSTANCE);
83 private SettingsWsSupport support = new SettingsWsSupport(userSession);
84 private WsActionTester ws = new WsActionTester(
85 new ListDefinitionsAction(dbClient, TestComponentFinder.from(db), userSession, propertyDefinitions, support));
89 project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto());
93 public void return_settings_definitions() {
95 propertyDefinitions.addComponent(PropertyDefinition
100 .subCategory("subCat")
101 .type(PropertyType.TEXT)
102 .defaultValue("default")
106 ListDefinitionsWsResponse result = executeRequest();
108 assertThat(result.getDefinitionsList()).hasSize(1);
109 Definition definition = result.getDefinitions(0);
110 assertThat(definition.getKey()).isEqualTo("foo");
111 assertThat(definition.getName()).isEqualTo("Foo");
112 assertThat(definition.getDescription()).isEqualTo("desc");
113 assertThat(definition.getCategory()).isEqualTo("cat");
114 assertThat(definition.getSubCategory()).isEqualTo("subCat");
115 assertThat(definition.getType()).isEqualTo(TEXT);
116 assertThat(definition.getDefaultValue()).isEqualTo("default");
117 assertThat(definition.getMultiValues()).isTrue();
121 public void return_settings_definitions_with_minimum_fields() {
123 propertyDefinitions.addComponent(PropertyDefinition
127 ListDefinitionsWsResponse result = executeRequest();
129 assertThat(result.getDefinitionsList()).hasSize(1);
130 Definition definition = result.getDefinitions(0);
131 assertThat(definition.getKey()).isEqualTo("foo");
132 assertThat(definition.getType()).isEqualTo(STRING);
133 assertThat(definition.getNameOneOfCase()).isEqualTo(NAMEONEOF_NOT_SET);
134 assertThat(definition.getCategoryOneOfCase()).isEqualTo(CATEGORYONEOF_NOT_SET);
135 assertThat(definition.getSubCategoryOneOfCase()).isEqualTo(SUBCATEGORYONEOF_NOT_SET);
136 assertThat(definition.getDefaultValueOneOfCase()).isEqualTo(DEFAULTVALUEONEOF_NOT_SET);
137 assertThat(definition.getMultiValues()).isFalse();
138 assertThat(definition.getOptionsCount()).isZero();
139 assertThat(definition.getFieldsCount()).isZero();
140 assertThat(definition.getDeprecatedKeyOneOfCase()).isEqualTo(DEPRECATEDKEYONEOF_NOT_SET);
144 public void return_settings_definitions_with_deprecated_key() {
146 propertyDefinitions.addComponent(PropertyDefinition
149 .deprecatedKey("deprecated")
152 ListDefinitionsWsResponse result = executeRequest();
154 assertThat(result.getDefinitionsList()).hasSize(1);
155 Definition definition = result.getDefinitions(0);
156 assertThat(definition.getKey()).isEqualTo("foo");
157 assertThat(definition.getName()).isEqualTo("Foo");
158 assertThat(definition.getDeprecatedKey()).isEqualTo("deprecated");
162 public void return_default_category() {
164 propertyDefinitions.addComponent(PropertyDefinition.builder("foo").build(), "default");
165 propertyDefinitions.addComponent(PropertyDefinition.builder("foo").category("").build(), "default");
167 ListDefinitionsWsResponse result = executeRequest();
169 assertThat(result.getDefinitionsList()).hasSize(1);
170 assertThat(result.getDefinitions(0).getCategory()).isEqualTo("default");
171 assertThat(result.getDefinitions(0).getSubCategory()).isEqualTo("default");
175 public void return_single_select_list_property() {
177 propertyDefinitions.addComponent(PropertyDefinition
179 .type(PropertyType.SINGLE_SELECT_LIST)
180 .options("one", "two")
183 ListDefinitionsWsResponse result = executeRequest();
185 assertThat(result.getDefinitionsList()).hasSize(1);
186 Definition definition = result.getDefinitions(0);
187 assertThat(definition.getType()).isEqualTo(SINGLE_SELECT_LIST);
188 assertThat(definition.getOptionsList()).containsExactly("one", "two");
192 public void return_JSON_property() {
194 propertyDefinitions.addComponent(PropertyDefinition
196 .type(PropertyType.JSON)
199 ListDefinitionsWsResponse result = executeRequest();
201 assertThat(result.getDefinitionsList()).hasSize(1);
202 Definition definition = result.getDefinitions(0);
203 assertThat(definition.getType()).isEqualTo(Settings.Type.JSON);
207 public void return_property_set() {
209 propertyDefinitions.addComponent(PropertyDefinition
211 .type(PropertyType.PROPERTY_SET)
213 PropertyFieldDefinition.build("boolean").name("Boolean").description("boolean desc").type(PropertyType.BOOLEAN).build(),
214 PropertyFieldDefinition.build("list").name("List").description("list desc").type(PropertyType.SINGLE_SELECT_LIST).options("one", "two").build())
217 ListDefinitionsWsResponse result = executeRequest();
219 assertThat(result.getDefinitionsList()).hasSize(1);
220 Definition definition = result.getDefinitions(0);
221 assertThat(definition.getType()).isEqualTo(PROPERTY_SET);
222 assertThat(definition.getFieldsList()).hasSize(2);
224 assertThat(definition.getFields(0).getKey()).isEqualTo("boolean");
225 assertThat(definition.getFields(0).getName()).isEqualTo("Boolean");
226 assertThat(definition.getFields(0).getDescription()).isEqualTo("boolean desc");
227 assertThat(definition.getFields(0).getType()).isEqualTo(BOOLEAN);
228 assertThat(definition.getFields(0).getOptionsCount()).isZero();
230 assertThat(definition.getFields(1).getKey()).isEqualTo("list");
231 assertThat(definition.getFields(1).getName()).isEqualTo("List");
232 assertThat(definition.getFields(1).getDescription()).isEqualTo("list desc");
233 assertThat(definition.getFields(1).getType()).isEqualTo(SINGLE_SELECT_LIST);
234 assertThat(definition.getFields(1).getOptionsList()).containsExactly("one", "two");
238 public void return_license_type_in_property_set() {
240 propertyDefinitions.addComponent(PropertyDefinition
242 .type(PropertyType.PROPERTY_SET)
243 .fields(PropertyFieldDefinition.build("license").name("License").type(PropertyType.LICENSE).build())
246 ListDefinitionsWsResponse result = executeRequest();
248 assertThat(result.getDefinitionsList()).hasSize(1);
249 assertThat(result.getDefinitions(0).getFieldsList()).extracting(Settings.Field::getKey, Settings.Field::getType).containsOnly(tuple("license", LICENSE));
253 public void return_global_settings_definitions() {
255 propertyDefinitions.addComponent(PropertyDefinition.builder("foo").build());
257 ListDefinitionsWsResponse result = executeRequest();
259 assertThat(result.getDefinitionsList()).hasSize(1);
263 public void definitions_are_ordered_by_category_then_index_then_name_case_insensitive() {
265 propertyDefinitions.addComponent(PropertyDefinition.builder("sonar.prop.11").category("cat-1").index(1).name("prop 1").build());
266 propertyDefinitions.addComponent(PropertyDefinition.builder("sonar.prop.12").category("cat-1").index(2).name("prop 2").build());
267 propertyDefinitions.addComponent(PropertyDefinition.builder("sonar.prop.13").category("CAT-1").index(1).name("prop 3").build());
268 propertyDefinitions.addComponent(PropertyDefinition.builder("sonar.prop.41").category("cat-0").index(25).name("prop 1").build());
270 ListDefinitionsWsResponse result = executeRequest();
272 assertThat(result.getDefinitionsList()).extracting(Definition::getKey)
273 .containsExactly("sonar.prop.41", "sonar.prop.11", "sonar.prop.13", "sonar.prop.12");
277 public void return_project_settings_def_by_project_key() {
278 logInAsProjectUser();
279 propertyDefinitions.addComponent(PropertyDefinition
281 .onQualifiers(PROJECT)
284 ListDefinitionsWsResponse result = executeRequest(project.getKey());
286 assertThat(result.getDefinitionsList()).hasSize(1);
290 public void return_only_global_properties_when_no_component_parameter() {
291 logInAsProjectUser();
292 propertyDefinitions.addComponents(asList(
293 PropertyDefinition.builder("global").build(),
294 PropertyDefinition.builder("global-and-project").onQualifiers(PROJECT).build(),
295 PropertyDefinition.builder("only-on-project").onlyOnQualifiers(PROJECT).build()));
297 ListDefinitionsWsResponse result = executeRequest();
299 assertThat(result.getDefinitionsList()).extracting("key").containsOnly("global", "global-and-project");
303 public void return_only_properties_available_for_component_qualifier() {
304 logInAsProjectUser();
305 propertyDefinitions.addComponents(asList(
306 PropertyDefinition.builder("global").build(),
307 PropertyDefinition.builder("global-and-project").onQualifiers(PROJECT).build(),
308 PropertyDefinition.builder("only-on-project").onlyOnQualifiers(PROJECT).build()));
310 ListDefinitionsWsResponse result = executeRequest(project.getKey());
312 assertThat(result.getDefinitionsList()).extracting("key").containsOnly("global-and-project", "only-on-project");
316 public void does_not_return_hidden_properties() {
318 propertyDefinitions.addComponent(PropertyDefinition.builder("foo").hidden().build());
320 ListDefinitionsWsResponse result = executeRequest();
322 assertThat(result.getDefinitionsList()).isEmpty();
326 public void return_license_type() {
328 propertyDefinitions.addComponents(asList(
329 PropertyDefinition.builder("plugin.license.secured").type(PropertyType.LICENSE).build(),
330 PropertyDefinition.builder("commercial.plugin").type(PropertyType.LICENSE).build()));
332 ListDefinitionsWsResponse result = executeRequest();
334 assertThat(result.getDefinitionsList()).extracting(Definition::getKey, Definition::getType)
335 .containsOnly(tuple("plugin.license.secured", LICENSE), tuple("commercial.plugin", LICENSE));
339 public void does_not_returned_secured_and_license_settings_when_not_authenticated() {
340 propertyDefinitions.addComponents(asList(
341 PropertyDefinition.builder("foo").build(),
342 PropertyDefinition.builder("secret.secured").build()));
344 ListDefinitionsWsResponse result = executeRequest();
346 assertThat(result.getDefinitionsList()).extracting(Definition::getKey).containsOnly("foo");
350 public void return_secured_settings_when_not_authenticated_but_with_scan_permission() {
351 userSession.anonymous().addPermission(SCAN);
352 propertyDefinitions.addComponents(asList(
353 PropertyDefinition.builder("foo").build(),
354 PropertyDefinition.builder("secret.secured").build()));
356 ListDefinitionsWsResponse result = executeRequest();
358 assertThat(result.getDefinitionsList()).extracting(Definition::getKey).containsOnly("foo", "secret.secured");
362 public void return_secured_settings_when_system_admin() {
364 propertyDefinitions.addComponents(asList(
365 PropertyDefinition.builder("foo").build(),
366 PropertyDefinition.builder("secret.secured").build()));
368 ListDefinitionsWsResponse result = executeRequest();
370 assertThat(result.getDefinitionsList()).extracting(Definition::getKey).containsOnly("foo", "secret.secured");
374 public void return_secured_settings_when_project_admin() {
375 logInAsProjectAdmin();
376 propertyDefinitions.addComponents(asList(
377 PropertyDefinition.builder("foo").onQualifiers(PROJECT).build(),
378 PropertyDefinition.builder("secret.secured").onQualifiers(PROJECT).build()));
380 ListDefinitionsWsResponse result = executeRequest(project.getKey());
382 assertThat(result.getDefinitionsList()).extracting(Definition::getKey).containsOnly("foo", "secret.secured");
386 public void fail_when_user_has_not_project_browse_permission() {
387 userSession.logIn("project-admin").addProjectPermission(CODEVIEWER, project);
388 propertyDefinitions.addComponent(PropertyDefinition.builder("foo").build());
390 assertThatThrownBy(() -> executeRequest(project.getKey()))
391 .isInstanceOf(ForbiddenException.class);
395 public void fail_when_component_not_found() {
396 assertThatThrownBy(() -> {
398 .setParam("component", "unknown")
401 .isInstanceOf(NotFoundException.class)
402 .hasMessage("Component key 'unknown' not found");
406 public void test_ws_definition() {
407 WebService.Action action = ws.getDef();
408 assertThat(action).isNotNull();
409 assertThat(action.isInternal()).isFalse();
410 assertThat(action.isPost()).isFalse();
411 assertThat(action.responseExampleAsString()).isNotEmpty();
412 assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component");
416 public void test_example_json_response() {
417 logInAsProjectAdmin();
418 propertyDefinitions.addComponents(asList(
419 PropertyDefinition.builder("sonar.string")
421 .description("String property")
422 .type(PropertyType.STRING)
427 PropertyDefinition.builder("sonar.list")
429 .description("List property")
430 .type(PropertyType.SINGLE_SELECT_LIST)
434 PropertyDefinition.builder("sonar.multiValues")
435 .name("Multi values")
436 .description("Multi values property")
437 .type(PropertyType.STRING)
441 PropertyDefinition.builder("sonar.propertySet")
442 .name("Property Set")
443 .description("Property Set property")
444 .type(PropertyType.PROPERTY_SET)
445 .category("property")
448 PropertyFieldDefinition.build("text")
450 .description("Text field description")
451 .type(PropertyType.TEXT)
453 PropertyFieldDefinition.build("list")
455 .description("List field description")
456 .type(PropertyType.SINGLE_SELECT_LIST)
457 .options("value1", "value2")
461 String result = ws.newRequest().setMediaType(JSON).execute().getInput();
463 JsonAssert.assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result);
466 private ListDefinitionsWsResponse executeRequest() {
467 return executeRequest(null);
470 private ListDefinitionsWsResponse executeRequest(@Nullable String key) {
471 TestRequest request = ws.newRequest();
473 request.setParam("component", key);
475 return request.executeProtobuf(ListDefinitionsWsResponse.class);
478 private void logIn() {
482 private void logInAsProjectUser() {
483 userSession.logIn().addProjectPermission(USER, project);
486 private void logInAsAdmin() {
487 userSession.logIn().addPermission(ADMINISTER);
490 private void logInAsProjectAdmin() {
492 .addProjectPermission(ADMIN, project)
493 .addProjectPermission(USER, project);