Skip to content

Commit

Permalink
Merge pull request #19312 from stuartwdouglas/clean-databases-devui
Browse files Browse the repository at this point in the history
Add ability to clean the DB in DevUI
  • Loading branch information
gsmet authored Aug 18, 2021
2 parents 47c287c + db5eab6 commit 1742e20
Show file tree
Hide file tree
Showing 20 changed files with 460 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.agroal.deployment;

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

import java.sql.Driver;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,4 @@ public DataSourcesExcludedFromHealthChecks get() {
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.quarkus.datasource.deployment.devservices;

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig;
import io.quarkus.datasource.runtime.DatabaseRecorder;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem;

public class DevUIDatasourceProcessor {

@BuildStep
public DevConsoleTemplateInfoBuildItem devConsoleInfo(
DataSourcesBuildTimeConfig dataSourceBuildTimeConfig) {
List<String> names = new ArrayList<>();
names.add("<default>");
names.addAll(dataSourceBuildTimeConfig.namedDataSources.keySet());
Collections.sort(names);
return new DevConsoleTemplateInfoBuildItem("dbs", names);
}

@BuildStep(onlyIf = IsDevelopment.class)
@Record(value = STATIC_INIT, optional = true)
DevConsoleRouteBuildItem devConsoleCleanDatabaseHandler(DatabaseRecorder recorder) {
return new DevConsoleRouteBuildItem("reset", "POST", recorder.devConsoleResetDatabaseHandler());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<a href="{urlbase}/reset" class="badge badge-light">
<i class="fa fa-snowplow fa-fw"></i>
Reset Databases</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{#include main}
{#title}Reset Databases{/title}
{#body}
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">Datasource</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{#for db in info:dbs}
<tr>
<td>
{db}
</td>
<td>
<form method="post" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="name" value="{db}">
<input id="invoke" type="submit" value="Reset" class="btn btn-primary btn-sm">
</form>
</td>
{/for}
</tbody>
</table>
{/body}
{/include}
10 changes: 10 additions & 0 deletions extensions/datasource/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-dev-console-runtime-spi</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.datasource.runtime;

import java.util.ServiceLoader;

import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.RoutingContext;

@Recorder
public class DatabaseRecorder {

public Handler<RoutingContext> devConsoleResetDatabaseHandler() {
// the usual issue of Vert.x hanging on to the first TCCL and setting it on all its threads
final ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
return new DevConsolePostHandler() {

@Override
protected void handlePostAsync(RoutingContext event, MultiMap form) throws Exception {
String name = form.get("name");
ServiceLoader<DatabaseSchemaProvider> dbs = ServiceLoader.load(DatabaseSchemaProvider.class,
Thread.currentThread().getContextClassLoader());
for (DatabaseSchemaProvider i : dbs) {
i.resetDatabase(name);
}
flashMessage(event, "Database successfully reset");
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.datasource.runtime;

/**
* A service interface that can be used to reset the database for dev and test mode.
*/
public interface DatabaseSchemaProvider {

void resetDatabase(String dbName);

void resetAllDatabases();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
artifact: ${project.groupId}:${project.artifactId}:${project.version}
name: "Datasource configuration"
name: "Datasources"
metadata:
keywords:
- "datasource"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.flyway.runtime;

import io.quarkus.datasource.runtime.DatabaseSchemaProvider;

public class FlywaySchemaProvider implements DatabaseSchemaProvider {
@Override
public void resetDatabase(String dbName) {
for (FlywayContainer i : FlywayRecorder.FLYWAY_CONTAINERS) {
if (i.getDataSourceName().equals(dbName)) {
i.getFlyway().clean();
i.getFlyway().migrate();
}
}
}

@Override
public void resetAllDatabases() {
for (FlywayContainer i : FlywayRecorder.FLYWAY_CONTAINERS) {
i.getFlyway().clean();
i.getFlyway().migrate();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.quarkus.flyway.runtime.FlywaySchemaProvider
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
import io.quarkus.hibernate.orm.runtime.devconsole.HibernateOrmDevConsoleIntegrator;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticDescriptor;
import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies;
import io.quarkus.hibernate.orm.runtime.schema.SchemaManagementIntegrator;
import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver;
import io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver;
import io.quarkus.panache.common.deployment.HibernateEnhancersRegisteredBuildItem;
Expand Down Expand Up @@ -459,6 +460,7 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder
}
if (launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT) {
integratorClasses.add(HibernateOrmDevConsoleIntegrator.class);
integratorClasses.add(SchemaManagementIntegrator.class);
}

Map<String, List<HibernateOrmIntegrationStaticDescriptor>> integrationStaticDescriptors = HibernateOrmIntegrationStaticConfiguredBuildItem
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.hibernate.orm;

import static org.hamcrest.Matchers.is;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusDevModeTest;
import io.restassured.RestAssured;

public class HibernateSchemaRecreateDevConsoleTestCase {
@RegisterExtension
final static QuarkusDevModeTest TEST = new QuarkusDevModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(MyEntity.class, MyEntityTestResource.class)
.addAsResource("application.properties")
.addAsResource("import.sql"));

@Test
public void testCleanDatabase() {
RestAssured.when().get("/my-entity/count").then().body(is("2"));
RestAssured.when().get("/my-entity/add").then().body(is("MyEntity:added"));
RestAssured.when().get("/my-entity/count").then().body(is("3"));
RestAssured.with()
.redirects().follow(false).formParam("name", "<default>").post("q/dev/io.quarkus.quarkus-datasource/reset")
.then()
.statusCode(303);
RestAssured.when().get("/my-entity/count").then().body(is("2"));

}

private void assertBodyIs(String expectedBody) {
RestAssured.when().get("/my-entity/2").then().body(is(expectedBody));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
Expand All @@ -25,4 +26,22 @@ public String getName(@PathParam("id") long id) {

return "no entity";
}

@GET
@Path("/add")
@Produces(MediaType.TEXT_PLAIN)
@Transactional
public String add() {
MyEntity entity = new MyEntity();
entity.setName("added");
em.persist(entity);
return entity.toString();
}

@GET
@Path("/count")
@Produces(MediaType.TEXT_PLAIN)
public int count() {
return em.createQuery("from MyEntity").getResultList().size();
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
INSERT INTO MyEntity(id, name) VALUES(1, 'default sql load script entity');
INSERT INTO MyEntity(id, name) VALUES(2, 'import.sql load script entity');
alter sequence myEntitySeq restart with 3;
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeDescriptor;
import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies;
import io.quarkus.hibernate.orm.runtime.schema.SchemaManagementIntegrator;
import io.quarkus.hibernate.orm.runtime.session.ForwardingSession;
import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver;
import io.quarkus.runtime.annotations.Recorder;
Expand Down Expand Up @@ -55,6 +56,12 @@ public void setupPersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRunti
public BeanContainerListener initMetadata(List<QuarkusPersistenceUnitDefinition> parsedPersistenceXmlDescriptors,
Scanner scanner, Collection<Class<? extends Integrator>> additionalIntegrators,
PreGeneratedProxies proxyDefinitions) {
SchemaManagementIntegrator.clearDsMap();
for (QuarkusPersistenceUnitDefinition i : parsedPersistenceXmlDescriptors) {
if (i.getDataSource().isPresent()) {
SchemaManagementIntegrator.mapDatasource(i.getDataSource().get(), i.getName());
}
}
return new BeanContainerListener() {
@Override
public void created(BeanContainer beanContainer) {
Expand Down Expand Up @@ -119,5 +126,4 @@ protected Session delegate() {
}
};
}

}
Loading

0 comments on commit 1742e20

Please sign in to comment.