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