-
-
Notifications
You must be signed in to change notification settings - Fork 104
Expand file tree
/
Copy pathSonarMasterCoverageRepository.java
More file actions
188 lines (160 loc) · 7.94 KB
/
Copy pathSonarMasterCoverageRepository.java
File metadata and controls
188 lines (160 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
Copyright 2015-2016 Artem Stasiuk
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.terma.jenkins.githubprcoveragestatus;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.List;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static org.apache.hc.core5.http.HttpStatus.SC_BAD_REQUEST;
@SuppressWarnings("WeakerAccess")
public class SonarMasterCoverageRepository implements MasterCoverageRepository {
private static final String SONAR_SEARCH_PROJECTS_API_PATH = "/api/projects/index";
private static final String SONAR_COMPONENT_MEASURE_API_PATH = "/api/measures/component";
public static final String SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME = "coverage";
private final String sonarUrl;
private final String login;
private final HttpClient httpClient;
private final ObjectMapper objectMapper = new ObjectMapper().disable(FAIL_ON_UNKNOWN_PROPERTIES);
private PrintStream buildLog;
public SonarMasterCoverageRepository(String sonarUrl, String login, String password, PrintStream buildLog) {
this.sonarUrl = sonarUrl;
this.login = login;
this.buildLog = buildLog;
if (this.login != null) {
try {
BasicCredentialsProvider provider = new BasicCredentialsProvider();
AuthScope scope = new AuthScope(HttpHost.create(sonarUrl));
provider.setCredentials(scope, new UsernamePasswordCredentials(login, password.toCharArray()));
this.httpClient = HttpClients.custom().setDefaultCredentialsProvider(provider).build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
} else {
this.httpClient = HttpClients.createDefault();
}
}
@Override
public float get(final String gitHubRepoUrl) {
final String repoName = GitUtils.getRepoName(gitHubRepoUrl);
log("Getting coverage for Git Repo URL: %s by repo name: %s", gitHubRepoUrl, repoName);
try {
final SonarProject sonarProject = getSonarProject(repoName);
return getCoverageMeasure(sonarProject);
} catch (Exception e) {
log("Failed to get master coverage for %s", gitHubRepoUrl);
log("Exception message '%s'", e);
e.printStackTrace(buildLog);
return 0;
}
}
/**
* Try to find the project in sonarqube based on the repo name from the git uri
*
* @return the sonar project found in case multiple are found, the first one is returned
* @throws SonarProjectRetrievalException if no project could be found or an error occurred during retrieval
*/
private SonarProject getSonarProject(final String repoName) throws SonarProjectRetrievalException {
final String searchUri = sonarUrl + SONAR_SEARCH_PROJECTS_API_PATH + "?search=" + repoName;
try (final CloseableHttpResponse response = executeGetRequest(searchUri)) {
final List<SonarProject> sonarProjects = objectMapper.readValue(EntityUtils.toString(response.getEntity()), new TypeReference<List<SonarProject>>() {
});
if (sonarProjects.isEmpty()) {
throw new SonarProjectRetrievalException("No sonar project found for repo" + repoName);
} else if (sonarProjects.size() == 1) {
log("Found project for repo name %s - %s", repoName, sonarProjects.get(0));
return sonarProjects.get(0);
} else {
log("Found multiple projects for repo name %s - found %s - returning first result", repoName, sonarProjects);
return sonarProjects.get(0);
}
} catch (final Exception e) {
throw new SonarProjectRetrievalException(String.format("failed to search for sonar project %s - %s", repoName, e.getMessage()), e);
}
}
/**
* Try to find code coverage measure for project in sonarqube
*
* @return the coverage found for the project
* @throws SonarCoverageMeasureRetrievalException if an error occurred during retrieval of the coverage
*/
private float getCoverageMeasure(SonarProject project) throws SonarCoverageMeasureRetrievalException {
final String uri = MessageFormat.format("{0}{1}?componentKey={2}&metricKeys={3}", sonarUrl, SONAR_COMPONENT_MEASURE_API_PATH, URLEncoder.encode(project.getKey()), SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME);
try (final CloseableHttpResponse response = executeGetRequest(uri)) {
String value = JsonUtils.findInJson(EntityUtils.toString(response.getEntity()), "component.measures[0].value");
return Float.parseFloat(value) / 100;
} catch (Exception e) {
throw new SonarCoverageMeasureRetrievalException(String.format("failed to get coverage measure for sonar project %s - %s", project.getKey(), e.getMessage()), e);
}
}
private CloseableHttpResponse executeGetRequest(String uri) throws IOException, HttpClientException, ParseException {
final HttpGet method = new HttpGet(uri);
CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(method);
int status = response.getCode();
if (status >= SC_BAD_REQUEST) {
throw new HttpClientException(uri, status, EntityUtils.toString(response.getEntity()));
}
return response;
}
private void log(String format, Object... arguments) {
buildLog.printf(format, arguments);
buildLog.println();
}
private static class HttpClientException extends Exception {
HttpClientException(String uri, int status, String reason) {
super("request to " + uri + " failed with " + status + " reason " + reason);
}
}
private static class SonarProject {
@JsonProperty("k")
String key;
String getKey() {
return key;
}
void setKey(String key) {
this.key = key;
}
@Override
public String toString() {
return MessageFormat.format("({0}(key = {1}))", this.getClass().getSimpleName(), key);
}
}
private static class SonarProjectRetrievalException extends Exception {
private SonarProjectRetrievalException(String message) {
super(message);
}
private SonarProjectRetrievalException(String message, Throwable cause) {
super(message, cause);
}
}
private static class SonarCoverageMeasureRetrievalException extends Exception {
private SonarCoverageMeasureRetrievalException(String message, Throwable cause) {
super(message, cause);
}
}
}