Skip to content

Commit

Permalink
Merge pull request quarkusio#35073 from phillip-kruger/dev-ui-flyway
Browse files Browse the repository at this point in the history
Dev UI: Migrate Flyway extension
  • Loading branch information
phillip-kruger authored Jul 31, 2023
2 parents 66c7e66 + d68896a commit 773b8c1
Show file tree
Hide file tree
Showing 11 changed files with 507 additions and 262 deletions.
5 changes: 5 additions & 0 deletions extensions/flyway/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-dev-ui-tests</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.quarkus.flyway.devui;

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

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import io.quarkus.agroal.spi.JdbcInitialSQLGeneratorBuildItem;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
import io.quarkus.devui.spi.page.Page;
import io.quarkus.flyway.runtime.FlywayBuildTimeConfig;
import io.quarkus.flyway.runtime.devui.FlywayDevUIRecorder;
import io.quarkus.flyway.runtime.devui.FlywayJsonRpcService;

public class FlywayDevUIProcessor {

@BuildStep(onlyIf = IsDevelopment.class)
@Record(value = RUNTIME_INIT, optional = true)
CardPageBuildItem create(FlywayDevUIRecorder recorder, FlywayBuildTimeConfig buildTimeConfig,
List<JdbcInitialSQLGeneratorBuildItem> generatorBuildItem,
CurateOutcomeBuildItem curateOutcomeBuildItem) {

Map<String, Supplier<String>> initialSqlSuppliers = new HashMap<>();
for (JdbcInitialSQLGeneratorBuildItem buildItem : generatorBuildItem) {
initialSqlSuppliers.put(buildItem.getDatabaseName(), buildItem.getSqlSupplier());
}

String artifactId = curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getArtifactId();

recorder.setInitialSqlSuppliers(initialSqlSuppliers, artifactId);

CardPageBuildItem card = new CardPageBuildItem();

card.addPage(Page.webComponentPageBuilder()
.componentLink("qwc-flyway-datasources.js")
.dynamicLabelJsonRPCMethodName("getNumberOfDatasources")
.icon("font-awesome-solid:database"));
return card;
}

@BuildStep(onlyIf = IsDevelopment.class)
JsonRPCProvidersBuildItem registerJsonRpcBackend() {
return new JsonRPCProvidersBuildItem(FlywayJsonRpcService.class);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { QwcHotReloadElement, html, css} from 'qwc-hot-reload-element';
import { JsonRpc } from 'jsonrpc';
import '@vaadin/icon';
import '@vaadin/button';
import '@vaadin/text-field';
import '@vaadin/text-area';
import '@vaadin/form-layout';
import '@vaadin/progress-bar';
import '@vaadin/checkbox';
import '@vaadin/grid';
import 'qui-alert';
import { columnBodyRenderer } from '@vaadin/grid/lit.js';
import { dialogRenderer } from '@vaadin/dialog/lit.js';
import '@vaadin/grid/vaadin-grid-sort-column.js';
import '@vaadin/progress-bar';
import { notifier } from 'notifier';

export class QwcFlywayDatasources extends QwcHotReloadElement {

jsonRpc = new JsonRpc(this);

static styles = css`
.button {
cursor: pointer;
}
.clearIcon {
color: var(--lumo-warning-text-color);
}`;

static properties = {
_ds: {state: true},
_selectedDs: {state: true},
_createDialogOpened: {state: true}
}

constructor() {
super();
this._ds = null;
this._selectedDs = null;
this._createDialogOpened = false;
}

connectedCallback() {
super.connectedCallback();
this.hotReload();
}

hotReload(){
this.jsonRpc.getDatasources().then(jsonRpcResponse => {
this._ds = jsonRpcResponse.result;
});
}

render() {
if (this._ds) {
return this._renderDataSourceTable();
} else {
return html`<vaadin-progress-bar class="progress" indeterminate></vaadin-progress-bar>`;
}
}

_renderDataSourceTable() {
return html`${this._renderCreateDialog()}
<vaadin-grid .items="${this._ds}" class="datatable" theme="no-border">
<vaadin-grid-column auto-width
header="Name"
${columnBodyRenderer(this._nameRenderer, [])}>
</vaadin-grid-column>
<vaadin-grid-column auto-width
header="Action"
${columnBodyRenderer(this._actionRenderer, [])}
resizable>
</vaadin-grid-column>
</vaadin-grid>`;
}

_actionRenderer(ds) {
return html`${this._renderMigrationButtons(ds)}
${this._renderCreateButton(ds)}`;
}

_renderMigrationButtons(ds) {
if(ds.hasMigrations){
return html`
<vaadin-button theme="small" @click=${() => this._clean(ds)} class="button">
<vaadin-icon class="clearIcon" icon="font-awesome-solid:broom"></vaadin-icon> Clean
</vaadin-button>
<vaadin-button theme="small" @click=${() => this._migrate(ds)} class="button">
<vaadin-icon icon="font-awesome-solid:arrow-right-arrow-left"></vaadin-icon> Migrate
</vaadin-button>`;
}
}

_renderCreateButton(ds) {
if(ds.createPossible){
return html`
<vaadin-button theme="small" @click=${() => this._showCreateDialog(ds)} class="button" title="Set up basic files for Flyway migrations to work. Initial file in db/migrations will be created and you can then add additional migration files">
<vaadin-icon icon="font-awesome-solid:plus"></vaadin-icon> Create Initial Migration File
</vaadin-button>`;
}
}

_nameRenderer(ds) {
return html`${ds.name}`;
}

_showCreateDialog(ds){
this._selectedDs = ds;
this._createDialogOpened = true;
}

_renderCreateDialog(){
return html`<vaadin-dialog class="createDialog"
header-title="Create"
.opened="${this._createDialogOpened}"
@opened-changed="${(e) => (this._createDialogOpened = e.detail.value)}"
${dialogRenderer(() => this._renderCreateDialogForm(), "Create")}
></vaadin-dialog>`;
}

_renderCreateDialogForm(){
let title = this._selectedDs.name + " Datasource";
return html`<b>${title}</b></br>
Set up an initial file from Hibernate ORM schema generation for Flyway migrations to work.<br/>
If you say yes, an initial file in <code>db/migrations</code> will be <br/>
created and you can then add additional migration files as documented.
${this._renderDialogButtons(this._selectedDs)}
`;
}

_renderDialogButtons(ds){
return html`<div style="display: flex; flex-direction: row-reverse; gap: 10px;">
<vaadin-button theme="secondary" @click=${() => this._create(this._selectedDs)}>Create</vaadin-button>
<vaadin-button theme="secondary error" @click=${this._cancelCreate}>Cancel</vaadin-button>
</div>`;
}

_clean(ds) {
this.jsonRpc.clean({ds: ds.name}).then(jsonRpcResponse => {
this._showResultNotification(jsonRpcResponse.result);
});
}

_migrate(ds) {
this.jsonRpc.migrate({ds: ds.name}).then(jsonRpcResponse => {
this._showResultNotification(jsonRpcResponse.result);
});
}

_create(ds) {
this.jsonRpc.create({ds: ds.name}).then(jsonRpcResponse => {
this._showResultNotification(jsonRpcResponse.result);
this._selectedDs = null;
this._createDialogOpened = false;
this.hotReload();
});
}

_cancelCreate(){
this._selectedDs = null;
this._createDialogOpened = false;
}

_showResultNotification(response){
if(response.type === "success"){
notifier.showInfoMessage(response.message + " (" + response.number + ")");
}else{
notifier.showWarningMessage(response.message);
}
}

}
customElements.define('qwc-flyway-datasources', QwcFlywayDatasources);
Loading

0 comments on commit 773b8c1

Please sign in to comment.