Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@
</actions>

<extensions defaultExtensionNs="com.intellij">
<errorHandler implementation="ro.redeul.google.go.diagnostics.error.ErrorReporter"/>

<toolWindow id="Go" anchor="bottom" icon="/icons/go_13x13.png" secondary="true" factoryClass="ro.redeul.google.go.ide.ui.GoToolWindowFactory" />

<stepsBeforeRunProvider implementation="ro.redeul.google.go.runner.beforeRunTasks.GoVetTaskProvider" />
Expand Down
4 changes: 4 additions & 0 deletions src/ro/redeul/google/go/GoBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,7 @@ warning.function.return.call.types.mismatch=The returned expressions don't match
warning.index.invalid=Invalid index {0} {1}
go.run.executablename=Output e&xecutable name\:
go.run.package=Ma&in package directory\:
go.error.report.message=<html>Error Submitting Feedback: {0}<br>\
Consider creating an issue at \
<a href="https://github.com/go-lang-plugin-org/go-lang-idea-plugin/issues">Github Issue Tracker</a></html>
go.error.report.action=&Report on Github
119 changes: 119 additions & 0 deletions src/ro/redeul/google/go/diagnostics/error/AnonymousFeedback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package ro.redeul.google.go.diagnostics.error;

import com.google.gson.Gson;
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.extensions.PluginId;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;

public class AnonymousFeedback {

public AnonymousFeedback() {
}

public static String sendFeedback(
AnonymousFeedback.HttpConnectionFactory httpConnectionFactory,
LinkedHashMap<String, String> environmentDetails) throws IOException {

sendFeedback(httpConnectionFactory, convertToGithubIssueFormat(environmentDetails));

return Long.toString(System.currentTimeMillis());
}

private static byte[] convertToGithubIssueFormat(LinkedHashMap<String, String> environmentDetails) {
LinkedHashMap<String, String> result = new LinkedHashMap<String, String>(5);
result.put("title", "[auto-generated] Crash in plugin");
result.put("body", generateGithubIssueBody(environmentDetails));

return ((new Gson()).toJson(result)).getBytes(Charset.forName("UTF-8"));
}

private static String generateGithubIssueBody(LinkedHashMap<String, String> body) {
String errorDescription = body.get("error.description");
if (errorDescription == null) {
errorDescription = "";
}
body.remove("error.description");

String errorMessage = body.get("error.message");
if (errorMessage == null || errorMessage.isEmpty()) {
errorMessage = "invalid error";
}
body.remove("error.message");

String stackTrace = body.get("error.stacktrace");
if (stackTrace == null || stackTrace.isEmpty()) {
stackTrace = "invalid stacktrace";
}
body.remove("error.stacktrace");

String result = "";

if (!errorDescription.isEmpty()) {
result += errorDescription + "\n\n";
}

for (Map.Entry<String, String> entry : body.entrySet()) {
result += entry.getKey() + ": " + entry.getValue() + "\n";
}

result += "\n```\n" + stackTrace + "\n```\n";

result += "\n```\n" + errorMessage + "\n```";

return result;
}

private static void sendFeedback(AnonymousFeedback.HttpConnectionFactory httpConnectionFactory, byte[] payload) throws IOException {
String url = "https://github-intellij-plugin.appspot.com/go-lang-plugin-org/go-lang-idea-plugin/submitError";
String userAgent = "golang IntelliJ IDEA plugin";

IdeaPluginDescriptorImpl pluginDescriptor = (IdeaPluginDescriptorImpl) PluginManager.getPlugin(PluginId.getId("ro.redeul.google.go"));
if (pluginDescriptor != null) {
String name = pluginDescriptor.getName();
String version = pluginDescriptor.getVersion();
userAgent = name + " (" + version + ")";
}

HttpURLConnection httpURLConnection = connect(httpConnectionFactory, url);
httpURLConnection.setDoOutput(true);
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setRequestProperty("User-Agent", userAgent);
httpURLConnection.setRequestProperty("Content-Type", "application/json");
OutputStream outputStream = httpURLConnection.getOutputStream();

try {
outputStream.write(payload);
} finally {
outputStream.close();
}

int responseCode = httpURLConnection.getResponseCode();
if (responseCode != 201) {
throw new RuntimeException("Expected HTTP_CREATED (201), obtained " + responseCode);
}
}

private static HttpURLConnection connect(AnonymousFeedback.HttpConnectionFactory httpConnectionFactory, String url) throws IOException {
HttpURLConnection httpURLConnection = httpConnectionFactory.openHttpConnection(url);
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setReadTimeout(5000);
return httpURLConnection;
}

public static class HttpConnectionFactory {
public HttpConnectionFactory() {
}

protected HttpURLConnection openHttpConnection(String url) throws IOException {
return (HttpURLConnection) ((new URL(url)).openConnection());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ro.redeul.google.go.diagnostics.error;

import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.util.Consumer;
import com.intellij.util.net.HttpConfigurable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.LinkedHashMap;

import static ro.redeul.google.go.diagnostics.error.AnonymousFeedback.sendFeedback;

/**
* Sends crash reports to Github.
* Extensively inspired by the one used in the Android Studio.
* https://android.googlesource.com/platform/tools/adt/idea/+/master/android/src/com/android/tools/idea/diagnostics/error/ErrorReporter.java
* As per answer from here: http://devnet.jetbrains.com/message/5526206;jsessionid=F5422B4AF1AFD05AAF032636E5455E90#5526206
*/
public class AnonymousFeedbackTask extends Task.Backgroundable {
private final Consumer<String> myCallback;
private final Consumer<Exception> myErrorCallback;
private final LinkedHashMap<String, String> myParams;

public AnonymousFeedbackTask(@Nullable Project project,
@NotNull String title,
boolean canBeCancelled,
LinkedHashMap<String, String> params,
final Consumer<String> callback,
final Consumer<Exception> errorCallback) {
super(project, title, canBeCancelled);

myParams = params;
myCallback = callback;
myErrorCallback = errorCallback;
}

@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
try {
String token = sendFeedback(new ProxyHttpConnectionFactory(), myParams);
myCallback.consume(token);
}
catch (Exception e) {
myErrorCallback.consume(e);
}
}

private static class ProxyHttpConnectionFactory extends AnonymousFeedback.HttpConnectionFactory {
@Override
protected HttpURLConnection openHttpConnection(String url) throws IOException {
return HttpConfigurable.getInstance().openHttpConnection(url);
}
}
}
138 changes: 138 additions & 0 deletions src/ro/redeul/google/go/diagnostics/error/ErrorReporter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ro.redeul.google.go.diagnostics.error;

import com.intellij.diagnostic.IdeErrorsDialog;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.diagnostic.ReportMessages;
import com.intellij.errorreport.bean.ErrorBean;
import com.intellij.ide.DataManager;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.idea.IdeaLogger;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.openapi.diagnostic.SubmittedReportInfo;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import ro.redeul.google.go.GoBundle;

import java.awt.*;
import java.util.LinkedHashMap;
import java.util.List;

/**
* Sends crash reports to Github.
* Extensively inspired by the one used in the Android Studio.
* https://android.googlesource.com/platform/tools/adt/idea/+/master/android/src/com/android/tools/idea/diagnostics/error/ErrorReporter.java
* As per answer from here: http://devnet.jetbrains.com/message/5526206;jsessionid=F5422B4AF1AFD05AAF032636E5455E90#5526206
*/
public class ErrorReporter extends ErrorReportSubmitter {
@Override
public String getReportActionText() {
return GoBundle.message("go.error.report.action");
}

@Override
public boolean submit(@NotNull IdeaLoggingEvent[] events, String additionalInfo, @NotNull Component parentComponent, @NotNull Consumer<SubmittedReportInfo> consumer) {
ErrorBean errorBean = new ErrorBean(events[0].getThrowable(), IdeaLogger.ourLastActionId);
return doSubmit(events[0], parentComponent, consumer, errorBean, additionalInfo);
}

private static boolean doSubmit(final IdeaLoggingEvent event,
final Component parentComponent,
final Consumer<SubmittedReportInfo> callback,
final ErrorBean bean,
final String description) {
final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent);

bean.setDescription(description);
bean.setMessage(event.getMessage());

Throwable throwable = event.getThrowable();
if (throwable != null) {
final PluginId pluginId = IdeErrorsDialog.findPluginId(throwable);
if (pluginId != null) {
final IdeaPluginDescriptor ideaPluginDescriptor = PluginManager.getPlugin(pluginId);
if (ideaPluginDescriptor != null && !ideaPluginDescriptor.isBundled()) {
bean.setPluginName(ideaPluginDescriptor.getName());
bean.setPluginVersion(ideaPluginDescriptor.getVersion());
}
}
}

Object data = event.getData();

if (data instanceof LogMessageEx) {
bean.setAttachments(((LogMessageEx)data).getAttachments());
}

LinkedHashMap<String, String> reportValues = IdeaITNProxy
.getKeyValuePairs(bean,
ApplicationManager.getApplication(),
(ApplicationInfoEx)ApplicationInfo.getInstance(),
ApplicationNamesInfo.getInstance());

final Project project = CommonDataKeys.PROJECT.getData(dataContext);

Consumer<String> successCallback = new Consumer<String>() {
@Override
public void consume(String token) {
final SubmittedReportInfo reportInfo = new SubmittedReportInfo(
null, "Issue " + token, SubmittedReportInfo.SubmissionStatus.NEW_ISSUE);
callback.consume(reportInfo);

ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT,
"Submitted",
NotificationType.INFORMATION,
null).setImportant(false).notify(project);
}
};

Consumer<Exception> errorCallback = new Consumer<Exception>() {
@Override
public void consume(Exception e) {
String message = GoBundle.message("go.error.report.message", e.getMessage());
ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT,
message,
NotificationType.ERROR,
NotificationListener.URL_OPENING_LISTENER).setImportant(false).notify(project);
}
};
AnonymousFeedbackTask task =
new AnonymousFeedbackTask(project, "Submitting error report", true, reportValues, successCallback, errorCallback);
if (project == null) {
task.run(new EmptyProgressIndicator());
} else {
ProgressManager.getInstance().run(task);
}
return true;
}
}
Loading