Skip to content

Better error messages #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 9, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
import com.neueda.jetbrains.plugin.graphdb.database.api.query.GraphQueryResultRow;
import com.neueda.jetbrains.plugin.graphdb.database.neo4j.bolt.data.Neo4jBoltQueryResultColumn;
import com.neueda.jetbrains.plugin.graphdb.database.neo4j.bolt.data.Neo4jBoltQueryResultRow;
import com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions.OpenCypherGremlinException;
import com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.query.OpenCypherGremlinQueryResult;
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.Result;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.opencypher.gremlin.client.CypherGremlinClient;

import java.net.URI;
Expand All @@ -24,6 +24,7 @@
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import static com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions.ExceptionWrapper.*;
import static java.util.Collections.*;
import static java.util.stream.Collectors.*;

Expand Down Expand Up @@ -98,7 +99,8 @@ public GraphQueryResult execute(String query, Map<String, Object> statementParam
if (query.toUpperCase().startsWith("EXPLAIN")) {
return new OpenCypherGremlinQueryResult(0, emptyList(), emptyList(), emptyList(), emptyList());
} else {
throw new ClientException(e.getMessage());
String exceptionMessage = wrapExceptionInMeaningMessage(e);
throw new OpenCypherGremlinException(exceptionMessage, e);
}
}
}
Expand Down Expand Up @@ -146,7 +148,8 @@ public GraphMetadata metadata() {

return new OpenCypherGremlinGraphMetadata(labelResult, relResult, vertexPropResult, edgePropResult);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
String exceptionMessage = wrapExceptionInMeaningMessage(e);
throw new OpenCypherGremlinException(exceptionMessage, e);
} finally {
gremlinClient.close();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions;

public enum ExceptionErrorMessages {
ERROR_OCCURRED("Error occurred."),
SYNTAX_WARNING("Please note that Cypher query is translated to Gremlin and may fail" +
" because of translation or database specifics. Make sure that flavor is properly configured" +
" in database connection configuration."),
SERIALIZER_EXCEPTION("Wrong serializer selected. Please check connection configuration."),
RESPONSE_EXCEPTION("Database connection failed. Please check database configuration" +
" (including username and password) and retry to connect."),
CONNECTION_EXCEPTION("Database connection failed. Please check database configuration" +
" (including username and password) and retry to connect.");

private final String description;

ExceptionErrorMessages(String description) {
this.description = description;
}

public String getDescription() {
return description;
}

@Override
public String toString() {
return description;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions;

import java.io.PrintWriter;
import java.io.StringWriter;

public class ExceptionWrapper {
public static final int SHORT_STRING_LENGTH = 150;
private static final String NON_THIN_CHARS = "[^iIl1\\.,']";

private static int textWidth(String str) {
return str.length() - str.replaceAll(NON_THIN_CHARS, "").length() / 2;
}

public static String ellipseString(String text, int targetLength) {
if (textWidth(text) <= targetLength) {
return text;
}
int end = text.lastIndexOf(' ', targetLength - 3);
if (end == -1) {
return text.substring(0, targetLength - 3) + "...";
}
int newEnd = end;
do {
end = newEnd;
newEnd = text.indexOf(' ', end + 1);
if (newEnd == -1) {
newEnd = text.length();
}

} while (textWidth(text.substring(0, newEnd) + "...") < targetLength);

return text.substring(0, end) + "...";
}

public static String getCause(Exception exception) {
StringBuilder exceptionCauses = new StringBuilder();
Throwable cause = exception.getCause();
int counter = 0;
while (cause != null && counter <= 50) {
exceptionCauses.append(cause.getMessage()).append(System.lineSeparator());
cause = cause.getCause();
counter++;
}
return exceptionCauses.toString();
}

public static String getStackTrace(final Throwable throwable) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}

public static String wrapExceptionInMeaningMessage(Exception exception) {
String exceptionMessage = exception.getMessage();
if (exceptionMessage != null) {
if (exceptionMessage.contains("SerializationException")) {
return ExceptionErrorMessages.SERIALIZER_EXCEPTION.getDescription();
}
if (exceptionMessage.contains("ResponseException")) {
return ExceptionErrorMessages.RESPONSE_EXCEPTION.getDescription();
}
if (exceptionMessage.contains("ConnectionException")) {
return ExceptionErrorMessages.CONNECTION_EXCEPTION.getDescription();
}
if (exceptionMessage.length() > SHORT_STRING_LENGTH) {
return ellipseString(exceptionMessage, SHORT_STRING_LENGTH);
}
return exceptionMessage;
} else {
return ExceptionErrorMessages.ERROR_OCCURRED.getDescription();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions;

public class OpenCypherGremlinException extends RuntimeException {
public OpenCypherGremlinException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.neueda.jetbrains.plugin.graphdb.test.integration.opencypher.gremlin;

import com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions.ExceptionWrapper;
import org.junit.Test;

import static org.junit.Assert.*;

public class ExceptionWrapperTest {
@Test
public void shouldEllipseString() throws Exception {
String shortString = "DataSource[Cosmos] - metadata refresh failed. Reason: java.util.concurrent.ExecutionException:" +
"org.apache.tinkerpop.gremlin.driver.exception.ResponseException:...";
String longString = "DataSource[Cosmos] - metadata refresh failed. Reason: java.util.concurrent.ExecutionException:" +
"org.apache.tinkerpop.gremlin.driver.exception.ResponseException: ActivityId : d0015df6-09d2-48a6-b3ab-32d026213a42";
String truncatedString = ExceptionWrapper.ellipseString(longString, ExceptionWrapper.SHORT_STRING_LENGTH);
assertEquals(shortString, truncatedString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public void wrongCredentials() throws Exception {
OpenCypherGremlinDatabase database = new OpenCypherGremlinDatabase(config);

assertThatThrownBy(() -> database.execute("RETURN 1"))
.hasMessageContaining("Username and/or password are incorrect");
.hasMessageContaining("Database connection failed. Please check database configuration" +
" (including username and password) and retry to connect.");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.IconButton;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.ui.JBUI;
import com.neueda.jetbrains.plugin.graphdb.database.api.query.GraphQueryResult;
import com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions.ExceptionErrorMessages;
import com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions.OpenCypherGremlinException;
import com.neueda.jetbrains.plugin.graphdb.jetbrains.actions.execute.ExecuteQueryPayload;
import com.neueda.jetbrains.plugin.graphdb.jetbrains.component.datasource.metadata.DataSourceMetadata;
import com.neueda.jetbrains.plugin.graphdb.jetbrains.component.datasource.state.DataSourceApi;
Expand All @@ -17,12 +24,16 @@
import com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.datasource.metadata.MetadataRetrieveEvent;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.util.Map;

import static com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent.PARAMS_ERROR_COMMON_MSG;
import static com.neueda.jetbrains.plugin.graphdb.database.opencypher.gremlin.exceptions.ExceptionWrapper.*;
import static com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent.*;
import static com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.datasource.interactions.DataSourceDialog.*;

public class LogPanel implements Disposable {
private static final String SHOW_DETAILS = "Details...";

private ConsoleView log;

Expand Down Expand Up @@ -107,7 +118,8 @@ public void metadataRefreshSucceed(DataSourceApi nodeDataSource, DataSourceMetad

@Override
public void metadataRefreshFailed(DataSourceApi nodeDataSource, Exception exception) {
error(String.format("DataSource[%s] - metadata refresh failed. Reason: ", nodeDataSource.getName()));
String prefix = String.format("DataSource[%s] - metadata refresh failed. Reason: ", nodeDataSource.getName());
error(prefix);
printException(exception);
newLine();
}
Expand All @@ -120,19 +132,19 @@ public void metadataRefreshFailed(DataSourceApi nodeDataSource, Exception except
});
}

public void userInput(String message) {
private void userInput(String message) {
log.print(message, ConsoleViewContentType.USER_INPUT);
}

public void printParametersMap(Map<String, Object> parameters) {
private void printParametersMap(Map<String, Object> parameters) {
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
String message = String.format("%s: %s", entry.getKey(), entry.getValue());
log.print(message, ConsoleViewContentType.USER_INPUT);
newLine();
}
}

public void info(String message) {
private void info(String message) {
log.print(message, ConsoleViewContentType.NORMAL_OUTPUT);
}

Expand All @@ -142,26 +154,53 @@ public void error(@Nullable String message) {
}
}

public void printException(Exception exception) {
private String printException(Exception exception) {
String errorMessage;
if (exception.getMessage() != null) {
error(exception.getMessage());
errorMessage = exception.getMessage();
} else {
error(exception.toString());
errorMessage = exception.toString();
}
error(errorMessage);
String newLine = System.lineSeparator();
String details = getCause(exception) + newLine + getStackTrace(exception);
log.printHyperlink(" " + SHOW_DETAILS, p -> showPopup("Error details", details, exception));
newLine();

Throwable cause = exception.getCause();
while (cause != null) {
error(cause.getMessage());
newLine();
cause = cause.getCause();
}
return errorMessage;
}

public void newLine() {
private void newLine() {
log.print("\n", ConsoleViewContentType.NORMAL_OUTPUT);
}

private void showPopup(String title, String details, Exception exception) {
JPanel popupPanel = new JPanel(new BorderLayout());
popupPanel.setBorder(JBUI.Borders.empty(THICKNESS));

JTextArea exceptionDetails = new JTextArea();
exceptionDetails.setLineWrap(false);
exceptionDetails.append(details);
JLabel jLabel = new JLabel(exception.getMessage(), AllIcons.Process.State.RedExcl, JLabel.LEFT);

JBScrollPane scrollPane = new JBScrollPane(exceptionDetails);
scrollPane.setPreferredSize(new Dimension(-1, HEIGHT));
popupPanel.add(jLabel, BorderLayout.NORTH);
popupPanel.add(scrollPane, BorderLayout.CENTER);
String gremlinTranslationWarning = exception instanceof OpenCypherGremlinException ? ExceptionErrorMessages.SYNTAX_WARNING.getDescription() : "";

JBPopupFactory.getInstance()
.createComponentPopupBuilder(
popupPanel,
log.getComponent())
.setTitle(title)
.setAdText(gremlinTranslationWarning)
.setResizable(true)
.setMovable(true)
.setCancelButton(new IconButton("Close", AllIcons.Actions.Close, AllIcons.Actions.CloseHovered))
.createPopup()
.showInFocusCenter();
}

@Override
public void dispose() {
log.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import javax.swing.*;
import java.awt.*;

import static com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent.PARAMS_ERROR_COMMON_MSG;
import static com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent.*;

public class ParametersPanel implements ParametersProvider {

Expand Down
Loading