diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-09-26 16:17:22 +0200 |
---|---|---|
committer | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-09-30 14:16:55 +0200 |
commit | c06807de9d0825b95d3752bf5e1480400a50c7a8 (patch) | |
tree | 5c46d90fd5ee42d9f68709d38508e84249c9a4cd | |
parent | 2bbfd0c6ca169d20ae2d51e7cbb029ad3f9ea73b (diff) | |
download | sonarqube-c06807de9d0825b95d3752bf5e1480400a50c7a8.tar.gz sonarqube-c06807de9d0825b95d3752bf5e1480400a50c7a8.zip |
SONAR-8120 WS measures/search check request
- all metrics exist
- all component ids or keys exist
- at least one metric provided
- at least one component provided
etc.
4 files changed, 165 insertions, 7 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java index 8d290cfa9b0..cc22ec4e42e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java @@ -21,8 +21,11 @@ package org.sonar.server.measure.ws; import com.google.common.collect.ImmutableMultimap; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collector; import java.util.stream.Collectors; import org.sonar.api.server.ws.Request; @@ -52,11 +55,11 @@ import static org.sonar.server.ws.KeyExamples.KEY_FILE_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_FILE_EXAMPLE_002; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002; +import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; public class SearchAction implements MeasuresWsAction { - private static final int MAX_NB_COMPONENTS = 100; static final String PARAM_COMPONENT_IDS = "componentIds"; static final String PARAM_COMPONENT_KEYS = "componentKeys"; @@ -79,7 +82,7 @@ public class SearchAction implements MeasuresWsAction { " <li>'Administer' rights on the provided components</li>" + " <li>'Browse' on the provided components</li>" + "</ul>", - MAX_NB_COMPONENTS, PARAM_COMPONENT_IDS, PARAM_COMPONENT_KEYS) + SearchRequest.MAX_NB_COMPONENTS, PARAM_COMPONENT_IDS, PARAM_COMPONENT_KEYS) .setSince("6.1") .setResponseExample(getClass().getResource("search-example.json")) .setHandler(this); @@ -142,18 +145,26 @@ public class SearchAction implements MeasuresWsAction { private List<MetricDto> searchMetrics() { requireNonNull(request); - return dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys()); + List<MetricDto> dbMetrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys()); + List<String> metricKeys = dbMetrics.stream().map(MetricDto::getKey).collect(Collectors.toList()); + checkRequest(request.getMetricKeys().size() == dbMetrics.size(), "The following metrics are not found: %s", + String.join(", ", difference(request.getMetricKeys(), metricKeys))); + return dbMetrics; } private List<ComponentDto> searchComponents() { requireNonNull(request); if (request.hasComponentIds()) { List<ComponentDto> componentsByUuid = searchByComponentUuids(dbSession, request.getComponentIds()); - checkArgument(componentsByUuid.size() == request.getComponentIds().size(), "Some components are not found in: '%s'", String.join(", ", request.getComponentIds())); + List<String> componentUuids = componentsByUuid.stream().map(ComponentDto::uuid).collect(Collectors.toList()); + checkArgument(componentsByUuid.size() == request.getComponentIds().size(), "The following component ids are not found: %s", + String.join(", ", difference(request.getComponentIds(), componentUuids))); return componentsByUuid; } else { List<ComponentDto> componentsByKey = searchByComponentKeys(dbSession, request.getComponentKeys()); - checkArgument(componentsByKey.size() == request.getComponentKeys().size(), "Some components are not found in: '%s'", String.join(", ", request.getComponentKeys())); + List<String> componentKeys = componentsByKey.stream().map(ComponentDto::key).collect(Collectors.toList()); + checkArgument(componentsByKey.size() == request.getComponentKeys().size(), "The following component keys are not found: %s", + String.join(", ", difference(request.getComponentKeys(), componentKeys))); return componentsByKey; } } @@ -176,6 +187,15 @@ public class SearchAction implements MeasuresWsAction { .build()); } + private List<String> difference(Collection<String> expected, Collection<String> actual) { + Set<String> actualSet = new HashSet<>(actual); + + return expected.stream() + .filter(value -> !actualSet.contains(value)) + .sorted(String::compareTo) + .collect(Collectors.toList()); + } + private SearchWsResponse buildResponse() { requireNonNull(metrics); requireNonNull(measures); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java index 9563cf615d5..a9bbb40b7c5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java @@ -23,9 +23,12 @@ package org.sonar.server.measure.ws; import com.google.common.base.Throwables; import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Qualifiers; import org.sonar.api.server.ws.WebService; @@ -37,11 +40,14 @@ import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; import org.sonar.db.metric.MetricDto; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.WsMeasures.Component; import org.sonarqube.ws.WsMeasures.SearchWsResponse; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.utils.DateUtils.parseDateTime; @@ -60,6 +66,8 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_ public class SearchActionTest { @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule public DbTester db = DbTester.create(System2.INSTANCE); ComponentDbTester componentDb = new ComponentDbTester(db); DbClient dbClient = db.getDbClient(); @@ -115,6 +123,128 @@ public class SearchActionTest { } @Test + public void fail_if_no_metric() { + ComponentDto project = componentDb.insertProject(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'metricKeys' parameter is missing"); + + callByComponentUuids(singletonList(project.uuid()), null); + } + + @Test + public void fail_if_empty_metric() { + ComponentDto project = componentDb.insertProject(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Metric keys must be provided"); + + callByComponentUuids(singletonList(project.uuid()), emptyList()); + } + + @Test + public void fail_if_unknown_metric() { + ComponentDto project = componentDb.insertProject(); + insertComplexityMetric(); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("The following metrics are not found: ncloc, violations"); + + callByComponentUuids(singletonList(project.uuid()), newArrayList("violations", "complexity", "ncloc")); + } + + @Test + public void fail_if_no_component() { + insertComplexityMetric(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Either component ids or component keys must be provided, not both"); + + call(null, null, singletonList("complexity")); + } + + @Test + public void fail_if_empty_component_uuid() { + insertComplexityMetric(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Either component ids or component keys must be provided, not both"); + + callByComponentUuids(emptyList(), singletonList("complexity")); + } + + @Test + public void fail_if_empty_component_key() { + insertComplexityMetric(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Either component ids or component keys must be provided, not both"); + + callByComponentKeys(emptyList(), singletonList("complexity")); + } + + @Test + public void fail_if_unknown_component_uuid() { + insertComplexityMetric(); + ComponentDto project = componentDb.insertProject(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The following component ids are not found: ANOTHER_PROJECT_ID, YOUR_PROJECT_ID"); + + callByComponentUuids(newArrayList("YOUR_PROJECT_ID", project.uuid(), "ANOTHER_PROJECT_ID"), singletonList("complexity")); + } + + @Test + public void fail_if_unknown_component_key() { + insertComplexityMetric(); + ComponentDto project = componentDb.insertProject(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The following component keys are not found: ANOTHER_PROJECT_KEY, YOUR_PROJECT_KEY"); + + callByComponentKeys(newArrayList("YOUR_PROJECT_KEY", project.key(), "ANOTHER_PROJECT_KEY"), singletonList("complexity")); + } + + @Test + public void fail_if_component_id_and_key() { + ComponentDto project = componentDb.insertProject(); + ComponentDto anotherProject = componentDb.insertProject(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Either component ids or component keys must be provided, not both"); + + call(singletonList(project.uuid()), singletonList(anotherProject.key()), singletonList("complexity")); + } + + @Test + public void fail_if_more_than_100_component_id() { + List<String> uuids = IntStream.rangeClosed(1, 101) + .mapToObj(i -> componentDb.insertProject()) + .map(ComponentDto::uuid) + .collect(Collectors.toList()); + insertComplexityMetric(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("101 components provided, more than maximum authorized (100)"); + + callByComponentUuids(uuids, singletonList("complexity")); + } + + @Test + public void fail_if_more_than_100_component_key() { + List<String> keys = IntStream.rangeClosed(1, 101) + .mapToObj(i -> componentDb.insertProject()) + .map(ComponentDto::key) + .collect(Collectors.toList()); + insertComplexityMetric(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("101 components provided, more than maximum authorized (100)"); + + callByComponentKeys(keys, singletonList("complexity")); + } + + @Test public void definition() { WebService.Action result = ws.getDef(); diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/SearchRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/SearchRequest.java index ad16a8c3a5b..7beedebec9f 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/SearchRequest.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/SearchRequest.java @@ -26,6 +26,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; public class SearchRequest { + public static final int MAX_NB_COMPONENTS = 100; + private final List<String> metricKeys; private final List<String> componentIds; private final List<String> componentKeys; @@ -89,7 +91,13 @@ public class SearchRequest { checkArgument( (componentIds != null && !componentIds.isEmpty()) ^ (componentKeys != null && !componentKeys.isEmpty()), - "Either component ids or component keys must be provided, not both."); + "Either component ids or component keys must be provided, not both"); + int nbComponents = componentIds == null ? componentKeys.size() : componentIds.size(); + checkArgument((componentIds != null && componentIds.size() < MAX_NB_COMPONENTS) + || (componentKeys != null && componentKeys.size() < MAX_NB_COMPONENTS), + "%s components provided, more than maximum authorized (%s)", + nbComponents, + MAX_NB_COMPONENTS); return new SearchRequest(this); } } diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/SearchRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/SearchRequestTest.java index 7cffedb9471..ee6de73dfd5 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/SearchRequestTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/SearchRequestTest.java @@ -118,6 +118,6 @@ public class SearchRequestTest { private void expectExceptionOnComponents() { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Either component ids or component keys must be provided, not both."); + expectedException.expectMessage("Either component ids or component keys must be provided, not both"); } } |