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