]> source.dussan.org Git - sonarqube.git/blob
3b7858bf3ef470032e38aa28cbb382d7f55ac126
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.issue.notification;
21
22 import java.util.Comparator;
23 import java.util.Date;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.function.ToIntFunction;
28 import javax.annotation.CheckForNull;
29 import javax.annotation.Nullable;
30 import org.sonar.api.notifications.Notification;
31 import org.sonar.api.rule.RuleKey;
32 import org.sonar.api.utils.DateUtils;
33 import org.sonar.api.utils.Durations;
34 import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;
35
36 import static java.util.Objects.requireNonNull;
37 import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH;
38 import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_VERSION;
39 import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST;
40 import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_DATE;
41 import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_KEY;
42 import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_NAME;
43 import static org.sonar.server.issue.notification.NewIssuesStatistics.Metric.ISSUE;
44
45 public class NewIssuesNotification extends Notification {
46
47   public static final String TYPE = "new-issues";
48   private static final long serialVersionUID = -6305871981920103093L;
49   private static final String COUNT = ".count";
50   private static final String LABEL = ".label";
51   private static final String DOT = ".";
52
53   private final transient DetailsSupplier detailsSupplier;
54   private final transient Durations durations;
55
56   public NewIssuesNotification(Durations durations, DetailsSupplier detailsSupplier) {
57     this(TYPE, durations, detailsSupplier);
58   }
59
60   protected NewIssuesNotification(String type, Durations durations, DetailsSupplier detailsSupplier) {
61     super(type);
62     this.durations = durations;
63     this.detailsSupplier = detailsSupplier;
64   }
65
66   public interface DetailsSupplier {
67     /**
68      * @throws NullPointerException if {@code ruleKey} is {@code null}
69      */
70     Optional<RuleDefinition> getRuleDefinitionByRuleKey(RuleKey ruleKey);
71
72     /**
73      * @throws NullPointerException if {@code uuid} is {@code null}
74      */
75     Optional<String> getComponentNameByUuid(String uuid);
76
77     /**
78      * @throws NullPointerException if {@code uuid} is {@code null}
79      */
80     Optional<String> getUserNameByUuid(String uuid);
81   }
82
83   public record RuleDefinition(String name, String language) {
84     public RuleDefinition(String name, @Nullable String language) {
85       this.name = requireNonNull(name, "name can't be null");
86       this.language = language;
87     }
88
89     @Override
90     public String toString() {
91       return "RuleDefinition{" + name + " (" + language + ')' + '}';
92     }
93   }
94
95   public NewIssuesNotification setAnalysisDate(Date d) {
96     setFieldValue(FIELD_PROJECT_DATE, DateUtils.formatDateTime(d));
97     return this;
98   }
99
100   public NewIssuesNotification setProject(String projectKey, String projectName, @Nullable String branchName, @Nullable String pullRequest) {
101     setFieldValue(FIELD_PROJECT_NAME, projectName);
102     setFieldValue(FIELD_PROJECT_KEY, projectKey);
103     if (branchName != null) {
104       setFieldValue(FIELD_BRANCH, branchName);
105     }
106     if (pullRequest != null) {
107       setFieldValue(FIELD_PULL_REQUEST, pullRequest);
108     }
109     return this;
110   }
111
112   @CheckForNull
113   public String getProjectKey() {
114     return getFieldValue(FIELD_PROJECT_KEY);
115   }
116
117   public NewIssuesNotification setProjectVersion(@Nullable String version) {
118     if (version != null) {
119       setFieldValue(FIELD_PROJECT_VERSION, version);
120     }
121     return this;
122   }
123
124   public NewIssuesNotification setStatistics(String projectName, NewIssuesStatistics.Stats stats) {
125     setDefaultMessage(stats.getIssueCount().getOnCurrentAnalysis() + " new issues on " + projectName + ".\n");
126
127     setIssueStatistics(stats);
128     setAssigneesStatistics(stats);
129     setTagsStatistics(stats);
130     setComponentsStatistics(stats);
131     setRuleStatistics(stats);
132
133     return this;
134   }
135
136   private void setRuleStatistics(NewIssuesStatistics.Stats stats) {
137     Metric metric = Metric.RULE;
138     List<Map.Entry<String, MetricStatsInt>> fiveBiggest = fiveBiggest(stats.getDistributedMetricStats(metric), MetricStatsInt::getOnCurrentAnalysis);
139     int i = 1;
140     for (Map.Entry<String, MetricStatsInt> ruleStats : fiveBiggest) {
141       String ruleKey = ruleStats.getKey();
142       RuleDefinition rule = detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.parse(ruleKey))
143         .orElseThrow(() -> new IllegalStateException(String.format("Rule with key '%s' does not exist", ruleKey)));
144       String name = rule.name() + " (" + rule.language() + ")";
145       setFieldValue(metric + DOT + i + LABEL, name);
146       setFieldValue(metric + DOT + i + COUNT, String.valueOf(ruleStats.getValue().getOnCurrentAnalysis()));
147       i++;
148     }
149   }
150
151   private void setComponentsStatistics(NewIssuesStatistics.Stats stats) {
152     Metric metric = Metric.COMPONENT;
153     int i = 1;
154     List<Map.Entry<String, MetricStatsInt>> fiveBiggest = fiveBiggest(stats.getDistributedMetricStats(metric), MetricStatsInt::getOnCurrentAnalysis);
155     for (Map.Entry<String, MetricStatsInt> componentStats : fiveBiggest) {
156       String uuid = componentStats.getKey();
157       String componentName = detailsSupplier.getComponentNameByUuid(uuid)
158         .orElseThrow(() -> new IllegalStateException(String.format("Component with uuid '%s' not found", uuid)));
159       setFieldValue(metric + DOT + i + LABEL, componentName);
160       setFieldValue(metric + DOT + i + COUNT, String.valueOf(componentStats.getValue().getOnCurrentAnalysis()));
161       i++;
162     }
163   }
164
165   private void setTagsStatistics(NewIssuesStatistics.Stats stats) {
166     Metric metric = Metric.TAG;
167     int i = 1;
168     for (Map.Entry<String, MetricStatsInt> tagStats : fiveBiggest(stats.getDistributedMetricStats(metric), MetricStatsInt::getOnCurrentAnalysis)) {
169       setFieldValue(metric + DOT + i + COUNT, String.valueOf(tagStats.getValue().getOnCurrentAnalysis()));
170       setFieldValue(metric + DOT + i + LABEL, tagStats.getKey());
171       i++;
172     }
173   }
174
175   private void setAssigneesStatistics(NewIssuesStatistics.Stats stats) {
176     Metric metric = Metric.ASSIGNEE;
177     List<Map.Entry<String, MetricStatsInt>> entries = fiveBiggest(stats.getDistributedMetricStats(metric), MetricStatsInt::getOnCurrentAnalysis);
178
179     int i = 1;
180     for (Map.Entry<String, MetricStatsInt> assigneeStats : entries) {
181       String assigneeUuid = assigneeStats.getKey();
182       String name = detailsSupplier.getUserNameByUuid(assigneeUuid).orElse(assigneeUuid);
183       setFieldValue(metric + DOT + i + LABEL, name);
184       setFieldValue(metric + DOT + i + COUNT, String.valueOf(assigneeStats.getValue().getOnCurrentAnalysis()));
185       i++;
186     }
187   }
188
189   private static List<Map.Entry<String, MetricStatsInt>> fiveBiggest(DistributedMetricStatsInt distributedMetricStatsInt, ToIntFunction<MetricStatsInt> biggerCriteria) {
190     Comparator<Map.Entry<String, MetricStatsInt>> comparator = Comparator.comparingInt(a -> biggerCriteria.applyAsInt(a.getValue()));
191     return distributedMetricStatsInt.getForLabels()
192       .entrySet()
193       .stream()
194       .filter(i -> biggerCriteria.applyAsInt(i.getValue()) > 0)
195       .sorted(comparator.reversed())
196       .limit(5)
197       .toList();
198   }
199
200   private void setIssueStatistics(NewIssuesStatistics.Stats stats) {
201     setFieldValue(ISSUE + COUNT, String.valueOf(stats.getIssueCount().getOnCurrentAnalysis()));
202   }
203
204   @Override
205   public boolean equals(Object obj) {
206     return super.equals(obj);
207   }
208
209   @Override
210   public int hashCode() {
211     return super.hashCode();
212   }
213 }