import org.junit.Test;
import org.sonarqube.ws.WsMeasures;
import org.sonarqube.ws.WsMeasures.ComponentTreeWsResponse;
+import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.measure.ComponentTreeWsRequest;
+import org.sonarqube.ws.client.measure.ComponentWsRequest;
import util.ItUtils;
import static com.google.common.collect.Lists.newArrayList;
assertThat(components).hasSize(2).extracting("key").containsOnly("sample:src/main/xoo/sample", FILE_KEY);
assertThat(components.get(0).getMeasures().getMeasuresList().get(0).getValue()).isEqualTo("13");
}
+
+ @Test
+ public void component() {
+ ComponentWsResponse response = wsClient.measures().component(new ComponentWsRequest()
+ .setComponentKey("sample")
+ .setMetricKeys(singletonList("ncloc"))
+ .setAdditionalFields(newArrayList("metrics", "periods")));
+
+ WsMeasures.Component component = response.getComponent();
+ assertThat(component.getKey()).isEqualTo("sample");
+ assertThat(component.getMeasures().getMeasuresList()).isNotEmpty();
+ assertThat(response.getMetrics().getMetricsList()).extracting("key").containsOnly("ncloc");
+
+ }
}
PROJECT_ID_AND_KEY("projectId", "projectKey"),
UUID_AND_KEY("uuid", "key"),
ID_AND_KEY("id", "key"),
+ COMPONENT_ID_AND_KEY("componentId", "componentKey"),
BASE_COMPONENT_ID_AND_KEY("baseComponentId", "baseComponentKey");
private final String uuidParamName;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.measure.MeasureDtoFunctions;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.metric.MetricDtoFunctions;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.measure.ws.MetricDtoWithBestValue.MetricDtoToMetricDtoWithBestValueFunction;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
+import org.sonarqube.ws.client.measure.ComponentWsRequest;
+
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.collect.FluentIterable.from;
+import static java.lang.String.format;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_KEY;
+import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
+import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
+import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotsToPeriods;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_METRICS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PERIODS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_KEY;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
+
+public class ComponentAction implements MeasuresWsAction {
+ private static final Set<String> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE = ImmutableSortedSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
+
+ private final DbClient dbClient;
+ private final ComponentFinder componentFinder;
+ private final UserSession userSession;
+
+ public ComponentAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_COMPONENT)
+ .setDescription(format("Return component with specified measures. The %s or the %s parameter must be provided.<br>" +
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ "<li>'Administer System'</li>" +
+ "<li>'Administer' rights on the specified project</li>" +
+ "<li>'Browse' on the specified project</li>" +
+ "</ul>",
+ PARAM_COMPONENT_ID, PARAM_COMPONENT_KEY))
+ .setResponseExample(getClass().getResource("component-example.json"))
+ .setSince("5.4")
+ .setHandler(this);
+
+ action.createParam(PARAM_COMPONENT_ID)
+ .setDescription("Component id")
+ .setExampleValue(UUID_EXAMPLE_01);
+
+ action.createParam(PARAM_COMPONENT_KEY)
+ .setDescription("Component key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+
+ createMetricKeysParameter(action);
+ createAdditionalFieldsParameter(action);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ ComponentWsResponse componentWsResponse = doHandle(toComponentWsRequest(request));
+ writeProtobuf(componentWsResponse, request, response);
+ }
+
+ private ComponentWsResponse doHandle(ComponentWsRequest request) {
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.getComponentId(), request.getComponentKey(), COMPONENT_ID_AND_KEY);
+ Optional<ComponentDto> refComponent = getReferenceComponent(dbSession, component);
+ checkPermissions(component);
+ SnapshotDto lastSnapshot = dbClient.snapshotDao().selectLastSnapshotByComponentId(dbSession, component.getId());
+ List<MetricDto> metrics = searchMetrics(dbSession, request);
+ List<WsMeasures.Period> periods = snapshotsToPeriods(lastSnapshot);
+ List<MeasureDto> measures = searchMeasures(dbSession, component, lastSnapshot, metrics, periods);
+
+ return buildResponse(request, component, refComponent, measures, metrics, periods);
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private Optional<ComponentDto> getReferenceComponent(DbSession dbSession, ComponentDto component) {
+ if (component.getCopyResourceId() == null) {
+ return Optional.absent();
+ }
+
+ return dbClient.componentDao().selectById(dbSession, component.getCopyResourceId());
+ }
+
+ private static ComponentWsResponse buildResponse(ComponentWsRequest request, ComponentDto component, Optional<ComponentDto> refComponent, List<MeasureDto> measures,
+ List<MetricDto> metrics, List<WsMeasures.Period> periods) {
+ ComponentWsResponse.Builder response = ComponentWsResponse.newBuilder();
+ Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDtoFunctions.toId());
+ Map<MetricDto, MeasureDto> measuresByMetric = new HashMap<>();
+ for (MeasureDto measure : measures) {
+ MetricDto metric = metricsById.get(measure.getMetricId());
+ measuresByMetric.put(metric, measure);
+ }
+ Map<Long, String> referenceComponentUuidById = new HashMap<>();
+ if (refComponent.isPresent()) {
+ referenceComponentUuidById.put(refComponent.get().getId(), refComponent.get().uuid());
+ }
+
+ response.setComponent(componentDtoToWsComponent(component, measuresByMetric, referenceComponentUuidById));
+
+ List<String> additionalFields = request.getAdditionalFields();
+ if (additionalFields != null) {
+ if (additionalFields.contains(ADDITIONAL_METRICS)) {
+ for (MetricDto metric : metrics) {
+ response.getMetricsBuilder().addMetrics(metricDtoToWsMetric(metric));
+ }
+ }
+ if (additionalFields.contains(ADDITIONAL_PERIODS)) {
+ response.getPeriodsBuilder().addAllPeriods(periods);
+ }
+ }
+
+ return response.build();
+ }
+
+ private List<MetricDto> searchMetrics(DbSession dbSession, ComponentWsRequest request) {
+ List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys());
+ if (metrics.size() < request.getMetricKeys().size()) {
+ List<String> foundMetricKeys = Lists.transform(metrics, MetricDtoFunctions.toKey());
+ Set<String> missingMetricKeys = Sets.difference(
+ new LinkedHashSet<>(request.getMetricKeys()),
+ new LinkedHashSet<>(foundMetricKeys));
+
+ throw new NotFoundException(format("The following metric keys are not found: %s", Joiner.on(", ").join(missingMetricKeys)));
+ }
+
+ return metrics;
+ }
+
+ private List<MeasureDto> searchMeasures(DbSession dbSession, ComponentDto component, @Nullable SnapshotDto snapshot, List<MetricDto> metrics, List<WsMeasures.Period> periods) {
+ if (snapshot == null) {
+ return emptyList();
+ }
+
+ List<Integer> metricIds = Lists.transform(metrics, MetricDtoFunctions.toId());
+ List<MeasureDto> measures = dbClient.measureDao().selectBySnapshotIdsAndMetricIds(dbSession, singletonList(snapshot.getId()), metricIds);
+ addBestValuesToMeasures(measures, component, metrics, periods);
+
+ return measures;
+ }
+
+ /**
+ * Conditions for best value measure:
+ * <ul>
+ * <li>component is a production file or test file</li>
+ * <li>metric is optimized for best value</li>
+ * </ul>
+ */
+ private static void addBestValuesToMeasures(List<MeasureDto> measures, ComponentDto component, List<MetricDto> metrics, List<WsMeasures.Period> periods) {
+ if (!QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier())) {
+ return;
+ }
+
+ List<MetricDtoWithBestValue> metricWithBestValueList = from(metrics)
+ .filter(MetricDtoFunctions.isOptimizedForBestValue())
+ .transform(new MetricDtoToMetricDtoWithBestValueFunction(periods))
+ .toList();
+ Map<Integer, MeasureDto> measuresByMetricId = Maps.uniqueIndex(measures, MeasureDtoFunctions.toMetricId());
+
+ for (MetricDtoWithBestValue metricWithBestValue : metricWithBestValueList) {
+ if (measuresByMetricId.get(metricWithBestValue.getMetric().getId()) == null) {
+ measures.add(metricWithBestValue.getBestValue());
+ }
+ }
+ }
+
+ private static ComponentWsRequest toComponentWsRequest(Request request) {
+ ComponentWsRequest componentWsRequest = new ComponentWsRequest()
+ .setComponentId(request.param(PARAM_COMPONENT_ID))
+ .setComponentKey(request.param(PARAM_COMPONENT_KEY))
+ .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
+ .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS));
+ checkRequest(!componentWsRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided");
+ return componentWsRequest;
+ }
+
+ private void checkPermissions(ComponentDto baseComponent) {
+ String projectUuid = firstNonNull(baseComponent.projectUuid(), baseComponent.uuid());
+ if (!userSession.hasPermission(GlobalPermissions.SYSTEM_ADMIN) &&
+ !userSession.hasComponentUuidPermission(UserRole.ADMIN, projectUuid) &&
+ !userSession.hasComponentUuidPermission(UserRole.USER, projectUuid)) {
+ throw insufficientPrivilegesException();
+ }
+ }
+}
import static java.lang.String.format;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT_TREE;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_METRICS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PERIODS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_ID;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_KEY;
static final String QUALIFIER_SORT = "qualifier";
static final String METRIC_SORT = "metric";
static final Set<String> SORTS = ImmutableSortedSet.of(NAME_SORT, PATH_SORT, QUALIFIER_SORT, METRIC_SORT);
- static final String ADDITIONAL_METRICS = "metrics";
- static final String ADDITIONAL_PERIODS = "periods";
- static final Set<String> ADDITIONAL_FIELDS = ImmutableSortedSet.of(ADDITIONAL_METRICS, ADDITIONAL_PERIODS);
private final ComponentTreeDataLoader dataLoader;
private final UserSession userSession;
.setDescription("Base component key.The search is based on this component.")
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
- action.createParam(PARAM_METRIC_KEYS)
- .setDescription("Metric keys")
- .setRequired(true)
- .setExampleValue("ncloc,complexity,violations");
-
action.createParam(PARAM_METRIC_SORT)
.setDescription(
format("Metric key to sort by. The '%s' parameter must contain the '%s' value. It must be part of the '%s' parameter", Param.SORT, METRIC_SORT, PARAM_METRIC_KEYS))
.setExampleValue("ncloc");
- action.createParam(PARAM_ADDITIONAL_FIELDS)
- .setDescription("Comma-separated list of additional fields that can be returned in the response.")
- .setPossibleValues(ADDITIONAL_FIELDS)
- .setExampleValue("periods,metrics");
-
+ createMetricKeysParameter(action);
+ createAdditionalFieldsParameter(action);
createQualifiersParameter(action, newQualifierParameterContext(userSession, i18n, resourceTypes));
action.createParam(PARAM_STRATEGY)
private static void addBestValuesToMeasures(Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric, List<ComponentDtoWithSnapshotId> components,
List<MetricDto> metrics, List<WsMeasures.Period> periods) {
List<MetricDtoWithBestValue> metricDtosWithBestValueMeasure = from(metrics)
- .filter(IsMetricOptimizedForBestValue.INSTANCE)
+ .filter(MetricDtoFunctions.isOptimizedForBestValue())
.transform(new MetricDtoToMetricDtoWithBestValue(periods))
.toList();
if (metricDtosWithBestValueMeasure.isEmpty()) {
List<ComponentDtoWithSnapshotId> componentsEligibleForBestValue = from(components).filter(IsFileComponent.INSTANCE).toList();
for (ComponentDtoWithSnapshotId component : componentsEligibleForBestValue) {
for (MetricDtoWithBestValue metricWithBestValue : metricDtosWithBestValueMeasure) {
- if (measuresByComponentUuidAndMetric.get(component.uuid(), metricWithBestValue.metric) == null) {
- measuresByComponentUuidAndMetric.put(component.uuid(), metricWithBestValue.metric, metricWithBestValue.bestValue);
+ if (measuresByComponentUuidAndMetric.get(component.uuid(), metricWithBestValue.getMetric()) == null) {
+ measuresByComponentUuidAndMetric.put(component.uuid(), metricWithBestValue.getMetric(), metricWithBestValue.getBestValue());
}
}
}
}
}
- private enum IsMetricOptimizedForBestValue implements Predicate<MetricDto> {
- INSTANCE;
-
- @Override
- public boolean apply(@Nonnull MetricDto input) {
- return input.isOptimizedBestValue() && input.getBestValue() != null;
- }
- }
-
private enum IsFileComponent implements Predicate<ComponentDtoWithSnapshotId> {
INSTANCE;
}
}
- private static class MetricDtoWithBestValue {
- private static final String LOWER_CASE_NEW_METRIC_PREFIX = "new_";
- private final MetricDto metric;
-
- private final MeasureDto bestValue;
-
- private MetricDtoWithBestValue(MetricDto metric, List<Integer> periodIndexes) {
- this.metric = metric;
- MeasureDto measure = new MeasureDto()
- .setMetricId(metric.getId())
- .setMetricKey(metric.getKey());
- boolean isNewTypeMetric = metric.getKey().toLowerCase().startsWith(LOWER_CASE_NEW_METRIC_PREFIX);
- if (isNewTypeMetric) {
- for (Integer periodIndex : periodIndexes) {
- measure.setVariation(periodIndex, 0.0d);
- }
- } else {
- measure.setValue(metric.getBestValue());
- }
-
- this.bestValue = measure;
- }
- }
-
private enum WsPeriodToIndex implements Function<WsMeasures.Period, Integer> {
INSTANCE;
/**
* Order by measure value, taking the metric direction into account
- * Metric direction is taken into account in {@link ComponentDtoWithSnapshotIdToNumericalMeasureValue}
*/
private static Ordering<ComponentDtoWithSnapshotId> metricOrdering(ComponentTreeWsRequest wsRequest, List<MetricDto> metrics,
Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric) {
add(
ComponentTreeDataLoader.class,
MeasuresWs.class,
- ComponentTreeAction.class
- );
+ ComponentTreeAction.class,
+ ComponentAction.class);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import org.sonar.api.server.ws.WebService.NewAction;
+import org.sonar.api.server.ws.WebService.NewParam;
+
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
+
+class MeasuresWsParametersBuilder {
+
+ private MeasuresWsParametersBuilder() {
+ // prevent instantiation
+ }
+
+ static NewParam createAdditionalFieldsParameter(NewAction action) {
+ return action.createParam(PARAM_ADDITIONAL_FIELDS)
+ .setDescription("Comma-separated list of additional fields that can be returned in the response.")
+ .setPossibleValues(ADDITIONAL_FIELDS)
+ .setExampleValue("periods,metrics");
+ }
+
+ static NewParam createMetricKeysParameter(NewAction action) {
+ return action.createParam(PARAM_METRIC_KEYS)
+ .setDescription("Metric keys")
+ .setRequired(true)
+ .setExampleValue("ncloc,complexity,violations");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.metric.MetricDto;
+import org.sonarqube.ws.WsMeasures;
+
+class MetricDtoWithBestValue {
+ private static final String LOWER_CASE_NEW_METRIC_PREFIX = "new_";
+
+ private final MetricDto metric;
+ private final MeasureDto bestValue;
+
+ MetricDtoWithBestValue(MetricDto metric, List<Integer> periodIndexes) {
+ this.metric = metric;
+ MeasureDto measure = new MeasureDto()
+ .setMetricId(metric.getId())
+ .setMetricKey(metric.getKey());
+ boolean isNewTypeMetric = metric.getKey().toLowerCase().startsWith(LOWER_CASE_NEW_METRIC_PREFIX);
+ if (isNewTypeMetric) {
+ for (Integer periodIndex : periodIndexes) {
+ measure.setVariation(periodIndex, 0.0d);
+ }
+ } else {
+ measure.setValue(metric.getBestValue());
+ }
+
+ this.bestValue = measure;
+ }
+
+ MetricDto getMetric() {
+ return metric;
+ }
+
+ MeasureDto getBestValue() {
+ return bestValue;
+ }
+
+ static class MetricDtoToMetricDtoWithBestValueFunction implements Function<MetricDto, MetricDtoWithBestValue> {
+ private final List<Integer> periodIndexes;
+
+ MetricDtoToMetricDtoWithBestValueFunction(List<WsMeasures.Period> periods) {
+ this.periodIndexes = Lists.transform(periods, WsPeriodToIndex.INSTANCE);
+ }
+
+ @Override
+ public MetricDtoWithBestValue apply(@Nonnull MetricDto input) {
+ return new MetricDtoWithBestValue(input, periodIndexes);
+ }
+ }
+
+ private enum WsPeriodToIndex implements Function<WsMeasures.Period, Integer> {
+ INSTANCE;
+
+ @Override
+ public Integer apply(@Nonnull WsMeasures.Period input) {
+ return input.getIndex();
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.db.component.SnapshotDto;
+import org.sonarqube.ws.WsMeasures;
+
+import static java.util.Collections.emptyList;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
+
+class SnapshotDtoToWsPeriods {
+ private SnapshotDtoToWsPeriods() {
+ // prevent instantiation
+ }
+
+ static List<WsMeasures.Period> snapshotsToPeriods(@Nullable SnapshotDto snapshot) {
+ if (snapshot == null) {
+ return emptyList();
+ }
+
+ List<WsMeasures.Period> periods = new ArrayList<>();
+ for (int periodIndex = 1; periodIndex <= 5; periodIndex++) {
+ if (snapshot.getPeriodDate(periodIndex) != null) {
+ periods.add(snapshotDtoToWsPeriod(snapshot, periodIndex));
+ }
+ }
+
+ return periods;
+ }
+
+ private static WsMeasures.Period snapshotDtoToWsPeriod(SnapshotDto snapshot, int periodIndex) {
+ WsMeasures.Period.Builder period = WsMeasures.Period.newBuilder();
+ period.setIndex(periodIndex);
+ if (snapshot.getPeriodMode(periodIndex) != null) {
+ period.setMode(snapshot.getPeriodMode(periodIndex));
+ }
+ if (snapshot.getPeriodModeParameter(periodIndex) != null) {
+ period.setParameter(snapshot.getPeriodModeParameter(periodIndex));
+ }
+ if (snapshot.getPeriodDate(periodIndex) != null) {
+ period.setDate(formatDateTime(snapshot.getPeriodDate(periodIndex)));
+ }
+
+ return period.build();
+ }
+}
--- /dev/null
+{
+ "component": {
+ "id": "AVIwDXE-bJbJqrw6wFv5",
+ "key": "MY_PROJECT:ElementImpl.java",
+ "name": "ElementImpl.java",
+ "qualifier": "FIL",
+ "path": "src/main/java/com/sonarsource/markdown/impl/ElementImpl.java",
+ "measures": [
+ {
+ "metric": "complexity",
+ "value": "12",
+ "periods": [
+ {
+ "index": 1,
+ "value": "2"
+ },
+ {
+ "index": 2,
+ "value": "0"
+ },
+ {
+ "index": 3,
+ "value": "0"
+ }
+ ]
+ },
+ {
+ "metric": "new_violations",
+ "periods": [
+ {
+ "index": 1,
+ "value": "25"
+ },
+ {
+ "index": 2,
+ "value": "0"
+ },
+ {
+ "index": 3,
+ "value": "25"
+ }
+ ]
+ },
+ {
+ "metric": "ncloc",
+ "value": "114",
+ "periods": [
+ {
+ "index": 1,
+ "value": "3"
+ },
+ {
+ "index": 2,
+ "value": "-5"
+ },
+ {
+ "index": 3,
+ "value": "5"
+ }
+ ]
+ }
+ ]
+ },
+ "metrics": [
+ {
+ "key": "complexity",
+ "name": "Complexity",
+ "description": "Cyclomatic complexity",
+ "domain": "Complexity",
+ "type": "INT",
+ "higherValuesAreBetter": false,
+ "qualitative": false,
+ "hidden": false,
+ "custom": false
+ },
+ {
+ "key": "ncloc",
+ "name": "Lines of code",
+ "description": "Non Commenting Lines of Code",
+ "domain": "Size",
+ "type": "INT",
+ "higherValuesAreBetter": false,
+ "qualitative": false,
+ "hidden": false,
+ "custom": false
+ },
+ {
+ "key": "new_violations",
+ "name": "New issues",
+ "description": "New Issues",
+ "domain": "Issues",
+ "type": "INT",
+ "higherValuesAreBetter": false,
+ "qualitative": true,
+ "hidden": false,
+ "custom": false
+ }
+ ],
+ "periods": [
+ {
+ "index": 1,
+ "mode": "previous_version",
+ "date": "2016-01-11T10:49:50+0100",
+ "parameter": "1.0-SNAPSHOT"
+ },
+ {
+ "index": 2,
+ "mode": "previous_analysis",
+ "date": "2016-01-11T10:50:06+0100",
+ "parameter": "2016-01-11"
+ },
+ {
+ "index": 3,
+ "mode": "days",
+ "date": "2016-01-11T10:38:45+0100",
+ "parameter": "30"
+ }
+ ]
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+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.component.ComponentFinder;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+import org.sonar.test.DbTests;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.utils.DateUtils.parseDateTime;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newProjectCopy;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonar.db.component.SnapshotTesting.createForComponent;
+import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
+import static org.sonar.db.metric.MetricTesting.newMetricDto;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
+
+@Category(DbTests.class)
+public class ComponentActionTest {
+ private static final String PROJECT_UUID = "project-uuid";
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ ComponentDbTester componentDb = new ComponentDbTester(db);
+ DbClient dbClient = db.getDbClient();
+ DbSession dbSession = db.getSession();
+
+ WsActionTester ws = new WsActionTester(new ComponentAction(dbClient, new ComponentFinder(dbClient), userSession));
+
+ @Before
+ public void setUp() {
+ userSession.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ }
+
+ @Test
+ public void json_example() {
+ insertJsonExampleData();
+
+ String response = ws.newRequest()
+ .setParam(PARAM_COMPONENT_ID, "AVIwDXE-bJbJqrw6wFv5")
+ .setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations")
+ .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")
+ .execute()
+ .getInput();
+
+ assertJson(response).isSimilarTo(getClass().getResource("component-example.json"));
+ }
+
+ @Test
+ public void provided_project() {
+ componentDb.insertComponent(newProjectDto(PROJECT_UUID));
+ userSession.anonymous().addProjectUuidPermissions(UserRole.USER, PROJECT_UUID);
+ insertNclocMetric();
+
+ ComponentWsResponse response = newRequest(PROJECT_UUID, "ncloc");
+
+ assertThat(response.getMetrics().getMetricsCount()).isEqualTo(1);
+ assertThat(response.getPeriods().getPeriodsCount()).isEqualTo(0);
+ assertThat(response.getComponent().getId()).isEqualTo(PROJECT_UUID);
+ }
+
+ @Test
+ public void without_additional_fields() {
+ componentDb.insertProjectAndSnapshot(newProjectDto("project-uuid"));
+ insertNclocMetric();
+
+ String response = ws.newRequest()
+ .setParam(PARAM_COMPONENT_ID, "project-uuid")
+ .setParam(PARAM_METRIC_KEYS, "ncloc")
+ .execute().getInput();
+
+ assertThat(response)
+ .doesNotContain("periods")
+ .doesNotContain("metrics");
+ }
+
+ @Test
+ public void reference_uuid_in_the_response() {
+ ComponentDto project = newProjectDto("project-uuid");
+ ComponentDto view = newView("view-uuid");
+ componentDb.insertViewAndSnapshot(view);
+ componentDb.insertProjectAndSnapshot(project);
+ componentDb.insertProjectAndSnapshot(newProjectCopy("project-uuid-copy", project, view));
+ insertNclocMetric();
+
+ ComponentWsResponse response = newRequest("project-uuid-copy", "ncloc");
+
+ assertThat(response.getComponent().getId()).isEqualTo("project-uuid-copy");
+ assertThat(response.getComponent().getRefId()).isEqualTo("project-uuid");
+ }
+
+ @Test
+ public void fail_when_a_metric_is_not_found() {
+ componentDb.insertProjectAndSnapshot(newProjectDto(PROJECT_UUID));
+ insertNclocMetric();
+ insertComplexityMetric();
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("The following metric keys are not found: unknown-metric, another-unknown-metric");
+
+ newRequest(PROJECT_UUID, "ncloc, complexity, unknown-metric, another-unknown-metric");
+ }
+
+ @Test
+ public void fail_when_empty_metric_keys_parameter() {
+ componentDb.insertProjectAndSnapshot(newProjectDto(PROJECT_UUID));
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("At least one metric key must be provided");
+
+ newRequest(PROJECT_UUID, "");
+ }
+
+ @Test
+ public void fail_when_not_enough_permission() {
+ userSession.setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN);
+ componentDb.insertProjectAndSnapshot(newProjectDto(PROJECT_UUID));
+ insertNclocMetric();
+
+ expectedException.expect(ForbiddenException.class);
+
+ newRequest(PROJECT_UUID, "ncloc");
+ }
+
+ private ComponentWsResponse newRequest(String componentUuid, String metricKeys) {
+ InputStream responseStream = ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF)
+ .setParam(PARAM_COMPONENT_ID, componentUuid)
+ .setParam(PARAM_METRIC_KEYS, metricKeys)
+ .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")
+ .execute()
+ .getInputStream();
+
+ try {
+ return ComponentWsResponse.parseFrom(responseStream);
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ private static MetricDto newMetricDtoWithoutOptimization() {
+ return newMetricDto()
+ .setWorstValue(null)
+ .setOptimizedBestValue(false)
+ .setBestValue(null)
+ .setUserManaged(false);
+ }
+
+ private MetricDto insertNclocMetric() {
+ MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
+ .setKey("ncloc")
+ .setShortName("Lines of code")
+ .setDescription("Non Commenting Lines of Code")
+ .setDomain("Size")
+ .setValueType("INT")
+ .setDirection(-1)
+ .setQualitative(false)
+ .setHidden(false)
+ .setUserManaged(false));
+ db.commit();
+ return metric;
+ }
+
+ private MetricDto insertComplexityMetric() {
+ MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
+ .setKey("complexity")
+ .setShortName("Complexity")
+ .setDescription("Cyclomatic complexity")
+ .setDomain("Complexity")
+ .setValueType("INT")
+ .setDirection(-1)
+ .setQualitative(false)
+ .setHidden(false)
+ .setUserManaged(false));
+ db.commit();
+ return metric;
+ }
+
+ private MetricDto insertNewViolationMetric() {
+ MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
+ .setKey("new_violations")
+ .setShortName("New issues")
+ .setDescription("New Issues")
+ .setDomain("Issues")
+ .setValueType("INT")
+ .setDirection(-1)
+ .setQualitative(true)
+ .setHidden(false)
+ .setUserManaged(false));
+ db.commit();
+ return metric;
+ }
+
+ private void insertJsonExampleData() {
+ ComponentDto project = newProjectDto(PROJECT_UUID);
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+
+ ComponentDto file = newFileDto(project)
+ .setUuid("AVIwDXE-bJbJqrw6wFv5")
+ .setKey("MY_PROJECT:ElementImpl.java")
+ .setName("ElementImpl.java")
+ .setQualifier(Qualifiers.FILE)
+ .setPath("src/main/java/com/sonarsource/markdown/impl/ElementImpl.java");
+ componentDb.insertComponent(file);
+ SnapshotDto fileSnapshot = dbClient.snapshotDao().insert(dbSession, createForComponent(file, projectSnapshot)
+ .setPeriodDate(1, parseDateTime("2016-01-11T10:49:50+0100").getTime())
+ .setPeriodMode(1, "previous_version")
+ .setPeriodParam(1, "1.0-SNAPSHOT")
+ .setPeriodDate(2, parseDateTime("2016-01-11T10:50:06+0100").getTime())
+ .setPeriodMode(2, "previous_analysis")
+ .setPeriodParam(2, "2016-01-11")
+ .setPeriodDate(3, parseDateTime("2016-01-11T10:38:45+0100").getTime())
+ .setPeriodMode(3, "days")
+ .setPeriodParam(3, "30"));
+
+ MetricDto complexity = insertComplexityMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(complexity, fileSnapshot.getId())
+ .setValue(12.0d)
+ .setVariation(1, 2.0d)
+ .setVariation(2, 0.0d)
+ .setVariation(3, 0.0d));
+
+ MetricDto ncloc = insertNclocMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(ncloc, fileSnapshot.getId())
+ .setValue(114.0d)
+ .setVariation(1, 3.0d)
+ .setVariation(2, -5.0d)
+ .setVariation(3, 5.0d));
+
+ MetricDto newViolations = insertNewViolationMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(newViolations, fileSnapshot.getId())
+ .setVariation(1, 25.0d)
+ .setVariation(2, 0.0d)
+ .setVariation(3, 25.0d));
+ db.commit();
+ }
+
+}
import static org.sonar.db.component.SnapshotTesting.newSnapshotForProject;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
-import static org.sonar.server.measure.ws.ComponentTreeAction.ADDITIONAL_PERIODS;
import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT;
import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT;
import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PERIODS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_ID;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new MeasuresWsModule().configure(container);
- assertThat(container.size()).isEqualTo(3 + 2);
+ assertThat(container.size()).isEqualTo(4 + 2);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.measure;
+
+import com.google.common.base.Function;
+import javax.annotation.Nonnull;
+
+public class MeasureDtoFunctions {
+ private MeasureDtoFunctions() {
+ // prevent instantiation
+ }
+
+ public static Function<MeasureDto, Integer> toMetricId() {
+ return ToMetricId.INSTANCE;
+ }
+
+ private enum ToMetricId implements Function<MeasureDto, Integer> {
+ INSTANCE;
+
+ @Override
+ public Integer apply(@Nonnull MeasureDto input) {
+ return input.getMetricId();
+ }
+ }
+}
package org.sonar.db.metric;
import com.google.common.base.Function;
+import com.google.common.base.Predicate;
import javax.annotation.Nonnull;
/**
return ToKey.INSTANCE;
}
+ public static Predicate<MetricDto> isOptimizedForBestValue() {
+ return IsMetricOptimizedForBestValue.INSTANCE;
+ }
+
private enum ToId implements Function<MetricDto, Integer> {
INSTANCE;
return input.getKey();
}
}
+
+ private enum IsMetricOptimizedForBestValue implements Predicate<MetricDto> {
+ INSTANCE;
+
+ @Override
+ public boolean apply(@Nonnull MetricDto input) {
+ return input.isOptimizedBestValue() && input.getBestValue() != null;
+ }
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ws.client.measure;
+
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class ComponentWsRequest {
+ private String componentId;
+ private String componentKey;
+ private List<String> metricKeys;
+ private List<String> additionalFields;
+
+ @CheckForNull
+ public String getComponentId() {
+ return componentId;
+ }
+
+ public ComponentWsRequest setComponentId(@Nullable String componentId) {
+ this.componentId = componentId;
+ return this;
+ }
+
+ @CheckForNull
+ public String getComponentKey() {
+ return componentKey;
+ }
+
+ public ComponentWsRequest setComponentKey(@Nullable String componentKey) {
+ this.componentKey = componentKey;
+ return this;
+ }
+
+ public List<String> getMetricKeys() {
+ return metricKeys;
+ }
+
+ public ComponentWsRequest setMetricKeys(@Nullable List<String> metricKeys) {
+ this.metricKeys = metricKeys;
+ return this;
+ }
+
+ @CheckForNull
+ public List<String> getAdditionalFields() {
+ return additionalFields;
+ }
+
+ public ComponentWsRequest setAdditionalFields(@Nullable List<String> additionalFields) {
+ this.additionalFields = additionalFields;
+ return this;
+ }
+}
*/
package org.sonarqube.ws.client.measure;
-import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.ComponentTreeWsResponse;
+import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsConnector;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT_TREE;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.CONTROLLER_MEASURES;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_ID;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_KEY;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_KEY;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS;
super(wsConnector, CONTROLLER_MEASURES);
}
- public WsMeasures.ComponentTreeWsResponse componentTree(ComponentTreeWsRequest request) {
+ public ComponentTreeWsResponse componentTree(ComponentTreeWsRequest request) {
GetRequest getRequest = new GetRequest(path(ACTION_COMPONENT_TREE))
.setParam(PARAM_BASE_COMPONENT_ID, request.getBaseComponentId())
.setParam(PARAM_BASE_COMPONENT_KEY, request.getBaseComponentKey())
.setParam("asc", request.getAsc())
.setParam(PARAM_METRIC_SORT, request.getMetricSort());
- return call(getRequest, WsMeasures.ComponentTreeWsResponse.parser());
+ return call(getRequest, ComponentTreeWsResponse.parser());
+ }
+
+ public ComponentWsResponse component(ComponentWsRequest request) {
+ GetRequest getRequest = new GetRequest(path(ACTION_COMPONENT))
+ .setParam(PARAM_COMPONENT_ID, request.getComponentId())
+ .setParam(PARAM_COMPONENT_KEY, request.getComponentKey())
+ .setParam(PARAM_ADDITIONAL_FIELDS, inlineMultipleParamValue(request.getAdditionalFields()))
+ .setParam(PARAM_METRIC_KEYS, inlineMultipleParamValue(request.getMetricKeys()));
+
+ return call(getRequest, ComponentWsResponse.parser());
}
}
package org.sonarqube.ws.client.measure;
-public class MeasuresWsParameters {
- private MeasuresWsParameters() {
- // static constants only
- }
+import com.google.common.collect.ImmutableSortedSet;
+import java.util.Set;
+public class MeasuresWsParameters {
public static final String CONTROLLER_MEASURES = "api/measures";
// actions
public static final String ACTION_COMPONENT_TREE = "component_tree";
+ public static final String ACTION_COMPONENT = "component";
// parameters
public static final String PARAM_BASE_COMPONENT_ID = "baseComponentId";
+
public static final String PARAM_BASE_COMPONENT_KEY = "baseComponentKey";
public static final String PARAM_STRATEGY = "strategy";
public static final String PARAM_QUALIFIERS = "qualifiers";
public static final String PARAM_METRIC_KEYS = "metricKeys";
public static final String PARAM_METRIC_SORT = "metricSort";
public static final String PARAM_ADDITIONAL_FIELDS = "additionalFields";
+ public static final String PARAM_COMPONENT_ID = "componentId";
+ public static final String PARAM_COMPONENT_KEY = "componentKey";
+ public static final String ADDITIONAL_METRICS = "metrics";
+
+ public static final String ADDITIONAL_PERIODS = "periods";
+ public static final Set<String> ADDITIONAL_FIELDS = ImmutableSortedSet.of(ADDITIONAL_METRICS, ADDITIONAL_PERIODS);
+
+ private MeasuresWsParameters() {
+ // static constants only
+ }
}
optional Periods periods = 5;
}
+// WS api/measures/component
+message ComponentWsResponse {
+ optional Component component = 1;
+ optional Metrics metrics = 2;
+ optional Periods periods = 3;
+}
+
message Component {
optional string id = 1;
optional string key = 2;