]> source.dussan.org Git - sonarqube.git/blob
f362cf0b55d1195f0a2d2afa8d008dd248f6bb4d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.measure.live;
21
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.stream.Collectors;
26 import org.sonar.api.config.Configuration;
27 import org.sonar.api.measures.Metric;
28 import org.sonar.db.DbClient;
29 import org.sonar.db.DbSession;
30 import org.sonar.db.component.BranchDto;
31 import org.sonar.db.component.BranchType;
32 import org.sonar.db.component.ComponentDto;
33 import org.sonar.db.component.SnapshotDto;
34 import org.sonar.db.measure.LiveMeasureDto;
35 import org.sonar.server.measure.DebtRatingGrid;
36 import org.sonar.server.measure.Rating;
37
38 import static com.google.common.base.Preconditions.checkState;
39 import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
40
41 public class LiveMeasureTreeUpdaterImpl implements LiveMeasureTreeUpdater {
42   private final DbClient dbClient;
43   private final MeasureUpdateFormulaFactory formulaFactory;
44   private final HotspotMeasureUpdater hotspotMeasureUpdater;
45
46   public LiveMeasureTreeUpdaterImpl(DbClient dbClient, MeasureUpdateFormulaFactory formulaFactory, HotspotMeasureUpdater hotspotMeasureUpdater) {
47     this.dbClient = dbClient;
48     this.formulaFactory = formulaFactory;
49     this.hotspotMeasureUpdater = hotspotMeasureUpdater;
50   }
51
52   @Override
53   public void update(DbSession dbSession, SnapshotDto lastAnalysis, Configuration config, ComponentIndex components, BranchDto branch, MeasureMatrix measures) {
54     long beginningOfLeak = getBeginningOfLeakPeriod(lastAnalysis, branch);
55     boolean shouldUseLeakFormulas = shouldUseLeakFormulas(lastAnalysis, branch);
56
57     // 1. set new measure from issues to each component from touched components to the root
58     updateMatrixWithIssues(dbSession, measures, components, config, shouldUseLeakFormulas, beginningOfLeak);
59
60     // 2. aggregate new measures up the component tree
61     updateMatrixWithHierarchy(measures, components, config, shouldUseLeakFormulas);
62
63     // 3. Count hotspots at root level
64     // this is only necessary because the count of reviewed and to_review hotspots is only saved for the root (not for all components).
65     // For that reason, we can't incrementally generate the new counts up the tree. To have the correct numbers for the root component, we
66     // run this extra step that set the hotspots measures to the root based on the total count of hotspots.
67     hotspotMeasureUpdater.apply(dbSession, measures, components, shouldUseLeakFormulas, beginningOfLeak);
68   }
69
70   private void updateMatrixWithHierarchy(MeasureMatrix matrix, ComponentIndex components, Configuration config, boolean useLeakFormulas) {
71     DebtRatingGrid debtRatingGrid = new DebtRatingGrid(config);
72     FormulaContextImpl context = new FormulaContextImpl(matrix, components, debtRatingGrid);
73     components.getSortedTree().forEach(c -> {
74       for (MeasureUpdateFormula formula : formulaFactory.getFormulas()) {
75         if (useLeakFormulas || !formula.isOnLeak()) {
76           context.change(c, formula);
77           try {
78             formula.computeHierarchy(context);
79           } catch (RuntimeException e) {
80             throw new IllegalStateException("Fail to compute " + formula.getMetric().getKey() + " on " + context.getComponent().getDbKey(), e);
81           }
82         }
83       }
84     });
85   }
86
87   private void updateMatrixWithIssues(DbSession dbSession, MeasureMatrix matrix, ComponentIndex components, Configuration config, boolean useLeakFormulas, long beginningOfLeak) {
88     DebtRatingGrid debtRatingGrid = new DebtRatingGrid(config);
89     FormulaContextImpl context = new FormulaContextImpl(matrix, components, debtRatingGrid);
90
91     components.getSortedTree().forEach(c -> {
92       IssueCounter issueCounter = new IssueCounter(dbClient.issueDao().selectIssueGroupsByComponent(dbSession, c, beginningOfLeak));
93       for (MeasureUpdateFormula formula : formulaFactory.getFormulas()) {
94         // use formulas when the leak period is defined, it's a PR, or the formula is not about the leak period
95         if (useLeakFormulas || !formula.isOnLeak()) {
96           context.change(c, formula);
97           try {
98             formula.compute(context, issueCounter);
99           } catch (RuntimeException e) {
100             throw new IllegalStateException("Fail to compute " + formula.getMetric().getKey() + " on " + context.getComponent().getDbKey(), e);
101           }
102         }
103       }
104     });
105   }
106
107   private static long getBeginningOfLeakPeriod(SnapshotDto lastAnalysis, BranchDto branch) {
108     if (isPR(branch)) {
109       return 0L;
110     } else if (REFERENCE_BRANCH.name().equals(lastAnalysis.getPeriodMode())) {
111       return -1;
112     } else {
113       return Optional.ofNullable(lastAnalysis.getPeriodDate()).orElse(Long.MAX_VALUE);
114     }
115   }
116
117   private static boolean isPR(BranchDto branch) {
118     return branch.getBranchType() == BranchType.PULL_REQUEST;
119   }
120
121   private static boolean shouldUseLeakFormulas(SnapshotDto lastAnalysis, BranchDto branch) {
122     return lastAnalysis.getPeriodDate() != null || isPR(branch) || REFERENCE_BRANCH.name().equals(lastAnalysis.getPeriodMode());
123   }
124
125   public static class FormulaContextImpl implements MeasureUpdateFormula.Context {
126     private final MeasureMatrix matrix;
127     private final ComponentIndex componentIndex;
128     private final DebtRatingGrid debtRatingGrid;
129     private ComponentDto currentComponent;
130     private MeasureUpdateFormula currentFormula;
131
132     public FormulaContextImpl(MeasureMatrix matrix, ComponentIndex componentIndex, DebtRatingGrid debtRatingGrid) {
133       this.matrix = matrix;
134       this.componentIndex = componentIndex;
135       this.debtRatingGrid = debtRatingGrid;
136     }
137
138     private void change(ComponentDto component, MeasureUpdateFormula formula) {
139       this.currentComponent = component;
140       this.currentFormula = formula;
141     }
142
143     public List<Double> getChildrenValues() {
144       List<ComponentDto> children = componentIndex.getChildren(currentComponent);
145       return children.stream()
146         .flatMap(c -> matrix.getMeasure(c, currentFormula.getMetric().getKey()).stream())
147         .map(LiveMeasureDto::getValue)
148         .filter(Objects::nonNull)
149         .collect(Collectors.toList());
150     }
151
152     public List<Double> getChildrenLeakValues() {
153       List<ComponentDto> children = componentIndex.getChildren(currentComponent);
154       return children.stream()
155         .flatMap(c -> matrix.getMeasure(c, currentFormula.getMetric().getKey()).stream())
156         .map(LiveMeasureDto::getVariation)
157         .filter(Objects::nonNull)
158         .collect(Collectors.toList());
159     }
160
161     @Override
162     public ComponentDto getComponent() {
163       return currentComponent;
164     }
165
166     @Override
167     public DebtRatingGrid getDebtRatingGrid() {
168       return debtRatingGrid;
169     }
170
171     @Override
172     public Optional<Double> getValue(Metric metric) {
173       Optional<LiveMeasureDto> measure = matrix.getMeasure(currentComponent, metric.getKey());
174       return measure.map(LiveMeasureDto::getValue);
175     }
176
177     @Override
178     public Optional<String> getText(Metric metric) {
179       Optional<LiveMeasureDto> measure = matrix.getMeasure(currentComponent, metric.getKey());
180       return measure.map(LiveMeasureDto::getTextValue);
181     }
182
183     @Override
184     public Optional<Double> getLeakValue(Metric metric) {
185       Optional<LiveMeasureDto> measure = matrix.getMeasure(currentComponent, metric.getKey());
186       return measure.map(LiveMeasureDto::getVariation);
187     }
188
189     @Override
190     public void setValue(double value) {
191       String metricKey = currentFormula.getMetric().getKey();
192       checkState(!currentFormula.isOnLeak(), "Formula of metric %s accepts only leak values", metricKey);
193       matrix.setValue(currentComponent, metricKey, value);
194     }
195
196     @Override
197     public void setLeakValue(double value) {
198       String metricKey = currentFormula.getMetric().getKey();
199       checkState(currentFormula.isOnLeak(), "Formula of metric %s does not accept leak values", metricKey);
200       matrix.setLeakValue(currentComponent, metricKey, value);
201     }
202
203     @Override
204     public void setValue(Rating value) {
205       String metricKey = currentFormula.getMetric().getKey();
206       checkState(!currentFormula.isOnLeak(), "Formula of metric %s accepts only leak values", metricKey);
207       matrix.setValue(currentComponent, metricKey, value);
208     }
209
210     @Override
211     public void setLeakValue(Rating value) {
212       String metricKey = currentFormula.getMetric().getKey();
213       checkState(currentFormula.isOnLeak(), "Formula of metric %s does not accept leak values", metricKey);
214       matrix.setLeakValue(currentComponent, metricKey, value);
215     }
216   }
217 }