.setHandler(this)
.addPagingParams(100, MAX_SIZE)
.setChangelog(
+ new Change("7.2", "field 'bestValue' is added to the response"),
new Change("6.3", format("Number of metric keys is limited to %s", MAX_METRIC_KEYS)),
new Change("6.6", "the response field id is deprecated. Use key instead."),
new Change("6.6", "the response field refId is deprecated. Use refKey instead."));
}
private static Measures.Component.Builder toWsComponent(ComponentDto component, Map<MetricDto, ComponentTreeData.Measure> measures,
- Map<String, ComponentDto> referenceComponentsByUuid) {
+ Map<String, ComponentDto> referenceComponentsByUuid) {
Measures.Component.Builder wsComponent = componentDtoToWsComponent(component);
ComponentDto referenceComponent = referenceComponentsByUuid.get(component.getCopyResourceUuid());
if (referenceComponent != null) {
Optional<SnapshotDto> baseSnapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, baseComponent.projectUuid());
if (!baseSnapshot.isPresent()) {
return ComponentTreeData.builder()
- .setBaseComponent(baseComponent)
- .build();
+ .setBaseComponent(baseComponent)
+ .build();
}
ComponentTreeQuery componentTreeQuery = toComponentTreeQuery(wsRequest, baseComponent);
List<ComponentDto> components = searchComponents(dbSession, componentTreeQuery);
List<MetricDto> metrics = searchMetrics(dbSession, wsRequest);
Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery,
- components,
- metrics);
+ components,
+ metrics);
components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest);
components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
components = paginateComponents(components, wsRequest);
return ComponentTreeData.builder()
- .setBaseComponent(baseComponent)
- .setComponentsFromDb(components)
- .setComponentCount(componentCount)
- .setMeasuresByComponentUuidAndMetric(measuresByComponentUuidAndMetric)
- .setMetrics(metrics)
- .setPeriods(snapshotToWsPeriods(baseSnapshot.get()))
- .setReferenceComponentsByUuid(searchReferenceComponentsById(dbSession, components))
- .build();
+ .setBaseComponent(baseComponent)
+ .setComponentsFromDb(components)
+ .setComponentCount(componentCount)
+ .setMeasuresByComponentUuidAndMetric(measuresByComponentUuidAndMetric)
+ .setMetrics(metrics)
+ .setPeriods(snapshotToWsPeriods(baseSnapshot.get()))
+ .setReferenceComponentsByUuid(searchReferenceComponentsById(dbSession, components))
+ .build();
}
}
private Map<String, ComponentDto> searchReferenceComponentsById(DbSession dbSession, List<ComponentDto> components) {
List<String> referenceComponentUUids = components.stream()
- .map(ComponentDto::getCopyResourceUuid)
- .filter(Objects::nonNull)
- .collect(MoreCollectors.toList(components.size()));
+ .map(ComponentDto::getCopyResourceUuid)
+ .filter(Objects::nonNull)
+ .collect(MoreCollectors.toList(components.size()));
if (referenceComponentUUids.isEmpty()) {
return emptyMap();
}
return FluentIterable.from(dbClient.componentDao().selectByUuids(dbSession, referenceComponentUUids))
- .uniqueIndex(ComponentDto::uuid);
+ .uniqueIndex(ComponentDto::uuid);
}
private List<ComponentDto> searchComponents(DbSession dbSession, ComponentTreeQuery componentTreeQuery) {
if (metrics.size() < metricKeys.size()) {
List<String> foundMetricKeys = Lists.transform(metrics, MetricDto::getKey);
Set<String> missingMetricKeys = Sets.difference(
- new LinkedHashSet<>(metricKeys),
- new LinkedHashSet<>(foundMetricKeys));
+ new LinkedHashSet<>(metricKeys),
+ new LinkedHashSet<>(foundMetricKeys));
throw new NotFoundException(format("The following metric keys are not found: %s", COMMA_JOINER.join(missingMetricKeys)));
}
String forbiddenMetrics = metrics.stream()
- .filter(metric -> ComponentTreeAction.FORBIDDEN_METRIC_TYPES.contains(metric.getValueType()))
- .map(MetricDto::getKey)
- .sorted()
- .collect(MoreCollectors.join(COMMA_JOINER));
+ .filter(metric -> ComponentTreeAction.FORBIDDEN_METRIC_TYPES.contains(metric.getValueType()))
+ .map(MetricDto::getKey)
+ .sorted()
+ .collect(MoreCollectors.join(COMMA_JOINER));
checkArgument(forbiddenMetrics.isEmpty(), "Metrics %s can't be requested in this web service. Please use api/measures/component", forbiddenMetrics);
return metrics;
}
Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDto::getId);
MeasureTreeQuery measureQuery = MeasureTreeQuery.builder()
- .setStrategy(MeasureTreeQuery.Strategy.valueOf(componentTreeQuery.getStrategy().name()))
- .setNameOrKeyQuery(componentTreeQuery.getNameOrKeyQuery())
- .setQualifiers(componentTreeQuery.getQualifiers())
- .setMetricIds(new ArrayList<>(metricsById.keySet()))
- .build();
+ .setStrategy(MeasureTreeQuery.Strategy.valueOf(componentTreeQuery.getStrategy().name()))
+ .setNameOrKeyQuery(componentTreeQuery.getNameOrKeyQuery())
+ .setQualifiers(componentTreeQuery.getQualifiers())
+ .setMetricIds(new ArrayList<>(metricsById.keySet()))
+ .build();
Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric = HashBasedTable.create(components.size(), metrics.size());
dbClient.liveMeasureDao().selectTreeByQuery(dbSession, baseComponent, measureQuery, result -> {
LiveMeasureDto measureDto = result.getResultObject();
measuresByComponentUuidAndMetric.put(
- measureDto.getComponentUuid(),
- metricsById.get(measureDto.getMetricId()),
- ComponentTreeData.Measure.createFromMeasureDto(measureDto));
+ measureDto.getComponentUuid(),
+ metricsById.get(measureDto.getMetricId()),
+ ComponentTreeData.Measure.createFromMeasureDto(measureDto));
});
addBestValuesToMeasures(measuresByComponentUuidAndMetric, components, metrics);
* </ul>
*/
private static void addBestValuesToMeasures(Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric, List<ComponentDto> components,
- List<MetricDto> metrics) {
+ List<MetricDto> metrics) {
List<MetricDtoWithBestValue> metricDtosWithBestValueMeasure = metrics.stream()
- .filter(MetricDtoFunctions.isOptimizedForBestValue())
- .map(new MetricDtoToMetricDtoWithBestValue())
- .collect(MoreCollectors.toList(metrics.size()));
+ .filter(MetricDtoFunctions.isOptimizedForBestValue())
+ .map(new MetricDtoToMetricDtoWithBestValue())
+ .collect(MoreCollectors.toList(metrics.size()));
if (metricDtosWithBestValueMeasure.isEmpty()) {
return;
}
for (MetricDtoWithBestValue metricWithBestValue : metricDtosWithBestValueMeasure) {
if (measuresByComponentUuidAndMetric.get(component.uuid(), metricWithBestValue.getMetric()) == null) {
measuresByComponentUuidAndMetric.put(component.uuid(), metricWithBestValue.getMetric(),
- ComponentTreeData.Measure.createFromMeasureDto(metricWithBestValue.getBestValue()));
+ ComponentTreeData.Measure.createFromMeasureDto(metricWithBestValue.getBestValue()));
}
}
});
checkState(metricToSort.isPresent(), "Metric '%s' not found", metricKeyToSort, wsRequest.getMetricKeys());
return components
- .stream()
- .filter(new HasMeasure(measuresByComponentUuidAndMetric, metricToSort.get(), wsRequest.getMetricPeriodSort()))
- .collect(MoreCollectors.toList(components.size()));
+ .stream()
+ .filter(new HasMeasure(measuresByComponentUuidAndMetric, metricToSort.get(), wsRequest.getMetricPeriodSort()))
+ .collect(MoreCollectors.toList(components.size()));
}
private static boolean componentWithMeasuresOnly(ComponentTreeRequest wsRequest) {
}
private static List<ComponentDto> sortComponents(List<ComponentDto> components, ComponentTreeRequest wsRequest, List<MetricDto> metrics,
- Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric) {
+ Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric) {
return ComponentTreeSort.sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
}
private static List<ComponentDto> paginateComponents(List<ComponentDto> components, ComponentTreeRequest wsRequest) {
return components.stream()
- .skip(offset(wsRequest.getPage(), wsRequest.getPageSize()))
- .limit(wsRequest.getPageSize())
- .collect(MoreCollectors.toList(wsRequest.getPageSize()));
+ .skip(offset(wsRequest.getPage(), wsRequest.getPageSize()))
+ .limit(wsRequest.getPageSize())
+ .collect(MoreCollectors.toList(wsRequest.getPageSize()));
}
@CheckForNull
List<String> childrenQualifiers = childrenQualifiers(wsRequest, baseComponent.qualifier());
ComponentTreeQuery.Builder componentTreeQueryBuilder = ComponentTreeQuery.builder()
- .setBaseUuid(baseComponent.uuid())
- .setStrategy(STRATEGIES.get(wsRequest.getStrategy()));
+ .setBaseUuid(baseComponent.uuid())
+ .setStrategy(STRATEGIES.get(wsRequest.getStrategy()));
if (wsRequest.getQuery() != null) {
componentTreeQueryBuilder.setNameOrKeyQuery(wsRequest.getQuery());
package org.sonar.server.measure.ws;
import javax.annotation.Nullable;
+import org.sonar.core.util.Protobuf;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonarqube.ws.Measures;
import org.sonarqube.ws.Measures.Measure;
+import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.measure.ws.MeasureValueFormatter.formatMeasureValue;
import static org.sonar.server.measure.ws.MeasureValueFormatter.formatNumericalValue;
static void updateMeasureBuilder(Measure.Builder measureBuilder, MetricDto metric, double doubleValue, @Nullable String stringValue, double variation) {
measureBuilder.setMetric(metric.getKey());
+ Double bestValue = metric.getBestValue();
// a measure value can be null, new_violations metric for example
if (!Double.isNaN(doubleValue) || stringValue != null) {
measureBuilder.setValue(formatMeasureValue(doubleValue, stringValue, metric));
+ setNullable(bestValue, v -> measureBuilder.setBestValue(doubleValue == v));
}
Measures.PeriodValue.Builder periodBuilder = Measures.PeriodValue.newBuilder();
if (Double.isNaN(variation)) {
return;
}
- measureBuilder.getPeriodsBuilder().addPeriodsValue(periodBuilder
+ Measures.PeriodValue.Builder builderForValue = periodBuilder
.clear()
.setIndex(1)
- .setValue(formatNumericalValue(variation, metric)));
+ .setValue(formatNumericalValue(variation, metric));
+ setNullable(bestValue, v -> builderForValue.setBestValue(variation == v));
+ measureBuilder.getPeriodsBuilder().addPeriodsValue(builderForValue);
}
}
.executeProtobuf(ComponentWsResponse.class);
assertThat(response.getComponent().getMeasuresList())
- .extracting(Measures.Measure::getMetric, Measures.Measure::getValue)
- .containsExactly(tuple(metric.getKey(), "7"));
+ .extracting(Measures.Measure::getMetric, Measures.Measure::getValue, Measures.Measure::getBestValue)
+ .containsExactly(tuple(metric.getKey(), "7", true));
}
@Test
import org.sonarqube.ws.Common;
import org.sonarqube.ws.Measures;
import org.sonarqube.ws.Measures.ComponentTreeWsResponse;
+import org.sonarqube.ws.Measures.PeriodValue;
import static java.lang.Double.parseDouble;
import static java.lang.String.format;
+import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
assertThat(response.getComponentsList().get(0).getMeasuresList()).extracting("metric").containsOnly("coverage");
// file measures
List<Measures.Measure> fileMeasures = response.getComponentsList().get(1).getMeasuresList();
- assertThat(fileMeasures).extracting("metric").containsOnly("ncloc", "coverage", "new_violations");
- assertThat(fileMeasures).extracting("value").containsOnly("100", "15.5", "");
+ assertThat(fileMeasures)
+ .extracting(Measures.Measure::getMetric, Measures.Measure::getValue, Measures.Measure::getBestValue, Measures.Measure::hasBestValue)
+ .containsExactlyInAnyOrder(tuple("ncloc", "100", true, true),
+ tuple("coverage", "15.5", false, false),
+ tuple("new_violations", "", false, false));
List<Common.Metric> metrics = response.getMetrics().getMetricsList();
assertThat(metrics).extracting("bestValue").contains("100", "");
assertThat(metrics).extracting("worstValue").contains("1000");
}
+ @Test
+ public void return_is_best_value_on_leak_measures() {
+ ComponentDto project = db.components().insertPrivateProject();
+ db.components().insertSnapshot(project);
+ userSession.anonymous().addProjectPermission(UserRole.USER, project);
+ ComponentDto file = newFileDto(project, null);
+ db.components().insertComponent(file);
+
+ MetricDto matchingBestValue = db.measures().insertMetric(m -> m
+ .setKey("new_lines")
+ .setValueType(INT.name())
+ .setBestValue(100d));
+ MetricDto doesNotMatchBestValue = db.measures().insertMetric(m -> m
+ .setKey("new_lines_2")
+ .setValueType(INT.name())
+ .setBestValue(100d));
+ MetricDto noBestValue = db.measures().insertMetric(m -> m
+ .setKey("new_violations")
+ .setValueType(INT.name())
+ .setBestValue(null));
+ db.measures().insertLiveMeasure(file, matchingBestValue, m -> m.setValue(null).setData((String) null).setVariation(100d));
+ db.measures().insertLiveMeasure(file, doesNotMatchBestValue, m -> m.setValue(null).setData((String) null).setVariation(10d));
+ db.measures().insertLiveMeasure(file, noBestValue, m -> m.setValue(null).setData((String) null).setVariation(42.0d));
+
+ ComponentTreeWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, project.getKey())
+ .setParam(PARAM_METRIC_KEYS, "new_lines,new_lines_2,new_violations")
+ .executeProtobuf(ComponentTreeWsResponse.class);
+
+ // file measures
+ List<Measures.Measure> fileMeasures = response.getComponentsList().get(0).getMeasuresList();
+ assertThat(fileMeasures)
+ .extracting(Measures.Measure::getMetric, m -> m.getPeriods().getPeriodsValueList())
+ .containsExactlyInAnyOrder(
+ tuple(matchingBestValue.getKey(), singletonList(PeriodValue.newBuilder().setIndex(1).setValue("100").setBestValue(true).build())),
+ tuple(doesNotMatchBestValue.getKey(), singletonList(PeriodValue.newBuilder().setIndex(1).setValue("10").setBestValue(false).build())),
+ tuple(noBestValue.getKey(), singletonList(PeriodValue.newBuilder().setIndex(1).setValue("42").build())));
+ }
+
@Test
public void use_best_value_for_rating() {
ComponentDto project = db.components().insertPrivateProject();
import org.sonarqube.ws.Measures.SearchWsResponse;
import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.transform;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
assertThat(measure.getValue()).isEqualTo("15.5");
}
+ @Test
+ public void return_best_value() {
+ ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
+ userSession.addProjectPermission(UserRole.USER, project);
+ MetricDto matchBestValue = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()).setBestValue(15.5d));
+ db.measures().insertLiveMeasure(project, matchBestValue, m -> m.setValue(15.5d));
+ MetricDto doesNotMatchBestValue = db.measures().insertMetric(m -> m.setValueType(INT.name()).setBestValue(50d));
+ db.measures().insertLiveMeasure(project, doesNotMatchBestValue, m -> m.setValue(40d));
+ MetricDto noBestValue = db.measures().insertMetric(m -> m.setValueType(INT.name()).setBestValue(null));
+ db.measures().insertLiveMeasure(project, noBestValue, m -> m.setValue(123d));
+
+ SearchWsResponse result = call(singletonList(project.getDbKey()),
+ asList(matchBestValue.getKey(), doesNotMatchBestValue.getKey(), noBestValue.getKey()));
+
+ List<Measure> measures = result.getMeasuresList();
+ assertThat(measures)
+ .extracting(Measure::getMetric, Measure::getValue, Measure::getBestValue, Measure::hasBestValue)
+ .containsExactlyInAnyOrder(
+ tuple(matchBestValue.getKey(), "15.5", true, true),
+ tuple(doesNotMatchBestValue.getKey(), "40", false, true),
+ tuple(noBestValue.getKey(), "123", false, false));
+ }
+
@Test
public void return_measures_on_leak_period() {
OrganizationDto organization = db.organizations().insert();
assertThat(measure.getValue()).isEqualTo("15.5");
}
-
@Test
public void return_measures_on_application() {
OrganizationDto organization = db.organizations().insert();
ComponentDto project2 = db.components().insertPrivateProject(db.getDefaultOrganization());
db.measures().insertLiveMeasure(project1, metric, m -> m.setValue(15.5d));
db.measures().insertLiveMeasure(project2, metric, m -> m.setValue(42.0d));
- Arrays.stream(new ComponentDto[]{project1}).forEach(p -> userSession.addProjectPermission(UserRole.USER, p));
+ Arrays.stream(new ComponentDto[] {project1}).forEach(p -> userSession.addProjectPermission(UserRole.USER, p));
SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey()), singletonList(metric.getKey()));
|}; */
/*:: type State = {
+ bestValue?: string,
components: Array<ComponentEnhanced>,
metric: ?Metric,
paging?: Paging,
const metricKeys = [metric.key];
const opts /*: Object */ = {
...getBranchLikeQuery(this.props.branchLike),
+ additionalFields: 'metrics',
metricSortFilter: 'withMeasuresOnly'
};
const isDiff = isDiffMetric(metric.key);
if (metric === this.props.metric) {
if (this.mounted) {
this.setState(({ selected } /*: State */) => ({
+ bestValue: r.metrics[0].bestValue,
components: r.components.map(component =>
enhanceComponent(component, metric, metrics)
),
if (metric === this.props.metric) {
if (this.mounted) {
this.setState(state => ({
+ bestValue: r.metrics[0].bestValue,
components: [
...state.components,
...r.components.map(component => enhanceComponent(component, metric, metrics))
const selectedIdx = this.getSelectedIndex();
return (
<FilesView
+ bestValue={this.state.bestValue}
branchLike={this.props.branchLike}
components={this.state.components}
fetchMore={this.fetchMoreComponents}
import ComponentsListRow from './ComponentsListRow';
import EmptyResult from './EmptyResult';
import { complementary } from '../config/complementary';
-import { getLocalizedMetricName } from '../../../helpers/l10n';
+import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n';
+import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures';
/*:: import type { Component, ComponentEnhanced } from '../types'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
+ bestValue?: string,
branchLike?: { id?: string; name: string },
components: Array<ComponentEnhanced>,
onClick: string => void,
selectedComponent?: ?string
|}; */
-export default function ComponentsList(
- {
- branchLike,
- components,
- onClick,
- metrics,
- metric,
- rootComponent,
- selectedComponent
- } /*: Props */
-) {
- if (!components.length) {
- return <EmptyResult />;
+/*:: type State = {
+ hideBest: boolean
+}; */
+
+export default class ComponentsList extends React.PureComponent {
+ /*:: props: Props; */
+ state /*: State */ = {
+ hideBest: true
+ };
+
+ componentWillReceiveProps(nextProps /*: Props */) {
+ if (nextProps.metric !== this.props.metric) {
+ this.setState({ hideBest: true });
+ }
+ }
+
+ displayAll = (event /*: Event */) => {
+ event.preventDefault();
+ this.setState({ hideBest: false });
+ };
+
+ hasBestValue(component /*: Component*/, otherMetrics /*: Array<Metric> */) {
+ const { metric } = this.props;
+ const focusedMeasure = component.measures.find(measure => measure.metric.key === metric.key);
+ if (isDiffMetric(focusedMeasure.metric.key)) {
+ return isPeriodBestValue(focusedMeasure, 1);
+ }
+ return focusedMeasure.bestValue;
}
- const otherMetrics = (complementary[metric.key] || []).map(key => metrics[key]);
- return (
- <table className="data zebra zebra-hover">
- {otherMetrics.length > 0 && (
- <thead>
- <tr>
- <th> </th>
- <th className="text-right">
- <span className="small">{getLocalizedMetricName(metric)}</span>
- </th>
- {otherMetrics.map(metric => (
- <th className="text-right" key={metric.key}>
- <span className="small">{getLocalizedMetricName(metric)}</span>
- </th>
- ))}
- </tr>
- </thead>
- )}
+ renderComponent(component /*: Component*/, otherMetrics /*: Array<Metric> */) {
+ const { branchLike, metric, selectedComponent, onClick, rootComponent } = this.props;
+ return (
+ <ComponentsListRow
+ branchLike={branchLike}
+ component={component}
+ isSelected={component.key === selectedComponent}
+ key={component.id}
+ metric={metric}
+ onClick={onClick}
+ otherMetrics={otherMetrics}
+ rootComponent={rootComponent}
+ />
+ );
+ }
- <tbody>
- {components.map(component => (
- <ComponentsListRow
- branchLike={branchLike}
- component={component}
- isSelected={component.key === selectedComponent}
- key={component.id}
- metric={metric}
- onClick={onClick}
- otherMetrics={otherMetrics}
- rootComponent={rootComponent}
- />
- ))}
- </tbody>
- </table>
- );
+ renderHiddenLink(hiddenCount /*: number*/, colCount /*: number*/) {
+ return (
+ <div className="alert alert-info spacer-top">
+ {translateWithParameters(
+ 'component_measures.hidden_best_score_metrics',
+ hiddenCount,
+ formatMeasure(this.props.bestValue, this.props.metric.type)
+ )}
+ <a className="spacer-left" href="#" onClick={this.displayAll}>
+ {translate('show_all')}
+ </a>
+ </div>
+ );
+ }
+
+ render() {
+ const { components, metric, metrics } = this.props;
+ if (!components.length) {
+ return <EmptyResult />;
+ }
+
+ const otherMetrics = (complementary[metric.key] || []).map(key => metrics[key]);
+ const notBestComponents = components.filter(
+ component => !this.hasBestValue(component, otherMetrics)
+ );
+ const hiddenCount = components.length - notBestComponents.length;
+ const shouldHideBest = this.state.hideBest && hiddenCount !== components.length;
+ return (
+ <React.Fragment>
+ <table className="data zebra zebra-hover">
+ {otherMetrics.length > 0 && (
+ <thead>
+ <tr>
+ <th> </th>
+ <th className="text-right">
+ <span className="small">{getLocalizedMetricName(metric)}</span>
+ </th>
+ {otherMetrics.map(metric => (
+ <th className="text-right" key={metric.key}>
+ <span className="small">{getLocalizedMetricName(metric)}</span>
+ </th>
+ ))}
+ </tr>
+ </thead>
+ )}
+
+ <tbody>
+ {(shouldHideBest ? notBestComponents : components).map(component =>
+ this.renderComponent(component, otherMetrics)
+ )}
+ </tbody>
+ </table>
+ {shouldHideBest &&
+ hiddenCount > 0 &&
+ this.renderHiddenLink(hiddenCount, otherMetrics.length + 3)}
+ </React.Fragment>
+ );
+ }
}
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
+ bestValue?: string,
branchLike?: { id?: string; name: string },
components: Array<ComponentEnhanced>,
fetchMore: () => void,
return (
<div ref={elem => (this.listContainer = elem)}>
<ComponentsList
+ bestValue={this.props.bestValue}
branchLike={this.props.branchLike}
components={this.props.components}
metric={this.props.metric}
export interface MeasurePeriod {
index: number;
value: string;
+ bestValue?: boolean;
}
export interface MeasureIntern {
return period ? period.value : undefined;
}
+export function isPeriodBestValue(
+ measure: Measure | MeasureEnhanced,
+ periodIndex: number
+): boolean {
+ const { periods } = measure;
+ const period = periods && periods.find(period => period.index === periodIndex);
+ return (period && period.bestValue) || false;
+}
+
/** Check if metric is differential */
export function isDiffMetric(metricKey: string): boolean {
return metricKey.indexOf('new_') === 0;
*/
// @flow
/*:: export type Metric = {
+ bestValue?: string,
custom?: boolean,
decimalScale?: number,
description?: string,
short_number_suffix.k=k
short_number_suffix.m=M
show_more=Show More
+show_all=Show All
should_be_unique=Should be unique
since_x=since {0}
since_previous_analysis=since previous analysis
component_measures.to_select_files=to select files
component_measures.to_navigate=to navigate
component_measures.to_navigate_files=to next/previous file
+component_measures.hidden_best_score_metrics=There are {0} hidden components with a score of {1}.
component_measures.overview.project_overview.facet=Project Overview
component_measures.overview.project_overview.title=Risk
optional string value = 2;
optional PeriodsValue periods = 3;
optional string component = 4;
+ optional bool bestValue = 5;
}
message PeriodsValue {
message PeriodValue {
optional int32 index = 1;
optional string value = 2;
+ optional bool bestValue = 3;
}