diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index e8009fd..ee74011 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -6,11 +6,4 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 4e1fc1a..4c42c46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,6 @@
UTF-8
- 2.2.0
@@ -25,42 +24,6 @@
21
-
- org.jetbrains.kotlin
- kotlin-maven-plugin
- ${kotlin.version}
-
-
- compile
- compile
-
- compile
-
-
-
- src/main/java
- target/generated-sources/annotations
-
-
-
-
- test-compile
- test-compile
-
- test-compile
-
-
-
- src/test/java
- target/generated-test-sources/test-annotations
-
-
-
-
-
- 1.8
-
-
org.apache.maven.plugins
maven-compiler-plugin
@@ -121,11 +84,18 @@
1.8.1
-
+
- org.mariadb.jdbc
- mariadb-java-client
- 3.5.4
+ org.postgresql
+ postgresql
+ 42.7.8
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.20.0
@@ -165,16 +135,5 @@
2.3.230
test
-
- org.jetbrains.kotlin
- kotlin-stdlib-jdk8
- ${kotlin.version}
-
-
- org.jetbrains.kotlin
- kotlin-test
- ${kotlin.version}
- test
-
diff --git a/src/main/java/dev/mduchene/App.java b/src/main/java/dev/mduchene/App.java
index 1cfc5f1..d3748d7 100644
--- a/src/main/java/dev/mduchene/App.java
+++ b/src/main/java/dev/mduchene/App.java
@@ -1,103 +1,20 @@
package dev.mduchene;
-import io.javalin.Javalin;
-import org.apache.commons.dbutils.DbUtils;
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.commons.dbutils.handlers.ScalarHandler;
-import org.apache.commons.io.FileUtils;
-import org.jooq.impl.DSL;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.time.LocalDateTime;
-import java.util.List;
-
-/** Hello world! */
public class App {
public static void main(String[] args) {
System.setProperty("org.jooq.no-tips", "true");
System.setProperty("org.jooq.no-logo", "true");
Db db =
- Db.Builder.create()
- .url("jdbc:mariadb://localhost:3306/bolts")
+ Db.builder()
+ .url("jdbc:postgresql://127.0.0.1:5432/bolts")
.user("root")
.password("root")
.build();
- db.init();
- List pendingBaseMigrations = Migration.of(db).getPendingBaseMigrations();
- for (File file : pendingBaseMigrations) {
- LoggerFactory.getLogger(App.class).info("Running migration: {}", file.getName());
- try (Connection connection = db.getConnection()) {
- String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
- db.run(connection, content);
- DSL.using(connection)
- .insertInto(DSL.table("_migrations"))
- .columns(DSL.field("name"), DSL.field("base"), DSL.field("executedAt"))
- .values(
- file.getName(), true, LocalDateTime.now()) // executedAt is null for base migrations
- .execute();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ Migration.of(db).runBaseMigrations();
- var app = Javalin.create(cnf -> {});
- app.before(
- ctx -> {
- Connection connection = db.getConnection();
- ctx.attribute("connection", connection);
- });
- app.after(
- ctx -> {
- Connection connection = ctx.attribute("connection");
- if (connection != null) {
- DbUtils.commitAndCloseQuietly(connection);
- }
- });
-
- app.exception(
- Exception.class,
- (e, ctx) -> {
- LoggerFactory.getLogger(App.class).error("error", e);
-
- Connection connection = ctx.attribute("connection");
- if (connection != null) {
- DbUtils.rollbackAndCloseQuietly(connection);
- }
- ctx.status(500);
- });
-
- app.get(
- "/",
- ctx -> {
- var r = new QueryRunner();
- Integer query =
- r.query(
- (Connection) ctx.attribute("connection"),
- "SELECT 1",
- resultSet -> {
- System.out.println("hello world");
- if (resultSet.next()) return resultSet.getInt(1);
-
- return null;
- });
-
- ctx.result(query.toString());
- });
-
- app.get(
- "/error",
- ctx -> {
- throw new RuntimeException("error");
- });
-
- app.start(3001);
+ Server server = Server.builder().db(db).build();
+ server.start();
}
}
diff --git a/src/main/java/dev/mduchene/Db.java b/src/main/java/dev/mduchene/Db.java
index 4497bff..70ccbd8 100644
--- a/src/main/java/dev/mduchene/Db.java
+++ b/src/main/java/dev/mduchene/Db.java
@@ -2,19 +2,12 @@ package dev.mduchene;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
-import org.jooq.DSLContext;
-import org.jooq.impl.DSL;
-import org.jooq.impl.SQLDataType;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.SQLException;
-import java.util.List;
-
import org.jooq.SQLDialect;
import org.jooq.conf.RenderQuotedNames;
import org.jooq.conf.Settings;
-import org.jooq.SQLDialect;
+import org.jooq.impl.DSL;
public class Db {
private HikariDataSource dataSource;
@@ -22,12 +15,9 @@ public class Db {
private Db() {}
- public void init() {
- try (Connection connection = dataSource.getConnection()) {
- connection.setAutoCommit(true);
- } catch (Exception e) {
- // Ignore if the table already exists
- }
+
+ public static Builder builder() {
+ return new Builder();
}
public static class Builder {
@@ -37,9 +27,6 @@ public class Db {
private Builder() {}
- public static Builder create() {
- return new Builder();
- }
public Builder url(String url) {
this.url = url;
diff --git a/src/main/java/dev/mduchene/JsonBodyHandler.java b/src/main/java/dev/mduchene/JsonBodyHandler.java
new file mode 100644
index 0000000..5b9cacf
--- /dev/null
+++ b/src/main/java/dev/mduchene/JsonBodyHandler.java
@@ -0,0 +1,37 @@
+package dev.mduchene;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.http.HttpResponse;
+import java.util.function.Supplier;
+
+public class JsonBodyHandler implements HttpResponse.BodyHandler {
+ private static JsonBodyHandler instance;
+
+ public static JsonBodyHandler getInstance() {
+ if (instance == null) {
+ instance = new JsonBodyHandler();
+ }
+ return instance;
+ }
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ @Override
+ public HttpResponse.BodySubscriber apply(HttpResponse.ResponseInfo responseInfo) {
+ HttpResponse.BodySubscriber upstream =
+ HttpResponse.BodySubscribers.ofInputStream();
+ return HttpResponse.BodySubscribers.mapping(upstream, this::toNode);
+ }
+
+ public JsonNode toNode(InputStream inputStream) {
+ try (InputStream stream = inputStream) {
+ return objectMapper.readTree(stream);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/dev/mduchene/Migration.java b/src/main/java/dev/mduchene/Migration.java
index e728891..871eb5b 100644
--- a/src/main/java/dev/mduchene/Migration.java
+++ b/src/main/java/dev/mduchene/Migration.java
@@ -1,14 +1,16 @@
package dev.mduchene;
import java.io.File;
+import java.nio.charset.StandardCharsets;
import java.sql.Connection;
-import java.sql.ResultSet;
+import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
+
+import org.apache.commons.io.FileUtils;
import org.jooq.Meta;
-import org.jooq.Record1;
-import org.jooq.SelectConditionStep;
import org.jooq.impl.DSL;
+import org.slf4j.LoggerFactory;
public class Migration {
@@ -30,6 +32,25 @@ public class Migration {
.collect(Collectors.toList());
}
+ public void runBaseMigrations() {
+ List pendingBaseMigrations = getPendingBaseMigrations();
+ for (File file : pendingBaseMigrations) {
+ LoggerFactory.getLogger(Migration.class).info("Running migration: {}", file.getName());
+ try (Connection connection = db.getConnection()) {
+ String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
+ db.run(connection, content);
+ DSL.using(connection)
+ .insertInto(DSL.table("_migrations"))
+ .columns(DSL.field("name"), DSL.field("base"), DSL.field("executedAt"))
+ .values(
+ file.getName(), true, LocalDateTime.now()) // executedAt is null for base migrations
+ .execute();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
public boolean tableExists(String tableName) {
// Get the Meta object, which contains all database metadata
try {
diff --git a/src/main/java/dev/mduchene/Server.java b/src/main/java/dev/mduchene/Server.java
new file mode 100644
index 0000000..d75a1f2
--- /dev/null
+++ b/src/main/java/dev/mduchene/Server.java
@@ -0,0 +1,98 @@
+package dev.mduchene;
+
+import io.javalin.Javalin;
+import org.apache.commons.dbutils.DbUtils;
+import org.apache.commons.dbutils.QueryRunner;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Connection;
+
+public class Server {
+ private Javalin app;
+ private Db db;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public Server start() {
+ app = Javalin.create(cnf -> {});
+ app.before(
+ ctx -> {
+ Connection connection = db.getConnection();
+ ctx.attribute("connection", connection);
+ });
+ app.after(
+ ctx -> {
+ Connection connection = ctx.attribute("connection");
+ if (connection != null) {
+ DbUtils.commitAndCloseQuietly(connection);
+ }
+ });
+
+ app.exception(
+ Exception.class,
+ (e, ctx) -> {
+ LoggerFactory.getLogger(App.class).error("error", e);
+
+ Connection connection = ctx.attribute("connection");
+ if (connection != null) {
+ DbUtils.rollbackAndCloseQuietly(connection);
+ }
+ ctx.status(500);
+ });
+
+ app.get(
+ "/",
+ ctx -> {
+ var r = new QueryRunner();
+ Integer query =
+ r.query(
+ (Connection) ctx.attribute("connection"),
+ "SELECT 1",
+ resultSet -> {
+ System.out.println("hello world");
+ if (resultSet.next()) return resultSet.getInt(1);
+
+ return null;
+ });
+
+ ctx.result(query.toString());
+ });
+
+ app.get(
+ "/error",
+ ctx -> {
+ throw new RuntimeException("error");
+ });
+
+ app.start(3001);
+ return this;
+ }
+
+ public Server stop() {
+ if (app != null) {
+ app.stop();
+ }
+ return this;
+ }
+
+
+
+ public static class Builder {
+ private Db db;
+
+ private Builder() {}
+
+ public Builder db(Db db) {
+ this.db = db;
+ return this;
+ }
+
+ public Server build() {
+ Server server = new Server();
+ server.db = this.db;
+ return server;
+ }
+ }
+}
diff --git a/src/test/java/dev/mduchene/BasicHttpCallTest.java b/src/test/java/dev/mduchene/BasicHttpCallTest.java
new file mode 100644
index 0000000..99ac0b7
--- /dev/null
+++ b/src/test/java/dev/mduchene/BasicHttpCallTest.java
@@ -0,0 +1,12 @@
+package dev.mduchene;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(BoltsIntegrationTest.class)
+public class BasicHttpCallTest {
+ @Test
+ void test() {
+get("http://localhost:7000/health");
+ }
+}
diff --git a/src/test/java/dev/mduchene/BoltsIntegrationTest.java b/src/test/java/dev/mduchene/BoltsIntegrationTest.java
new file mode 100644
index 0000000..4cb742a
--- /dev/null
+++ b/src/test/java/dev/mduchene/BoltsIntegrationTest.java
@@ -0,0 +1,43 @@
+package dev.mduchene;
+
+import org.junit.jupiter.api.extension.*;
+
+import java.sql.Connection;
+
+public class BoltsIntegrationTest
+ implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, AfterAllCallback {
+
+ private Server server;
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ Db db =
+ Db.builder()
+ .url("jdbc:postgresql://127.0.0.1:5432/bolts_test")
+ .user("root")
+ .password("root")
+ .build();
+
+ try (Connection connection = db.getConnection()) {
+ connection.createStatement().execute("DROP SCHEMA IF EXISTS public CASCADE;");
+ connection.createStatement().execute("CREATE SCHEMA public;");
+ }
+
+ Migration.of(db).runBaseMigrations();
+
+ server = Server.builder().db(db).build().start();
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ if (server != null) {
+ server.stop();
+ }
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {}
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {}
+}
diff --git a/src/test/java/dev/mduchene/MigrationTest.java b/src/test/java/dev/mduchene/MigrationTest.java
index 229e0ef..41a95fc 100644
--- a/src/test/java/dev/mduchene/MigrationTest.java
+++ b/src/test/java/dev/mduchene/MigrationTest.java
@@ -1,96 +1,9 @@
package dev.mduchene;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Objects;
-
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.extension.ExtendWith;
+@ExtendWith(BoltsIntegrationTest.class)
public class MigrationTest {
-// private Db db;
-// private Migration migration;
-//
-// @BeforeEach
-// public void setUp() {
-// db = Db.Builder.create()
-// .url("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL")
-// .user("sa")
-// .password("")
-// .build();
-// db.init();
-// migration = Migration.of(db);
-// }
-//
-// @AfterEach
-// public void tearDown() throws IOException {
-// // Clean up the created migration files
-// File migrationsDir = new File("src/main/resources/migrations");
-// for (File file : Objects.requireNonNull(migrationsDir.listFiles())) {
-// if (!file.getName().equals(".gitkeep")) {
-// Files.delete(file.toPath());
-// }
-// }
-// db.run("DROP ALL OBJECTS");
-// }
-//
-//
-//
-// @Test
-// public void testCreateMigration() {
-// migration.create("test_migration");
-// File migrationsDir = new File("src/main/resources/migrations");
-// assertEquals(1, Objects.requireNonNull(migrationsDir.listFiles()).length);
-// assertTrue(Objects.requireNonNull(migrationsDir.listFiles())[0].getName().contains("_test_migration.sql"));
-// }
-//
-// @Test
-// public void testGetPendingMigrations() throws IOException {
-// // Create a dummy migration file
-// Path newFile = Paths.get("src/main/resources/migrations/20230101000000_initial.sql");
-// Files.createFile(newFile);
-//
-// List pending = migration.getPending();
-// assertEquals(1, pending.size());
-// assertEquals("20230101000000_initial.sql", pending.get(0));
-//
-// // Run the migration
-// db.run("INSERT INTO _migrations (name) VALUES ('20230101000000_initial.sql')");
-//
-// pending = migration.getPending();
-// assertEquals(0, pending.size());
-// }
-//
-// @Test
-// public void testRunMigrations() throws IOException {
-// // Create a dummy migration file with some SQL
-// String migrationName = "20230101000001_create_users_table.sql";
-// Path newFile = Paths.get("src/main/resources/migrations/" + migrationName);
-// Files.write(newFile, "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));".getBytes());
-//
-// // Run pending migrations
-// List pending = migration.getPending();
-// migration.run(pending);
-//
-// // Check if the table was created
-// try {
-// db.run("SELECT * FROM users");
-// } catch (Exception e) {
-// fail("Table 'users' should have been created by the migration.");
-// }
-//
-// // Check if the migration was recorded in the _migrations table
-//// List ranMigrations = db.getRanMigrations();
-//// assertEquals(1, ranMigrations.size());
-//// assertEquals(migrationName, ranMigrations.get(0));
-// }
}
diff --git a/src/test/java/dev/mduchene/TestUtil.java b/src/test/java/dev/mduchene/TestUtil.java
new file mode 100644
index 0000000..b596742
--- /dev/null
+++ b/src/test/java/dev/mduchene/TestUtil.java
@@ -0,0 +1,22 @@
+package dev.mduchene;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+public class TestUtil {
+ public static HttpResponse get(String url) {
+ UriBuilder.
+ HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET().build();
+
+ try (var client = HttpClient.newHttpClient()) {
+ return client.send(request, JsonBodyHandler.getInstance());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}