@@ -39,9 +39,11 @@ import org.sonarqube.ws.client.setting.ListDefinitionsRequest; | |||
import static com.google.common.base.Strings.emptyToNull; | |||
import static org.sonar.api.web.UserRole.USER; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.server.setting.ws.SettingsWs.SETTING_ON_BRANCHES; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonarqube.ws.client.setting.SettingsWsParameters.ACTION_LIST_DEFINITIONS; | |||
import static org.sonarqube.ws.client.setting.SettingsWsParameters.PARAM_BRANCH; | |||
import static org.sonarqube.ws.client.setting.SettingsWsParameters.PARAM_COMPONENT; | |||
public class ListDefinitionsAction implements SettingsWsAction { | |||
@@ -79,6 +81,7 @@ public class ListDefinitionsAction implements SettingsWsAction { | |||
action.createParam(PARAM_COMPONENT) | |||
.setDescription("Component key") | |||
.setExampleValue(KEY_PROJECT_EXAMPLE_001); | |||
settingsWsSupport.addBranchParam(action); | |||
} | |||
@Override | |||
@@ -93,6 +96,7 @@ public class ListDefinitionsAction implements SettingsWsAction { | |||
ListDefinitionsWsResponse.Builder wsResponse = ListDefinitionsWsResponse.newBuilder(); | |||
propertyDefinitions.getAll().stream() | |||
.filter(definition -> qualifier.isPresent() ? definition.qualifiers().contains(qualifier.get()) : definition.global()) | |||
.filter(definition -> wsRequest.getBranch() == null || SETTING_ON_BRANCHES.contains(definition.key())) | |||
.filter(settingsWsSupport.isDefinitionVisible(component)) | |||
.forEach(definition -> addDefinition(definition, wsResponse)); | |||
return wsResponse.build(); | |||
@@ -101,6 +105,7 @@ public class ListDefinitionsAction implements SettingsWsAction { | |||
private static ListDefinitionsRequest toWsRequest(Request request) { | |||
return ListDefinitionsRequest.builder() | |||
.setComponent(request.param(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.build(); | |||
} | |||
@@ -108,13 +113,13 @@ public class ListDefinitionsAction implements SettingsWsAction { | |||
return component.isPresent() ? Optional.of(component.get().qualifier()) : Optional.empty(); | |||
} | |||
private Optional<ComponentDto> loadComponent(ListDefinitionsRequest valuesRequest) { | |||
private Optional<ComponentDto> loadComponent(ListDefinitionsRequest request) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
String componentKey = valuesRequest.getComponent(); | |||
String componentKey = request.getComponent(); | |||
if (componentKey == null) { | |||
return Optional.empty(); | |||
} | |||
ComponentDto component = componentFinder.getByKey(dbSession, componentKey); | |||
ComponentDto component = componentFinder.getByKeyAndOptionalBranch(dbSession, componentKey, request.getBranch()); | |||
userSession.checkComponentPermission(USER, component); | |||
return Optional.of(component); | |||
} |
@@ -21,7 +21,6 @@ package org.sonar.server.setting.ws; | |||
import com.google.common.collect.ArrayListMultimap; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.ListMultimap; | |||
import com.google.gson.Gson; | |||
import com.google.gson.JsonSyntaxException; | |||
@@ -59,8 +58,6 @@ import org.sonarqube.ws.client.setting.SetRequest; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.lang.String.format; | |||
import static org.sonar.core.config.CorePropertyDefinitions.LEAK_PERIOD; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonarqube.ws.client.setting.SettingsWsParameters.ACTION_SET; | |||
@@ -74,7 +71,6 @@ import static org.sonarqube.ws.client.setting.SettingsWsParameters.PARAM_VALUES; | |||
public class SetAction implements SettingsWsAction { | |||
private static final Collector<CharSequence, ?, String> COMMA_JOINER = Collectors.joining(","); | |||
private static final String MSG_NO_EMPTY_VALUE = "A non empty value must be provided"; | |||
public static final Set<String> SETTING_ON_BRANCHES = ImmutableSet.of(LEAK_PERIOD); | |||
private final PropertyDefinitions propertyDefinitions; | |||
private final DbClient dbClient; | |||
@@ -83,9 +79,10 @@ public class SetAction implements SettingsWsAction { | |||
private final SettingsUpdater settingsUpdater; | |||
private final SettingsChangeNotifier settingsChangeNotifier; | |||
private final SettingValidations validations; | |||
private final SettingsWsSupport settingsWsSupport; | |||
public SetAction(PropertyDefinitions propertyDefinitions, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, | |||
SettingsUpdater settingsUpdater, SettingsChangeNotifier settingsChangeNotifier, SettingValidations validations) { | |||
SettingsUpdater settingsUpdater, SettingsChangeNotifier settingsChangeNotifier, SettingValidations validations, SettingsWsSupport settingsWsSupport) { | |||
this.propertyDefinitions = propertyDefinitions; | |||
this.dbClient = dbClient; | |||
this.componentFinder = componentFinder; | |||
@@ -93,6 +90,7 @@ public class SetAction implements SettingsWsAction { | |||
this.settingsUpdater = settingsUpdater; | |||
this.settingsChangeNotifier = settingsChangeNotifier; | |||
this.validations = validations; | |||
this.settingsWsSupport = settingsWsSupport; | |||
} | |||
@Override | |||
@@ -131,12 +129,7 @@ public class SetAction implements SettingsWsAction { | |||
.setDescription("Component key") | |||
.setDeprecatedKey("componentKey", "6.3") | |||
.setExampleValue(KEY_PROJECT_EXAMPLE_001); | |||
action.createParam(PARAM_BRANCH) | |||
.setDescription("Branch key. Only available on following settings : %s", SETTING_ON_BRANCHES.stream().collect(COMMA_JOINER)) | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
settingsWsSupport.addBranchParam(action); | |||
} | |||
@Override | |||
@@ -207,7 +200,8 @@ public class SetAction implements SettingsWsAction { | |||
SettingData settingData = new SettingData(settingKey, valuesFromRequest(request), component.orElse(null)); | |||
ImmutableList.of(validations.scope(), validations.qualifier(), validations.valueType()) | |||
.forEach(validation -> validation.accept(settingData)); | |||
component.map(ComponentDto::getBranch).ifPresent(b -> checkArgument(SETTING_ON_BRANCHES.contains(settingKey), format("Setting '%s' cannot be set on a branch", settingKey))); | |||
component.map(ComponentDto::getBranch) | |||
.ifPresent(b -> checkArgument(SettingsWs.SETTING_ON_BRANCHES.contains(settingKey), format("Setting '%s' cannot be set on a branch", settingKey))); | |||
} | |||
private static void validatePropertySet(SetRequest request, @Nullable PropertyDefinition definition) { |
@@ -19,12 +19,17 @@ | |||
*/ | |||
package org.sonar.server.setting.ws; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.Set; | |||
import org.sonar.api.server.ws.WebService; | |||
import static org.sonar.core.config.CorePropertyDefinitions.LEAK_PERIOD; | |||
import static org.sonarqube.ws.client.setting.SettingsWsParameters.CONTROLLER_SETTINGS; | |||
public class SettingsWs implements WebService { | |||
public static final Set<String> SETTING_ON_BRANCHES = ImmutableSet.of(LEAK_PERIOD); | |||
private final SettingsWsAction[] actions; | |||
public SettingsWs(SettingsWsAction... actions) { |
@@ -21,21 +21,28 @@ package org.sonar.server.setting.ws; | |||
import java.util.Optional; | |||
import java.util.function.Predicate; | |||
import java.util.stream.Collector; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.config.PropertyDefinition; | |||
import org.sonar.api.server.ServerSide; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||
import org.sonar.db.permission.OrganizationPermission; | |||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||
import org.sonar.server.user.UserSession; | |||
import static org.sonar.api.PropertyType.LICENSE; | |||
import static org.sonar.api.web.UserRole.ADMIN; | |||
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonarqube.ws.client.setting.SettingsWsParameters.PARAM_BRANCH; | |||
@ServerSide | |||
public class SettingsWsSupport { | |||
private static final Collector<CharSequence, ?, String> COMMA_JOINER = Collectors.joining(","); | |||
public static final String DOT_SECURED = ".secured"; | |||
public static final String DOT_LICENSE = ".license"; | |||
private static final String LICENSE_SUFFIX = DOT_LICENSE + DOT_SECURED; | |||
@@ -81,4 +88,12 @@ public class SettingsWsSupport { | |||
.map(c -> userSession.hasComponentPermission(projectPermission, c)) | |||
.orElse(false); | |||
} | |||
WebService.NewParam addBranchParam(WebService.NewAction action){ | |||
return action.createParam(PARAM_BRANCH) | |||
.setDescription("Branch key. Only available on following settings : %s", SettingsWs.SETTING_ON_BRANCHES.stream().collect(COMMA_JOINER)) | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
} | |||
} |
@@ -29,6 +29,7 @@ import org.sonar.api.config.PropertyDefinition; | |||
import org.sonar.api.config.PropertyDefinitions; | |||
import org.sonar.api.config.PropertyFieldDefinition; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.server.ws.WebService.Param; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbTester; | |||
@@ -38,6 +39,7 @@ import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.server.component.TestComponentFinder; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||
import org.sonar.server.organization.TestDefaultOrganizationProvider; | |||
import org.sonar.server.tester.UserSessionRule; | |||
@@ -47,6 +49,7 @@ import org.sonar.test.JsonAssert; | |||
import org.sonarqube.ws.Settings; | |||
import org.sonarqube.ws.Settings.ListDefinitionsWsResponse; | |||
import static java.lang.String.format; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.groups.Tuple.tuple; | |||
@@ -379,6 +382,23 @@ public class ListDefinitionsActionTest { | |||
assertThat(result.getDefinitionsList()).extracting(Settings.Definition::getKey).containsOnly("foo", "secret.secured", "plugin.license.secured"); | |||
} | |||
@Test | |||
public void definitions_on_branch() throws Exception { | |||
ComponentDto project = db.components().insertMainBranch(); | |||
userSession.logIn().addProjectPermission(USER, project); | |||
ComponentDto branch = db.components().insertProjectBranch(project); | |||
propertyDefinitions.addComponents(asList( | |||
PropertyDefinition.builder("sonar.leak.period").onQualifiers(PROJECT).build(), | |||
PropertyDefinition.builder("other").onQualifiers(PROJECT).build())); | |||
ListDefinitionsWsResponse result = ws.newRequest() | |||
.setParam("component", branch.getKey()) | |||
.setParam("branch", branch.getBranch()) | |||
.executeProtobuf(Settings.ListDefinitionsWsResponse.class); | |||
assertThat(result.getDefinitionsList()).extracting(Settings.Definition::getKey).containsExactlyInAnyOrder("sonar.leak.period"); | |||
} | |||
@Test | |||
public void fail_when_user_has_not_project_browse_permission() throws Exception { | |||
userSession.logIn("project-admin").addProjectPermission(CODEVIEWER, project); | |||
@@ -389,6 +409,31 @@ public class ListDefinitionsActionTest { | |||
executeRequest(project.getDbKey()); | |||
} | |||
@Test | |||
public void fail_when_component_not_found() { | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage("Component key 'unknown' not found"); | |||
ws.newRequest() | |||
.setParam("component", "unknown") | |||
.execute(); | |||
} | |||
@Test | |||
public void fail_when_branch_not_found() { | |||
ComponentDto project = db.components().insertMainBranch(); | |||
ComponentDto branch = db.components().insertProjectBranch(project); | |||
userSession.logIn().addProjectPermission(USER, project); | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage(format("Component '%s' on branch 'unknown' not found", branch.getKey())); | |||
ws.newRequest() | |||
.setParam("component", branch.getKey()) | |||
.setParam("branch", "unknown") | |||
.execute(); | |||
} | |||
@Test | |||
public void test_ws_definition() { | |||
WebService.Action action = ws.getDef(); | |||
@@ -396,7 +441,7 @@ public class ListDefinitionsActionTest { | |||
assertThat(action.isInternal()).isFalse(); | |||
assertThat(action.isPost()).isFalse(); | |||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||
assertThat(action.params()).hasSize(1); | |||
assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component", "branch"); | |||
} | |||
@Test |
@@ -54,6 +54,8 @@ import org.sonar.server.exceptions.BadRequestException; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.i18n.I18nRule; | |||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||
import org.sonar.server.organization.TestDefaultOrganizationProvider; | |||
import org.sonar.server.platform.SettingsChangeNotifier; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.TestRequest; | |||
@@ -92,7 +94,9 @@ public class SetActionTest { | |||
private FakeSettingsNotifier settingsChangeNotifier = new FakeSettingsNotifier(dbClient); | |||
private SettingsUpdater settingsUpdater = new SettingsUpdater(dbClient, definitions); | |||
private SettingValidations validations = new SettingValidations(definitions, dbClient, i18n); | |||
private SetAction underTest = new SetAction(definitions, dbClient, componentFinder, userSession, settingsUpdater, settingsChangeNotifier, validations); | |||
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); | |||
private SetAction underTest = new SetAction(definitions, dbClient, componentFinder, userSession, settingsUpdater, settingsChangeNotifier, validations, | |||
new SettingsWsSupport(defaultOrganizationProvider, userSession)); | |||
private WsActionTester ws = new WsActionTester(underTest); | |||
@@ -939,14 +943,14 @@ public class SetActionTest { | |||
@Test | |||
public void fail_when_component_not_found() { | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage("Component key 'unknown' not found"); | |||
ws.newRequest() | |||
.setParam("key", "foo") | |||
.setParam("value", "2") | |||
.setParam("component", "unknown") | |||
.execute(); | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage("Component key 'unknown' not found"); | |||
ws.newRequest() | |||
.setParam("key", "foo") | |||
.setParam("value", "2") | |||
.setParam("component", "unknown") | |||
.execute(); | |||
} | |||
@Test |
@@ -27,9 +27,11 @@ import javax.annotation.concurrent.Immutable; | |||
public class ListDefinitionsRequest { | |||
private final String component; | |||
private final String branch; | |||
private ListDefinitionsRequest(Builder builder) { | |||
this.component = builder.component; | |||
this.branch = builder.branch; | |||
} | |||
@CheckForNull | |||
@@ -37,12 +39,18 @@ public class ListDefinitionsRequest { | |||
return component; | |||
} | |||
@CheckForNull | |||
public String getBranch() { | |||
return branch; | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
public static class Builder { | |||
private String component; | |||
private String branch; | |||
private Builder() { | |||
// enforce factory method use | |||
@@ -53,6 +61,11 @@ public class ListDefinitionsRequest { | |||
return this; | |||
} | |||
public Builder setBranch(@Nullable String branch) { | |||
this.branch = branch; | |||
return this; | |||
} | |||
public ListDefinitionsRequest build() { | |||
return new ListDefinitionsRequest(this); | |||
} |
@@ -46,7 +46,8 @@ public class SettingsService extends BaseService { | |||
public ListDefinitionsWsResponse listDefinitions(ListDefinitionsRequest request) { | |||
GetRequest getRequest = new GetRequest(path(ACTION_LIST_DEFINITIONS)) | |||
.setParam(PARAM_COMPONENT, request.getComponent()); | |||
.setParam(PARAM_COMPONENT, request.getComponent()) | |||
.setParam(PARAM_BRANCH, request.getBranch()); | |||
return call(getRequest, ListDefinitionsWsResponse.parser()); | |||
} | |||
@@ -33,17 +33,27 @@ public class ListDefinitionsRequestTest { | |||
ListDefinitionsRequest.Builder underTest = ListDefinitionsRequest.builder(); | |||
@Test | |||
public void create_request_with_no_component() { | |||
public void create_request_with_nothing() { | |||
ListDefinitionsRequest result = underTest.build(); | |||
assertThat(result.getComponent()).isNull(); | |||
assertThat(result.getBranch()).isNull(); | |||
} | |||
@Test | |||
public void create_request_with_component_key() { | |||
public void create_request_with_component() { | |||
ListDefinitionsRequest result = underTest.setComponent("projectKey").build(); | |||
assertThat(result.getComponent()).isEqualTo("projectKey"); | |||
assertThat(result.getBranch()).isNull(); | |||
} | |||
@Test | |||
public void create_request_with_component_and_branch() { | |||
ListDefinitionsRequest result = underTest.setComponent("projectKey").setBranch("branch").build(); | |||
assertThat(result.getComponent()).isEqualTo("projectKey"); | |||
assertThat(result.getBranch()).isEqualTo("branch"); | |||
} | |||
} |
@@ -49,12 +49,14 @@ public class SettingsServiceTest { | |||
public void list_definitions() { | |||
underTest.listDefinitions(ListDefinitionsRequest.builder() | |||
.setComponent("KEY") | |||
.setBranch("BRANCH") | |||
.build()); | |||
GetRequest getRequest = serviceTester.getGetRequest(); | |||
assertThat(serviceTester.getGetParser()).isSameAs(ListDefinitionsWsResponse.parser()); | |||
serviceTester.assertThat(getRequest) | |||
.hasParam(PARAM_COMPONENT, "KEY") | |||
.hasParam(PARAM_BRANCH, "BRANCH") | |||
.andNoOtherParam(); | |||
} | |||