All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.server.computation.step.QualityGateMeasuresStep Maven / Gradle / Ivy

There is a newer version: 7.0
Show newest version
/*
 * 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.computation.step;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.CrawlerDepthLimit;
import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler;
import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.computation.component.TypeAwareVisitorAdapter;
import org.sonar.server.computation.measure.Measure;
import org.sonar.server.computation.measure.MeasureRepository;
import org.sonar.server.computation.measure.QualityGateStatus;
import org.sonar.server.computation.measure.qualitygatedetails.EvaluatedCondition;
import org.sonar.server.computation.measure.qualitygatedetails.QualityGateDetailsData;
import org.sonar.server.computation.metric.Metric;
import org.sonar.server.computation.metric.MetricRepository;
import org.sonar.server.computation.qualitygate.Condition;
import org.sonar.server.computation.qualitygate.ConditionEvaluator;
import org.sonar.server.computation.qualitygate.ConditionStatus;
import org.sonar.server.computation.qualitygate.EvaluationResult;
import org.sonar.server.computation.qualitygate.EvaluationResultTextConverter;
import org.sonar.server.computation.qualitygate.MutableQualityGateStatusHolder;
import org.sonar.server.computation.qualitygate.QualityGate;
import org.sonar.server.computation.qualitygate.QualityGateHolder;

import static com.google.common.collect.FluentIterable.from;
import static java.lang.String.format;
import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER;
import static org.sonar.server.computation.qualitygate.ConditionStatus.NO_VALUE_STATUS;
import static org.sonar.server.computation.qualitygate.ConditionStatus.create;

/**
 * This step:
 * 
    *
  • updates the QualityGateStatus of all the project's measures for the metrics of the conditions of the current * QualityGate (retrieved from {@link QualityGateHolder})
  • *
  • computes the measures on the project for metrics {@link CoreMetrics#QUALITY_GATE_DETAILS_KEY} and * {@link CoreMetrics#ALERT_STATUS_KEY}
  • *
* * It must be executed after the computation of differential measures {@link ComputeMeasureVariationsStep} */ public class QualityGateMeasuresStep implements ComputationStep { private static final Ordering PERIOD_ORDERING = Ordering.natural().nullsLast() .onResultOf(ConditionToPeriod.INSTANCE); private final TreeRootHolder treeRootHolder; private final QualityGateHolder qualityGateHolder; private final MutableQualityGateStatusHolder qualityGateStatusHolder; private final MeasureRepository measureRepository; private final MetricRepository metricRepository; private final EvaluationResultTextConverter evaluationResultTextConverter; public QualityGateMeasuresStep(TreeRootHolder treeRootHolder, QualityGateHolder qualityGateHolder, MutableQualityGateStatusHolder qualityGateStatusHolder, MeasureRepository measureRepository, MetricRepository metricRepository, EvaluationResultTextConverter evaluationResultTextConverter) { this.treeRootHolder = treeRootHolder; this.qualityGateHolder = qualityGateHolder; this.qualityGateStatusHolder = qualityGateStatusHolder; this.evaluationResultTextConverter = evaluationResultTextConverter; this.measureRepository = measureRepository; this.metricRepository = metricRepository; } @Override public void execute() { new DepthTraversalTypeAwareCrawler( new TypeAwareVisitorAdapter(CrawlerDepthLimit.PROJECT, PRE_ORDER) { @Override public void visitProject(Component project) { executeForProject(project); } }).visit(treeRootHolder.getRoot()); } private void executeForProject(Component project) { Optional qualityGate = qualityGateHolder.getQualityGate(); if (qualityGate.isPresent()) { QualityGateDetailsDataBuilder builder = new QualityGateDetailsDataBuilder(); updateMeasures(project, qualityGate.get().getConditions(), builder); addProjectMeasure(project, builder); updateQualityGateStatusHolder(qualityGate.get(), builder); } } private void updateQualityGateStatusHolder(QualityGate qualityGate, QualityGateDetailsDataBuilder builder) { qualityGateStatusHolder.setStatus(convert(builder.getGlobalLevel()), createStatusPerCondition(qualityGate.getConditions(), builder.getEvaluatedConditions())); } private static ConditionStatus.EvaluationStatus toEvaluationStatus(Measure.Level globalLevel) { switch (globalLevel) { case OK: return ConditionStatus.EvaluationStatus.OK; case WARN: return ConditionStatus.EvaluationStatus.WARN; case ERROR: return ConditionStatus.EvaluationStatus.ERROR; default: throw new IllegalArgumentException(format( "Unsupported value '%s' of Measure.Level can not be converted to EvaluationStatus", globalLevel)); } } private static org.sonar.server.computation.qualitygate.QualityGateStatus convert(Measure.Level globalLevel) { switch (globalLevel) { case OK: return org.sonar.server.computation.qualitygate.QualityGateStatus.OK; case WARN: return org.sonar.server.computation.qualitygate.QualityGateStatus.WARN; case ERROR: return org.sonar.server.computation.qualitygate.QualityGateStatus.ERROR; default: throw new IllegalArgumentException(format( "Unsupported value '%s' of Measure.Level can not be converted to QualityGateStatus", globalLevel)); } } private static Map createStatusPerCondition(Iterable conditions, Iterable evaluatedConditions) { Map evaluatedConditionPerCondition = from(evaluatedConditions) .uniqueIndex(EvaluatedConditionToCondition.INSTANCE); ImmutableMap.Builder builder = ImmutableMap.builder(); for (Condition condition : conditions) { EvaluatedCondition evaluatedCondition = evaluatedConditionPerCondition.get(condition); if (evaluatedCondition == null) { builder.put(condition, NO_VALUE_STATUS); } else { builder.put(condition, create(toEvaluationStatus(evaluatedCondition.getLevel()), evaluatedCondition.getActualValue())); } } return builder.build(); } private void updateMeasures(Component project, Set conditions, QualityGateDetailsDataBuilder builder) { Multimap conditionsPerMetric = from(conditions).index(ConditionToMetric.INSTANCE); for (Map.Entry> entry : conditionsPerMetric.asMap().entrySet()) { Metric metric = entry.getKey(); Optional measure = measureRepository.getRawMeasure(project, metric); if (!measure.isPresent()) { continue; } MetricEvaluationResult metricEvaluationResult = evaluateQualityGate(measure.get(), entry.getValue()); String text = evaluationResultTextConverter.asText(metricEvaluationResult.condition, metricEvaluationResult.evaluationResult); builder.addLabel(text); Measure updatedMeasure = Measure.updatedMeasureBuilder(measure.get()) .setQualityGateStatus(new QualityGateStatus(metricEvaluationResult.evaluationResult.getLevel(), text)) .create(); measureRepository.update(project, metric, updatedMeasure); builder.addEvaluatedCondition(metricEvaluationResult); } } private static MetricEvaluationResult evaluateQualityGate(Measure measure, Collection conditions) { ConditionEvaluator conditionEvaluator = new ConditionEvaluator(); MetricEvaluationResult metricEvaluationResult = null; for (Condition newCondition : PERIOD_ORDERING.immutableSortedCopy(conditions)) { EvaluationResult newEvaluationResult = conditionEvaluator.evaluate(newCondition, measure); if (metricEvaluationResult == null || newEvaluationResult.getLevel().ordinal() > metricEvaluationResult.evaluationResult.getLevel().ordinal()) { metricEvaluationResult = new MetricEvaluationResult(newEvaluationResult, newCondition); } } return metricEvaluationResult; } private void addProjectMeasure(Component project, QualityGateDetailsDataBuilder builder) { Measure globalMeasure = Measure.newMeasureBuilder() .setQualityGateStatus(new QualityGateStatus(builder.getGlobalLevel(), StringUtils.join(builder.getLabels(), ", "))) .create(builder.getGlobalLevel()); Metric metric = metricRepository.getByKey(CoreMetrics.ALERT_STATUS_KEY); measureRepository.add(project, metric, globalMeasure); String detailMeasureValue = new QualityGateDetailsData(builder.getGlobalLevel(), builder.getEvaluatedConditions()).toJson(); Measure detailsMeasure = Measure.newMeasureBuilder().create(detailMeasureValue); Metric qgDetailsMetric = metricRepository.getByKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY); measureRepository.add(project, qgDetailsMetric, detailsMeasure); } @Override public String getDescription() { return "Compute Quality Gate measures"; } private static final class QualityGateDetailsDataBuilder { private Measure.Level globalLevel = Measure.Level.OK; private List labels = new ArrayList<>(); private List evaluatedConditions = new ArrayList<>(); public Measure.Level getGlobalLevel() { return globalLevel; } public void addLabel(@Nullable String label) { if (StringUtils.isNotBlank(label)) { labels.add(label); } } public List getLabels() { return labels; } public void addEvaluatedCondition(MetricEvaluationResult metricEvaluationResult) { Measure.Level level = metricEvaluationResult.evaluationResult.getLevel(); if (Measure.Level.WARN == level && this.globalLevel != Measure.Level.ERROR) { globalLevel = Measure.Level.WARN; } else if (Measure.Level.ERROR == level) { globalLevel = Measure.Level.ERROR; } evaluatedConditions.add( new EvaluatedCondition(metricEvaluationResult.condition, level, metricEvaluationResult.evaluationResult.getValue())); } public List getEvaluatedConditions() { return evaluatedConditions; } } private enum EvaluatedConditionToCondition implements Function { INSTANCE; @Override @Nonnull public Condition apply(@Nonnull EvaluatedCondition input) { return input.getCondition(); } } private static class MetricEvaluationResult { private final EvaluationResult evaluationResult; private final Condition condition; private MetricEvaluationResult(EvaluationResult evaluationResult, Condition condition) { this.evaluationResult = evaluationResult; this.condition = condition; } } private enum ConditionToMetric implements Function { INSTANCE; @Override @Nonnull public Metric apply(@Nonnull Condition input) { return input.getMetric(); } } public enum ConditionToPeriod implements Function { INSTANCE; @Override @Nullable public Integer apply(@Nonnull Condition input) { return input.getPeriod(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy