travaux
This commit is contained in:
parent
9ab8902dd6
commit
8d987ee50e
7
.idea/kotlinc.xml
generated
7
.idea/kotlinc.xml
generated
@ -6,11 +6,4 @@
|
||||
<component name="Kotlin2JvmCompilerArguments">
|
||||
<option name="jvmTarget" value="1.8" />
|
||||
</component>
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="apiVersion" value="2.2" />
|
||||
<option name="languageVersion" value="2.2" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.2.0" />
|
||||
</component>
|
||||
</project>
|
||||
63
pom.xml
63
pom.xml
@ -12,7 +12,6 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.version>2.2.0</kotlin.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@ -25,42 +24,6 @@
|
||||
<target>21</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<source>src/main/java</source>
|
||||
<source>target/generated-sources/annotations</source>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<source>src/test/java</source>
|
||||
<source>target/generated-test-sources/test-annotations</source>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<jvmTarget>1.8</jvmTarget>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@ -121,11 +84,18 @@
|
||||
<version>1.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
|
||||
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>3.5.4</version>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.7.8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.20.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@ -165,16 +135,5 @@
|
||||
<version>2.3.230</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -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<File> 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
Migration.of(db).runBaseMigrations();
|
||||
|
||||
Server server = Server.builder().db(db).build();
|
||||
server.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
37
src/main/java/dev/mduchene/JsonBodyHandler.java
Normal file
37
src/main/java/dev/mduchene/JsonBodyHandler.java
Normal file
@ -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<JsonNode> {
|
||||
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<JsonNode> apply(HttpResponse.ResponseInfo responseInfo) {
|
||||
HttpResponse.BodySubscriber<InputStream> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<File> 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 {
|
||||
|
||||
98
src/main/java/dev/mduchene/Server.java
Normal file
98
src/main/java/dev/mduchene/Server.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/test/java/dev/mduchene/BasicHttpCallTest.java
Normal file
12
src/test/java/dev/mduchene/BasicHttpCallTest.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
43
src/test/java/dev/mduchene/BoltsIntegrationTest.java
Normal file
43
src/test/java/dev/mduchene/BoltsIntegrationTest.java
Normal file
@ -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 {}
|
||||
}
|
||||
@ -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<String> 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<String> 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<String> ranMigrations = db.getRanMigrations();
|
||||
//// assertEquals(1, ranMigrations.size());
|
||||
//// assertEquals(migrationName, ranMigrations.get(0));
|
||||
// }
|
||||
}
|
||||
|
||||
22
src/test/java/dev/mduchene/TestUtil.java
Normal file
22
src/test/java/dev/mduchene/TestUtil.java
Normal file
@ -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<JsonNode> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user