]> source.dussan.org Git - sonarqube.git/blob
8f5f6ebf4e6d24aad714a56d8b80bd0d00e56e9b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 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.pushapi.issues;
21
22 import com.google.gson.Gson;
23 import com.google.gson.GsonBuilder;
24 import java.util.Collection;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 import javax.annotation.CheckForNull;
30 import javax.annotation.Nullable;
31 import org.sonar.api.server.ServerSide;
32 import org.sonar.core.issue.DefaultIssue;
33 import org.sonar.core.issue.FieldDiffs.Diff;
34 import org.sonar.core.util.issue.Issue;
35 import org.sonar.core.util.issue.IssueChangedEvent;
36 import org.sonar.db.DbClient;
37 import org.sonar.db.DbSession;
38 import org.sonar.db.component.BranchDto;
39 import org.sonar.db.component.ComponentDto;
40 import org.sonar.db.pushevent.PushEventDto;
41
42 import static java.nio.charset.StandardCharsets.UTF_8;
43 import static org.elasticsearch.common.Strings.isNullOrEmpty;
44 import static org.sonar.api.issue.DefaultTransitions.ACCEPT;
45 import static org.sonar.api.issue.DefaultTransitions.CONFIRM;
46 import static org.sonar.api.issue.DefaultTransitions.FALSE_POSITIVE;
47 import static org.sonar.api.issue.DefaultTransitions.UNCONFIRM;
48 import static org.sonar.api.issue.DefaultTransitions.WONT_FIX;
49 import static org.sonar.db.component.BranchType.BRANCH;
50
51 @ServerSide
52 public class IssueChangeEventServiceImpl implements IssueChangeEventService {
53   private static final Gson GSON = new GsonBuilder().create();
54
55   private static final String EVENT_NAME = "IssueChanged";
56   private static final String FALSE_POSITIVE_KEY = "FALSE-POSITIVE";
57   private static final String WONT_FIX_KEY = "WONTFIX";
58
59   private static final String RESOLUTION_KEY = "resolution";
60   private static final String SEVERITY_KEY = "severity";
61   private static final String TYPE_KEY = "type";
62
63   private final DbClient dbClient;
64
65   public IssueChangeEventServiceImpl(DbClient dbClient) {
66     this.dbClient = dbClient;
67   }
68
69   @Override
70   public void distributeIssueChangeEvent(DefaultIssue issue, @Nullable String severity, @Nullable String type, @Nullable String transition,
71     BranchDto branch, String projectKey) {
72     Issue changedIssue = new Issue(issue.key(), branch.getKey());
73
74     Boolean resolved = isResolved(transition);
75
76     if (severity == null && type == null && resolved == null) {
77       return;
78     }
79
80     IssueChangedEvent event = new IssueChangedEvent(projectKey, new Issue[]{changedIssue},
81       resolved, severity, type);
82
83     persistEvent(event, branch.getProjectUuid());
84   }
85
86   @Override
87   public void distributeIssueChangeEvent(Collection<DefaultIssue> issues, Map<String, ComponentDto> projectsByUuid,
88     Map<String, BranchDto> branchesByProjectUuid) {
89
90     for (Entry<String, ComponentDto> entry : projectsByUuid.entrySet()) {
91       String projectKey = entry.getValue().getKey();
92
93       Set<DefaultIssue> issuesInProject = issues
94         .stream()
95         .filter(i -> i.projectUuid().equals(entry.getKey()))
96         .collect(Collectors.toSet());
97
98       Issue[] issueChanges = issuesInProject.stream()
99         .filter(i -> branchesByProjectUuid.get(i.projectUuid()).getBranchType().equals(BRANCH))
100         .map(i -> new Issue(i.key(), branchesByProjectUuid.get(i.projectUuid()).getKey()))
101         .toArray(Issue[]::new);
102
103       if (issueChanges.length == 0) {
104         continue;
105       }
106
107       IssueChangedEvent event = getIssueChangedEvent(projectKey, issuesInProject, issueChanges);
108
109       if (event != null) {
110         BranchDto branchDto = branchesByProjectUuid.get(entry.getKey());
111         persistEvent(event, branchDto.getProjectUuid());
112       }
113     }
114   }
115
116   @CheckForNull
117   private static IssueChangedEvent getIssueChangedEvent(String projectKey, Set<DefaultIssue> issuesInProject, Issue[] issueChanges) {
118     DefaultIssue firstIssue = issuesInProject.stream().iterator().next();
119
120     if (firstIssue.currentChange() == null) {
121       return null;
122     }
123
124     Boolean resolved = null;
125     String severity = null;
126     String type = null;
127
128     boolean isRelevantEvent = false;
129     Map<String, Diff> diffs = firstIssue.currentChange().diffs();
130
131     if (diffs.containsKey(RESOLUTION_KEY)) {
132       resolved = diffs.get(RESOLUTION_KEY).newValue() == null ? false : isResolved(diffs.get(RESOLUTION_KEY).newValue().toString());
133       isRelevantEvent = true;
134     }
135
136     if (diffs.containsKey(SEVERITY_KEY)) {
137       severity = diffs.get(SEVERITY_KEY).newValue() == null ? null : diffs.get(SEVERITY_KEY).newValue().toString();
138       isRelevantEvent = true;
139     }
140
141     if (diffs.containsKey(TYPE_KEY)) {
142       type = diffs.get(TYPE_KEY).newValue() == null ? null : diffs.get(TYPE_KEY).newValue().toString();
143       isRelevantEvent = true;
144     }
145
146     if (!isRelevantEvent) {
147       return null;
148     }
149
150     return new IssueChangedEvent(projectKey, issueChanges, resolved, severity, type);
151   }
152
153   @CheckForNull
154   private static Boolean isResolved(@Nullable String transitionOrStatus) {
155     if (isNullOrEmpty(transitionOrStatus)) {
156       return null;
157     }
158
159     if (transitionOrStatus.equals(CONFIRM) || transitionOrStatus.equals(UNCONFIRM)) {
160       return null;
161     }
162
163     return transitionOrStatus.equals(ACCEPT) || transitionOrStatus.equals(WONT_FIX) || transitionOrStatus.equals(FALSE_POSITIVE) ||
164       transitionOrStatus.equals(FALSE_POSITIVE_KEY) || transitionOrStatus.equals(WONT_FIX_KEY);
165   }
166
167   private void persistEvent(IssueChangedEvent event, String entry) {
168     try (DbSession dbSession = dbClient.openSession(false)) {
169       PushEventDto eventDto = new PushEventDto()
170         .setName(EVENT_NAME)
171         .setProjectUuid(entry)
172         .setPayload(serializeIssueToPushEvent(event));
173       dbClient.pushEventDao().insert(dbSession, eventDto);
174       dbSession.commit();
175     }
176   }
177
178   private static byte[] serializeIssueToPushEvent(IssueChangedEvent event) {
179     return GSON.toJson(event).getBytes(UTF_8);
180   }
181 }