You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SetActionTest.java 41KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  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.
  10. *
  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.
  15. *
  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.
  19. */
  20. package org.sonar.server.setting.ws;
  21. import com.google.common.collect.ImmutableMap;
  22. import com.google.gson.Gson;
  23. import java.net.HttpURLConnection;
  24. import java.util.List;
  25. import java.util.Random;
  26. import javax.annotation.Nullable;
  27. import org.junit.Before;
  28. import org.junit.Rule;
  29. import org.junit.Test;
  30. import org.junit.rules.ExpectedException;
  31. import org.sonar.api.PropertyType;
  32. import org.sonar.api.config.PropertyDefinition;
  33. import org.sonar.api.config.PropertyDefinitions;
  34. import org.sonar.api.config.PropertyFieldDefinition;
  35. import org.sonar.api.resources.Qualifiers;
  36. import org.sonar.api.server.ws.WebService;
  37. import org.sonar.api.server.ws.WebService.Param;
  38. import org.sonar.api.utils.System2;
  39. import org.sonar.api.web.UserRole;
  40. import org.sonar.db.DbClient;
  41. import org.sonar.db.DbSession;
  42. import org.sonar.db.DbTester;
  43. import org.sonar.db.component.ComponentDto;
  44. import org.sonar.db.component.ComponentTesting;
  45. import org.sonar.db.organization.OrganizationDto;
  46. import org.sonar.db.property.PropertyDbTester;
  47. import org.sonar.db.property.PropertyDto;
  48. import org.sonar.db.property.PropertyQuery;
  49. import org.sonar.process.ProcessProperties;
  50. import org.sonar.scanner.protocol.GsonHelper;
  51. import org.sonar.server.component.ComponentFinder;
  52. import org.sonar.server.component.TestComponentFinder;
  53. import org.sonar.server.exceptions.BadRequestException;
  54. import org.sonar.server.exceptions.ForbiddenException;
  55. import org.sonar.server.exceptions.NotFoundException;
  56. import org.sonar.server.l18n.I18nRule;
  57. import org.sonar.server.organization.DefaultOrganizationProvider;
  58. import org.sonar.server.organization.TestDefaultOrganizationProvider;
  59. import org.sonar.server.platform.SettingsChangeNotifier;
  60. import org.sonar.server.tester.UserSessionRule;
  61. import org.sonar.server.ws.TestRequest;
  62. import org.sonar.server.ws.TestResponse;
  63. import org.sonar.server.ws.WsActionTester;
  64. import static com.google.common.collect.Lists.newArrayList;
  65. import static java.lang.String.format;
  66. import static java.util.Collections.singletonList;
  67. import static org.assertj.core.api.Assertions.assertThat;
  68. import static org.assertj.core.groups.Tuple.tuple;
  69. import static org.sonar.db.component.ComponentTesting.newView;
  70. import static org.sonar.db.metric.MetricTesting.newMetricDto;
  71. import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto;
  72. import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto;
  73. import static org.sonar.db.user.UserTesting.newUserDto;
  74. public class SetActionTest {
  75. private static final Gson GSON = GsonHelper.create();
  76. @Rule
  77. public ExpectedException expectedException = ExpectedException.none();
  78. @Rule
  79. public UserSessionRule userSession = UserSessionRule.standalone().logIn();
  80. @Rule
  81. public DbTester db = DbTester.create(System2.INSTANCE);
  82. private PropertyDbTester propertyDb = new PropertyDbTester(db);
  83. private DbClient dbClient = db.getDbClient();
  84. private DbSession dbSession = db.getSession();
  85. private ComponentFinder componentFinder = TestComponentFinder.from(db);
  86. private I18nRule i18n = new I18nRule();
  87. private PropertyDefinitions definitions = new PropertyDefinitions();
  88. private FakeSettingsNotifier settingsChangeNotifier = new FakeSettingsNotifier(dbClient);
  89. private SettingsUpdater settingsUpdater = new SettingsUpdater(dbClient, definitions);
  90. private SettingValidations validations = new SettingValidations(definitions, dbClient, i18n);
  91. private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
  92. private SetAction underTest = new SetAction(definitions, dbClient, componentFinder, userSession, settingsUpdater, settingsChangeNotifier, validations,
  93. new SettingsWsSupport(defaultOrganizationProvider, userSession));
  94. private WsActionTester ws = new WsActionTester(underTest);
  95. @Before
  96. public void setUp() {
  97. // by default test doesn't care about permissions
  98. userSession.logIn().setSystemAdministrator();
  99. }
  100. @Test
  101. public void empty_204_response() {
  102. TestResponse result = ws.newRequest()
  103. .setParam("key", "my.key")
  104. .setParam("value", "my value")
  105. .execute();
  106. assertThat(result.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
  107. assertThat(result.getInput()).isEmpty();
  108. }
  109. @Test
  110. public void persist_new_global_setting() {
  111. callForGlobalSetting("my.key", "my,value");
  112. assertGlobalSetting("my.key", "my,value");
  113. assertThat(settingsChangeNotifier.wasCalled).isTrue();
  114. }
  115. @Test
  116. public void update_existing_global_setting() {
  117. propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my value"));
  118. assertGlobalSetting("my.key", "my value");
  119. callForGlobalSetting("my.key", "my new value");
  120. assertGlobalSetting("my.key", "my new value");
  121. assertThat(settingsChangeNotifier.wasCalled).isTrue();
  122. }
  123. @Test
  124. public void persist_new_project_setting() {
  125. propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my global value"));
  126. ComponentDto project = db.components().insertPrivateProject();
  127. logInAsProjectAdministrator(project);
  128. callForProjectSettingByKey("my.key", "my project value", project.getDbKey());
  129. assertGlobalSetting("my.key", "my global value");
  130. assertComponentSetting("my.key", "my project value", project.getId());
  131. assertThat(settingsChangeNotifier.wasCalled).isFalse();
  132. }
  133. @Test
  134. public void persist_project_property_with_project_admin_permission() {
  135. ComponentDto project = db.components().insertPrivateProject();
  136. logInAsProjectAdministrator(project);
  137. callForProjectSettingByKey("my.key", "my value", project.getDbKey());
  138. assertComponentSetting("my.key", "my value", project.getId());
  139. }
  140. @Test
  141. public void update_existing_project_setting() {
  142. propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my global value"));
  143. ComponentDto project = db.components().insertPrivateProject();
  144. propertyDb.insertProperty(newComponentPropertyDto("my.key", "my project value", project));
  145. assertComponentSetting("my.key", "my project value", project.getId());
  146. logInAsProjectAdministrator(project);
  147. callForProjectSettingByKey("my.key", "my new project value", project.getDbKey());
  148. assertComponentSetting("my.key", "my new project value", project.getId());
  149. }
  150. @Test
  151. public void persist_several_multi_value_setting() {
  152. callForMultiValueGlobalSetting("my.key", newArrayList("first,Value", "second,Value", "third,Value"));
  153. String expectedValue = "first%2CValue,second%2CValue,third%2CValue";
  154. assertGlobalSetting("my.key", expectedValue);
  155. assertThat(settingsChangeNotifier.wasCalled).isTrue();
  156. }
  157. @Test
  158. public void persist_one_multi_value_setting() {
  159. callForMultiValueGlobalSetting("my.key", newArrayList("first,Value"));
  160. assertGlobalSetting("my.key", "first%2CValue");
  161. }
  162. @Test
  163. public void persist_property_set_setting() {
  164. definitions.addComponent(PropertyDefinition
  165. .builder("my.key")
  166. .name("foo")
  167. .description("desc")
  168. .category("cat")
  169. .subCategory("subCat")
  170. .type(PropertyType.PROPERTY_SET)
  171. .defaultValue("default")
  172. .fields(newArrayList(
  173. PropertyFieldDefinition.build("firstField")
  174. .name("First Field")
  175. .type(PropertyType.STRING)
  176. .build(),
  177. PropertyFieldDefinition.build("secondField")
  178. .name("Second Field")
  179. .type(PropertyType.STRING)
  180. .build()))
  181. .build());
  182. callForGlobalPropertySet("my.key", newArrayList(
  183. GSON.toJson(ImmutableMap.of("firstField", "firstValue", "secondField", "secondValue")),
  184. GSON.toJson(ImmutableMap.of("firstField", "anotherFirstValue", "secondField", "anotherSecondValue")),
  185. GSON.toJson(ImmutableMap.of("firstField", "yetFirstValue", "secondField", "yetSecondValue"))));
  186. assertThat(dbClient.propertiesDao().selectGlobalProperties(dbSession)).hasSize(7);
  187. assertGlobalSetting("my.key", "1,2,3");
  188. assertGlobalSetting("my.key.1.firstField", "firstValue");
  189. assertGlobalSetting("my.key.1.secondField", "secondValue");
  190. assertGlobalSetting("my.key.2.firstField", "anotherFirstValue");
  191. assertGlobalSetting("my.key.2.secondField", "anotherSecondValue");
  192. assertGlobalSetting("my.key.3.firstField", "yetFirstValue");
  193. assertGlobalSetting("my.key.3.secondField", "yetSecondValue");
  194. assertThat(settingsChangeNotifier.wasCalled).isTrue();
  195. }
  196. @Test
  197. public void update_property_set_setting() {
  198. definitions.addComponent(PropertyDefinition
  199. .builder("my.key")
  200. .name("foo")
  201. .description("desc")
  202. .category("cat")
  203. .subCategory("subCat")
  204. .type(PropertyType.PROPERTY_SET)
  205. .defaultValue("default")
  206. .fields(newArrayList(
  207. PropertyFieldDefinition.build("firstField")
  208. .name("First Field")
  209. .type(PropertyType.STRING)
  210. .build(),
  211. PropertyFieldDefinition.build("secondField")
  212. .name("Second Field")
  213. .type(PropertyType.STRING)
  214. .build()))
  215. .build());
  216. propertyDb.insertProperties(
  217. newGlobalPropertyDto("my.key", "1,2,3,4"),
  218. newGlobalPropertyDto("my.key.1.firstField", "oldFirstValue"),
  219. newGlobalPropertyDto("my.key.1.secondField", "oldSecondValue"),
  220. newGlobalPropertyDto("my.key.2.firstField", "anotherOldFirstValue"),
  221. newGlobalPropertyDto("my.key.2.secondField", "anotherOldSecondValue"),
  222. newGlobalPropertyDto("my.key.3.firstField", "oldFirstValue"),
  223. newGlobalPropertyDto("my.key.3.secondField", "oldSecondValue"),
  224. newGlobalPropertyDto("my.key.4.firstField", "anotherOldFirstValue"),
  225. newGlobalPropertyDto("my.key.4.secondField", "anotherOldSecondValue"));
  226. callForGlobalPropertySet("my.key", newArrayList(
  227. GSON.toJson(ImmutableMap.of("firstField", "firstValue", "secondField", "secondValue")),
  228. GSON.toJson(ImmutableMap.of("firstField", "anotherFirstValue", "secondField", "anotherSecondValue")),
  229. GSON.toJson(ImmutableMap.of("firstField", "yetFirstValue", "secondField", "yetSecondValue"))));
  230. assertThat(dbClient.propertiesDao().selectGlobalProperties(dbSession)).hasSize(7);
  231. assertGlobalSetting("my.key", "1,2,3");
  232. assertGlobalSetting("my.key.1.firstField", "firstValue");
  233. assertGlobalSetting("my.key.1.secondField", "secondValue");
  234. assertGlobalSetting("my.key.2.firstField", "anotherFirstValue");
  235. assertGlobalSetting("my.key.2.secondField", "anotherSecondValue");
  236. assertGlobalSetting("my.key.3.firstField", "yetFirstValue");
  237. assertGlobalSetting("my.key.3.secondField", "yetSecondValue");
  238. assertThat(settingsChangeNotifier.wasCalled).isTrue();
  239. }
  240. @Test
  241. public void update_property_set_on_component_setting() {
  242. definitions.addComponent(PropertyDefinition
  243. .builder("my.key")
  244. .name("foo")
  245. .description("desc")
  246. .category("cat")
  247. .subCategory("subCat")
  248. .type(PropertyType.PROPERTY_SET)
  249. .defaultValue("default")
  250. .onQualifiers(Qualifiers.PROJECT)
  251. .fields(newArrayList(
  252. PropertyFieldDefinition.build("firstField")
  253. .name("First Field")
  254. .type(PropertyType.STRING)
  255. .build(),
  256. PropertyFieldDefinition.build("secondField")
  257. .name("Second Field")
  258. .type(PropertyType.STRING)
  259. .build()))
  260. .build());
  261. ComponentDto project = db.components().insertPrivateProject();
  262. propertyDb.insertProperties(
  263. newGlobalPropertyDto("my.key", "1"),
  264. newGlobalPropertyDto("my.key.1.firstField", "oldFirstValue"),
  265. newGlobalPropertyDto("my.key.1.secondField", "oldSecondValue"),
  266. newComponentPropertyDto("my.key", "1", project),
  267. newComponentPropertyDto("my.key.1.firstField", "componentFirstValue", project),
  268. newComponentPropertyDto("my.key.1.firstField", "componentSecondValue", project));
  269. logInAsProjectAdministrator(project);
  270. callForComponentPropertySet("my.key", newArrayList(
  271. GSON.toJson(ImmutableMap.of("firstField", "firstValue", "secondField", "secondValue")),
  272. GSON.toJson(ImmutableMap.of("firstField", "anotherFirstValue", "secondField", "anotherSecondValue"))),
  273. project.getDbKey());
  274. assertThat(dbClient.propertiesDao().selectGlobalProperties(dbSession)).hasSize(3);
  275. assertThat(dbClient.propertiesDao().selectProjectProperties(dbSession, project.getDbKey())).hasSize(5);
  276. assertGlobalSetting("my.key", "1");
  277. assertGlobalSetting("my.key.1.firstField", "oldFirstValue");
  278. assertGlobalSetting("my.key.1.secondField", "oldSecondValue");
  279. Long projectId = project.getId();
  280. assertComponentSetting("my.key", "1,2", projectId);
  281. assertComponentSetting("my.key.1.firstField", "firstValue", projectId);
  282. assertComponentSetting("my.key.1.secondField", "secondValue", projectId);
  283. assertComponentSetting("my.key.2.firstField", "anotherFirstValue", projectId);
  284. assertComponentSetting("my.key.2.secondField", "anotherSecondValue", projectId);
  285. assertThat(settingsChangeNotifier.wasCalled).isFalse();
  286. }
  287. @Test
  288. public void persist_multi_value_with_type_metric() {
  289. definitions.addComponent(PropertyDefinition
  290. .builder("my_key")
  291. .name("foo")
  292. .description("desc")
  293. .category("cat")
  294. .subCategory("subCat")
  295. .type(PropertyType.METRIC)
  296. .defaultValue("default")
  297. .multiValues(true)
  298. .build());
  299. dbClient.metricDao().insert(dbSession, newMetricDto().setKey("metric_key_1"));
  300. dbClient.metricDao().insert(dbSession, newMetricDto().setKey("metric_key_2"));
  301. dbSession.commit();
  302. callForMultiValueGlobalSetting("my_key", newArrayList("metric_key_1", "metric_key_2"));
  303. assertGlobalSetting("my_key", "metric_key_1,metric_key_2");
  304. }
  305. @Test
  306. public void persist_multi_value_with_type_logIn() {
  307. definitions.addComponent(PropertyDefinition
  308. .builder("my.key")
  309. .name("foo")
  310. .description("desc")
  311. .category("cat")
  312. .subCategory("subCat")
  313. .type(PropertyType.USER_LOGIN)
  314. .defaultValue("default")
  315. .multiValues(true)
  316. .build());
  317. db.users().insertUser(newUserDto().setLogin("login.1"));
  318. db.users().insertUser(newUserDto().setLogin("login.2"));
  319. callForMultiValueGlobalSetting("my.key", newArrayList("login.1", "login.2"));
  320. assertGlobalSetting("my.key", "login.1,login.2");
  321. }
  322. @Test
  323. public void user_setting_is_not_updated() {
  324. propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my user value").setUserId(42));
  325. propertyDb.insertProperty(newGlobalPropertyDto("my.key", "my global value"));
  326. callForGlobalSetting("my.key", "my new global value");
  327. assertGlobalSetting("my.key", "my new global value");
  328. assertUserSetting("my.key", "my user value", 42);
  329. }
  330. @Test
  331. public void persist_global_property_with_deprecated_key() {
  332. definitions.addComponent(PropertyDefinition
  333. .builder("my.key")
  334. .deprecatedKey("my.old.key")
  335. .name("foo")
  336. .description("desc")
  337. .category("cat")
  338. .subCategory("subCat")
  339. .type(PropertyType.STRING)
  340. .defaultValue("default")
  341. .build());
  342. callForGlobalSetting("my.old.key", "My Value");
  343. assertGlobalSetting("my.key", "My Value");
  344. }
  345. @Test
  346. public void persist_global_setting_with_non_ascii_characters() {
  347. callForGlobalSetting("my.key", "fi±∞…");
  348. assertGlobalSetting("my.key", "fi±∞…");
  349. assertThat(settingsChangeNotifier.wasCalled).isTrue();
  350. }
  351. @Test
  352. public void set_leak_on_branch() {
  353. ComponentDto project = db.components().insertMainBranch();
  354. logInAsProjectAdministrator(project);
  355. ComponentDto branch = db.components().insertProjectBranch(project);
  356. String leakKey = "sonar.leak.period";
  357. definitions.addComponent(PropertyDefinition.builder(leakKey)
  358. .name("Leak")
  359. .description("desc")
  360. .onQualifiers(Qualifiers.PROJECT)
  361. .build());
  362. propertyDb.insertProperties(newComponentPropertyDto(leakKey, "1", branch));
  363. ws.newRequest()
  364. .setParam("key", leakKey)
  365. .setParam("value", "2")
  366. .setParam("component", branch.getKey())
  367. .setParam("branch", branch.getBranch())
  368. .execute();
  369. assertComponentSetting(leakKey, "2", branch.getId());
  370. }
  371. @Test
  372. public void fail_when_no_key() {
  373. expectedException.expect(IllegalArgumentException.class);
  374. callForGlobalSetting(null, "my value");
  375. }
  376. @Test
  377. public void fail_when_empty_key_value() {
  378. expectedException.expect(IllegalArgumentException.class);
  379. expectedException.expectMessage("The 'key' parameter is missing");
  380. callForGlobalSetting(" ", "my value");
  381. }
  382. @Test
  383. public void fail_when_no_value() {
  384. expectedException.expect(BadRequestException.class);
  385. expectedException.expectMessage("Either 'value', 'values' or 'fieldValues' must be provided");
  386. callForGlobalSetting("my.key", null);
  387. }
  388. @Test
  389. public void fail_when_empty_value() {
  390. expectedException.expect(BadRequestException.class);
  391. expectedException.expectMessage("A non empty value must be provided");
  392. callForGlobalSetting("my.key", "");
  393. }
  394. @Test
  395. public void fail_when_one_empty_value_on_multi_value() {
  396. expectedException.expect(BadRequestException.class);
  397. expectedException.expectMessage("A non empty value must be provided");
  398. callForMultiValueGlobalSetting("my.key", newArrayList("oneValue", " ", "anotherValue"));
  399. }
  400. @Test
  401. public void throw_ForbiddenException_if_not_system_administrator() {
  402. userSession.logIn().setNonSystemAdministrator();
  403. expectedException.expect(ForbiddenException.class);
  404. expectedException.expectMessage("Insufficient privileges");
  405. callForGlobalSetting("my.key", "my value");
  406. }
  407. @Test
  408. public void fail_when_data_and_type_do_not_match() {
  409. definitions.addComponent(PropertyDefinition
  410. .builder("my.key")
  411. .name("foo")
  412. .description("desc")
  413. .category("cat")
  414. .subCategory("subCat")
  415. .type(PropertyType.INTEGER)
  416. .defaultValue("default")
  417. .build());
  418. i18n.put("property.error.notInteger", "Not an integer error message");
  419. expectedException.expect(BadRequestException.class);
  420. expectedException.expectMessage("Not an integer error message");
  421. callForGlobalSetting("my.key", "My Value");
  422. }
  423. @Test
  424. public void fail_when_data_and_metric_type_with_invalid_key() {
  425. definitions.addComponent(PropertyDefinition
  426. .builder("my_key")
  427. .name("foo")
  428. .description("desc")
  429. .category("cat")
  430. .subCategory("subCat")
  431. .type(PropertyType.METRIC)
  432. .defaultValue("default")
  433. .multiValues(true)
  434. .build());
  435. dbClient.metricDao().insert(dbSession, newMetricDto().setKey("metric_key"));
  436. dbClient.metricDao().insert(dbSession, newMetricDto().setKey("metric_disabled_key").setEnabled(false));
  437. dbSession.commit();
  438. expectedException.expect(BadRequestException.class);
  439. expectedException.expectMessage("Error when validating metric setting with key 'my_key' and values [metric_key, metric_disabled_key]. A value is not a valid metric key.");
  440. callForMultiValueGlobalSetting("my_key", newArrayList("metric_key", "metric_disabled_key"));
  441. }
  442. @Test
  443. public void fail_when_data_and_login_type_with_invalid_logIn() {
  444. definitions.addComponent(PropertyDefinition
  445. .builder("my.key")
  446. .name("foo")
  447. .description("desc")
  448. .category("cat")
  449. .subCategory("subCat")
  450. .type(PropertyType.USER_LOGIN)
  451. .defaultValue("default")
  452. .multiValues(true)
  453. .build());
  454. db.users().insertUser(newUserDto().setLogin("login.1"));
  455. db.users().insertUser(newUserDto().setLogin("login.2").setActive(false));
  456. expectedException.expect(BadRequestException.class);
  457. expectedException.expectMessage("Error when validating login setting with key 'my.key' and values [login.1, login.2]. A value is not a valid login.");
  458. callForMultiValueGlobalSetting("my.key", newArrayList("login.1", "login.2"));
  459. }
  460. @Test
  461. public void fail_when_data_and_type_do_not_match_with_unknown_error_key() {
  462. definitions.addComponent(PropertyDefinition
  463. .builder("my.key")
  464. .name("foo")
  465. .description("desc")
  466. .category("cat")
  467. .subCategory("subCat")
  468. .type(PropertyType.INTEGER)
  469. .defaultValue("default")
  470. .build());
  471. expectedException.expect(BadRequestException.class);
  472. expectedException.expectMessage("Error when validating setting with key 'my.key' and value [My Value, My Other Value]");
  473. callForMultiValueGlobalSetting("my.key", newArrayList("My Value", "My Other Value"));
  474. }
  475. @Test
  476. public void fail_when_global_with_property_only_on_projects() {
  477. definitions.addComponent(PropertyDefinition
  478. .builder("my.key")
  479. .name("foo")
  480. .description("desc")
  481. .category("cat")
  482. .subCategory("subCat")
  483. .type(PropertyType.INTEGER)
  484. .defaultValue("default")
  485. .onlyOnQualifiers(Qualifiers.PROJECT)
  486. .build());
  487. expectedException.expect(BadRequestException.class);
  488. expectedException.expectMessage("Setting 'my.key' cannot be global");
  489. callForGlobalSetting("my.key", "42");
  490. }
  491. @Test
  492. public void fail_when_view_property_when_on_projects_only() {
  493. definitions.addComponent(PropertyDefinition
  494. .builder("my.key")
  495. .name("foo")
  496. .description("desc")
  497. .category("cat")
  498. .subCategory("subCat")
  499. .type(PropertyType.STRING)
  500. .defaultValue("default")
  501. .onQualifiers(Qualifiers.PROJECT)
  502. .build());
  503. ComponentDto view = db.components().insertComponent(newView(db.getDefaultOrganization(), "view-uuid"));
  504. i18n.put("qualifier." + Qualifiers.VIEW, "View");
  505. expectedException.expect(BadRequestException.class);
  506. expectedException.expectMessage("Setting 'my.key' cannot be set on a View");
  507. logInAsProjectAdministrator(view);
  508. callForProjectSettingByKey("my.key", "My Value", view.getDbKey());
  509. }
  510. @Test
  511. public void fail_when_property_with_definition_when_component_qualifier_does_not_match() {
  512. ComponentDto project = randomPublicOrPrivateProject();
  513. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  514. definitions.addComponent(PropertyDefinition
  515. .builder("my.key")
  516. .name("foo")
  517. .description("desc")
  518. .category("cat")
  519. .subCategory("subCat")
  520. .type(PropertyType.STRING)
  521. .defaultValue("default")
  522. .onQualifiers(Qualifiers.PROJECT)
  523. .build());
  524. i18n.put("qualifier." + file.qualifier(), "CptLabel");
  525. logInAsProjectAdministrator(project);
  526. expectedException.expect(BadRequestException.class);
  527. expectedException.expectMessage("Setting 'my.key' cannot be set on a CptLabel");
  528. callForProjectSettingByKey("my.key", "My Value", file.getDbKey());
  529. }
  530. @Test
  531. public void succeed_for_property_without_definition_when_set_on_project_component() {
  532. ComponentDto project = randomPublicOrPrivateProject();
  533. succeedForPropertyWithoutDefinitionAndValidComponent(project, project);
  534. }
  535. @Test
  536. public void succeed_for_property_without_definition_when_set_on_module_component() {
  537. ComponentDto project = randomPublicOrPrivateProject();
  538. ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
  539. succeedForPropertyWithoutDefinitionAndValidComponent(project, module);
  540. }
  541. @Test
  542. public void fail_for_property_without_definition_when_set_on_directory_component() {
  543. ComponentDto project = randomPublicOrPrivateProject();
  544. ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(project, "A/B"));
  545. failForPropertyWithoutDefinitionOnUnsupportedComponent(project, directory);
  546. }
  547. @Test
  548. public void fail_for_property_without_definition_when_set_on_file_component() {
  549. ComponentDto project = randomPublicOrPrivateProject();
  550. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  551. failForPropertyWithoutDefinitionOnUnsupportedComponent(project, file);
  552. }
  553. @Test
  554. public void succeed_for_property_without_definition_when_set_on_view_component() {
  555. ComponentDto view = db.components().insertView();
  556. succeedForPropertyWithoutDefinitionAndValidComponent(view, view);
  557. }
  558. @Test
  559. public void succeed_for_property_without_definition_when_set_on_subview_component() {
  560. ComponentDto view = db.components().insertView();
  561. ComponentDto subview = db.components().insertComponent(ComponentTesting.newSubView(view));
  562. succeedForPropertyWithoutDefinitionAndValidComponent(view, subview);
  563. }
  564. @Test
  565. public void fail_for_property_without_definition_when_set_on_projectCopy_component() {
  566. ComponentDto view = db.components().insertView();
  567. ComponentDto projectCopy = db.components().insertComponent(ComponentTesting.newProjectCopy("a", db.components().insertPrivateProject(), view));
  568. failForPropertyWithoutDefinitionOnUnsupportedComponent(view, projectCopy);
  569. }
  570. private void succeedForPropertyWithoutDefinitionAndValidComponent(ComponentDto project, ComponentDto module) {
  571. logInAsProjectAdministrator(project);
  572. callForProjectSettingByKey("my.key", "My Value", module.getDbKey());
  573. assertComponentSetting("my.key", "My Value", module.getId());
  574. }
  575. private void failForPropertyWithoutDefinitionOnUnsupportedComponent(ComponentDto root, ComponentDto component) {
  576. i18n.put("qualifier." + component.qualifier(), "QualifierLabel");
  577. logInAsProjectAdministrator(root);
  578. expectedException.expect(BadRequestException.class);
  579. expectedException.expectMessage("Setting 'my.key' cannot be set on a QualifierLabel");
  580. callForProjectSettingByKey("my.key", "My Value", component.getDbKey());
  581. }
  582. @Test
  583. public void fail_when_single_and_multi_value_provided() {
  584. expectedException.expect(BadRequestException.class);
  585. expectedException.expectMessage("Either 'value', 'values' or 'fieldValues' must be provided");
  586. call("my.key", "My Value", newArrayList("Another Value"), null, null);
  587. }
  588. @Test
  589. public void fail_when_multi_definition_and_single_value_provided() {
  590. definitions.addComponent(PropertyDefinition
  591. .builder("my.key")
  592. .name("foo")
  593. .description("desc")
  594. .category("cat")
  595. .type(PropertyType.STRING)
  596. .multiValues(true)
  597. .build());
  598. expectedException.expect(BadRequestException.class);
  599. expectedException.expectMessage("Parameter 'value' must be used for single value setting. Parameter 'values' must be used for multi value setting.");
  600. callForGlobalSetting("my.key", "My Value");
  601. }
  602. @Test
  603. public void fail_when_single_definition_and_multi_value_provided() {
  604. definitions.addComponent(PropertyDefinition
  605. .builder("my.key")
  606. .name("foo")
  607. .description("desc")
  608. .category("cat")
  609. .type(PropertyType.STRING)
  610. .multiValues(false)
  611. .build());
  612. expectedException.expect(BadRequestException.class);
  613. expectedException.expectMessage("Parameter 'value' must be used for single value setting. Parameter 'values' must be used for multi value setting.");
  614. callForMultiValueGlobalSetting("my.key", newArrayList("My Value"));
  615. }
  616. @Test
  617. public void fail_when_empty_values_on_one_property_set() {
  618. definitions.addComponent(PropertyDefinition
  619. .builder("my.key")
  620. .name("foo")
  621. .description("desc")
  622. .category("cat")
  623. .subCategory("subCat")
  624. .type(PropertyType.PROPERTY_SET)
  625. .defaultValue("default")
  626. .fields(newArrayList(
  627. PropertyFieldDefinition.build("firstField")
  628. .name("First Field")
  629. .type(PropertyType.STRING)
  630. .build(),
  631. PropertyFieldDefinition.build("secondField")
  632. .name("Second Field")
  633. .type(PropertyType.STRING)
  634. .build()))
  635. .build());
  636. expectedException.expect(BadRequestException.class);
  637. expectedException.expectMessage("A non empty value must be provided");
  638. callForGlobalPropertySet("my.key", newArrayList(
  639. GSON.toJson(ImmutableMap.of("firstField", "firstValue", "secondField", "secondValue")),
  640. GSON.toJson(ImmutableMap.of("firstField", "", "secondField", "")),
  641. GSON.toJson(ImmutableMap.of("firstField", "yetFirstValue", "secondField", "yetSecondValue"))));
  642. }
  643. @Test
  644. public void do_not_fail_when_only_one_empty_value_on_one_property_set() {
  645. definitions.addComponent(PropertyDefinition
  646. .builder("my.key")
  647. .name("foo")
  648. .description("desc")
  649. .category("cat")
  650. .subCategory("subCat")
  651. .type(PropertyType.PROPERTY_SET)
  652. .defaultValue("default")
  653. .fields(newArrayList(
  654. PropertyFieldDefinition.build("firstField")
  655. .name("First Field")
  656. .type(PropertyType.STRING)
  657. .build(),
  658. PropertyFieldDefinition.build("secondField")
  659. .name("Second Field")
  660. .type(PropertyType.STRING)
  661. .build()))
  662. .build());
  663. callForGlobalPropertySet("my.key", newArrayList(
  664. GSON.toJson(ImmutableMap.of("firstField", "firstValue", "secondField", "secondValue")),
  665. GSON.toJson(ImmutableMap.of("firstField", "anotherFirstValue", "secondField", "")),
  666. GSON.toJson(ImmutableMap.of("firstField", "yetFirstValue", "secondField", "yetSecondValue"))));
  667. assertGlobalSetting("my.key", "1,2,3");
  668. }
  669. @Test
  670. public void fail_when_property_set_setting_is_not_defined() {
  671. expectedException.expect(BadRequestException.class);
  672. expectedException.expectMessage("Setting 'my.key' is undefined");
  673. callForGlobalPropertySet("my.key", singletonList("{\"field\":\"value\"}"));
  674. }
  675. @Test
  676. public void fail_when_property_set_with_unknown_field() {
  677. definitions.addComponent(PropertyDefinition
  678. .builder("my.key")
  679. .name("foo")
  680. .description("desc")
  681. .category("cat")
  682. .subCategory("subCat")
  683. .type(PropertyType.PROPERTY_SET)
  684. .defaultValue("default")
  685. .fields(newArrayList(
  686. PropertyFieldDefinition.build("field")
  687. .name("Field")
  688. .type(PropertyType.STRING)
  689. .build()))
  690. .build());
  691. expectedException.expect(BadRequestException.class);
  692. expectedException.expectMessage("Unknown field key 'unknownField' for setting 'my.key'");
  693. callForGlobalPropertySet("my.key", newArrayList(GSON.toJson(ImmutableMap.of("field", "value", "unknownField", "anotherValue"))));
  694. }
  695. @Test
  696. public void fail_when_property_set_has_field_with_incorrect_type() {
  697. definitions.addComponent(PropertyDefinition
  698. .builder("my.key")
  699. .name("foo")
  700. .description("desc")
  701. .category("cat")
  702. .subCategory("subCat")
  703. .type(PropertyType.PROPERTY_SET)
  704. .defaultValue("default")
  705. .fields(newArrayList(
  706. PropertyFieldDefinition.build("field")
  707. .name("Field")
  708. .type(PropertyType.INTEGER)
  709. .build()))
  710. .build());
  711. expectedException.expect(BadRequestException.class);
  712. expectedException.expectMessage("Error when validating setting with key 'my.key'. Field 'field' has incorrect value 'notAnInt'.");
  713. callForGlobalPropertySet("my.key", newArrayList(GSON.toJson(ImmutableMap.of("field", "notAnInt"))));
  714. }
  715. @Test
  716. public void fail_when_property_set_has_a_null_field_value() {
  717. definitions.addComponent(PropertyDefinition
  718. .builder("my.key")
  719. .name("foo")
  720. .description("desc")
  721. .category("cat")
  722. .subCategory("subCat")
  723. .type(PropertyType.PROPERTY_SET)
  724. .defaultValue("default")
  725. .fields(newArrayList(
  726. PropertyFieldDefinition.build("field")
  727. .name("Field")
  728. .type(PropertyType.STRING)
  729. .build()))
  730. .build());
  731. expectedException.expect(BadRequestException.class);
  732. expectedException.expectMessage("A non empty value must be provided");
  733. callForGlobalPropertySet("my.key", newArrayList("{\"field\": null}"));
  734. }
  735. @Test
  736. public void fail_when_property_set_with_invalid_json() {
  737. definitions.addComponent(PropertyDefinition
  738. .builder("my.key")
  739. .name("foo")
  740. .description("desc")
  741. .category("cat")
  742. .subCategory("subCat")
  743. .type(PropertyType.PROPERTY_SET)
  744. .defaultValue("default")
  745. .fields(newArrayList(
  746. PropertyFieldDefinition.build("field")
  747. .name("Field")
  748. .type(PropertyType.STRING)
  749. .build()))
  750. .build());
  751. expectedException.expect(BadRequestException.class);
  752. expectedException.expectMessage("JSON 'incorrectJson:incorrectJson' does not respect expected format for setting 'my.key'. " +
  753. "Ex: {\"field1\":\"value1\", \"field2\":\"value2\"}");
  754. callForGlobalPropertySet("my.key", newArrayList("incorrectJson:incorrectJson"));
  755. }
  756. @Test
  757. public void fail_when_property_set_with_json_of_the_wrong_format() {
  758. definitions.addComponent(PropertyDefinition
  759. .builder("my.key")
  760. .name("foo")
  761. .description("desc")
  762. .category("cat")
  763. .subCategory("subCat")
  764. .type(PropertyType.PROPERTY_SET)
  765. .defaultValue("default")
  766. .fields(newArrayList(
  767. PropertyFieldDefinition.build("field")
  768. .name("Field")
  769. .type(PropertyType.STRING)
  770. .build()))
  771. .build());
  772. expectedException.expect(BadRequestException.class);
  773. expectedException.expectMessage("JSON '[{\"field\":\"v1\"}, {\"field\":\"v2\"}]' does not respect expected format for setting 'my.key'. " +
  774. "Ex: {\"field1\":\"value1\", \"field2\":\"value2\"}");
  775. callForGlobalPropertySet("my.key", newArrayList("[{\"field\":\"v1\"}, {\"field\":\"v2\"}]"));
  776. }
  777. @Test
  778. public void fail_when_property_set_on_component_of_global_setting() {
  779. definitions.addComponent(PropertyDefinition
  780. .builder("my.key")
  781. .name("foo")
  782. .description("desc")
  783. .category("cat")
  784. .subCategory("subCat")
  785. .type(PropertyType.PROPERTY_SET)
  786. .defaultValue("default")
  787. .fields(newArrayList(PropertyFieldDefinition.build("firstField").name("First Field").type(PropertyType.STRING).build()))
  788. .build());
  789. i18n.put("qualifier." + Qualifiers.PROJECT, "Project");
  790. ComponentDto project = db.components().insertPrivateProject();
  791. logInAsProjectAdministrator(project);
  792. expectedException.expect(BadRequestException.class);
  793. expectedException.expectMessage("Setting 'my.key' cannot be set on a Project");
  794. callForComponentPropertySet("my.key", newArrayList(
  795. GSON.toJson(ImmutableMap.of("firstField", "firstValue"))), project.getDbKey());
  796. }
  797. @Test
  798. public void fail_when_using_branch_db_key() throws Exception {
  799. OrganizationDto organization = db.organizations().insert();
  800. ComponentDto project = db.components().insertMainBranch(organization);
  801. userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
  802. ComponentDto branch = db.components().insertProjectBranch(project);
  803. expectedException.expect(NotFoundException.class);
  804. expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
  805. callForProjectSettingByKey("my.key", "My Value", branch.getDbKey());
  806. }
  807. @Test
  808. public void fail_when_component_not_found() {
  809. expectedException.expect(NotFoundException.class);
  810. expectedException.expectMessage("Component key 'unknown' not found");
  811. ws.newRequest()
  812. .setParam("key", "foo")
  813. .setParam("value", "2")
  814. .setParam("component", "unknown")
  815. .execute();
  816. }
  817. @Test
  818. public void fail_when_branch_not_found() {
  819. ComponentDto project = db.components().insertMainBranch();
  820. logInAsProjectAdministrator(project);
  821. ComponentDto branch = db.components().insertProjectBranch(project);
  822. String settingKey = "not_allowed_on_branch";
  823. expectedException.expect(NotFoundException.class);
  824. expectedException.expectMessage(format("Component '%s' on branch 'unknown' not found", branch.getKey()));
  825. ws.newRequest()
  826. .setParam("key", settingKey)
  827. .setParam("value", "2")
  828. .setParam("component", branch.getKey())
  829. .setParam("branch", "unknown")
  830. .execute();
  831. }
  832. @Test
  833. public void fail_when_setting_not_allowed_setting_on_branch() {
  834. ComponentDto project = db.components().insertMainBranch();
  835. logInAsProjectAdministrator(project);
  836. ComponentDto branch = db.components().insertProjectBranch(project);
  837. String settingKey = "not_allowed_on_branch";
  838. expectedException.expect(IllegalArgumentException.class);
  839. expectedException.expectMessage(format("Setting '%s' cannot be set on a branch", settingKey));
  840. ws.newRequest()
  841. .setParam("key", settingKey)
  842. .setParam("value", "2")
  843. .setParam("component", branch.getKey())
  844. .setParam("branch", branch.getBranch())
  845. .execute();
  846. }
  847. @Test
  848. public void fail_when_setting_key_is_defined_in_sonar_properties() {
  849. ComponentDto project = db.components().insertPrivateProject();
  850. logInAsProjectAdministrator(project);
  851. String settingKey = ProcessProperties.Property.JDBC_URL.getKey();
  852. expectedException.expect(IllegalArgumentException.class);
  853. expectedException.expectMessage(format("Setting '%s' can only be used in sonar.properties", settingKey));
  854. ws.newRequest()
  855. .setParam("key", settingKey)
  856. .setParam("value", "any value")
  857. .setParam("component", project.getKey())
  858. .execute();
  859. }
  860. @Test
  861. public void definition() {
  862. WebService.Action definition = ws.getDef();
  863. assertThat(definition.key()).isEqualTo("set");
  864. assertThat(definition.isPost()).isTrue();
  865. assertThat(definition.isInternal()).isFalse();
  866. assertThat(definition.since()).isEqualTo("6.1");
  867. assertThat(definition.params()).extracting(Param::key)
  868. .containsOnly("key", "value", "values", "fieldValues", "component", "branch", "pullRequest");
  869. Param branch = definition.param("branch");
  870. assertThat(branch.isInternal()).isTrue();
  871. assertThat(branch.since()).isEqualTo("6.6");
  872. assertThat(branch.description()).isEqualTo("Branch key. Only available on following settings : sonar.leak.period");
  873. }
  874. private void assertGlobalSetting(String key, String value) {
  875. PropertyDto result = dbClient.propertiesDao().selectGlobalProperty(key);
  876. assertThat(result)
  877. .extracting(PropertyDto::getKey, PropertyDto::getValue, PropertyDto::getResourceId)
  878. .containsExactly(key, value, null);
  879. }
  880. private void assertUserSetting(String key, String value, int userId) {
  881. List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setKey(key).setUserId(userId).build(), dbSession);
  882. assertThat(result).hasSize(1)
  883. .extracting(PropertyDto::getKey, PropertyDto::getValue, PropertyDto::getUserId)
  884. .containsExactly(tuple(key, value, userId));
  885. }
  886. private void assertComponentSetting(String key, String value, long componentId) {
  887. PropertyDto result = dbClient.propertiesDao().selectProjectProperty(componentId, key);
  888. assertThat(result)
  889. .isNotNull()
  890. .extracting(PropertyDto::getKey, PropertyDto::getValue, PropertyDto::getResourceId)
  891. .containsExactly(key, value, componentId);
  892. }
  893. private void callForGlobalSetting(@Nullable String key, @Nullable String value) {
  894. call(key, value, null, null, null);
  895. }
  896. private void callForMultiValueGlobalSetting(@Nullable String key, @Nullable List<String> values) {
  897. call(key, null, values, null, null);
  898. }
  899. private void callForGlobalPropertySet(@Nullable String key, @Nullable List<String> fieldValues) {
  900. call(key, null, null, fieldValues, null);
  901. }
  902. private void callForComponentPropertySet(@Nullable String key, @Nullable List<String> fieldValues, @Nullable String componentKey) {
  903. call(key, null, null, fieldValues, componentKey);
  904. }
  905. private void callForProjectSettingByKey(@Nullable String key, @Nullable String value, @Nullable String componentKey) {
  906. call(key, value, null, null, componentKey);
  907. }
  908. private void call(@Nullable String key, @Nullable String value, @Nullable List<String> values, @Nullable List<String> fieldValues, @Nullable String componentKey) {
  909. TestRequest request = ws.newRequest();
  910. if (key != null) {
  911. request.setParam("key", key);
  912. }
  913. if (value != null) {
  914. request.setParam("value", value);
  915. }
  916. if (values != null) {
  917. request.setMultiParam("values", values);
  918. }
  919. if (fieldValues != null) {
  920. request.setMultiParam("fieldValues", fieldValues);
  921. }
  922. if (componentKey != null) {
  923. request.setParam("component", componentKey);
  924. }
  925. request.execute();
  926. }
  927. private static class FakeSettingsNotifier extends SettingsChangeNotifier {
  928. private final DbClient dbClient;
  929. private boolean wasCalled = false;
  930. private FakeSettingsNotifier(DbClient dbClient) {
  931. this.dbClient = dbClient;
  932. }
  933. @Override
  934. public void onGlobalPropertyChange(String key, @Nullable String value) {
  935. wasCalled = true;
  936. PropertyDto property = dbClient.propertiesDao().selectGlobalProperty(key);
  937. assertThat(property.getValue()).isEqualTo(value);
  938. }
  939. }
  940. private void logInAsProjectAdministrator(ComponentDto project) {
  941. userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
  942. }
  943. private ComponentDto randomPublicOrPrivateProject() {
  944. return new Random().nextBoolean() ? db.components().insertPrivateProject() : db.components().insertPublicProject();
  945. }
  946. }