Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dedicated Dev UI interface to execute HQL (Hibernate ORM) queries #46728

Merged
merged 1 commit into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,10 @@ public DataSet executeSQL(String datasource, String sql, Integer pageNumber, Int
}
}
} else {
return new DataSet(null, null, "Only supported for Local Databases", null, -1);
return new DataSet(null, null,
"Datasource access not allowed. By default only local databases are enabled; you can use the"
+ " 'quarkus.datasource.dev-ui.allowed-db-host' configuration property to configure allowed hosts ('*' to allow all).",
null, -1);
}
} catch (SQLException ex) {
return new DataSet(null, null, ex.getMessage(), null, -1);
Expand Down Expand Up @@ -325,14 +328,20 @@ private List<String> getPrimaryKeys(DatabaseMetaData metaData, String tableSchem
}

private boolean isAllowedDatabase(AgroalDataSource ads) {
final String allowedHost = this.allowedHost == null ? null : this.allowedHost.trim();
if (allowedHost != null && allowedHost.equals("*")) {
// special value indicating to allow any host
return true;
}

AgroalDataSourceConfiguration configuration = ads.getConfiguration();
String jdbcUrl = configuration.connectionPoolConfiguration().connectionFactoryConfiguration().jdbcUrl();

try {
if (jdbcUrl.startsWith("jdbc:h2:mem:") || jdbcUrl.startsWith("jdbc:h2:file:")
|| jdbcUrl.startsWith("jdbc:h2:tcp://localhost")
|| (this.allowedHost != null && !this.allowedHost.isBlank()
&& jdbcUrl.startsWith("jdbc:h2:tcp://" + this.allowedHost))
|| (allowedHost != null && !allowedHost.isBlank()
&& jdbcUrl.startsWith("jdbc:h2:tcp://" + allowedHost))
|| jdbcUrl.startsWith("jdbc:derby:memory:")) {
return true;
}
Expand All @@ -343,7 +352,7 @@ private boolean isAllowedDatabase(AgroalDataSource ads) {
String host = uri.getHost();

return host != null && ((host.equals("localhost") || host.equals("127.0.0.1") || host.equals("::1")) ||
(this.allowedHost != null && !this.allowedHost.isBlank() && host.equalsIgnoreCase(this.allowedHost)));
(allowedHost != null && !allowedHost.isBlank() && host.equalsIgnoreCase(allowedHost)));

} catch (URISyntaxException e) {
Log.warn(e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public interface DevUIBuildTimeConfig {
public Optional<String> appendToDefaultSelect();

/**
* Allowed database host. By default only localhost is allowed. Any provided host here will also be allowed
* Allowed database host. By default, only localhost is allowed. Any provided host here will also be allowed.
* You can use the special value {@code *} to allow any DB host.
*/
public Optional<String> allowedDBHost();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ default Map<String, HibernateOrmConfigPersistenceUnit> namedPersistenceUnits() {
*/
HibernateOrmConfigMetric metrics();

/**
* Dev UI.
*/
@WithDefaults
@WithName("dev-ui")
HibernateOrmConfigDevUI devui();

default boolean isAnyNonPersistenceXmlPropertySet() {
// Do NOT include persistenceXml in here.
return defaultPersistenceUnit().isAnyPropertySet() ||
Expand Down Expand Up @@ -182,4 +189,12 @@ default boolean isAnyPropertySet() {
}
}

@ConfigGroup
interface HibernateOrmConfigDevUI {
/**
* Allow hql queries in the Dev UI page
*/
@WithDefault("false")
boolean allowHql();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
import io.quarkus.devui.spi.page.Page;
import io.quarkus.hibernate.orm.deployment.HibernateOrmConfig;
import io.quarkus.hibernate.orm.deployment.HibernateOrmEnabled;
import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem;
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
Expand All @@ -22,7 +23,7 @@
public class HibernateOrmDevUIProcessor {

@BuildStep
public CardPageBuildItem create() {
public CardPageBuildItem create(HibernateOrmConfig config) {
CardPageBuildItem card = new CardPageBuildItem();
card.addPage(Page.webComponentPageBuilder()
.title("Persistence Units")
Expand All @@ -39,7 +40,11 @@ public CardPageBuildItem create() {
.componentLink("hibernate-orm-named-queries.js")
.icon("font-awesome-solid:circle-question")
.dynamicLabelJsonRPCMethodName("getNumberOfNamedQueries"));

card.addPage(Page.webComponentPageBuilder()
.title("HQL Console")
.componentLink("hibernate-orm-hql-console.js")
.icon("font-awesome-solid:play")
.metadata("allowHql", String.valueOf(config.devui().allowHql())));
return card;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export class HibernateOrmEntityTypesComponent extends QwcHotReloadElement {
}
return html`
<vaadin-grid .items="${pu.managedEntities}" class="datatable" theme="no-border row-stripes">
<vaadin-grid-column auto-width
header="JPA entity name"
path="name">
</vaadin-grid-column>
<vaadin-grid-column auto-width
header="Class name"
path="className">
Expand Down
Loading
Loading