Skip to content

Commit

Permalink
Merge pull request #32270 from gsmet/dev-ui-build-steps
Browse files Browse the repository at this point in the history
Dev UI - First version of build steps and build items pages
  • Loading branch information
gsmet authored Apr 4, 2023
2 parents 578da31 + bb8003b commit 231881d
Show file tree
Hide file tree
Showing 12 changed files with 613 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -361,22 +361,19 @@ void createBuildTimeData(BuildProducer<BuildTimeConstBuildItem> buildTimeConstPr
internalBuildTimeData.addBuildTimeData("devServices", devServiceDescriptions);

Page buildSteps = Page.webComponentPageBuilder().internal()
.namespace("devui-build-information")
.namespace("devui-build-metrics")
.title("Build Steps")
.icon("font-awesome-solid:hammer")
.componentLink("qwc-build-steps.js").build();
internalBuildTimeData.addBuildTimeData("buildSteps", "TODO: Build Steps");

Page buildItems = Page.webComponentPageBuilder().internal()
.namespace("devui-build-information")
.namespace("devui-build-metrics")
.title("Build Items")
.icon("font-awesome-solid:trowel")
.componentLink("qwc-build-items.js").build();
internalBuildTimeData.addBuildTimeData("buildItems", "TODO: Build Items");

// Add default menu items
@SuppressWarnings("unchecked")
List<Page> sectionMenu = new ArrayList(
List<Page> sectionMenu = new ArrayList<>(
List.of(extensions, configuration, configurationSourceEditor, continuousTesting, devServices,
buildSteps, buildItems));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.quarkus.devui.deployment.build;

import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem;
import io.quarkus.devui.runtime.build.BuildMetricsDevUIRecorder;
import io.quarkus.devui.runtime.build.BuildMetricsJsonRPCService;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;

@BuildSteps(onlyIf = { IsDevelopment.class })
public class BuildMetricsDevUIProcessor {

@BuildStep
@Record(RUNTIME_INIT)
public void create(BuildMetricsDevUIRecorder recorder,
BuildSystemTargetBuildItem buildSystemTarget) {
recorder.setBuildMetricsPath(buildSystemTarget.getOutputDirectory().resolve("build-metrics.json").toString());
}

@BuildStep
AdditionalBeanBuildItem additionalBeans() {
return AdditionalBeanBuildItem
.builder()
.addBeanClass(BuildMetricsJsonRPCService.class)
.setUnremovable()
.setDefaultScope(DotNames.APPLICATION_SCOPED)
.build();
}

@BuildStep
JsonRPCProvidersBuildItem createJsonRPCService() {
return new JsonRPCProvidersBuildItem("devui-build-metrics", BuildMetricsJsonRPCService.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

@Deprecated(forRemoval = true)
public class BuildMetricsDevConsoleProcessor {

private static final Logger LOG = Logger.getLogger(BuildMetricsDevConsoleProcessor.class.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
import io.quarkus.devui.runtime.config.ConfigJsonRpcService;
import io.quarkus.devui.runtime.config.ConfigJsonRPCService;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.vertx.http.runtime.devmode.ConfigDescription;
import io.quarkus.vertx.http.runtime.devmode.ConfigDescriptionsManager;
Expand Down Expand Up @@ -161,7 +161,7 @@ JsonRPCProvidersBuildItem registerJsonRpcService() {
updateConfig(values);
return null;
});
return new JsonRPCProvidersBuildItem("configuration", ConfigJsonRpcService.class);
return new JsonRPCProvidersBuildItem("configuration", ConfigJsonRPCService.class);
}

private Map<String, String> filterAndApplyProfile(Map<String, String> autoconfig, List<String> configFilter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class JsonRpc {

_extensionName;
_logTraffic;

/**
*
* @param {type} host the component using this.
Expand All @@ -96,7 +96,7 @@ export class JsonRpc {
* Again serviceIdentifier can allow multiple backends
* In the case of cards or logs, the namespace will be passed in as an attribute (as this component is not registered with the router)
* Again serviceIdentifier can allow multiple backends
* @param {type} logTraffic - if traffic should be logged in the Dev UI Log (json-prc log)
* @param {type} logTraffic - if traffic should be logged in the Dev UI Log (json-rpc log)
* @param {type} serviceIdentifier - if needed, a backend service identifier
* @returns {Proxy}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,115 @@
import { LitElement, html, css} from 'lit';
import { buildItems } from 'devui-data';
import { QwcHotReloadElement, html, css} from 'qwc-hot-reload-element';

import { JsonRpc } from 'jsonrpc';
import '@vaadin/grid';
import { columnBodyRenderer } from '@vaadin/grid/lit.js';
import '@vaadin/grid/vaadin-grid-sort-column.js';
import '@vaadin/icon';
import '@vaadin/text-field';
import '@vaadin/vertical-layout';
import '@vaadin/horizontal-layout';

/**
* This component shows the Build Items
*/
export class QwcBuildItems extends LitElement {
export class QwcBuildItems extends QwcHotReloadElement {

jsonRpc = new JsonRpc("devui-build-metrics", true);

static styles = css`
.todo {
padding-left: 10px;
height: 100%;
}`;
.build-items {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
vaadin-grid {
height: 100%;
}
vaadin-grid-sort-column {
color: red;
}
.summary {
margin-bottom: 15px;
}
.datatable {
width: 100%;
}`;

static properties = {
_items: {state: true}
_buildStepsMetrics: { state: true },
_filtered: {state: true, type: Array}
};

constructor() {
super();
this._items = buildItems;
this.hotReload();
}

hotReload(){
this.jsonRpc.getBuildStepsMetrics().then(e => {
this._buildStepsMetrics = e.result;
this._filtered = this._buildStepsMetrics.items;
});
}

render() {
if(this._items){
return html`<div class="todo">${this._items}</div>`;
if (this._buildStepsMetrics && this._filtered) {
return this._render();
}else {
return html`<span>Loading build items...</span>`;
}
}

_match(value, term) {
if (!value) {
return false;
}
return value.toLowerCase().includes(term.toLowerCase());
}

_filter(e) {
const searchTerm = (e.detail.value || '').trim();
if (searchTerm === '') {
this._filtered = this._buildStepsMetrics.items;
return;
}

this._filtered = this._buildStepsMetrics.items.filter((item) => {
return this._match(item.class, searchTerm);
});
}

_render() {
return html`<div class="build-items">
<div class="summary">Produced <strong>${this._buildStepsMetrics.itemsCount}</strong> build items.</div>
<vaadin-text-field
placeholder="Filter"
style="width: 100%;"
@value-changed="${(e) => this._filter(e)}">
<vaadin-icon slot="prefix" icon="font-awesome-solid:filter"></vaadin-icon>
</vaadin-text-field>
<vaadin-grid .items="${this._filtered}" class="datatable" theme="row-stripes">
<vaadin-grid-sort-column resizable
header="Build item"
path="class"
${columnBodyRenderer(this._classRenderer, [])}>
</vaadin-grid-sort-column>
<vaadin-grid-sort-column auto-width resizable flex-grow="0"
header="Count"
path="count">
</vaadin-grid-sort-column>
</vaadin-grid></div>`;
}

_classRenderer(item) {
return html`<code>${item.class}</code>`;
}
}
customElements.define('qwc-build-items', QwcBuildItems);
Original file line number Diff line number Diff line change
@@ -1,29 +1,125 @@
import { LitElement, html, css} from 'lit';
import { buildSteps } from 'devui-data';
import { QwcHotReloadElement, html, css} from 'qwc-hot-reload-element';

import { JsonRpc } from 'jsonrpc';
import '@vaadin/grid';
import { columnBodyRenderer } from '@vaadin/grid/lit.js';
import '@vaadin/grid/vaadin-grid-sort-column.js';
import '@vaadin/icon';
import '@vaadin/text-field';
import '@vaadin/vertical-layout';
import '@vaadin/horizontal-layout';

/**
* This component shows the Build Steps
*/
export class QwcBuildSteps extends LitElement {
export class QwcBuildSteps extends QwcHotReloadElement {

jsonRpc = new JsonRpc("devui-build-metrics", true);

static styles = css`
.todo {
padding-left: 10px;
height: 100%;
}`;
.build-steps {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
vaadin-grid {
height: 100%;
}
vaadin-grid-sort-column {
color: red;
}
.summary {
margin-bottom: 15px;
}
.datatable {
width: 100%;
}`;

static properties = {
_steps: {state: true}
_buildStepsMetrics: { state: true },
_filtered: {state: true, type: Array}
};

constructor() {
super();
this._steps = buildSteps;
this.hotReload();
}

hotReload(){
this.jsonRpc.getBuildStepsMetrics().then(e => {
this._buildStepsMetrics = e.result;
this._filtered = this._buildStepsMetrics.records;
});
}

render() {
if(this._steps){
return html`<div class="todo">${this._steps}</div>`;
if (this._buildStepsMetrics && this._filtered) {
return this._render();
}else {
return html`<span>Loading build steps...</span>`;
}
}

_match(value, term) {
if (!value) {
return false;
}
return value.toLowerCase().includes(term.toLowerCase());
}

_filter(e) {
const searchTerm = (e.detail.value || '').trim();
if (searchTerm === '') {
this._filtered = this._buildStepsMetrics.records;
return;
}

this._filtered = this._buildStepsMetrics.records.filter((record) => {
return this._match(record.stepId, searchTerm);
});
}

_render() {
return html`<div class="build-steps">
<div class="summary">Executed <strong>${this._buildStepsMetrics.records.length}</strong> build steps on <strong>${Object.keys(this._buildStepsMetrics.threadSlotRecords).length}</strong> threads in <strong>${this._buildStepsMetrics.duration} ms</strong>.</div>
<vaadin-text-field
placeholder="Filter"
style="width: 100%;"
@value-changed="${(e) => this._filter(e)}">
<vaadin-icon slot="prefix" icon="font-awesome-solid:filter"></vaadin-icon>
</vaadin-text-field>
<vaadin-grid .items="${this._filtered}" class="datatable" theme="row-stripes">
<vaadin-grid-sort-column resizable
header="Build step"
path="stepId"
${columnBodyRenderer(this._stepIdRenderer, [])}>
</vaadin-grid-sort-column>
<vaadin-grid-sort-column auto-width resizable flex-grow="0"
header="Started"
path="started">
</vaadin-grid-sort-column>
<vaadin-grid-sort-column auto-width resizable flex-grow="0"
header="Duration (ms)"
path="duration">
</vaadin-grid-sort-column>
<vaadin-grid-sort-column auto-width resizable flex-grow="0"
header="Thread"
path="thread">
</vaadin-grid-sort-column>
</vaadin-grid></div>`;
}

_stepIdRenderer(record) {
return html`<code>${record.stepId}</code>`;
}
}
customElements.define('qwc-build-steps', QwcBuildSteps);
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class QwcDevServices extends LitElement {
return html`${items}`;
} else {
return html`<p class="no-dev-services">
<span>You do not have any Dev Services running</span>
<span>You do not have any Dev Services running.</span>
<a href="https://quarkus.io/guides/dev-services" target="_blank">Read more about Dev Services</a>
</p>
`
Expand Down
Loading

0 comments on commit 231881d

Please sign in to comment.