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 39KB

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