Skip to content

Commit

Permalink
Allow adding and removing extensions from Dev UI
Browse files Browse the repository at this point in the history
Signed-off-by: Phillip Kruger <phillip.kruger@gmail.com>
  • Loading branch information
phillip-kruger committed Oct 16, 2024
1 parent 143bd48 commit 2f58b72
Show file tree
Hide file tree
Showing 8 changed files with 744 additions and 26 deletions.
12 changes: 11 additions & 1 deletion extensions/vertx-http/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,17 @@
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devtools-common</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
package io.quarkus.devui.deployment.menu;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.dev.spi.DevModeType;
import io.quarkus.devtools.commands.AddExtensions;
import io.quarkus.devtools.commands.ListCategories;
import io.quarkus.devtools.commands.ListExtensions;
import io.quarkus.devtools.commands.RemoveExtensions;
import io.quarkus.devtools.commands.data.QuarkusCommandOutcome;
import io.quarkus.devtools.messagewriter.MessageWriter;
import io.quarkus.devtools.project.QuarkusProject;
import io.quarkus.devtools.project.QuarkusProjectHelper;
import io.quarkus.devui.deployment.ExtensionsBuildItem;
import io.quarkus.devui.deployment.InternalPageBuildItem;
import io.quarkus.devui.deployment.extension.Extension;
import io.quarkus.devui.deployment.extension.ExtensionGroup;
import io.quarkus.devui.spi.buildtime.BuildTimeActionBuildItem;
import io.quarkus.devui.spi.page.Page;

/**
Expand All @@ -30,11 +51,167 @@ InternalPageBuildItem createExtensionsPages(ExtensionsBuildItem extensionsBuildI

// Page
extensionsPages.addPage(Page.webComponentPageBuilder()
.namespace("devui-extensions")
.namespace(NAMESPACE)
.title("Extensions")
.icon("font-awesome-solid:puzzle-piece")
.componentLink("qwc-extensions.js"));

return extensionsPages;
}
}

@BuildStep(onlyIf = IsDevelopment.class)
void createBuildTimeActions(BuildProducer<BuildTimeActionBuildItem> buildTimeActionProducer,
LaunchModeBuildItem launchModeBuildItem) {

if (launchModeBuildItem.getDevModeType().isPresent()
&& launchModeBuildItem.getDevModeType().get().equals(DevModeType.LOCAL)) {

ObjectMapper objectMapper = new ObjectMapper();

Path projectRoot = Paths.get(System.getProperty("user.dir")).toAbsolutePath();
QuarkusProject quarkusProject = QuarkusProjectHelper.getProject(projectRoot);

BuildTimeActionBuildItem buildTimeActions = new BuildTimeActionBuildItem(NAMESPACE);

getCategories(buildTimeActions, quarkusProject, objectMapper);
getInstallableExtensions(buildTimeActions, quarkusProject, objectMapper);
getInstalledNamespaces(buildTimeActions, quarkusProject, objectMapper);
removeExtension(buildTimeActions, quarkusProject);
addExtension(buildTimeActions, quarkusProject);
buildTimeActionProducer.produce(buildTimeActions);
}
}

private void getCategories(BuildTimeActionBuildItem buildTimeActions, QuarkusProject quarkusProject,
ObjectMapper objectMapper) {
buildTimeActions.addAction(new Object() {
}.getClass().getEnclosingMethod().getName(), ignored -> {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {

QuarkusCommandOutcome outcome = new ListCategories(quarkusProject, MessageWriter.info(printStream))
.format("json")
.execute();

if (outcome.isSuccess()) {
String jsonString = byteArrayOutputStream.toString();
return objectMapper.readTree(jsonString);
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}

private void getInstallableExtensions(BuildTimeActionBuildItem buildTimeActions, QuarkusProject quarkusProject,
ObjectMapper objectMapper) {
buildTimeActions.addAction(new Object() {
}.getClass().getEnclosingMethod().getName(), ignored -> {

try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {

QuarkusCommandOutcome outcome = new ListExtensions(quarkusProject, MessageWriter.info(printStream))
.installed(false)
.all(false)
.format("json")
.execute();

if (outcome.isSuccess()) {
String jsonString = byteArrayOutputStream.toString();
return objectMapper.readTree(jsonString);
}

return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}

private void getInstalledNamespaces(BuildTimeActionBuildItem buildTimeActions, QuarkusProject quarkusProject,
ObjectMapper objectMapper) {
buildTimeActions.addAction(new Object() {
}.getClass().getEnclosingMethod().getName(), ignored -> {

try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {

QuarkusCommandOutcome outcome = new ListExtensions(quarkusProject, MessageWriter.info(printStream))
.installed(true)
.all(false)
.format("json")
.execute();

if (outcome.isSuccess()) {
String jsonString = byteArrayOutputStream.toString();
JsonNode rootArrayNode = objectMapper.readTree(jsonString);
List<String> namespaceList = new ArrayList<>();

if (rootArrayNode.isArray()) {
for (JsonNode jsonNode : rootArrayNode) {
JsonNode artifactNode = jsonNode.path("artifact");
String groupId = artifactNode.path("groupId").asText();
String artifactId = artifactNode.path("artifactId").asText();
namespaceList.add(groupId + "." + artifactId);
}
}
return namespaceList;
}

return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}

private void removeExtension(BuildTimeActionBuildItem buildTimeActions, QuarkusProject quarkusProject) {
buildTimeActions.addAction(new Object() {
}.getClass().getEnclosingMethod().getName(), params -> {
String extensionArtifactId = params.get("extensionArtifactId");

try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {

QuarkusCommandOutcome outcome = new RemoveExtensions(quarkusProject, MessageWriter.info(printStream))
.extensions(Set.of(extensionArtifactId))
.execute();

if (outcome.isSuccess()) {
return true;
} else {
return false;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}

private void addExtension(BuildTimeActionBuildItem buildTimeActions, QuarkusProject quarkusProject) {
buildTimeActions.addAction(new Object() {
}.getClass().getEnclosingMethod().getName(), params -> {
String extensionArtifactId = params.get("extensionArtifactId");

try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {

QuarkusCommandOutcome outcome = new AddExtensions(quarkusProject, MessageWriter.info(printStream))
.extensions(Set.of(extensionArtifactId))
.execute();

if (outcome.isSuccess()) {
return true;
} else {
return false;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}

private static final String NAMESPACE = "devui-extensions";
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,9 @@ export class QwcConfiguration extends observeState(LitElement) {
return html`<vaadin-grid .items="${this._filtered}" style="width: 100%;" class="${className}" theme="row-stripes"
.detailsOpenedItems="${this._detailsOpenedItem}"
@active-item-changed="${(event) => {
const prop = event.detail.value;
this._detailsOpenedItem = prop ? [prop] : [];
}}"
const prop = event.detail.value;
this._detailsOpenedItem = prop ? [prop] : [];
}}"
${gridRowDetailsRenderer(this._descriptionRenderer, [])}
>
<vaadin-grid-sort-column auto-width class="cell" flex-grow="0" path="configPhase" header='Phase'
Expand Down
Loading

0 comments on commit 2f58b72

Please sign in to comment.