diff --git a/.github/ISSUE_TEMPLATE/error_report.yml b/.github/ISSUE_TEMPLATE/error_report.yml new file mode 100644 index 000000000..ffd2d96fd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/error_report.yml @@ -0,0 +1,47 @@ +name: Quarkus Tools Plugin Error Report +description: "File a bug report for Quarkus Tool Plugin for IntelliJ" +title: "bug: " +labels: + - bug +projects: + - redhat-developer/37 +body: + - type: markdown + attributes: + value: | + Thanks for reporting this issue!! Please include as much information as possible in the Bug Description + **Please make sure to search for the title of this report** before submitting a new issue to avoid duplicates. + Tip: You can attach images, videos, or log files by dragging it into the text box. + - id: report + type: textarea + attributes: + label: Bug Description + description: Please describe the error. Include steps for reproducing the behavior and actual results. + value: | + ### Steps to reproduce + 1. + 1. + + ### Actual results + Actual result + + ### Screenshots or screencast + Please attach screenshots or a screencast that helps to understand the issue. + Make sure these screenshots or screencast have no sensitive information. + validations: + required: false + - id: info + type: textarea + attributes: + label: IDE Information + description: This information is automatically gathered. + validations: + required: true + - id: stacktrace + type: textarea + attributes: + label: Stacktrace and user message + description: | + This information is automatically gathered from IntelliJ Error Report Screen. + validations: + required: true \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/error/QuarkusPluginIssueReporter.java b/src/main/java/com/redhat/devtools/intellij/quarkus/error/QuarkusPluginIssueReporter.java new file mode 100644 index 000000000..84e680124 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/error/QuarkusPluginIssueReporter.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.error; + +import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.application.ApplicationInfo; +import com.intellij.openapi.diagnostic.ErrorReportSubmitter; +import com.intellij.openapi.diagnostic.IdeaLoggingEvent; +import com.intellij.openapi.diagnostic.SubmittedReportInfo; +import com.intellij.openapi.extensions.PluginDescriptor; +import com.intellij.openapi.util.NlsActions; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.util.Consumer; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.Component; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +public class QuarkusPluginIssueReporter extends ErrorReportSubmitter { + + private static final String GITHUB_ISSUE_BASE_URL = + "https://github.com/redhat-developer/intellij-quarkus/issues/new?template=error_report.yml"; + + private static final String SYSTEM_INFO_TEMPLATE = """ + | Attribute | Value | + |----------------------------|-------| + | **OS** | %s | + | **IDE** | %s | + | **JDK** | %s | + | **Quarkus Tools Version** | %s | + """; + + public static final int MAX_URL_LENGTH = 8191; + + final String ideVersion; + String pluginVersion; + final String operatingSystem; + final String jdkVersion; + + public QuarkusPluginIssueReporter(){ + ApplicationInfo applicationInfo = ApplicationInfo.getInstance(); + operatingSystem = SystemInfo.getOsNameAndVersion() + "-" + SystemInfo.OS_ARCH; + ideVersion = String.join(" ", applicationInfo.getVersionName(), + applicationInfo.getFullVersion(), applicationInfo.getBuild().asString() ); + jdkVersion = String.join(" ", System.getProperty("java.vm.name"), + SystemInfo.JAVA_VERSION, SystemInfo.JAVA_RUNTIME_VERSION, SystemInfo.JAVA_VENDOR ); + } + + @Override + public @NlsActions.ActionText @NotNull String getReportActionText() { + return "Create GitHub Issue"; + } + + @Override + public boolean submit(IdeaLoggingEvent @NotNull [] events, + @Nullable String userMessage, + @NotNull Component parentComponent, + @NotNull Consumer consumer) { + try { + IdeaLoggingEvent event = events[0]; + String url = generateUrl(event.getThrowableText(), userMessage); + BrowserUtil.browse(url); + consumer.consume(new SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.NEW_ISSUE)); + } catch (Exception e) { + consumer.consume(new SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.FAILED)); + return false; + } + return true; + } + + String generateUrl(String throwableText, @Nullable String userMessage) { + PluginDescriptor pluginDescriptor = getPluginDescriptor(); + pluginVersion = pluginDescriptor!=null? pluginDescriptor.getVersion() : "Unknown"; + String systemInfo = encode(formatSystemInfo()); + String titleLine = encode(throwableText.lines() + .findFirst().orElse("Unable to get exception message")); + String url = GITHUB_ISSUE_BASE_URL +"&title=" + titleLine + "&info=" + systemInfo + "&stacktrace="; + int available = MAX_URL_LENGTH - url.length(); + String stackTrace = truncateStacktrace(throwableText, userMessage, available); + return url + stackTrace; + } + + private String truncateStacktrace(String throwableText, @Nullable String userMessage, int available) { + String stackTrace = encode(formatStacktrace(throwableText, + Optional.ofNullable(userMessage).orElse("User didn't provide any message"))); + if (stackTrace.length() > available) { + stackTrace = stackTrace.substring(0, available-1); + } + return stackTrace; + } + + private String formatSystemInfo() { + return SYSTEM_INFO_TEMPLATE.formatted(operatingSystem, ideVersion, jdkVersion, pluginVersion); + } + + private String formatStacktrace(String stackTrace, String userMessage) { + return """ + **User message**: *%s* + **Stacktrace:** + ``` + %s + ``` + """.formatted(userMessage, stackTrace); + } + + private String encode(final String text) { + return URLEncoder.encode(text, StandardCharsets.UTF_8); + } + +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index fe981e505..4c4ee8e14 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -472,6 +472,7 @@ + diff --git a/src/test/java/com/redhat/devtools/intellij/quarkus/error/QuarkusPluginIssueReporterTest.java b/src/test/java/com/redhat/devtools/intellij/quarkus/error/QuarkusPluginIssueReporterTest.java new file mode 100644 index 000000000..4063ccb64 --- /dev/null +++ b/src/test/java/com/redhat/devtools/intellij/quarkus/error/QuarkusPluginIssueReporterTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.error; + +import com.intellij.testFramework.fixtures.BasePlatformTestCase; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static com.redhat.devtools.intellij.quarkus.error.QuarkusPluginIssueReporter.MAX_URL_LENGTH; +import static org.assertj.core.api.Assertions.assertThat; + +public class QuarkusPluginIssueReporterTest extends BasePlatformTestCase { + private QuarkusPluginIssueReporter reporter; + + @Override + protected void setUp() throws Exception { + super.setUp(); + reporter = new QuarkusPluginIssueReporter(); + } + + private static final String STACKTRACE = + """ + java.lang.NullPointerException: Cannot invoke "javax.swing.JCheckBox.setSelected(boolean)" because "platformCheckbox" is null + at com.redhat.devtools.intellij.quarkus.projectWizard.QuarkusExtensionsStep.getComponent(QuarkusExtensionsStep.java:150) + at com.intellij.ide.wizard.AbstractWizard.updateStep(AbstractWizard.java:483) + """; + + public void testEncodedUrlWithExceptionAndMessage() { + String userMessage = "I ran into this error with quarkus project wizard"; + String url = URLDecoder.decode(reporter.generateUrl(STACKTRACE, userMessage), StandardCharsets.UTF_8); + assertThat(url).as("Stacktrace not found in decoded url").contains(STACKTRACE); + assertThat(url).as("userMessage not found in decoded url").contains(userMessage); + List systemInfo = List.of(reporter.ideVersion, reporter.jdkVersion, + reporter.operatingSystem, reporter.pluginVersion); + assertThat(systemInfo).allSatisfy(attribute -> assertThat(url).contains(attribute)); + } + + public void testLongStacktraceIsTruncated() { + String stackTrace = STACKTRACE.concat("A".repeat(MAX_URL_LENGTH)); + String url = reporter.generateUrl(stackTrace, "Hallo"); + String decodedUrl = URLDecoder.decode(url, StandardCharsets.UTF_8); + assertThat(decodedUrl).as("Initial Stacktrace not found in decoded url").contains(STACKTRACE); + assertThat(url.length()).isLessThan(MAX_URL_LENGTH); + } +}