1 | package com.jsql.util; | |
2 | ||
3 | import com.jsql.model.InjectionModel; | |
4 | import org.apache.commons.lang3.SystemUtils; | |
5 | import org.apache.commons.lang3.exception.ExceptionUtils; | |
6 | import org.apache.logging.log4j.LogManager; | |
7 | import org.apache.logging.log4j.Logger; | |
8 | import org.json.JSONException; | |
9 | import org.json.JSONObject; | |
10 | ||
11 | import java.io.IOException; | |
12 | import java.net.URI; | |
13 | import java.net.http.HttpRequest; | |
14 | import java.net.http.HttpRequest.BodyPublishers; | |
15 | import java.net.http.HttpResponse; | |
16 | import java.net.http.HttpResponse.BodyHandlers; | |
17 | import java.time.Duration; | |
18 | ||
19 | /** | |
20 | * Utility class used to connect to GitHub Rest webservices. | |
21 | * It uses jsql-robot profile to post data to GitHub. | |
22 | */ | |
23 | public class GitUtil { | |
24 | | |
25 | /** | |
26 | * Log4j logger sent to view. | |
27 | */ | |
28 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
29 | | |
30 | /** | |
31 | * Application useful information as json object from GitHub repository. | |
32 | * Used to get current development version and community news. | |
33 | */ | |
34 | private JSONObject jsonObject; | |
35 | | |
36 | /** | |
37 | * Define explicit labels to declare method parameters. | |
38 | * Used for code readability only. | |
39 | */ | |
40 | public enum ShowOnConsole { | |
41 | YES, | |
42 | NO | |
43 | } | |
44 | ||
45 | private final InjectionModel injectionModel; | |
46 | | |
47 | public GitUtil(InjectionModel injectionModel) { | |
48 | this.injectionModel = injectionModel; | |
49 | } | |
50 | ||
51 | /** | |
52 | * Verify if application is up-to-date against the version on GitHub. | |
53 | * @param displayUpdateMessage YES for manual update verification, hidden otherwise | |
54 | */ | |
55 | public void checkUpdate(ShowOnConsole displayUpdateMessage) { | |
56 | | |
57 |
1
1. checkUpdate : negated conditional → NO_COVERAGE |
if (displayUpdateMessage == ShowOnConsole.YES) { |
58 | LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("UPDATE_LOADING")); | |
59 | } | |
60 | | |
61 | try { | |
62 | var versionGit = Float.parseFloat(this.getJSONObject().getString("version")); | |
63 | | |
64 |
2
1. checkUpdate : changed conditional boundary → NO_COVERAGE 2. checkUpdate : negated conditional → NO_COVERAGE |
if (versionGit > Float.parseFloat(this.injectionModel.getVersionJsql())) { |
65 | LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("UPDATE_NEW_VERSION")); | |
66 |
1
1. checkUpdate : negated conditional → NO_COVERAGE |
} else if (displayUpdateMessage == ShowOnConsole.YES) { |
67 | LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, () -> I18nUtil.valueByKey("UPDATE_UPTODATE")); | |
68 | } | |
69 | } catch (NumberFormatException | JSONException e) { | |
70 | LOGGER.log(LogLevelUtil.CONSOLE_ERROR, I18nUtil.valueByKey("UPDATE_EXCEPTION")); | |
71 | } | |
72 | } | |
73 | | |
74 | /** | |
75 | * Define the body of an issue to send to GitHub for an unhandled exception. | |
76 | * It adds different system data to the body and remove sensible data like | |
77 | * injection URL. | |
78 | * @param threadName name of thread where the exception occurred | |
79 | * @param throwable unhandled exception to report to GitHub | |
80 | */ | |
81 | public void sendUnhandledException(String threadName, Throwable throwable) { | |
82 | | |
83 | var osMetadata = String.join( | |
84 | "\n", | |
85 | String.format( | |
86 | "jSQL: v%s", | |
87 | this.injectionModel.getVersionJsql() | |
88 | ), | |
89 | String.format( | |
90 | "Java: v%s-%s-%s on %s", | |
91 | SystemUtils.JAVA_VERSION, | |
92 | SystemUtils.OS_ARCH, | |
93 | SystemUtils.USER_LANGUAGE, | |
94 | SystemUtils.JAVA_RUNTIME_NAME | |
95 | ), | |
96 | String.format( | |
97 | "OS: %s (v%s)", | |
98 | SystemUtils.OS_NAME, SystemUtils.OS_VERSION | |
99 | ), | |
100 | String.format( | |
101 | "Desktop: %s", | |
102 |
1
1. sendUnhandledException : negated conditional → NO_COVERAGE |
System.getProperty("sun.desktop") != null |
103 | ? System.getProperty("sun.desktop") | |
104 | : "undefined" | |
105 | ), | |
106 | String.format( | |
107 | "Strategy: %s", | |
108 |
1
1. sendUnhandledException : negated conditional → NO_COVERAGE |
this.injectionModel.getMediatorStrategy().getStrategy() != null |
109 | ? this.injectionModel.getMediatorStrategy().getStrategy().getName() | |
110 | : "undefined" | |
111 | ), | |
112 | String.format( | |
113 | "Db engine: %s", | |
114 | this.injectionModel.getMediatorVendor().getVendor().toString() | |
115 | ) | |
116 | ); | |
117 | | |
118 | var exceptionText = String.format( | |
119 | "Exception on %s%n%s%n", | |
120 | threadName, | |
121 | ExceptionUtils.getStackTrace(throwable).trim() | |
122 | ); | |
123 | | |
124 | var clientDescription = String.format( | |
125 | "```yaml%n%s%n```%n```java%n%s```", | |
126 | osMetadata, | |
127 | exceptionText | |
128 | ); | |
129 | | |
130 | clientDescription = clientDescription.replaceAll("(https?://[.a-zA-Z_0-9]*)+", org.apache.commons.lang3.StringUtils.EMPTY); | |
131 | | |
132 |
1
1. sendUnhandledException : removed call to com/jsql/util/GitUtil::sendReport → NO_COVERAGE |
this.sendReport(clientDescription, ShowOnConsole.NO, "Unhandled "+ throwable.getClass().getSimpleName()); |
133 | } | |
134 | | |
135 | /** | |
136 | * Connect to GitHub webservices and create an Issue on the repository. | |
137 | * Used by translation protocol, unhandled exception detection and manual Issue reporting. | |
138 | * @param reportBody text of the Issue | |
139 | * @param showOnConsole in case of manual Issue reporting. Hidden in case of automatic reporting of unhandled exception. | |
140 | * @param reportTitle title of the Issue | |
141 | */ | |
142 | public void sendReport(String reportBody, ShowOnConsole showOnConsole, String reportTitle) { | |
143 | | |
144 |
1
1. sendReport : negated conditional → NO_COVERAGE |
if (this.injectionModel.getMediatorUtils().getProxyUtil().isNotLive(showOnConsole)) { |
145 | return; | |
146 | } | |
147 | ||
148 | var httpRequest = HttpRequest.newBuilder() | |
149 | .uri(URI.create(this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty("github.issues.url"))) | |
150 | .setHeader( | |
151 | "Authorization", | |
152 | "token " | |
153 | + StringUtil.base64Decode( | |
154 | this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty("github.token") | |
155 | ) | |
156 | ) | |
157 | .POST(BodyPublishers.ofString( | |
158 | new JSONObject() | |
159 | .put("title", reportTitle) | |
160 | .put("body", reportBody) | |
161 | .toString() | |
162 | )) | |
163 | .timeout(Duration.ofSeconds(15)) | |
164 | .build(); | |
165 | | |
166 | try { | |
167 | HttpResponse<String> response = this.injectionModel.getMediatorUtils().getConnectionUtil().getHttpClient().send(httpRequest, BodyHandlers.ofString()); | |
168 | | |
169 |
1
1. sendReport : removed call to com/jsql/util/GitUtil::readGithubResponse → NO_COVERAGE |
this.readGithubResponse(response, showOnConsole); |
170 | | |
171 | } catch (InterruptedException | IOException e) { | |
172 | | |
173 |
1
1. sendReport : negated conditional → NO_COVERAGE |
if (showOnConsole == ShowOnConsole.YES) { |
174 | LOGGER.log( | |
175 | LogLevelUtil.CONSOLE_ERROR, | |
176 | String.format("Error during GitHub report connection: %s", e.getMessage()) | |
177 | ); | |
178 | } | |
179 | | |
180 |
1
1. sendReport : negated conditional → NO_COVERAGE |
if (e instanceof InterruptedException) { |
181 |
1
1. sendReport : removed call to java/lang/Thread::interrupt → NO_COVERAGE |
Thread.currentThread().interrupt(); |
182 | } | |
183 | } | |
184 | } | |
185 | | |
186 | private void readGithubResponse(HttpResponse<String> response, ShowOnConsole showOnConsole) throws IOException { | |
187 | try { | |
188 | // Read the response | |
189 | String sourcePage = response.body(); | |
190 | ||
191 |
1
1. readGithubResponse : negated conditional → NO_COVERAGE |
if (showOnConsole == ShowOnConsole.YES) { |
192 | | |
193 | var jsonObjectResponse = new JSONObject(sourcePage); | |
194 | var urlIssue = jsonObjectResponse.getString("html_url"); | |
195 | LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Sent to GitHub: {}", urlIssue); | |
196 | } | |
197 | } catch (Exception e) { | |
198 | throw new IOException("Connection to the GitHub API failed, check your connection or update jSQL"); | |
199 | } | |
200 | } | |
201 | | |
202 | /** | |
203 | * Displays news information on the console from GitHub web service. | |
204 | * Infos concern the general roadmap for the application, current development status | |
205 | * and other useful statements for the community. | |
206 | */ | |
207 | public void showNews() { | |
208 | try { | |
209 | var news = this.getJSONObject().getJSONArray("news"); | |
210 | | |
211 |
2
1. showNews : negated conditional → NO_COVERAGE 2. showNews : changed conditional boundary → NO_COVERAGE |
for (var index = 0 ; index < news.length() ; index++) { |
212 | LOGGER.log(LogLevelUtil.CONSOLE_INFORM, news.get(index)); | |
213 | } | |
214 | } catch (JSONException e) { | |
215 | LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Connection to the GitHub API failed"); | |
216 | } | |
217 | } | |
218 | | |
219 | /** | |
220 | * Instantiate the jsonObject from json data if not already set. | |
221 | * @return jsonObject describing json data | |
222 | */ | |
223 | public JSONObject getJSONObject() { | |
224 | ||
225 |
1
1. getJSONObject : negated conditional → NO_COVERAGE |
if (this.jsonObject == null) { |
226 | | |
227 | String json = this.injectionModel.getMediatorUtils().getConnectionUtil().getSource( | |
228 | this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty("github.webservice.url") | |
229 | ); | |
230 | | |
231 | // Fix #45349: JSONException on new JSONObject(json) | |
232 | try { | |
233 | this.jsonObject = new JSONObject(json); | |
234 | } catch (JSONException e) { | |
235 | | |
236 | try { | |
237 | this.jsonObject = new JSONObject("{\"version\": \"0\", \"news\": []}"); | |
238 | } catch (JSONException eInner) { | |
239 | LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Fetching default JSON failed", eInner); | |
240 | } | |
241 | | |
242 | LOGGER.log( | |
243 | LogLevelUtil.CONSOLE_ERROR, | |
244 | "Fetching configuration from GitHub failed. Wait for service to be available, check your connection or update jSQL" | |
245 | ); | |
246 | } | |
247 | } | |
248 | | |
249 |
1
1. getJSONObject : replaced return value with null for com/jsql/util/GitUtil::getJSONObject → NO_COVERAGE |
return this.jsonObject; |
250 | } | |
251 | } | |
Mutations | ||
57 |
1.1 |
|
64 |
1.1 2.2 |
|
66 |
1.1 |
|
102 |
1.1 |
|
108 |
1.1 |
|
132 |
1.1 |
|
144 |
1.1 |
|
169 |
1.1 |
|
173 |
1.1 |
|
180 |
1.1 |
|
181 |
1.1 |
|
191 |
1.1 |
|
211 |
1.1 2.2 |
|
225 |
1.1 |
|
249 |
1.1 |