diff --git a/src/main/java/org/cryptomator/common/ErrorCode.java b/src/main/java/org/cryptomator/common/ErrorCode.java index 7def1287b..80cea8e00 100644 --- a/src/main/java/org/cryptomator/common/ErrorCode.java +++ b/src/main/java/org/cryptomator/common/ErrorCode.java @@ -32,17 +32,14 @@ public class ErrorCode { this.rootCauseSpecificFrames = rootCauseSpecificFrames; } - // visible for testing - String methodCode() { + public String methodCode() { return format(traceCode(rootCause, LATEST_FRAME)); } - // visible for testing - String rootCauseCode() { + public String rootCauseCode() { return format(traceCode(rootCause, rootCauseSpecificFrames)); } - // visible for testing String throwableCode() { return format(traceCode(throwable, ALL_FRAMES)); } diff --git a/src/main/java/org/cryptomator/ui/common/HttpHelper.java b/src/main/java/org/cryptomator/ui/common/HttpHelper.java new file mode 100644 index 000000000..5ee63098b --- /dev/null +++ b/src/main/java/org/cryptomator/ui/common/HttpHelper.java @@ -0,0 +1,19 @@ +package org.cryptomator.ui.common; + +import com.google.common.io.CharStreams; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; + +public class HttpHelper { + + public static String readBody(HttpResponse response) throws IOException { + try (var in = response.body(); var reader = new InputStreamReader(in, StandardCharsets.UTF_8)) { + return CharStreams.toString(reader); + } + } + +} diff --git a/src/main/java/org/cryptomator/ui/error/ErrorController.java b/src/main/java/org/cryptomator/ui/error/ErrorController.java index d3ac0f60f..00704f852 100644 --- a/src/main/java/org/cryptomator/ui/error/ErrorController.java +++ b/src/main/java/org/cryptomator/ui/error/ErrorController.java @@ -2,13 +2,11 @@ package org.cryptomator.ui.error; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; -import com.google.gson.JsonObject; import org.cryptomator.common.Environment; import org.cryptomator.common.ErrorCode; import org.cryptomator.common.Nullable; import org.cryptomator.ui.common.FxController; -import java.lang.reflect.Type; import javax.inject.Inject; import javax.inject.Named; import javafx.application.Application; @@ -20,32 +18,22 @@ import javafx.scene.Scene; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; +import java.io.InputStreamReader; import java.net.URI; -import java.net.URL; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.Comparator; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; -import com.google.gson.JsonParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.InputStreamReader; -import java.util.Set; - public class ErrorController implements FxController { - private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class); private static final String ERROR_CODES_URL = "https://gist.githubusercontent.com/cryptobot/accba9fb9555e7192271b85606f97230/raw/errorcodes.json"; private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s"; private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s"; @@ -70,100 +58,28 @@ public class ErrorController implements FxController { private final Stage window; private final Environment environment; - private BooleanProperty copiedDetails = new SimpleBooleanProperty(); - private BooleanProperty lookUpSolutionVisibility = new SimpleBooleanProperty(); - private final HttpClient httpClient; - - List errorDiscussionList; + private final BooleanProperty copiedDetails = new SimpleBooleanProperty(); + private final BooleanProperty lookUpSolutionVisibility = new SimpleBooleanProperty(); + private final BooleanProperty isLoadingHttpResponse = new SimpleBooleanProperty(); + private ErrorDiscussion matchingErrorDiscussion; @Inject - ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment) { + ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment, ExecutorService executorService) { this.application = application; this.stackTrace = stackTrace; this.errorCode = errorCode; this.previousScene = previousScene; this.window = window; this.environment = environment; - this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); - - - HttpClient httpClient = HttpClient.newHttpClient(); + isLoadingHttpResponse.set(true); + HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); HttpRequest httpRequest = HttpRequest.newBuilder() .uri(URI.create(ERROR_CODES_URL)) .build(); - - CompletableFuture> future = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()); - - future.thenAcceptAsync(response -> { - int statusCode = response.statusCode(); - if (statusCode == 200) { - String jsonString = response.body(); - //LOG.debug("jpkED - jsonString : " + jsonString); - //System.out.println(jsonString); - - //JsonObject jsonObject = (JsonObject)new JsonParser().parse(jsonString); - - jsonString = "[{\"id\":\"D_kwDOAPryk84ASwC1\",\"upvoteCount\":4,\"title\":\"Error GH1B:GH1B:NJFJ\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2710\",\"answer\":{\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2710#discussioncomment-5191708\",\"upvoteCount\":3},\"comments\":8},{\"id\":\"D_kwDOAPryk84ASw_I\",\"upvoteCount\":1,\"title\":\"Error GH1B:GH1B:NJFJ\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2716\",\"answer\":null,\"comments\":2},{\"id\":\"D_kwDOAPryk84ASxAg\",\"upvoteCount\":1,\"title\":\"Error GH1B:GH1B:NJFJ\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2717\",\"answer\":{\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2717#discussioncomment-5181996\",\"upvoteCount\":2},\"comments\":3},{\"id\":\"D_kwDOAPryk84ASxDK\",\"upvoteCount\":1,\"title\":\"Error GH1B:GH1B:NJFJ\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2718\",\"answer\":null,\"comments\":0},{\"id\":\"D_kwDOAPryk84ATj-U\",\"upvoteCount\":1,\"title\":\"ErrorCode N05M:GEAO:GEAO\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2877\",\"answer\":null,\"comments\":1},{\"id\":\"D_kwDOAPryk84ATl2o\",\"upvoteCount\":1,\"title\":\"Error GH1B:GH1B:NJFJ\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2883\",\"answer\":{\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2883#discussioncomment-5769879\",\"upvoteCount\":1},\"comments\":1},{\"id\":\"D_kwDOAPryk84ATqCM\",\"upvoteCount\":1,\"title\":\"Error Code 4VHF:1S9S:5EOP\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2887\",\"answer\":null,\"comments\":0},{\"id\":\"D_kwDOAPryk84ATqDF\",\"upvoteCount\":1,\"title\":\"Error H1VR:OTAS:OTAS\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2888\",\"answer\":null,\"comments\":0},{\"id\":\"D_kwDOAPryk84ATrPD\",\"upvoteCount\":1,\"title\":\"Error S4DB:IV2H:I3UI\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2891\",\"answer\":{\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2891#discussioncomment-5797329\",\"upvoteCount\":1},\"comments\":1},{\"id\":\"D_kwDOAPryk84ATtJZ\",\"upvoteCount\":1,\"title\":\"Error 3MAT:BDUS:BDUS\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2894\",\"answer\":null,\"comments\":0},{\"id\":\"D_kwDOAPryk84ATuDc\",\"upvoteCount\":1,\"title\":\"Error QPDR:AFGD:AFGD\",\"url\":\"https://github.com/cryptomator/cryptomator/discussions/2895\",\"answer\":null,\"comments\":0}]"; - - Gson gson = new Gson(); - Type listType = new TypeToken>(){}.getType(); - errorDiscussionList = gson.fromJson(jsonString,listType); - - - - loadJsonToErrorDiscussionList(); - - - - - LOG.debug("jpkED - errorDiscussionList loaded | amount:"+errorDiscussionList.size()+""); - - //find exact matching - List newErrorDiscussionList = new ArrayList<>(); - newErrorDiscussionList.addAll(errorDiscussionList); - newErrorDiscussionList.removeIf(errorDiscussion -> !errorDiscussion.getErrorCode().contains(getErrorCode())); - if(newErrorDiscussionList.size()>0){ - errorDiscussionList.clear(); - errorDiscussionList.addAll(newErrorDiscussionList); - LOG.debug("jpkED - exact match | amount:"+errorDiscussionList.size()+""); - - lookUpSolutionVisibility.set(true); - } - else{ - //find method code matching - newErrorDiscussionList.clear(); - newErrorDiscussionList.addAll(errorDiscussionList); - newErrorDiscussionList.removeIf(errorDiscussion -> !errorDiscussion.getErrorCode().contains(getErrorCodeMethodCode())); - if(newErrorDiscussionList.size()>0){ - errorDiscussionList.clear(); - errorDiscussionList.addAll(newErrorDiscussionList); - LOG.debug("jpkED - method match | amount:"+errorDiscussionList.size()+""); - } - else{ - errorDiscussionList.clear(); - LOG.debug("jpkED - nothing found"); - - } - } - - if(errorDiscussionList.size()!=0){ - //answered - newErrorDiscussionList.clear(); - newErrorDiscussionList.addAll(errorDiscussionList); - newErrorDiscussionList.removeIf(errorDiscussion -> errorDiscussion.answer == null); - if(newErrorDiscussionList.size()>0){ - errorDiscussionList.clear(); - errorDiscussionList.addAll(newErrorDiscussionList); - LOG.debug("jpkED - answered | amount:"+errorDiscussionList.size()+""); - } - Collections.sort(errorDiscussionList, this::errorDiscussionComparator); - LOG.debug("jpkED - most upvote | id: " + errorDiscussionList.get(0).id + " | title: " + errorDiscussionList.get(0).title + " | upvoteCount:" + errorDiscussionList.get(0).upvoteCount); - } - } - }).join(); - - + httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream()) + .thenAcceptAsync(this::loadHttpResponse, executorService) + .whenCompleteAsync((r,e) -> isLoadingHttpResponse.set(false), Platform::runLater); } @FXML @@ -180,8 +96,9 @@ public class ErrorController implements FxController { @FXML public void showSolution() { - if(errorDiscussionList.size()!=0) { - application.getHostServices().showDocument(errorDiscussionList.get(0).url); + if(matchingErrorDiscussion != null){ + //TODO: errorDiscussion.url or errorDiscussion.answer.url + application.getHostServices().showDocument(matchingErrorDiscussion.url); } } @@ -215,8 +132,75 @@ public class ErrorController implements FxController { }); } - /* Getter/Setter */ + private void loadHttpResponse(HttpResponse response){ + if (response.statusCode() == 200) { + Map map = new Gson().fromJson( + new InputStreamReader(response.body(),StandardCharsets.UTF_8), + new TypeToken>(){}.getType()); + if(!map.values().stream().filter(this::isPartialMatchFilter).findFirst().isEmpty()) { + lookUpSolutionVisibility.set(true); + Comparator comp = this::compareExactMatch; + matchingErrorDiscussion = map.values().stream().sorted(comp + .thenComparing(this::compareSecondLevelMatch) + .thenComparing(this::compareThirdLevelMatch) + .thenComparing(this::compareIsAnswered) + .thenComparing(this::compareUpvoteCount) + ).findFirst().get(); + } + } + } + + private boolean isPartialMatchFilter(ErrorDiscussion errorDiscussion) { + return errorDiscussion.title.contains(" " +errorCode.methodCode()); + } + + public int compareUpvoteCount(ErrorDiscussion ed1, ErrorDiscussion ed2) { + return Integer.compare(ed2.upvoteCount, ed1.upvoteCount); + } + + public int compareIsAnswered(ErrorDiscussion ed1, ErrorDiscussion ed2) { + if (ed1.answer!=null && ed2.answer==null) { + return -1; + } else if (ed1.answer==null && ed2.answer!=null) { + return 1; + } else { + return 0; + } + } + public int compareExactMatch(ErrorDiscussion ed1, ErrorDiscussion ed2) { + if (ed1.title.contains(getErrorCode()) && !ed2.title.contains(getErrorCode())) { + return -1; + } else if (!ed1.title.contains(getErrorCode()) && ed2.title.contains(getErrorCode())) { + return 1; + } else { + return 0; + } + } + + public int compareSecondLevelMatch(ErrorDiscussion ed1, ErrorDiscussion ed2) { + String value = " " + errorCode.methodCode() + ErrorCode.DELIM + errorCode.rootCauseCode(); + if (ed1.title.contains(value) && !ed2.title.contains(value)) { + return -1; + } else if (!ed1.title.contains(value) && ed2.title.contains(value)) { + return 1; + } else { + return 0; + } + } + + public int compareThirdLevelMatch(ErrorDiscussion ed1, ErrorDiscussion ed2) { + String value = " " + errorCode.methodCode(); + if (ed1.title.contains(value) && !ed2.title.contains(value)) { + return -1; + } else if (!ed1.title.contains(value) && ed2.title.contains(value)) { + return 1; + } else { + return 0; + } + } + + /* Getter/Setter */ public boolean isPreviousScenePresent() { return previousScene != null; } @@ -226,11 +210,7 @@ public class ErrorController implements FxController { } public String getErrorCode() { - //return "GH1B:GH1B:NJFJ"; // 31 exact match - 4 answered - //return "GIJU:E215:E215"; // 1 exact match - 0 answered - //return "6PHE:UG0C:UG0C"; // 0 exact match - 8 method match - 3 answered - //return "0000:0000:0000"; // 0 match - return errorCode.toString(); // 0 exact match - 8 method match - 3 answered + return errorCode.toString(); } public String getDetailText() { @@ -253,56 +233,12 @@ public class ErrorController implements FxController { return lookUpSolutionVisibility.get(); } - public String getDetailTexts() { - return "```\nError Code " + getErrorCode() + "\n" + getStackTrace() + "\n```"; + public BooleanProperty isLoadingHttpResponseProperty() { + return isLoadingHttpResponse; } - private void filterListByMethodCode(){ - List newErrorDiscussionList = new ArrayList<>(); - for (int i = 0; i < errorDiscussionList.size(); i++) { - ErrorDiscussion errorDiscussion = errorDiscussionList.get(i); - if(errorDiscussion.getMethodCode().equals(getErrorCodeMethodCode())){ - newErrorDiscussionList.add(errorDiscussion); - } - } - if(newErrorDiscussionList.size()!=0){ - errorDiscussionList = newErrorDiscussionList; - lookUpSolutionVisibility.set(true); - LOG.debug("jpkED - found matching method code | amount:"+errorDiscussionList.size()+""); - } - else{ - errorDiscussionList = newErrorDiscussionList; // to clear results - LOG.debug("jpkED - no matching method code found"); - } + public boolean getIsLoadingHttpResponse() { + return isLoadingHttpResponse.get(); } - private int errorDiscussionComparator(ErrorDiscussion t, ErrorDiscussion t1) { - return Integer.compare(t1.upvoteCount, t.upvoteCount); - } - - private String getErrorCodeMethodCode(){ - return getErrorCode().substring(0,4); - } - - private void loadJsonToErrorDiscussionList(){ - String errorCodesUrl = "https://gist.githubusercontent.com/cryptobot/accba9fb9555e7192271b85606f97230/raw/errorcodes.json"; - try (InputStream is = new URL(errorCodesUrl).openStream()) { //TODO: HttpClient(){ async request - - JsonObject jsonObject = (JsonObject)new JsonParser().parse(new InputStreamReader(is,"UTF-8")); - - LOG.debug("jpkED - jsonObject | "+jsonObject.toString()); - - Set keys = jsonObject.keySet(); - errorDiscussionList = new ArrayList<>(); - for (int i = 0; i < jsonObject.size(); i++) { - errorDiscussionList.add(new Gson().fromJson(jsonObject.get(keys.stream().toList().get(i)),ErrorDiscussion.class)); - } - LOG.debug("jpkED - errorDiscussionList loaded | amount:"+errorDiscussionList.size()+""); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - - } } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/error/ErrorDiscussion.java b/src/main/java/org/cryptomator/ui/error/ErrorDiscussion.java index c6f568d70..31e271b82 100644 --- a/src/main/java/org/cryptomator/ui/error/ErrorDiscussion.java +++ b/src/main/java/org/cryptomator/ui/error/ErrorDiscussion.java @@ -2,27 +2,11 @@ package org.cryptomator.ui.error; public class ErrorDiscussion { - String id; int upvoteCount; String title; String url; - int comments; Answer answer; - String getUpvoteCount(){return upvoteCount+"";} - - String getErrorCode(){return title.substring(6);} - String getMethodCode(){ - return title.substring(6,10); - } - String getRootCauseCode(){ - return title.substring(11,15); - } - - String getThrowableCode(){ - return title.substring(16,20); - } - class Answer{ private String url; private int upvoteCount; diff --git a/src/main/resources/fxml/error.fxml b/src/main/resources/fxml/error.fxml index 857e6478d..ffac5e639 100644 --- a/src/main/resources/fxml/error.fxml +++ b/src/main/resources/fxml/error.fxml @@ -1,6 +1,7 @@ + @@ -31,26 +32,28 @@ - - -