Skip to content

Commit

Permalink
Get rid of hack based on old DevUI to exchange information between OR…
Browse files Browse the repository at this point in the history
…M and Flyway extensions
  • Loading branch information
yrodiere committed Mar 20, 2023
1 parent e3c14fd commit 50a0589
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,23 @@
package io.quarkus.flyway;

import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
import static java.util.List.of;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import org.eclipse.microprofile.config.ConfigProvider;

import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
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.dev.config.CurrentConfig;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
import io.quarkus.flyway.runtime.FlywayBuildTimeConfig;
import io.quarkus.flyway.runtime.FlywayContainersSupplier;
import io.quarkus.flyway.runtime.FlywayDataSourceBuildTimeConfig;
import io.quarkus.flyway.runtime.devconsole.FlywayDevConsoleRecorder;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.RoutingContext;
import io.quarkus.runtime.configuration.ConfigUtils;

public class FlywayDevConsoleProcessor {

Expand All @@ -42,71 +31,24 @@ public DevConsoleRuntimeTemplateInfoBuildItem collectBeanInfo(
@BuildStep
@Record(value = RUNTIME_INIT, optional = true)
DevConsoleRouteBuildItem invokeEndpoint(FlywayDevConsoleRecorder recorder) {
return new DevConsoleRouteBuildItem("datasources", "POST", recorder.handler());
return new DevConsoleRouteBuildItem("datasources", "POST", recorder.datasourcesHandler());
}

@BuildStep
DevConsoleRouteBuildItem invokeEndpoint(List<JdbcInitialSQLGeneratorBuildItem> generatorBuildItem,
@Record(value = RUNTIME_INIT, optional = true)
DevConsoleRouteBuildItem invokeEndpoint(FlywayDevConsoleRecorder recorder,
List<JdbcInitialSQLGeneratorBuildItem> generatorBuildItem,
FlywayBuildTimeConfig buildTimeConfig,
CurateOutcomeBuildItem curateOutcomeBuildItem) {
return new DevConsoleRouteBuildItem("create-initial-migration", "POST", new DevConsolePostHandler() {
@Override
protected void handlePostAsync(RoutingContext event, MultiMap form) throws Exception {
String name = form.get("datasource");
JdbcInitialSQLGeneratorBuildItem found = null;
for (var i : generatorBuildItem) {
if (i.getDatabaseName().equals(name)) {
found = i;
break;
}
}
if (found == null) {
flashMessage(event, "Unable to find SQL generator");
return;
}
FlywayDataSourceBuildTimeConfig config = buildTimeConfig.getConfigForDataSourceName(name);
if (config.locations.isEmpty()) {
flashMessage(event, "Datasource has no locations configured");
return;
}
System.out.println(found.getSqlSupplier().get());

List<Path> resourcesDir = DevConsoleManager.getHotReplacementContext().getResourcesDir();
if (resourcesDir.isEmpty()) {
flashMessage(event, "No resource directory found");
return;
}

// In the current project only
Path path = resourcesDir.get(0);

Path migrationDir = path.resolve(config.locations.get(0));
Files.createDirectories(migrationDir);
Path file = migrationDir.resolve(
"V1.0.0__" + curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getArtifactId() + ".sql");
Files.writeString(file, found.getSqlSupplier().get());
flashMessage(event, file + " was created");
Map<String, String> newConfig = new HashMap<>();
if (ConfigProvider.getConfig().getOptionalValue("quarkus.flyway.baseline-on-migrate", String.class).isEmpty()) {
newConfig.put("quarkus.flyway.baseline-on-migrate", "true");
}
if (ConfigProvider.getConfig().getOptionalValue("quarkus.flyway.migrate-at-start", String.class).isEmpty()) {
newConfig.put("quarkus.flyway.migrate-at-start", "true");
}
for (var profile : of("test", "dev")) {
if (ConfigProvider.getConfig().getOptionalValue("quarkus.flyway.clean-at-start", String.class).isEmpty()) {
newConfig.put("%" + profile + ".quarkus.flyway.clean-at-start", "true");
}
}
CurrentConfig.EDITOR.accept(newConfig);
//force a scan, to make sure everything is up-to-date
DevConsoleManager.getHotReplacementContext().doScan(true);
flashMessage(event, "Initial migration created, Flyway will now manage this datasource");
event.response().setStatusCode(HttpResponseStatus.SEE_OTHER.code()).headers()
.set(HttpHeaderNames.LOCATION,
event.request().absoluteURI().replace("create-initial-migration", "datasources"));
event.response().end();
}
});
Map<String, Supplier<String>> initialSqlSuppliers = new HashMap<>();
for (JdbcInitialSQLGeneratorBuildItem buildItem : generatorBuildItem) {
initialSqlSuppliers.put(buildItem.getDatabaseName(), buildItem.getSqlSupplier());
}
return new DevConsoleRouteBuildItem("create-initial-migration", "POST",
recorder.createInitialMigrationHandler(buildTimeConfig,
curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getArtifactId(), initialSqlSuppliers,
ConfigUtils.isPropertyPresent("quarkus.flyway.baseline-on-migrate"),
ConfigUtils.isPropertyPresent("quarkus.flyway.migrate-at-start"),
ConfigUtils.isPropertyPresent("quarkus.flyway.clean-at-start")));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
package io.quarkus.flyway.runtime.devconsole;

import static java.util.List.of;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import org.flywaydb.core.Flyway;

import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.quarkus.dev.config.CurrentConfig;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.devconsole.runtime.spi.FlashScopeUtil.FlashMessageStatus;
import io.quarkus.flyway.runtime.FlywayBuildTimeConfig;
import io.quarkus.flyway.runtime.FlywayContainer;
import io.quarkus.flyway.runtime.FlywayContainersSupplier;
import io.quarkus.flyway.runtime.FlywayDataSourceBuildTimeConfig;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
Expand All @@ -16,7 +30,7 @@
@Recorder
public class FlywayDevConsoleRecorder {

public Handler<RoutingContext> handler() {
public Handler<RoutingContext> datasourcesHandler() {
return new DevConsolePostHandler() {
@Override
protected void handlePost(RoutingContext event, MultiMap form) throws Exception {
Expand Down Expand Up @@ -44,4 +58,66 @@ protected void handlePost(RoutingContext event, MultiMap form) throws Exception
}
};
}

public Handler<RoutingContext> createInitialMigrationHandler(FlywayBuildTimeConfig buildTimeConfig,
String artifactId,
Map<String, Supplier<String>> initialSqlSuppliers,
// We can't interrogate these in the recorder because the config actually has defaults at that point
boolean isBaselineOnMigrateConfigured,
boolean isMigrateAtStartConfigured,
boolean isCleanAtStartConfigured) {
return new DevConsolePostHandler() {
@Override
protected void handlePostAsync(RoutingContext event, MultiMap form) throws Exception {
String name = form.get("datasource");
Supplier<String> found = initialSqlSuppliers.get(name);
if (found == null) {
flashMessage(event, "Unable to find SQL generator");
return;
}
FlywayDataSourceBuildTimeConfig config = buildTimeConfig.getConfigForDataSourceName(name);
if (config.locations.isEmpty()) {
flashMessage(event, "Datasource has no locations configured");
return;
}
System.out.println(found.get());

List<Path> resourcesDir = DevConsoleManager.getHotReplacementContext().getResourcesDir();
if (resourcesDir.isEmpty()) {
flashMessage(event, "No resource directory found");
return;
}

// In the current project only
Path path = resourcesDir.get(0);

Path migrationDir = path.resolve(config.locations.get(0));
Files.createDirectories(migrationDir);
Path file = migrationDir.resolve(
"V1.0.0__" + artifactId + ".sql");
Files.writeString(file, found.get());
flashMessage(event, file + " was created");
Map<String, String> newConfig = new HashMap<>();
if (!isBaselineOnMigrateConfigured) {
newConfig.put("quarkus.flyway.baseline-on-migrate", "true");
}
if (!isMigrateAtStartConfigured) {
newConfig.put("quarkus.flyway.migrate-at-start", "true");
}
for (var profile : of("test", "dev")) {
if (!isCleanAtStartConfigured) {
newConfig.put("%" + profile + ".quarkus.flyway.clean-at-start", "true");
}
}
CurrentConfig.EDITOR.accept(newConfig);
//force a scan, to make sure everything is up-to-date
DevConsoleManager.getHotReplacementContext().doScan(true);
flashMessage(event, "Initial migration created, Flyway will now manage this datasource");
event.response().setStatusCode(HttpResponseStatus.SEE_OTHER.code()).headers()
.set(HttpHeaderNames.LOCATION,
event.request().absoluteURI().replace("create-initial-migration", "datasources"));
event.response().end();
}
};
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
package io.quarkus.hibernate.orm.deployment.dev;

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

import io.quarkus.agroal.spi.JdbcInitialSQLGeneratorBuildItem;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
import io.quarkus.hibernate.orm.deployment.HibernateOrmEnabled;
import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem;
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
import io.quarkus.hibernate.orm.runtime.dev.HibernateOrmDevInfoCreateDDLSupplier;
import io.quarkus.hibernate.orm.runtime.dev.HibernateOrmDevInfoSupplier;

@BuildSteps(onlyIf = { HibernateOrmEnabled.class, IsDevelopment.class })
Expand All @@ -28,30 +19,4 @@ public DevConsoleRuntimeTemplateInfoBuildItem exposeInfo(CurateOutcomeBuildItem
curateOutcomeBuildItem);
}

// FIXME migrate to a JsonRpcService
@BuildStep
void handleInitialSql(List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptorBuildItems,
BuildProducer<DevConsoleRuntimeTemplateInfoBuildItem> runtimeInfoProducer,
BuildProducer<JdbcInitialSQLGeneratorBuildItem> initialSQLGeneratorBuildItemBuildProducer,
CurateOutcomeBuildItem curateOutcomeBuildItem) {
for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) {
String puName = puDescriptor.getPersistenceUnitName();
String dsName = puDescriptor.getConfig().getDataSource().orElse(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME);
DevConsoleRuntimeTemplateInfoBuildItem devConsoleRuntimeTemplateInfoBuildItem = new DevConsoleRuntimeTemplateInfoBuildItem(
"create-ddl." + puName, new HibernateOrmDevInfoCreateDDLSupplier(puName), this.getClass(),
curateOutcomeBuildItem);
runtimeInfoProducer.produce(devConsoleRuntimeTemplateInfoBuildItem);
initialSQLGeneratorBuildItemBuildProducer
.produce(new JdbcInitialSQLGeneratorBuildItem(dsName, new Supplier<String>() {
@Override
public String get() {
return DevConsoleManager.getTemplateInfo()
.get(devConsoleRuntimeTemplateInfoBuildItem.getGroupId() + "."
+ devConsoleRuntimeTemplateInfoBuildItem.getArtifactId())
.get(devConsoleRuntimeTemplateInfoBuildItem.getName()).toString();
}
}));
}
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package io.quarkus.hibernate.orm.deployment.dev;

import java.util.List;

import io.quarkus.agroal.spi.JdbcInitialSQLGeneratorBuildItem;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
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.HibernateOrmEnabled;
import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem;
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
import io.quarkus.hibernate.orm.runtime.dev.HibernateOrmDevInfoCreateDDLSupplier;
import io.quarkus.hibernate.orm.runtime.dev.HibernateOrmDevJsonRpcService;

@BuildSteps(onlyIf = { HibernateOrmEnabled.class, IsDevelopment.class })
Expand Down Expand Up @@ -40,4 +47,15 @@ JsonRPCProvidersBuildItem createJsonRPCService() {
return new JsonRPCProvidersBuildItem(NAME, HibernateOrmDevJsonRpcService.class);
}

@BuildStep
void handleInitialSql(List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptorBuildItems,
BuildProducer<JdbcInitialSQLGeneratorBuildItem> initialSQLGeneratorBuildItemBuildProducer) {
for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) {
String puName = puDescriptor.getPersistenceUnitName();
String dsName = puDescriptor.getConfig().getDataSource().orElse(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME);
initialSQLGeneratorBuildItemBuildProducer
.produce(new JdbcInitialSQLGeneratorBuildItem(dsName, new HibernateOrmDevInfoCreateDDLSupplier(puName)));
}
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package io.quarkus.hibernate.orm.runtime.dev;

import io.quarkus.arc.profile.IfBuildProfile;
import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.arc.profile.IfBuildProfile;

@ApplicationScoped
@IfBuildProfile("dev")
public class HibernateOrmDevJsonRpcService {
Expand Down

0 comments on commit 50a0589

Please sign in to comment.