diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ReportIssuesProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ReportIssuesProcessor.java new file mode 100644 index 0000000000000..93cc904c281b8 --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ReportIssuesProcessor.java @@ -0,0 +1,28 @@ +package io.quarkus.devui.deployment.menu; + +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.devui.deployment.InternalPageBuildItem; +import io.quarkus.devui.runtime.ReportIssuesJsonRPCService; +import io.quarkus.devui.spi.JsonRPCProvidersBuildItem; +import io.quarkus.devui.spi.page.Page; + +public class ReportIssuesProcessor { + @BuildStep(onlyIf = IsDevelopment.class) + JsonRPCProvidersBuildItem registerJsonRpcService() { + return new JsonRPCProvidersBuildItem("report-issues", ReportIssuesJsonRPCService.class); + } + + @BuildStep(onlyIf = IsDevelopment.class) + InternalPageBuildItem createReportIssuePage() { + InternalPageBuildItem item = new InternalPageBuildItem("Report an Issue", 99); + + item.addPage(Page.webComponentPageBuilder().internal() + .namespace("devui-report-issues") + .title("Report an issue") + .icon("font-awesome-solid:bug") + .componentLink("qwc-report-issues.js")); + + return item; + } +} diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-report-issues.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-report-issues.js new file mode 100644 index 0000000000000..a541bba7c581d --- /dev/null +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-report-issues.js @@ -0,0 +1,44 @@ +import { LitElement, html, css} from 'lit'; +import { JsonRpc } from 'jsonrpc'; +import '@vaadin/button'; + +/** + * This component shows the Report Issues Page + */ +export class QwcReportIssues extends LitElement { + + jsonRpc = new JsonRpc("report-issues", true); + + static styles = css` + .todo { + padding-left: 10px; + height: 100%; + }`; + + constructor() { + super(); + } + + render() { + return html` + + Report a bug in upstream Quarkus + Request a new feature/enhancement in upstream Quarkus + + `; + } + + _reportBug(event){ + event.preventDefault(); + this.jsonRpc.reportBug().then(e => { + window.open(e.result.url, "_blank"); + }); + } + + _reportFeature(event) { + event.preventDefault(); + window.open("https://github.com/quarkusio/quarkus/issues/new?assignees=&labels=kind%2Fenhancement&template=feature_request.yml", "_blank"); + } + +} +customElements.define('qwc-report-issues', QwcReportIssues); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/ReportIssuesJsonRPCService.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/ReportIssuesJsonRPCService.java new file mode 100644 index 0000000000000..0d9836c390253 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/ReportIssuesJsonRPCService.java @@ -0,0 +1,159 @@ +package io.quarkus.devui.runtime; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; + +import jakarta.enterprise.context.Dependent; + +import io.quarkus.logging.Log; +import io.smallrye.common.os.OS; +import io.vertx.core.json.JsonObject; + +@Dependent +public class ReportIssuesJsonRPCService { + + public JsonObject reportBug() { + URLBuilder urlBuilder = new URLBuilder( + "https://github.com/quarkusio/quarkus/issues/new?labels=kind%2Fbug&template=bug_report.yml"); + gatherInfo(urlBuilder); + return new JsonObject( + Map.of("url", urlBuilder.toString())); + } + + String run(String... command) { + Process process = null; + StringBuilder responseBuilder = new StringBuilder(); + try { + ProcessBuilder processBuilder = new ProcessBuilder().command(command); + + process = processBuilder.start(); + try (InputStream inputStream = process.getInputStream()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + responseBuilder.append(line); + } + safeWaitFor(process); + } + } catch (Throwable t) { + safeWaitFor(process); + throw t; + } + } catch (Exception e) { + Log.warn("Error while running command: " + Arrays.toString(command), e); + return ""; + } finally { + if (process != null && process.isAlive()) { + process.destroy(); + } + } + return responseBuilder.toString(); + } + + private static void safeWaitFor(Process process) { + boolean intr = false; + try { + for (;;) + try { + process.waitFor(); + return; + } catch (InterruptedException ex) { + intr = true; + } + } finally { + if (intr) { + Thread.currentThread().interrupt(); + } + } + } + + void gatherInfo(URLBuilder builder) { + builder.addQueryParameter("java_version", System.getProperty("java.version")) + .addQueryParameter("quarkus_version", + Objects.toString(getClass().getPackage().getImplementationVersion(), "999-SNAPSHOT")); + if (OS.WINDOWS.isCurrent()) { + builder.addQueryParameter("uname", run("cmd.exe", "/C", "ver")); + if (isMavenProject()) { + if (Files.exists(Path.of("mvnw.cmd"))) { + builder.addQueryParameter("build_tool", run("./mvnw.cmd", "--version")); + } else { + // Use the system Maven + builder.addQueryParameter("build_tool", run("mvn", "--version")); + } + } else if (isGradleProject()) { + if (Files.exists(Path.of("gradlew.bat"))) { + builder.addQueryParameter("build_tool", run("./gradlew.bat", "--version")); + } else { + // Use the system Gradle + builder.addQueryParameter("build_tool", run("gradle", "--version")); + } + } + } else { + builder.addQueryParameter("uname", run("uname", "-a")); + if (isMavenProject()) { + if (Files.exists(Path.of("mvnw"))) { + builder.addQueryParameter("build_tool", run("./mvnw", "--version")); + } else { + // Use the system Maven + builder.addQueryParameter("build_tool", run("mvn", "--version")); + } + } else if (isGradleProject()) { + if (Files.exists(Path.of("gradlew"))) { + builder.addQueryParameter("build_tool", run("./gradlew", "--version")); + } else { + // Use the system Gradle + builder.addQueryParameter("build_tool", run("gradle", "--version")); + } + } + } + } + + private static boolean isMavenProject() { + return Files.exists(Path.of("pom.xml")); + } + + private static boolean isGradleProject() { + return Files.exists(Path.of("build.gradle")) || Files.exists(Path.of("build.gradle.kts")); + } + + static class URLBuilder { + private final StringBuilder url; + + public URLBuilder(String url) { + this.url = new StringBuilder(url); + } + + public URLBuilder addQueryParameter(String key, String value) { + if (this.url.indexOf("?") == -1) { + this.url.append("?"); + } else { + this.url.append("&"); + } + + this.url.append(encodeToUTF(key).replaceAll("[+]", "%20")).append("=") + .append(encodeToUTF(value.replaceAll(System.lineSeparator(), "%20")).replaceAll("[+]", "%20")); + return this; + } + + static String encodeToUTF(String value) { + try { + return URLEncoder.encode(value, StandardCharsets.UTF_8.displayName()); + } catch (UnsupportedEncodingException var2) { + throw new RuntimeException(var2); + } + } + + public String toString() { + return this.url.toString(); + } + } + +}