GitUtil.java

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
Location : checkUpdate
Killed by : none
negated conditional → NO_COVERAGE

64

1.1
Location : checkUpdate
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : checkUpdate
Killed by : none
negated conditional → NO_COVERAGE

66

1.1
Location : checkUpdate
Killed by : none
negated conditional → NO_COVERAGE

102

1.1
Location : sendUnhandledException
Killed by : none
negated conditional → NO_COVERAGE

108

1.1
Location : sendUnhandledException
Killed by : none
negated conditional → NO_COVERAGE

132

1.1
Location : sendUnhandledException
Killed by : none
removed call to com/jsql/util/GitUtil::sendReport → NO_COVERAGE

144

1.1
Location : sendReport
Killed by : none
negated conditional → NO_COVERAGE

169

1.1
Location : sendReport
Killed by : none
removed call to com/jsql/util/GitUtil::readGithubResponse → NO_COVERAGE

173

1.1
Location : sendReport
Killed by : none
negated conditional → NO_COVERAGE

180

1.1
Location : sendReport
Killed by : none
negated conditional → NO_COVERAGE

181

1.1
Location : sendReport
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

191

1.1
Location : readGithubResponse
Killed by : none
negated conditional → NO_COVERAGE

211

1.1
Location : showNews
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : showNews
Killed by : none
changed conditional boundary → NO_COVERAGE

225

1.1
Location : getJSONObject
Killed by : none
negated conditional → NO_COVERAGE

249

1.1
Location : getJSONObject
Killed by : none
replaced return value with null for com/jsql/util/GitUtil::getJSONObject → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.16.1