diff --git a/.github/workflows/test-master.yml b/.github/workflows/test-master.yml
index 06dcbc3..2a76840 100644
--- a/.github/workflows/test-master.yml
+++ b/.github/workflows/test-master.yml
@@ -23,4 +23,4 @@ jobs:
run: "./gradlew testClasses"
- name: Test
- run: "./gradlew test"
+ run: "./gradlew test jacocoTestReport --no-parallel"
diff --git a/.github/workflows/test-pr.yml b/.github/workflows/test-pr.yml
index 717277f..1bf0de3 100644
--- a/.github/workflows/test-pr.yml
+++ b/.github/workflows/test-pr.yml
@@ -34,7 +34,7 @@ jobs:
run: "./gradlew testClasses"
- name: Test
- run: "./gradlew test jacocoTestReport"
+ run: "./gradlew test jacocoTestReport --no-parallel"
- name: Test Report
if: matrix.java == '17'
diff --git a/build.gradle b/build.gradle
index 0726a35..d6ad895 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,6 @@
plugins {
id "com.diffplug.spotless" version "6.19.0"
+ id "org.graalvm.buildtools.native" version "0.10.1" // or dependsOn fails in graalvm examples
id "com.asarkar.gradle.build-time-tracker" version "4.3.0"
}
@@ -9,6 +10,7 @@ version = koraVersion
repositories {
mavenLocal()
mavenCentral()
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
subprojects {
@@ -22,6 +24,7 @@ subprojects {
repositories {
mavenLocal()
mavenCentral()
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
spotless {
@@ -35,7 +38,7 @@ subprojects {
}
configurations {
- all {
+ configureEach {
resolutionStrategy {
cacheChangingModulesFor 0, "seconds" // check for updates every build
}
diff --git a/gradle.properties b/gradle.properties
index fdec7f2..2eecb93 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
groupId=ru.tinkoff.kora
-koraVersion=1.0.9
+koraVersion=1.1.1
##### GRADLE #####
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 943f0cb..d64cd49 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 17655d0..a80b22c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 65dcd68..1aa94a4 100755
--- a/gradlew
+++ b/gradlew
@@ -83,10 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
@@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done
fi
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/gradlew.bat b/gradlew.bat
index 93e3f59..25da30d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
diff --git a/kora-java-cache-caffeine/build.gradle b/kora-java-cache-caffeine/build.gradle
index d23a0e8..c455d8f 100644
--- a/kora-java-cache-caffeine/build.gradle
+++ b/kora-java-cache-caffeine/build.gradle
@@ -68,13 +68,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-cache-caffeine/src/main/resources/application.conf b/kora-java-cache-caffeine/src/main/resources/application.conf
index 1cb02f8..81540ee 100644
--- a/kora-java-cache-caffeine/src/main/resources/application.conf
+++ b/kora-java-cache-caffeine/src/main/resources/application.conf
@@ -5,6 +5,6 @@ my-cache {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-cache-caffeine/src/main/resources/logback.xml b/kora-java-cache-caffeine/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-cache-caffeine/src/main/resources/logback.xml
+++ b/kora-java-cache-caffeine/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-cache-caffeine/src/test/resources/logback-test.xml b/kora-java-cache-caffeine/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-cache-caffeine/src/test/resources/logback-test.xml
+++ b/kora-java-cache-caffeine/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-cache-redis/build.gradle b/kora-java-cache-redis/build.gradle
index 108fa48..0690ced 100644
--- a/kora-java-cache-redis/build.gradle
+++ b/kora-java-cache-redis/build.gradle
@@ -32,7 +32,7 @@ dependencies {
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-redis:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-redis:0.11.0"
testImplementation "redis.clients:jedis:4.4.3"
}
@@ -70,13 +70,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-cache-redis/src/main/resources/application.conf b/kora-java-cache-redis/src/main/resources/application.conf
index 757489c..8b29348 100644
--- a/kora-java-cache-redis/src/main/resources/application.conf
+++ b/kora-java-cache-redis/src/main/resources/application.conf
@@ -16,6 +16,6 @@ lettuce {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-cache-redis/src/main/resources/logback.xml b/kora-java-cache-redis/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-cache-redis/src/main/resources/logback.xml
+++ b/kora-java-cache-redis/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-cache-redis/src/test/java/ru/tinkoff/kora/example/cache/caffeine/RedisCachedServiceTests.java b/kora-java-cache-redis/src/test/java/ru/tinkoff/kora/example/cache/caffeine/RedisCachedServiceTests.java
index 5769b5d..91e6a8b 100644
--- a/kora-java-cache-redis/src/test/java/ru/tinkoff/kora/example/cache/caffeine/RedisCachedServiceTests.java
+++ b/kora-java-cache-redis/src/test/java/ru/tinkoff/kora/example/cache/caffeine/RedisCachedServiceTests.java
@@ -4,13 +4,13 @@
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.redis.*;
+import io.goodforgod.testcontainers.extensions.redis.ConnectionRedis;
+import io.goodforgod.testcontainers.extensions.redis.RedisConnection;
+import io.goodforgod.testcontainers.extensions.redis.TestcontainersRedis;
import java.math.BigDecimal;
-import java.time.Duration;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.testcontainers.utility.DockerImageName;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTestConfigModifier;
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
@@ -20,11 +20,7 @@
@KoraAppTest(Application.class)
class RedisCachedServiceTests implements KoraAppTestConfigModifier {
- @ContainerRedis
- private static final RedisContainer> CONTAINER = new RedisContainer<>(DockerImageName.parse("redis:7.2-alpine"))
- .waitAfterStart(Duration.ofSeconds(1));
-
- @ContainerRedisConnection
+ @ConnectionRedis
private RedisConnection connection;
@TestComponent
@@ -42,7 +38,8 @@ public KoraConfigModification config() {
}
@BeforeEach
- void cleanup() {
+ void cleanup() throws Exception {
+ Thread.sleep(150);
cache.invalidateAll();
}
diff --git a/kora-java-cache-redis/src/test/resources/logback-test.xml b/kora-java-cache-redis/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-cache-redis/src/test/resources/logback-test.xml
+++ b/kora-java-cache-redis/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-config-hocon/build.gradle b/kora-java-config-hocon/build.gradle
index d0ca9be..0eb3772 100644
--- a/kora-java-config-hocon/build.gradle
+++ b/kora-java-config-hocon/build.gradle
@@ -68,13 +68,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-config-hocon/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java b/kora-java-config-hocon/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java
index bed9a6d..9489f54 100644
--- a/kora-java-config-hocon/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java
+++ b/kora-java-config-hocon/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java
@@ -1,6 +1,8 @@
package ru.tinkoff.kora.example.config.hocon;
import jakarta.annotation.Nullable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.time.*;
import java.util.*;
import java.util.regex.Pattern;
@@ -46,13 +48,11 @@ public interface FooConfig {
long valueLong();
- // TODO 1.0.5
- // BigInteger valueBigInt();
+ BigInteger valueBigInt();
double valueDouble();
- // TODO 1.0.5
- // BigDecimal valueBigDecimal();
+ BigDecimal valueBigDecimal();
boolean valueBoolean();
diff --git a/kora-java-config-hocon/src/main/resources/application.conf b/kora-java-config-hocon/src/main/resources/application.conf
index 40b842b..e60e9ee 100644
--- a/kora-java-config-hocon/src/main/resources/application.conf
+++ b/kora-java-config-hocon/src/main/resources/application.conf
@@ -18,11 +18,9 @@ foo {
valueDuration = "250s"
valueInt = 1
valueLong = 2
- //TODO 1.0.5
-// valueBigInt = 3
+ valueBigInt = 3
valueDouble = 4.1
- //TODO 1.0.5
-// valueBigDecimal = 5.1
+ valueBigDecimal = 5.1
valueBoolean = true
valueListAsString = "v1,v2"
valueListAsArray = ["v1", "v2"]
diff --git a/kora-java-config-hocon/src/main/resources/logback.xml b/kora-java-config-hocon/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-config-hocon/src/main/resources/logback.xml
+++ b/kora-java-config-hocon/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-config-hocon/src/test/resources/logback-test.xml b/kora-java-config-hocon/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-config-hocon/src/test/resources/logback-test.xml
+++ b/kora-java-config-hocon/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-config-yaml/build.gradle b/kora-java-config-yaml/build.gradle
index 7526cc5..61cb4de 100644
--- a/kora-java-config-yaml/build.gradle
+++ b/kora-java-config-yaml/build.gradle
@@ -68,13 +68,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-config-yaml/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java b/kora-java-config-yaml/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java
index bed9a6d..9489f54 100644
--- a/kora-java-config-yaml/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java
+++ b/kora-java-config-yaml/src/main/java/ru/tinkoff/kora/example/config/hocon/FooConfig.java
@@ -1,6 +1,8 @@
package ru.tinkoff.kora.example.config.hocon;
import jakarta.annotation.Nullable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.time.*;
import java.util.*;
import java.util.regex.Pattern;
@@ -46,13 +48,11 @@ public interface FooConfig {
long valueLong();
- // TODO 1.0.5
- // BigInteger valueBigInt();
+ BigInteger valueBigInt();
double valueDouble();
- // TODO 1.0.5
- // BigDecimal valueBigDecimal();
+ BigDecimal valueBigDecimal();
boolean valueBoolean();
diff --git a/kora-java-config-yaml/src/main/resources/application.yaml b/kora-java-config-yaml/src/main/resources/application.yaml
index 84a11b3..e276c32 100644
--- a/kora-java-config-yaml/src/main/resources/application.yaml
+++ b/kora-java-config-yaml/src/main/resources/application.yaml
@@ -18,11 +18,9 @@ foo:
valueDuration: "250s"
valueInt: 1
valueLong: 2
- #TODO 1.0.5
-# valueBigInt: 3
+ valueBigInt: 3
valueDouble: 4.1
- #TODO 1.0.5
-# valueBigDecimal: 5.1
+ valueBigDecimal: 5.1
valueBoolean: true
valueListAsString: "v1,v2"
valueListAsArray: ["v1", "v2"]
diff --git a/kora-java-config-yaml/src/main/resources/logback.xml b/kora-java-config-yaml/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-config-yaml/src/main/resources/logback.xml
+++ b/kora-java-config-yaml/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-config-yaml/src/test/resources/logback-test.xml b/kora-java-config-yaml/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-config-yaml/src/test/resources/logback-test.xml
+++ b/kora-java-config-yaml/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-crud/README.md b/kora-java-crud/README.md
index a833051..d986333 100644
--- a/kora-java-crud/README.md
+++ b/kora-java-crud/README.md
@@ -1,6 +1,6 @@
# Kora Java CRUD Service
-Пример сервиса реализованного на Kora с HTTP [CRUD](https://appmaster.io/ru/blog/grubye-operatsii-chto-takoe-grubye-operatsii) API,
+Пример сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
в качестве базы данных выступает Postgres, используется кэш Caffeine, а также другие модули которые использовались бы в реальном приложении в бою.
В примере использовались модули:
diff --git a/kora-java-crud/build.gradle b/kora-java-crud/build.gradle
index 5fa2863..a24676d 100644
--- a/kora-java-crud/build.gradle
+++ b/kora-java-crud/build.gradle
@@ -42,7 +42,6 @@ dependencies {
implementation "ru.tinkoff.kora:micrometer-module"
implementation "ru.tinkoff.kora:json-module"
implementation "ru.tinkoff.kora:validation-module"
- implementation "ru.tinkoff.kora:validation-common"
implementation "ru.tinkoff.kora:cache-caffeine"
implementation "ru.tinkoff.kora:resilient-kora"
implementation "ru.tinkoff.kora:config-hocon"
@@ -50,19 +49,18 @@ dependencies {
implementation "ru.tinkoff.kora:logging-logback"
implementation "org.mapstruct:mapstruct:1.5.5.Final"
- implementation "org.postgresql:postgresql:42.6.0"
+ implementation "org.postgresql:postgresql:42.7.2"
testImplementation "org.json:json:20231013"
testImplementation "org.skyscreamer:jsonassert:1.5.1"
testImplementation "org.mockito:mockito-core:5.6.0"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
testImplementation "org.testcontainers:junit-jupiter:1.17.6"
}
-// OpenAPI Generator for HTTP Server
-tasks.register("openApiGenerateHttpServer", GenerateTask) {
+openApiGenerate {
generatorName = "kora"
group = "openapi tools"
inputSpec = "$projectDir/src/main/resources/openapi/http-server.yaml"
@@ -71,12 +69,12 @@ tasks.register("openApiGenerateHttpServer", GenerateTask) {
modelPackage = "ru.tinkoff.kora.example.crud.openapi.http.server.model"
invokerPackage = "ru.tinkoff.kora.example.crud.openapi.http.server.invoker"
configOptions = [
- mode : "java-server", // так же есть java_server вариация HTTP Server"а
+ mode : "java-server", // так же есть java-server вариация HTTP Server"а
enableServerValidation: "true"
]
}
-compileJava.dependsOn tasks.openApiGenerateHttpServer
+compileJava.dependsOn tasks.openApiGenerate
test.dependsOn tasks.shadowJar
//noinspection GroovyAssignabilityCheck
@@ -128,13 +126,12 @@ flyway {
locations = ["classpath:db/migration"]
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-crud/src/main/resources/application.conf b/kora-java-crud/src/main/resources/application.conf
index 6047c88..08f5f45 100644
--- a/kora-java-crud/src/main/resources/application.conf
+++ b/kora-java-crud/src/main/resources/application.conf
@@ -9,7 +9,7 @@ db {
username = ${POSTGRES_USER}
password = ${POSTGRES_PASS}
maxPoolSize = 10
- poolName = "example"
+ poolName = "kora"
initializationFailTimeout = "10s"
}
@@ -55,6 +55,6 @@ resilient {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-crud/src/main/resources/logback.xml b/kora-java-crud/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-crud/src/main/resources/logback.xml
+++ b/kora-java-crud/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-crud/src/main/resources/openapi/http-server.yaml b/kora-java-crud/src/main/resources/openapi/http-server.yaml
index 8851744..8d99f49 100644
--- a/kora-java-crud/src/main/resources/openapi/http-server.yaml
+++ b/kora-java-crud/src/main/resources/openapi/http-server.yaml
@@ -79,6 +79,7 @@ paths:
type: integer
format: int64
nullable: false
+ minimum: 1
responses:
'200':
description: successful operation
diff --git a/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/AppContainer.java b/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/AppContainer.java
index a222a98..6d1e115 100644
--- a/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/AppContainer.java
+++ b/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/AppContainer.java
@@ -2,6 +2,7 @@
import java.net.URI;
import java.nio.file.Paths;
+import java.time.Duration;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
@@ -31,6 +32,7 @@ public static AppContainer build() {
protected void configure() {
super.configure();
withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
}
diff --git a/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/PetControllerTests.java b/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/PetControllerTests.java
index 4bd0700..fa979dc 100644
--- a/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/PetControllerTests.java
+++ b/kora-java-crud/src/test/java/ru/tinkoff/kora/example/crud/PetControllerTests.java
@@ -4,10 +4,10 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.Network;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
@@ -20,7 +20,7 @@
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
network = @Network(shared = true),
mode = ContainerMode.PER_RUN,
migration = @Migration(
@@ -32,11 +32,11 @@ class PetControllerTests {
private static final AppContainer container = AppContainer.build()
.withNetwork(org.testcontainers.containers.Network.SHARED);
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@BeforeAll
- public static void setup(@ContainerPostgresConnection JdbcConnection connection) {
+ public static void setup(@ConnectionPostgreSQL JdbcConnection connection) {
var params = connection.paramsInNetwork().orElseThrow();
container.withEnv(Map.of(
"POSTGRES_JDBC_URL", params.jdbcUrl(),
diff --git a/kora-java-crud/src/test/resources/logback-test.xml b/kora-java-crud/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-crud/src/test/resources/logback-test.xml
+++ b/kora-java-crud/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-database-cassandra/build.gradle b/kora-java-database-cassandra/build.gradle
index 6fb03fb..410e88c 100644
--- a/kora-java-database-cassandra/build.gradle
+++ b/kora-java-database-cassandra/build.gradle
@@ -27,13 +27,13 @@ dependencies {
annotationProcessor "ru.tinkoff.kora:annotation-processors"
implementation "ru.tinkoff.kora:database-cassandra"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-cassandra:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-cassandra:0.11.0"
}
//noinspection GroovyAssignabilityCheck
@@ -74,13 +74,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-database-cassandra/src/main/resources/application.conf b/kora-java-database-cassandra/src/main/resources/application.conf
index 4e30017..1438826 100644
--- a/kora-java-database-cassandra/src/main/resources/application.conf
+++ b/kora-java-database-cassandra/src/main/resources/application.conf
@@ -1,12 +1,12 @@
cassandra {
auth {
- login = ${?CASSANDRA_USER}
- password = ${?CASSANDRA_PASS}
+ login = ${CASSANDRA_USER}
+ password = ${CASSANDRA_PASS}
}
basic {
- contactPoints = ${?CASSANDRA_CONTACT_POINTS}
- dc = ${?CASSANDRA_DC}
- sessionKeyspace = ${?CASSANDRA_KEYSPACE}
+ contactPoints = ${CASSANDRA_CONTACT_POINTS}
+ dc = ${CASSANDRA_DC}
+ sessionKeyspace = ${CASSANDRA_KEYSPACE}
request {
timeout = 5s
}
@@ -16,6 +16,6 @@ cassandra {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-database-cassandra/src/main/resources/logback.xml b/kora-java-database-cassandra/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-database-cassandra/src/main/resources/logback.xml
+++ b/kora-java-database-cassandra/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-database-cassandra/src/main/resources/migrations/setup.cql b/kora-java-database-cassandra/src/main/resources/migrations/setup.cql
index 87fe9d3..68acc94 100644
--- a/kora-java-database-cassandra/src/main/resources/migrations/setup.cql
+++ b/kora-java-database-cassandra/src/main/resources/migrations/setup.cql
@@ -1,6 +1,4 @@
-CREATE KEYSPACE IF NOT EXISTS cassandra WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};
-
-CREATE TABLE IF NOT EXISTS cassandra.entities
+CREATE TABLE IF NOT EXISTS entities
(
id VARCHAR,
value1 INT,
@@ -10,10 +8,10 @@ CREATE TABLE IF NOT EXISTS cassandra.entities
);
-CREATE TYPE IF NOT EXISTS cassandra.username(first text, last text);
+CREATE TYPE IF NOT EXISTS username(first text, last text);
-CREATE TABLE IF NOT EXISTS cassandra.entities_udt
+CREATE TABLE IF NOT EXISTS entities_udt
(
id VARCHAR,
name FROZEN,
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudReactorTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudReactorTests.java
index 8c8ea1e..90ac21d 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudReactorTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudReactorTests.java
@@ -4,7 +4,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.time.Duration;
@@ -20,13 +20,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraCrudReactorTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -36,11 +36,11 @@ class CassandraCrudReactorTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudSyncTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudSyncTests.java
index baf3ac9..31b4488 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudSyncTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraCrudSyncTests.java
@@ -4,7 +4,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.util.List;
@@ -19,13 +19,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraCrudSyncTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -35,11 +35,11 @@ class CassandraCrudSyncTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperParameterTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperParameterTests.java
index d4a30bc..8dd0fcd 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperParameterTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperParameterTests.java
@@ -5,7 +5,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.util.List;
@@ -20,13 +20,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraMapperParameterTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -38,11 +38,11 @@ class CassandraMapperParameterTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetReactiveTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetReactiveTests.java
index 87672d8..ab079df 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetReactiveTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetReactiveTests.java
@@ -5,7 +5,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.util.List;
@@ -20,13 +20,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraMapperResultSetReactiveTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -38,11 +38,11 @@ class CassandraMapperResultSetReactiveTests implements KoraAppTestConfigModifier
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetTests.java
index f298ea9..f407209 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperResultSetTests.java
@@ -5,7 +5,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.util.List;
@@ -20,13 +20,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraMapperResultSetTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -38,11 +38,11 @@ class CassandraMapperResultSetTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowColumnTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowColumnTests.java
index 9ef000c..7c52f79 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowColumnTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowColumnTests.java
@@ -4,7 +4,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.util.List;
@@ -20,13 +20,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraMapperRowColumnTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -38,11 +38,11 @@ class CassandraMapperRowColumnTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowTests.java
index a959d94..fef8d97 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraMapperRowTests.java
@@ -4,7 +4,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import java.util.List;
@@ -19,13 +19,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraMapperRowTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -37,11 +37,11 @@ class CassandraMapperRowTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraUdtTests.java b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraUdtTests.java
index ecd630f..cd6e99a 100644
--- a/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraUdtTests.java
+++ b/kora-java-database-cassandra/src/test/java/ru/tinkoff/kora/example/cassandra/CassandraUdtTests.java
@@ -5,7 +5,7 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
-import io.goodforgod.testcontainers.extensions.cassandra.ContainerCassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
import io.goodforgod.testcontainers.extensions.cassandra.Migration;
import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
import org.jetbrains.annotations.NotNull;
@@ -19,13 +19,13 @@
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.SCRIPTS,
- migrations = { "migrations" },
+ locations = { "migrations" },
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class CassandraUdtTests implements KoraAppTestConfigModifier {
- @ContainerCassandraConnection
+ @ConnectionCassandra
private CassandraConnection connection;
@TestComponent
@@ -35,11 +35,11 @@ class CassandraUdtTests implements KoraAppTestConfigModifier {
@Override
public KoraConfigModification config() {
return KoraConfigModification
- .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().host() + ":" + connection.params().port())
+ .ofSystemProperty("CASSANDRA_CONTACT_POINTS", connection.params().contactPoint())
.withSystemProperty("CASSANDRA_USER", connection.params().username())
.withSystemProperty("CASSANDRA_PASS", connection.params().password())
.withSystemProperty("CASSANDRA_DC", connection.params().datacenter())
- .withSystemProperty("CASSANDRA_KEYSPACE", "cassandra");
+ .withSystemProperty("CASSANDRA_KEYSPACE", connection.params().keyspace());
}
@Test
diff --git a/kora-java-database-cassandra/src/test/resources/logback-test.xml b/kora-java-database-cassandra/src/test/resources/logback-test.xml
index e730b42..adccdab 100644
--- a/kora-java-database-cassandra/src/test/resources/logback-test.xml
+++ b/kora-java-database-cassandra/src/test/resources/logback-test.xml
@@ -16,11 +16,9 @@
-
-
+
-
-
+
diff --git a/kora-java-database-jdbc/build.gradle b/kora-java-database-jdbc/build.gradle
index 21027ec..4f5382a 100644
--- a/kora-java-database-jdbc/build.gradle
+++ b/kora-java-database-jdbc/build.gradle
@@ -26,16 +26,15 @@ dependencies {
koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
annotationProcessor "ru.tinkoff.kora:annotation-processors"
- implementation "org.postgresql:postgresql:42.6.0"
+ implementation "org.postgresql:postgresql:42.7.2"
implementation "ru.tinkoff.kora:database-jdbc"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
- implementation "io.azam.ulidj:ulidj:1.0.4" // For sortable UUID (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
}
//noinspection GroovyAssignabilityCheck
@@ -74,13 +73,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-database-jdbc/src/main/resources/application.conf b/kora-java-database-jdbc/src/main/resources/application.conf
index 0906df7..b367d0e 100644
--- a/kora-java-database-jdbc/src/main/resources/application.conf
+++ b/kora-java-database-jdbc/src/main/resources/application.conf
@@ -3,12 +3,12 @@ db {
username = ${POSTGRES_USER}
password = ${POSTGRES_PASS}
maxPoolSize = 10
- poolName = "example"
+ poolName = "kora"
}
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-database-jdbc/src/main/resources/logback.xml b/kora-java-database-jdbc/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-database-jdbc/src/main/resources/logback.xml
+++ b/kora-java-database-jdbc/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudMacrosTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudMacrosTests.java
index 142ceb8..b4348f2 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudMacrosTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudMacrosTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class JdbcCrudMacrosTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudReactorTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudReactorTests.java
index f690faf..a8b8dc1 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudReactorTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudReactorTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.time.Duration;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class JdbcCrudReactorTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudSyncTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudSyncTests.java
index bcf591c..19e6f96 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudSyncTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcCrudSyncTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class JdbcCrudSyncTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomCompositeTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomCompositeTests.java
index ed8dde3..eb43c63 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomCompositeTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomCompositeTests.java
@@ -3,10 +3,7 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
-import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
-import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.*;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -17,7 +14,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -26,7 +23,7 @@
@KoraAppTest(Application.class)
class JdbcIdRandomCompositeTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomTests.java
index a5035bf..a6b4107 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdRandomTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import ru.tinkoff.kora.example.jdbc.JdbcIdRandomRepository.Entity;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class JdbcIdRandomTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
@@ -41,7 +41,7 @@ public KoraConfigModification config() {
@Test
void insertOne() {
// given
- var entityCreate = new Entity("Bob");
+ var entityCreate = new Entity("Ivan");
// when
repository.insert(entityCreate);
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceCompositeTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceCompositeTests.java
index 233b000..77e8378 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceCompositeTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceCompositeTests.java
@@ -3,10 +3,7 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
-import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
-import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.*;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -17,7 +14,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -26,7 +23,7 @@
@KoraAppTest(Application.class)
class JdbcIdSequenceCompositeTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceTests.java
index 38853a1..9fb4e37 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcIdSequenceTests.java
@@ -4,10 +4,10 @@
import static ru.tinkoff.kora.example.jdbc.JdbcIdSequenceRepository.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class JdbcIdSequenceTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcJsonbTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcJsonbTests.java
index 21df0a3..59695a3 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcJsonbTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcJsonbTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class JdbcJsonbTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperColumnTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperColumnTests.java
index 7e7deb6..ba063a6 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperColumnTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperColumnTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class JdbcMapperColumnTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperParameterTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperParameterTests.java
index e42d714..67b6bd2 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperParameterTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperParameterTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class JdbcMapperParameterTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperResultSetTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperResultSetTests.java
index e9c378d..0a259cd 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperResultSetTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperResultSetTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class JdbcMapperResultSetTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperRowTests.java b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperRowTests.java
index 522e26e..e18fec2 100644
--- a/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperRowTests.java
+++ b/kora-java-database-jdbc/src/test/java/ru/tinkoff/kora/example/jdbc/JdbcMapperRowTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class JdbcMapperRowTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-jdbc/src/test/resources/logback-test.xml b/kora-java-database-jdbc/src/test/resources/logback-test.xml
index e730b42..eb35050 100644
--- a/kora-java-database-jdbc/src/test/resources/logback-test.xml
+++ b/kora-java-database-jdbc/src/test/resources/logback-test.xml
@@ -16,11 +16,10 @@
-
-
+
-
+
diff --git a/kora-java-database-r2dbc/build.gradle b/kora-java-database-r2dbc/build.gradle
index 450c8ef..1e7021a 100644
--- a/kora-java-database-r2dbc/build.gradle
+++ b/kora-java-database-r2dbc/build.gradle
@@ -26,16 +26,16 @@ dependencies {
koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
annotationProcessor "ru.tinkoff.kora:annotation-processors"
- runtimeOnly "io.netty:netty-transport-native-epoll:4.1.100.Final:linux-x86_64"
- implementation "org.postgresql:r2dbc-postgresql:1.0.0.RELEASE"
+ runtimeOnly "io.netty:netty-transport-native-epoll:4.1.109.Final:linux-x86_64"
+ implementation "org.postgresql:r2dbc-postgresql:1.0.4.RELEASE"
implementation "ru.tinkoff.kora:database-r2dbc"
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.9.6"
- testImplementation "org.postgresql:postgresql:42.6.0"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
+ testImplementation "org.postgresql:postgresql:42.7.2"
}
//noinspection GroovyAssignabilityCheck
@@ -74,13 +74,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-database-r2dbc/src/main/resources/application.conf b/kora-java-database-r2dbc/src/main/resources/application.conf
index aa5039d..e1e3829 100644
--- a/kora-java-database-r2dbc/src/main/resources/application.conf
+++ b/kora-java-database-r2dbc/src/main/resources/application.conf
@@ -3,12 +3,12 @@ db {
username = ${POSTGRES_USER}
password = ${POSTGRES_PASS}
maxPoolSize = 10
- poolName = "example"
+ poolName = "kora"
}
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-database-r2dbc/src/main/resources/logback.xml b/kora-java-database-r2dbc/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-database-r2dbc/src/main/resources/logback.xml
+++ b/kora-java-database-r2dbc/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudMacrosTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudMacrosTests.java
index 64b5f19..f95bc75 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudMacrosTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudMacrosTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.time.Duration;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class R2dbcCrudMacrosTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudSyncTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudSyncTests.java
index 446b3d9..05f90d5 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudSyncTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudSyncTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class R2dbcCrudSyncTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudTests.java
index e308b11..794d3b7 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcCrudTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.time.Duration;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class R2dbcCrudTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdRandomTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdRandomTests.java
index fba9fee..32f7318 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdRandomTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdRandomTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class R2dbcIdRandomTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
@@ -43,7 +43,7 @@ public KoraConfigModification config() {
@Test
void syncSingleSuccess() {
// given
- var entityCreate = new R2dbcIdRandomRepository.Entity("Bob");
+ var entityCreate = new R2dbcIdRandomRepository.Entity("Ivan");
// when
repository.insert(entityCreate).block();
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdSequenceTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdSequenceTests.java
index fa981cd..b24f13e 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdSequenceTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcIdSequenceTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class R2dbcIdSequenceTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
@@ -43,7 +43,7 @@ public KoraConfigModification config() {
@Test
void syncSingleSuccess() {
// given
- var entityCreate = new R2dbcIdSequenceRepository.Entity("Bob");
+ var entityCreate = new R2dbcIdSequenceRepository.Entity("Ivan");
// when
long id = repository.insert(entityCreate).block();
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperColumnTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperColumnTests.java
index a6e78d0..ae01003 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperColumnTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperColumnTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class R2dbcMapperColumnTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperParameterTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperParameterTests.java
index b316a92..e323b72 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperParameterTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperParameterTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class R2dbcMapperParameterTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperRowTests.java b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperRowTests.java
index 5eb9f48..dc50ff1 100644
--- a/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperRowTests.java
+++ b/kora-java-database-r2dbc/src/test/java/ru/tinkoff/kora/example/r2dbc/R2dbcMapperRowTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class R2dbcMapperRowTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-r2dbc/src/test/resources/logback-test.xml b/kora-java-database-r2dbc/src/test/resources/logback-test.xml
index e730b42..eb35050 100644
--- a/kora-java-database-r2dbc/src/test/resources/logback-test.xml
+++ b/kora-java-database-r2dbc/src/test/resources/logback-test.xml
@@ -16,11 +16,10 @@
-
-
+
-
+
diff --git a/kora-java-database-vertx/build.gradle b/kora-java-database-vertx/build.gradle
index a157b9a..eb9e138 100644
--- a/kora-java-database-vertx/build.gradle
+++ b/kora-java-database-vertx/build.gradle
@@ -29,14 +29,14 @@ dependencies {
implementation "ru.tinkoff.kora:database-vertx"
implementation "io.vertx:vertx-pg-client:4.3.8"
implementation "com.ongres.scram:client:2.1"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.9.6"
- testImplementation "org.postgresql:postgresql:42.6.0"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
+ testImplementation "org.postgresql:postgresql:42.7.2"
}
//noinspection GroovyAssignabilityCheck
@@ -75,13 +75,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-database-vertx/src/main/resources/application.conf b/kora-java-database-vertx/src/main/resources/application.conf
index 48fb0b7..861ab14 100644
--- a/kora-java-database-vertx/src/main/resources/application.conf
+++ b/kora-java-database-vertx/src/main/resources/application.conf
@@ -3,12 +3,12 @@ db {
username = ${POSTGRES_USER}
password = ${POSTGRES_PASS}
maxPoolSize = 10
- poolName = "example"
+ poolName = "kora"
}
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-database-vertx/src/main/resources/logback.xml b/kora-java-database-vertx/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-database-vertx/src/main/resources/logback.xml
+++ b/kora-java-database-vertx/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudReactorTests.java b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudReactorTests.java
index 056550d..a8039f1 100644
--- a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudReactorTests.java
+++ b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudReactorTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class VertxCrudReactorTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudSyncTests.java b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudSyncTests.java
index ec58839..cd64fc3 100644
--- a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudSyncTests.java
+++ b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxCrudSyncTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class VertxCrudSyncTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperColumnTests.java b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperColumnTests.java
index 1435361..ec1eadb 100644
--- a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperColumnTests.java
+++ b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperColumnTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class VertxMapperColumnTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperParameterTests.java b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperParameterTests.java
index e9af4ba..8c422a6 100644
--- a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperParameterTests.java
+++ b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperParameterTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
@@ -17,7 +17,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -26,7 +26,7 @@
@KoraAppTest(Application.class)
class VertxMapperParameterTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperResultSetTests.java b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperResultSetTests.java
index c8df504..3dc943d 100644
--- a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperResultSetTests.java
+++ b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperResultSetTests.java
@@ -4,10 +4,10 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -16,7 +16,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class VertxMapperResultSetTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperRowTests.java b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperRowTests.java
index 9b2c036..97b2342 100644
--- a/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperRowTests.java
+++ b/kora-java-database-vertx/src/test/java/ru/tinkoff/kora/example/vertx/VertxMapperRowTests.java
@@ -3,10 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
import io.goodforgod.testcontainers.extensions.jdbc.Migration;
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,7 +15,7 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class VertxMapperRowTests implements KoraAppTestConfigModifier {
- @ContainerPostgresConnection
+ @ConnectionPostgreSQL
private JdbcConnection connection;
@TestComponent
diff --git a/kora-java-database-vertx/src/test/resources/logback-test.xml b/kora-java-database-vertx/src/test/resources/logback-test.xml
index e730b42..eb35050 100644
--- a/kora-java-database-vertx/src/test/resources/logback-test.xml
+++ b/kora-java-database-vertx/src/test/resources/logback-test.xml
@@ -16,11 +16,10 @@
-
-
+
-
+
diff --git a/kora-java-graalvm-crud-cassandra/Dockerfile b/kora-java-graalvm-crud-cassandra/Dockerfile
new file mode 100644
index 0000000..de40743
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/Dockerfile
@@ -0,0 +1,25 @@
+FROM ghcr.io/graalvm/native-image-community:21 as builder
+
+ARG APP_DIR=/opt/application
+ARG JAR_DIR=build/libs
+WORKDIR $APP_DIR
+
+ADD $JAR_DIR/*.jar $APP_DIR/application.jar
+
+RUN native-image --no-fallback -classpath $APP_DIR/application.jar
+
+FROM ubuntu:noble-20240212 as runner
+
+ARG APP_DIR=/opt/application
+WORKDIR $APP_DIR
+
+COPY --from=builder $APP_DIR/application $APP_DIR/application
+
+ARG DOCKER_USER=app
+RUN groupadd -r $DOCKER_USER && useradd -rg $DOCKER_USER $DOCKER_USER
+RUN chmod +x application
+USER $DOCKER_USER
+
+EXPOSE 8080/tcp
+EXPOSE 8085/tcp
+CMD "/opt/application/application"
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-cassandra/README.md b/kora-java-graalvm-crud-cassandra/README.md
new file mode 100644
index 0000000..d440c92
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/README.md
@@ -0,0 +1,49 @@
+# Kora Java GraalVM CRUD Cassandra Service
+
+Пример сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
+в качестве базы данных выступает Cassandra, используется кэш Redis, а также другие модули которые использовались бы в реальном приложении в бою.
+
+В примере использовались модули:
+- [HTTP Server](https://kora-projects.github.io/kora-docs/ru/documentation/http-server/)
+- [HTTP Server OpenAPI Generation](https://kora-projects.github.io/kora-docs/ru/documentation/openapi-codegen/)
+- [Probes](https://kora-projects.github.io/kora-docs/ru/documentation/probes/)
+- [Metrics](https://kora-projects.github.io/kora-docs/ru/documentation/metrics/)
+- [Database Cassandra](https://kora-projects.github.io/kora-docs/ru/documentation/database-cassandra/)
+- [JSON](https://kora-projects.github.io/kora-docs/ru/documentation/json/)
+- [Resilient](https://kora-projects.github.io/kora-docs/ru/documentation/resilient/)
+- [Validation](https://kora-projects.github.io/kora-docs/ru/documentation/validation/)
+- [Cache Redis](https://kora-projects.github.io/kora-docs/ru/documentation/cache/#redis)
+
+Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_21/)
+
+## Build
+
+Собрать артефакт:
+
+```shell
+./gradlew shadowJar
+docker build -t kora-java-graalvm-crud-cassandra .
+```
+
+### Generate
+
+Сгенерировать API для HTTP Server:
+```shell
+./gradlew openApiGenerateHttpServer
+```
+
+## Run
+
+Запустить локально:
+```shell
+./gradlew run
+```
+
+## Test
+
+Тесты используют [Testcontainers](https://java.testcontainers.org/), требуется [Docker](https://docs.docker.com/engine/install/) окружение для запуска тестов или аналогичные контейнерные окружения ([colima](https://github.com/abiosoft/colima) / итп)
+
+Протестировать локально:
+```shell
+./gradlew test
+```
diff --git a/kora-java-graalvm-crud-cassandra/build.gradle b/kora-java-graalvm-crud-cassandra/build.gradle
new file mode 100644
index 0000000..8c57607
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/build.gradle
@@ -0,0 +1,179 @@
+buildscript {
+ dependencies {
+ classpath("ru.tinkoff.kora:openapi-generator:$koraVersion")
+ }
+}
+
+plugins {
+ id "java"
+ id "jacoco"
+ id "application"
+
+ id "org.openapi.generator" version "7.1.0"
+ id "com.github.johnrengelman.shadow" version "8.1.1"
+ id "org.graalvm.buildtools.native" version "0.10.1"
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+mainClassName = "ru.tinkoff.kora.example.graalvm.crud.cassandra.Application"
+
+sourceCompatibility = JavaVersion.VERSION_17
+targetCompatibility = JavaVersion.VERSION_17
+
+configurations {
+ koraBom
+ implementation.extendsFrom(koraBom)
+ annotationProcessor.extendsFrom(koraBom)
+}
+
+dependencies {
+ koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
+ annotationProcessor "org.mapstruct:mapstruct-processor:1.5.5.Final"
+ annotationProcessor "ru.tinkoff.kora:annotation-processors"
+ annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
+ compileOnly "io.goodforgod:graalvm-hint-annotations:1.2.0"
+
+ implementation "ru.tinkoff.kora:http-server-undertow"
+ implementation "ru.tinkoff.kora:database-cassandra"
+ implementation "ru.tinkoff.kora:micrometer-module"
+ implementation "ru.tinkoff.kora:json-module"
+ implementation "ru.tinkoff.kora:validation-module"
+ implementation "ru.tinkoff.kora:cache-redis"
+ implementation "ru.tinkoff.kora:resilient-kora"
+ implementation "ru.tinkoff.kora:config-hocon"
+ implementation "ru.tinkoff.kora:openapi-management"
+ implementation "ru.tinkoff.kora:logging-logback"
+
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
+ implementation "org.mapstruct:mapstruct:1.5.5.Final"
+
+ testImplementation "org.json:json:20231013"
+ testImplementation "org.skyscreamer:jsonassert:1.5.1"
+ testImplementation "redis.clients:jedis:4.4.3"
+
+ testImplementation "org.mockito:mockito-core:5.6.0"
+ testImplementation "ru.tinkoff.kora:test-junit5"
+ testImplementation "io.goodforgod:testcontainers-extensions-cassandra:0.11.0"
+ testImplementation "io.goodforgod:testcontainers-extensions-redis:0.11.0"
+ testImplementation "org.testcontainers:junit-jupiter:1.17.6"
+}
+
+openApiGenerate {
+ generatorName = "kora"
+ group = "openapi tools"
+ inputSpec = "$projectDir/src/main/resources/openapi/http-server.yaml"
+ outputDir = "$buildDir/generated/openapi"
+ apiPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.api"
+ modelPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.model"
+ invokerPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.invoker"
+ configOptions = [
+ mode : "java-reactive-server", // так же есть java-server вариация HTTP Server"а
+ enableServerValidation: "true"
+ ]
+}
+
+graalvmNative {
+ binaries {
+ main {
+ imageName = "$project.name"
+ mainClass = "$mainClassName"
+ javaLauncher = javaToolchains.launcherFor {
+ languageVersion = JavaLanguageVersion.of(21)
+ vendor = JvmVendorSpec.matching("GraalVM Community")
+ }
+ }
+ }
+ metadataRepository {
+ enabled = true
+ }
+}
+
+compileJava.dependsOn tasks.openApiGenerate
+processResources.dependsOn tasks.collectReachabilityMetadata
+test.dependsOn tasks.shadowJar
+
+//noinspection GroovyAssignabilityCheck
+run {
+ environment([
+ "CASSANDRA_CONTACT_POINTS": "$cassandraHost:$cassandraPort",
+ "CASSANDRA_USER" : "$cassandraUser",
+ "CASSANDRA_PASS" : "$cassandraPassword",
+ "CASSANDRA_DC" : "$cassandraDatacenter",
+ "CASSANDRA_KEYSPACE" : "$cassandraKeyspace",
+ "REDIS_URL" : "redis://$redisHost:$redisPort/$redisDatabase",
+ "REDIS_USER" : "$redisUser",
+ "REDIS_PASS" : "$redisPassword",
+ ])
+}
+
+test {
+ jvmArgs += [
+ "-XX:+TieredCompilation",
+ "-XX:TieredStopAtLevel=1",
+ ]
+
+ environment([
+ "": ""
+ ])
+
+ useJUnitPlatform()
+ testLogging {
+ showStandardStreams(true)
+ events("passed", "skipped", "failed")
+ exceptionFormat("full")
+ }
+
+ jacoco {
+ excludes += ["**/Application*"]
+ }
+
+ reports {
+ html.required = false
+ junitXml.required = false
+ }
+}
+
+sourceSets {
+ main {
+ java.srcDirs += "$buildDir/generated/openapi"
+ resources.srcDirs += "$buildDir/native-reachability-metadata"
+ }
+}
+
+jar.enabled = false
+shadowJar {
+ mergeServiceFiles()
+ manifest {
+ attributes "Main-Class": mainClassName
+ attributes "Implementation-Version": koraVersion
+ }
+}
+
+artifacts {
+ archives shadowJar
+}
+
+compileJava {
+ options.encoding("UTF-8")
+ options.incremental(true)
+ options.fork = true
+}
+
+check.dependsOn jacocoTestReport
+jacocoTestReport {
+ reports {
+ xml.required = true
+ html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
+ }
+}
+
+javadoc {
+ options.encoding = "UTF-8"
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption("html5", true)
+ }
+}
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-cassandra/gradle.properties b/kora-java-graalvm-crud-cassandra/gradle.properties
new file mode 100644
index 0000000..ed8614b
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/gradle.properties
@@ -0,0 +1,16 @@
+##### CASSANDRA #####
+cassandraHost=localhost
+cassandraPort=9042
+cassandraUser=cassandra
+cassandraPassword=cassandra
+cassandraDatacenter=datacenter1
+cassandraKeyspace=petshop
+
+
+##### REDIS #####
+redisHost=localhost
+redisPort=6379
+redisDatabase=0
+redisUser=default
+redisPassword=redis
+
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/Application.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/Application.java
new file mode 100644
index 0000000..edd762a
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/Application.java
@@ -0,0 +1,36 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra;
+
+import io.goodforgod.graalvm.hint.annotation.NativeImageHint;
+import io.goodforgod.graalvm.hint.annotation.ResourceHint;
+import ru.tinkoff.kora.application.graph.KoraApplication;
+import ru.tinkoff.kora.cache.redis.RedisCacheModule;
+import ru.tinkoff.kora.common.KoraApp;
+import ru.tinkoff.kora.config.hocon.HoconConfigModule;
+import ru.tinkoff.kora.database.cassandra.CassandraDatabaseModule;
+import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
+import ru.tinkoff.kora.json.module.JsonModule;
+import ru.tinkoff.kora.logging.logback.LogbackModule;
+import ru.tinkoff.kora.micrometer.module.MetricsModule;
+import ru.tinkoff.kora.openapi.management.OpenApiManagementModule;
+import ru.tinkoff.kora.resilient.ResilientModule;
+import ru.tinkoff.kora.validation.module.ValidationModule;
+
+@ResourceHint(include = { "openapi/http-server.yaml" })
+@NativeImageHint(name = "application", entrypoint = Application.class)
+@KoraApp
+public interface Application extends
+ HoconConfigModule,
+ LogbackModule,
+ CassandraDatabaseModule,
+ ValidationModule,
+ JsonModule,
+ RedisCacheModule,
+ ResilientModule,
+ MetricsModule,
+ OpenApiManagementModule,
+ UndertowHttpServerModule {
+
+ static void main(String[] args) {
+ KoraApplication.run(ApplicationGraph::graph);
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/controller/HttpExceptionHandler.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/controller/HttpExceptionHandler.java
new file mode 100644
index 0000000..7c0085c
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/controller/HttpExceptionHandler.java
@@ -0,0 +1,42 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.controller;
+
+import io.micrometer.core.instrument.config.validate.ValidationException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeoutException;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.common.Context;
+import ru.tinkoff.kora.common.Tag;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.http.common.body.HttpBody;
+import ru.tinkoff.kora.http.server.common.*;
+import ru.tinkoff.kora.json.common.JsonWriter;
+
+@Tag(HttpServerModule.class)
+@Component
+public final class HttpExceptionHandler implements HttpServerInterceptor {
+
+ private final JsonWriter errorJsonWriter;
+
+ public HttpExceptionHandler(JsonWriter errorJsonWriter) {
+ this.errorJsonWriter = errorJsonWriter;
+ }
+
+ @Override
+ public CompletionStage intercept(Context context, HttpServerRequest request, InterceptChain chain)
+ throws Exception {
+ return chain.process(context, request).exceptionally(e -> {
+ if (e instanceof HttpServerResponseException ex) {
+ return ex;
+ }
+
+ var body = HttpBody.json(errorJsonWriter.toByteArrayUnchecked(new MessageTO(e.getMessage())));
+ if (e instanceof IllegalArgumentException || e instanceof ValidationException) {
+ return HttpServerResponse.of(400, body);
+ } else if (e instanceof TimeoutException) {
+ return HttpServerResponse.of(408, body);
+ } else {
+ return HttpServerResponse.of(500, body);
+ }
+ });
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/controller/PetDelegate.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/controller/PetDelegate.java
new file mode 100644
index 0000000..0cf73f4
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/controller/PetDelegate.java
@@ -0,0 +1,86 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.controller;
+
+import static ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiResponses.*;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.mapper.PetMapper;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.service.PetService;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiDelegate;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+
+@Component
+public final class PetDelegate implements PetApiDelegate {
+
+ private final PetMapper petMapper;
+ private final PetService petService;
+
+ public PetDelegate(PetMapper petMapper, PetService petService) {
+ this.petMapper = petMapper;
+ this.petService = petService;
+ }
+
+ @Override
+ public Mono getPetById(long petId) {
+ if (petId < 0) {
+ return Mono.just(new GetPetByIdApiResponse.GetPetById400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.findByID(petId)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return ((GetPetByIdApiResponse) new GetPetByIdApiResponse.GetPetById200ApiResponse(body));
+ })
+ .switchIfEmpty(Mono.fromSupplier(() -> new GetPetByIdApiResponse.GetPetById404ApiResponse(notFound(petId))));
+ }
+
+ @Override
+ public Mono addPet(PetCreateTO petCreateTO) {
+ return petService.add(petCreateTO)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return new AddPetApiResponse.AddPet200ApiResponse(body);
+ });
+ }
+
+ @Override
+ public Mono updatePet(long petId, PetUpdateTO petUpdateTO) {
+ if (petId < 0) {
+ return Mono.just(new UpdatePetApiResponse.UpdatePet400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.update(petId, petUpdateTO)
+ .map(updated -> {
+ var body = petMapper.asDTO(updated);
+ return ((UpdatePetApiResponse) new UpdatePetApiResponse.UpdatePet200ApiResponse(body));
+ })
+ .switchIfEmpty(Mono.fromSupplier(() -> new UpdatePetApiResponse.UpdatePet404ApiResponse(notFound(petId))));
+ }
+
+ @Override
+ public Mono deletePet(long petId) {
+ if (petId < 0) {
+ return Mono.just(new DeletePetApiResponse.DeletePet400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.delete(petId)
+ .map(isDeleted -> {
+ if (isDeleted) {
+ return new DeletePetApiResponse.DeletePet200ApiResponse(
+ new MessageTO("Successfully deleted pet with ID: " + petId));
+ } else {
+ return new DeletePetApiResponse.DeletePet404ApiResponse(notFound(petId));
+ }
+ });
+ }
+
+ private static MessageTO notFound(long petId) {
+ return new MessageTO("Pet not found for ID: " + petId);
+ }
+
+ private static MessageTO malformedId(long petId) {
+ return new MessageTO("Pet malformed ID: " + petId);
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/model/dao/Pet.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/model/dao/Pet.java
new file mode 100644
index 0000000..3d97b86
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/model/dao/Pet.java
@@ -0,0 +1,27 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+import ru.tinkoff.kora.json.common.annotation.Json;
+
+@Json
+@Table("pets")
+public record Pet(@Id @Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Status status,
+ @Column("category") String category) {
+
+ public enum Status {
+
+ AVAILABLE(0),
+ PENDING(10),
+ SOLD(20);
+
+ public final int code;
+
+ Status(int code) {
+ this.code = code;
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/model/mapper/PetMapper.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/model/mapper/PetMapper.java
new file mode 100644
index 0000000..651b5fc
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/model/mapper/PetMapper.java
@@ -0,0 +1,18 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.model.mapper;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetTO;
+
+@Mapper
+public interface PetMapper {
+
+ @Mapping(source = "pet", target = "category")
+ PetTO asDTO(Pet pet);
+
+ @Mapping(source = "id", target = "id")
+ @Mapping(source = "category", target = "name")
+ CategoryTO asCategoryTO(Pet pet);
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/PetRepository.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/PetRepository.java
new file mode 100644
index 0000000..0a29fb1
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/PetRepository.java
@@ -0,0 +1,27 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.repository;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.database.cassandra.CassandraRepository;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao.Pet;
+
+@Repository
+public interface PetRepository extends CassandraRepository {
+
+ @Query("""
+ SELECT %{return#selects}
+ FROM %{return#table}
+ WHERE id = :id
+ """)
+ Mono findById(long id);
+
+ @Query("INSERT INTO %{entity#inserts}")
+ Mono insert(Pet entity);
+
+ @Query("UPDATE %{entity#table} SET %{entity#updates} WHERE %{entity#where = @id}")
+ Mono update(Pet entity);
+
+ @Query("DELETE FROM pets WHERE id = :id")
+ Mono deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/mapper/PetStatusParameterMapper.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/mapper/PetStatusParameterMapper.java
new file mode 100644
index 0000000..4545540
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/mapper/PetStatusParameterMapper.java
@@ -0,0 +1,20 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.repository.mapper;
+
+import com.datastax.oss.driver.api.core.data.SettableByName;
+import jakarta.annotation.Nullable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.cassandra.mapper.parameter.CassandraParameterColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao.Pet;
+
+@Component
+public final class PetStatusParameterMapper implements CassandraParameterColumnMapper {
+
+ @Override
+ public void apply(SettableByName> stmt, int index, @Nullable Pet.Status value) {
+ if (value == null) {
+ stmt.setToNull(index);
+ } else {
+ stmt.set(index, value.code, Integer.class);
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/mapper/PetStatusResultMapper.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/mapper/PetStatusResultMapper.java
new file mode 100644
index 0000000..d61b1f7
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/repository/mapper/PetStatusResultMapper.java
@@ -0,0 +1,24 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.repository.mapper;
+
+import com.datastax.oss.driver.api.core.data.GettableByName;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.cassandra.mapper.result.CassandraRowColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao.Pet;
+
+@Component
+public final class PetStatusResultMapper implements CassandraRowColumnMapper {
+
+ private final Pet.Status[] statuses = Pet.Status.values();
+
+ @Override
+ public Pet.Status apply(GettableByName row, int index) {
+ final int code = row.get(index, Integer.class);
+ for (Pet.Status status : statuses) {
+ if (code == status.code) {
+ return status;
+ }
+ }
+
+ throw new IllegalStateException("Unknown code: " + code);
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/service/PetCache.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/service/PetCache.java
new file mode 100644
index 0000000..b3dd4e9
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/service/PetCache.java
@@ -0,0 +1,11 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.service;
+
+import ru.tinkoff.kora.cache.annotation.Cache;
+import ru.tinkoff.kora.cache.redis.RedisCache;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao.Pet;
+import ru.tinkoff.kora.json.common.annotation.Json;
+
+@Cache("pet-cache")
+public interface PetCache extends RedisCache {
+
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/service/PetService.java b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/service/PetService.java
new file mode 100644
index 0000000..558a105
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/service/PetService.java
@@ -0,0 +1,75 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra.service;
+
+import java.util.concurrent.ThreadLocalRandom;
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.cache.annotation.CacheInvalidate;
+import ru.tinkoff.kora.cache.annotation.CachePut;
+import ru.tinkoff.kora.cache.annotation.Cacheable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.repository.PetRepository;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.resilient.circuitbreaker.annotation.CircuitBreaker;
+import ru.tinkoff.kora.resilient.retry.annotation.Retry;
+import ru.tinkoff.kora.resilient.timeout.annotation.Timeout;
+
+@Component
+public class PetService {
+
+ private final PetRepository petRepository;
+
+ public PetService(PetRepository petRepository) {
+ this.petRepository = petRepository;
+ }
+
+ @Cacheable(PetCache.class)
+ @CircuitBreaker("pet")
+ @Retry("pet")
+ @Timeout("pet")
+ public Mono findByID(long petId) {
+ return petRepository.findById(petId);
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ public Mono add(PetCreateTO createTO) {
+ final long petId = ThreadLocalRandom.current().nextLong(0, Long.MAX_VALUE);
+ final Pet pet = new Pet(petId, createTO.name(), Pet.Status.AVAILABLE, createTO.category().name());
+ return petRepository.insert(pet).then(Mono.just(pet));
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CachePut(value = PetCache.class, parameters = "id")
+ public Mono update(long id, PetUpdateTO updateTO) {
+ return petRepository.findById(id)
+ .flatMap(pet -> {
+ var status = (updateTO.status() == null)
+ ? pet.status()
+ : toStatus(updateTO.status());
+
+ var category = (updateTO.category() == null)
+ ? pet.category()
+ : updateTO.category().name();
+
+ var petUpdate = new Pet(pet.id(), updateTO.name(), status, category);
+ return petRepository.update(petUpdate).then(Mono.just(petUpdate));
+ });
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CacheInvalidate(PetCache.class)
+ public Mono delete(long petId) {
+ return petRepository.deleteById(petId).thenReturn(true);
+ }
+
+ private static Pet.Status toStatus(PetUpdateTO.StatusEnum statusEnum) {
+ return switch (statusEnum) {
+ case AVAILABLE -> Pet.Status.AVAILABLE;
+ case PENDING -> Pet.Status.PENDING;
+ case SOLD -> Pet.Status.SOLD;
+ };
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/resources/application.conf b/kora-java-graalvm-crud-cassandra/src/main/resources/application.conf
new file mode 100644
index 0000000..1c2e776
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/resources/application.conf
@@ -0,0 +1,75 @@
+httpServer {
+ publicApiHttpPort = 8080
+ privateApiHttpPort = 8085
+}
+
+
+cassandra {
+ auth {
+ login = ${CASSANDRA_USER}
+ password = ${CASSANDRA_PASS}
+ }
+ basic {
+ contactPoints = ${CASSANDRA_CONTACT_POINTS}
+ dc = ${CASSANDRA_DC}
+ sessionKeyspace = ${CASSANDRA_KEYSPACE}
+ request {
+ timeout = 5s
+ }
+ }
+}
+
+pet-cache {
+ maximumSize = 1000
+ expireAfterWrite = ${?CACHE_EXPIRE_WRITE}
+ keyPrefix = "pet-"
+}
+
+
+lettuce {
+ uri = ${REDIS_URL}
+ user = ${REDIS_USER}
+ password = ${REDIS_PASS}
+ socketTimeout = 15s
+ commandTimeout = 15s
+}
+
+
+openapi {
+ management {
+ enabled = true
+ file = "openapi/http-server.yaml"
+ swaggerui {
+ enabled = true
+ }
+ rapidoc {
+ enabled = true
+ }
+ }
+}
+
+
+resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 50
+ minimumRequiredCalls = 25
+ failureRateThreshold = 50
+ permittedCallsInHalfOpenState = 10
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+}
+
+
+logging.level {
+ "root": "WARN"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
+ "ru.tinkoff.kora.application.graph.internal.loom.VirtualThreadExecutorHolder": "DEBUG"
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/main/resources/logback.xml b/kora-java-graalvm-crud-cassandra/src/main/resources/logback.xml
new file mode 100644
index 0000000..745e83f
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ UTF-8
+ %d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-cassandra/src/main/resources/migrations/setup.cql b/kora-java-graalvm-crud-cassandra/src/main/resources/migrations/setup.cql
new file mode 100644
index 0000000..52d87ac
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/resources/migrations/setup.cql
@@ -0,0 +1,8 @@
+CREATE TABLE IF NOT EXISTS pets
+(
+ id BIGINT,
+ name VARCHAR,
+ status INT,
+ category VARCHAR,
+ PRIMARY KEY (id)
+);
diff --git a/kora-java-graalvm-crud-cassandra/src/main/resources/openapi/http-server.yaml b/kora-java-graalvm-crud-cassandra/src/main/resources/openapi/http-server.yaml
new file mode 100644
index 0000000..8d99f49
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/main/resources/openapi/http-server.yaml
@@ -0,0 +1,315 @@
+openapi: 3.0.3
+info:
+ title: Swagger Petstore - OpenAPI 3.0
+ description: |-
+ This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
+ Swagger at [https://swagger.io](https://swagger.io).
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ version: 1.0.11
+externalDocs:
+ description: Find out more about Swagger
+ url: https://swagger.io
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: https://swagger.io
+paths:
+ /v3/pets:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: Add a new pet to the store
+ operationId: addPet
+ requestBody:
+ required: true
+ description: Create a new pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetCreateTO'
+ responses:
+ '200':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ /v3/pets/{id}:
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: id
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ nullable: false
+ minimum: 1
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: Update an existing pet by Id
+ operationId: updatePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ requestBody:
+ description: Update an existent pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetUpdateTO'
+ required: true
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: delete a pet
+ operationId: deletePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+components:
+ schemas:
+ MessageTO:
+ type: object
+ properties:
+ message:
+ type: string
+ CategoryTO:
+ required:
+ - id
+ - name
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 1
+ name:
+ type: string
+ example: Dogs
+ CategoryCreateTO:
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ type: string
+ example: Dogs
+ PetStatusTO:
+ properties:
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ PetTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 10
+ nullable: false
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryTO'
+ PetCreateTO:
+ required:
+ - name
+ - category
+ type: object
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
+ PetUpdateTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
diff --git a/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/AppContainer.java b/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/AppContainer.java
new file mode 100644
index 0000000..a61700a
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/AppContainer.java
@@ -0,0 +1,47 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.time.Duration;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.utility.DockerImageName;
+
+public final class AppContainer extends GenericContainer {
+
+ private AppContainer() {
+ super(new ImageFromDockerfile("kora-java-graalvm-crud-cassandra")
+ .withDockerfile(Paths.get("Dockerfile").toAbsolutePath()));
+ }
+
+ private AppContainer(DockerImageName image) {
+ super(image);
+ }
+
+ public static AppContainer build() {
+ final String appImage = System.getenv("IMAGE_KORA_JAVA_GRAALVM_CRUD_CASSANDRA");
+ return (appImage != null && !appImage.isBlank())
+ ? new AppContainer(DockerImageName.parse(appImage))
+ : new AppContainer();
+ }
+
+ @Override
+ protected void configure() {
+ super.configure();
+ withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
+ withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
+ waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
+ }
+
+ public int getPort() {
+ return getMappedPort(8080);
+ }
+
+ public URI getURI() {
+ return URI.create(String.format("http://%s:%s", getHost(), getPort()));
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/PetControllerTests.java b/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/PetControllerTests.java
new file mode 100644
index 0000000..522ae7a
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/PetControllerTests.java
@@ -0,0 +1,220 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import io.goodforgod.testcontainers.extensions.ContainerMode;
+import io.goodforgod.testcontainers.extensions.Network;
+import io.goodforgod.testcontainers.extensions.cassandra.CassandraConnection;
+import io.goodforgod.testcontainers.extensions.cassandra.ConnectionCassandra;
+import io.goodforgod.testcontainers.extensions.cassandra.Migration;
+import io.goodforgod.testcontainers.extensions.cassandra.TestcontainersCassandra;
+import io.goodforgod.testcontainers.extensions.redis.ConnectionRedis;
+import io.goodforgod.testcontainers.extensions.redis.RedisConnection;
+import io.goodforgod.testcontainers.extensions.redis.TestcontainersRedis;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Map;
+import org.json.JSONObject;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+@TestcontainersCassandra(
+ network = @Network(shared = true),
+ mode = ContainerMode.PER_RUN,
+ migration = @Migration(
+ locations = "migrations",
+ engine = Migration.Engines.SCRIPTS,
+ apply = Migration.Mode.PER_METHOD,
+ drop = Migration.Mode.PER_METHOD))
+@TestcontainersRedis(
+ network = @Network(shared = true),
+ mode = ContainerMode.PER_RUN)
+class PetControllerTests {
+
+ private static final AppContainer container = AppContainer.build()
+ .withNetwork(org.testcontainers.containers.Network.SHARED);
+
+ @ConnectionCassandra
+ private CassandraConnection connection;
+
+ @BeforeEach
+ public void setup(@ConnectionCassandra CassandraConnection cassandraConnection,
+ @ConnectionRedis RedisConnection redisConnection) {
+ if (!container.isRunning()) {
+ var paramsCassandra = cassandraConnection.paramsInNetwork().orElseThrow();
+ var paramsRedis = redisConnection.paramsInNetwork().orElseThrow();
+ container.withEnv(Map.of(
+ "CASSANDRA_CONTACT_POINTS", paramsCassandra.contactPoint(),
+ "CASSANDRA_USER", paramsCassandra.username(),
+ "CASSANDRA_PASS", paramsCassandra.password(),
+ "CASSANDRA_DC", paramsCassandra.datacenter(),
+ "CASSANDRA_KEYSPACE", paramsCassandra.keyspace(),
+ "CACHE_EXPIRE_WRITE", "0s",
+ "REDIS_URL", paramsRedis.uri().toString(),
+ "REDIS_USER", paramsRedis.username(),
+ "REDIS_PASS", paramsRedis.password()));
+
+ container.start();
+ }
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ container.stop();
+ }
+
+ @Test
+ void addPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var requestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var request = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, response.statusCode(), response.body());
+
+ // then
+ connection.assertCountsEquals(1, "pets");
+ var responseBody = new JSONObject(response.body());
+ assertNotNull(responseBody.query("/id"));
+ assertNotEquals(0L, responseBody.query("/id"));
+ assertNotNull(responseBody.query("/status"));
+ assertEquals(requestBody.query("/name"), responseBody.query("/name"));
+ assertNotNull(responseBody.query("/category/id"));
+ assertEquals(requestBody.query("/category/name"), responseBody.query("/category/name"));
+ }
+
+ @Test
+ void getPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, getResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(createResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void updatePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var updateRequestBody = new JSONObject()
+ .put("name", "doggie2")
+ .put("status", "pending")
+ .put("category", new JSONObject()
+ .put("name", "Dogs2"));
+
+ var updateRequest = HttpRequest.newBuilder()
+ .PUT(HttpRequest.BodyPublishers.ofString(updateRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var updateResponse = httpClient.send(updateRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, updateResponse.statusCode(), updateResponse.body());
+ var updateResponseBody = new JSONObject(updateResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(updateResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void deletePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var deleteRequest = HttpRequest.newBuilder()
+ .DELETE()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var deleteResponse = httpClient.send(deleteRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, deleteResponse.statusCode(), deleteResponse.body());
+
+ // then
+ connection.assertCountsEquals(0, "pets");
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/PetServiceTests.java b/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/PetServiceTests.java
new file mode 100644
index 0000000..393cfcd
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/test/java/ru/tinkoff/kora/example/graalvm/crud/cassandra/PetServiceTests.java
@@ -0,0 +1,112 @@
+package ru.tinkoff.kora.example.graalvm.crud.cassandra;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.repository.PetRepository;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.service.PetCache;
+import ru.tinkoff.kora.example.graalvm.crud.cassandra.service.PetService;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTestConfigModifier;
+import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
+import ru.tinkoff.kora.test.extension.junit5.TestComponent;
+
+@KoraAppTest(Application.class)
+class PetServiceTests implements KoraAppTestConfigModifier {
+
+ @Mock
+ @TestComponent
+ private PetCache petCache;
+ @Mock
+ @TestComponent
+ private PetRepository petRepository;
+
+ @TestComponent
+ private PetService petService;
+
+ @NotNull
+ @Override
+ public KoraConfigModification config() {
+ return KoraConfigModification.ofString("""
+ resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 2
+ minimumRequiredCalls = 2
+ failureRateThreshold = 100
+ permittedCallsInHalfOpenState = 1
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+ }
+ """);
+ }
+
+ @Test
+ void updatePetWithNewCategoryCreated() {
+ // given
+ mockCache();
+ mockRepository();
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog"))).block();
+ assertNotEquals(0L, added.id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.just(added));
+ Mockito.when(petRepository.update(any())).thenReturn(Mono.empty());
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("cat"))).blockOptional();
+ assertTrue(updated.isPresent());
+ assertNotEquals(0L, updated.get().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ }
+
+ @Test
+ void updatePetWithSameCategory() {
+ // given
+ mockCache();
+ mockRepository();
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog"))).block();
+ assertNotEquals(0L, added.id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.just(added));
+ Mockito.when(petRepository.update(any())).thenReturn(Mono.empty());
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("dog"))).blockOptional();
+ assertTrue(updated.isPresent());
+ assertNotEquals(0L, updated.get().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ }
+
+ private void mockCache() {
+ Mockito.when(petCache.getAsync(anyLong())).thenReturn(CompletableFuture.completedFuture(null));
+ Mockito.when(petCache.put(anyLong(), any())).then(invocation -> invocation.getArguments()[1]);
+ Mockito.when(petCache.getAsync(anyCollection())).thenReturn(CompletableFuture.completedFuture(Collections.emptyMap()));
+ }
+
+ private void mockRepository() {
+ Mockito.when(petRepository.insert(any())).thenReturn(Mono.empty());
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.empty());
+ }
+}
diff --git a/kora-java-graalvm-crud-cassandra/src/test/resources/logback-test.xml b/kora-java-graalvm-crud-cassandra/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..adccdab
--- /dev/null
+++ b/kora-java-graalvm-crud-cassandra/src/test/resources/logback-test.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ UTF-8
+ %cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-jdbc/Dockerfile b/kora-java-graalvm-crud-jdbc/Dockerfile
new file mode 100644
index 0000000..de40743
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/Dockerfile
@@ -0,0 +1,25 @@
+FROM ghcr.io/graalvm/native-image-community:21 as builder
+
+ARG APP_DIR=/opt/application
+ARG JAR_DIR=build/libs
+WORKDIR $APP_DIR
+
+ADD $JAR_DIR/*.jar $APP_DIR/application.jar
+
+RUN native-image --no-fallback -classpath $APP_DIR/application.jar
+
+FROM ubuntu:noble-20240212 as runner
+
+ARG APP_DIR=/opt/application
+WORKDIR $APP_DIR
+
+COPY --from=builder $APP_DIR/application $APP_DIR/application
+
+ARG DOCKER_USER=app
+RUN groupadd -r $DOCKER_USER && useradd -rg $DOCKER_USER $DOCKER_USER
+RUN chmod +x application
+USER $DOCKER_USER
+
+EXPOSE 8080/tcp
+EXPOSE 8085/tcp
+CMD "/opt/application/application"
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-jdbc/README.md b/kora-java-graalvm-crud-jdbc/README.md
new file mode 100644
index 0000000..86b668c
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/README.md
@@ -0,0 +1,49 @@
+# Kora Java GraalVM CRUD JDBC Service
+
+Пример сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
+в качестве базы данных выступает Postgres, используется кэш Caffeine, а также другие модули которые использовались бы в реальном приложении в бою.
+
+В примере использовались модули:
+- [HTTP Server](https://kora-projects.github.io/kora-docs/ru/documentation/http-server/)
+- [HTTP Server OpenAPI Generation](https://kora-projects.github.io/kora-docs/ru/documentation/openapi-codegen/)
+- [Probes](https://kora-projects.github.io/kora-docs/ru/documentation/probes/)
+- [Metrics](https://kora-projects.github.io/kora-docs/ru/documentation/metrics/)
+- [Database JDBC](https://kora-projects.github.io/kora-docs/ru/documentation/database-jdbc/)
+- [JSON](https://kora-projects.github.io/kora-docs/ru/documentation/json/)
+- [Resilient](https://kora-projects.github.io/kora-docs/ru/documentation/resilient/)
+- [Validation](https://kora-projects.github.io/kora-docs/ru/documentation/validation/)
+- [Cache Caffeine](https://kora-projects.github.io/kora-docs/ru/documentation/cache/#caffeine)
+
+Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_21/)
+
+## Build
+
+Собрать артефакт:
+
+```shell
+./gradlew shadowJar
+docker build -t kora-java-graalvm-crud-jdbc .
+```
+
+### Generate
+
+Сгенерировать API для HTTP Server:
+```shell
+./gradlew openApiGenerateHttpServer
+```
+
+## Run
+
+Запустить локально:
+```shell
+./gradlew run
+```
+
+## Test
+
+Тесты используют [Testcontainers](https://java.testcontainers.org/), требуется [Docker](https://docs.docker.com/engine/install/) окружение для запуска тестов или аналогичные контейнерные окружения ([colima](https://github.com/abiosoft/colima) / итп)
+
+Протестировать локально:
+```shell
+./gradlew test
+```
diff --git a/kora-java-graalvm-crud-jdbc/build.gradle b/kora-java-graalvm-crud-jdbc/build.gradle
new file mode 100644
index 0000000..99501a7
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/build.gradle
@@ -0,0 +1,180 @@
+buildscript {
+ dependencies {
+ classpath("ru.tinkoff.kora:openapi-generator:$koraVersion")
+ }
+}
+
+plugins {
+ id "java"
+ id "jacoco"
+ id "application"
+
+ id "org.openapi.generator" version "7.1.0"
+ id "com.github.johnrengelman.shadow" version "8.1.1"
+ id "org.flywaydb.flyway" version "8.4.2"
+ id "org.graalvm.buildtools.native" version "0.10.1"
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+mainClassName = "ru.tinkoff.kora.example.graalvm.crud.jdbc.Application"
+
+sourceCompatibility = JavaVersion.VERSION_17
+targetCompatibility = JavaVersion.VERSION_17
+
+configurations {
+ koraBom
+ implementation.extendsFrom(koraBom)
+ annotationProcessor.extendsFrom(koraBom)
+}
+
+dependencies {
+ koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
+ annotationProcessor "org.mapstruct:mapstruct-processor:1.5.5.Final"
+ annotationProcessor "ru.tinkoff.kora:annotation-processors"
+ annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
+ compileOnly "io.goodforgod:graalvm-hint-annotations:1.2.0"
+
+ implementation "ru.tinkoff.kora:http-server-undertow"
+ implementation "ru.tinkoff.kora:database-jdbc"
+ implementation "ru.tinkoff.kora:micrometer-module"
+ implementation "ru.tinkoff.kora:json-module"
+ implementation "ru.tinkoff.kora:validation-module"
+ implementation "ru.tinkoff.kora:cache-caffeine"
+ implementation "ru.tinkoff.kora:resilient-kora"
+ implementation "ru.tinkoff.kora:config-hocon"
+ implementation "ru.tinkoff.kora:openapi-management"
+ implementation "ru.tinkoff.kora:logging-logback"
+
+ runtimeOnly "org.postgresql:postgresql:42.7.2"
+ implementation "org.mapstruct:mapstruct:1.5.5.Final"
+
+ testImplementation "org.json:json:20231013"
+ testImplementation "org.skyscreamer:jsonassert:1.5.1"
+
+ testImplementation "org.mockito:mockito-core:5.6.0"
+ testImplementation "ru.tinkoff.kora:test-junit5"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
+ testImplementation "org.testcontainers:junit-jupiter:1.17.6"
+}
+
+openApiGenerate {
+ generatorName = "kora"
+ group = "openapi tools"
+ inputSpec = "$projectDir/src/main/resources/openapi/http-server.yaml"
+ outputDir = "$buildDir/generated/openapi"
+ apiPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.api"
+ modelPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.model"
+ invokerPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.invoker"
+ configOptions = [
+ mode : "java-server", // так же есть java-server вариация HTTP Server"а
+ enableServerValidation: "true"
+ ]
+}
+
+graalvmNative {
+ binaries {
+ main {
+ imageName = "$project.name"
+ mainClass = "$mainClassName"
+ javaLauncher = javaToolchains.launcherFor {
+ languageVersion = JavaLanguageVersion.of(21)
+ vendor = JvmVendorSpec.matching("GraalVM Community")
+ }
+ }
+ }
+ metadataRepository {
+ enabled = true
+ }
+}
+
+compileJava.dependsOn tasks.openApiGenerate
+processResources.dependsOn tasks.collectReachabilityMetadata
+test.dependsOn tasks.shadowJar
+
+//noinspection GroovyAssignabilityCheck
+run {
+ environment([
+ "POSTGRES_JDBC_URL": "jdbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase",
+ "POSTGRES_USER" : "$postgresUser",
+ "POSTGRES_PASS" : "$postgresPassword",
+ ])
+}
+
+test {
+ jvmArgs += [
+ "-XX:+TieredCompilation",
+ "-XX:TieredStopAtLevel=1",
+ ]
+
+ environment([
+ "": ""
+ ])
+
+ useJUnitPlatform()
+ testLogging {
+ showStandardStreams(true)
+ events("passed", "skipped", "failed")
+ exceptionFormat("full")
+ }
+
+ jacoco {
+ excludes += ["**/Application*"]
+ }
+
+ reports {
+ html.required = false
+ junitXml.required = false
+ }
+}
+
+sourceSets {
+ main {
+ java.srcDirs += "$buildDir/generated/openapi"
+ resources.srcDirs += "$buildDir/native-reachability-metadata"
+ }
+}
+
+flyway {
+ url = "jdbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase"
+ user = "$postgresUser"
+ password = "$postgresPassword"
+ locations = ["classpath:db/migration"]
+}
+
+jar.enabled = false
+shadowJar {
+ mergeServiceFiles()
+ manifest {
+ attributes "Main-Class": mainClassName
+ attributes "Implementation-Version": koraVersion
+ }
+}
+
+artifacts {
+ archives shadowJar
+}
+
+compileJava {
+ options.encoding("UTF-8")
+ options.incremental(true)
+ options.fork = true
+}
+
+check.dependsOn jacocoTestReport
+jacocoTestReport {
+ reports {
+ xml.required = true
+ html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
+ }
+}
+
+javadoc {
+ options.encoding = "UTF-8"
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption("html5", true)
+ }
+}
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-jdbc/gradle.properties b/kora-java-graalvm-crud-jdbc/gradle.properties
new file mode 100644
index 0000000..8e7a509
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/gradle.properties
@@ -0,0 +1,6 @@
+##### POSTGRES #####
+postgresHost=localhost
+postgresPort=5432
+postgresUser=postgres
+postgresPassword=postgres
+postgresDatabase=postgres
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/Application.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/Application.java
new file mode 100644
index 0000000..e7870e5
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/Application.java
@@ -0,0 +1,36 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc;
+
+import io.goodforgod.graalvm.hint.annotation.NativeImageHint;
+import io.goodforgod.graalvm.hint.annotation.ResourceHint;
+import ru.tinkoff.kora.application.graph.KoraApplication;
+import ru.tinkoff.kora.cache.caffeine.CaffeineCacheModule;
+import ru.tinkoff.kora.common.KoraApp;
+import ru.tinkoff.kora.config.hocon.HoconConfigModule;
+import ru.tinkoff.kora.database.jdbc.JdbcDatabaseModule;
+import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
+import ru.tinkoff.kora.json.module.JsonModule;
+import ru.tinkoff.kora.logging.logback.LogbackModule;
+import ru.tinkoff.kora.micrometer.module.MetricsModule;
+import ru.tinkoff.kora.openapi.management.OpenApiManagementModule;
+import ru.tinkoff.kora.resilient.ResilientModule;
+import ru.tinkoff.kora.validation.module.ValidationModule;
+
+@ResourceHint(include = { "openapi/http-server.yaml" })
+@NativeImageHint(name = "application", entrypoint = Application.class)
+@KoraApp
+public interface Application extends
+ HoconConfigModule,
+ LogbackModule,
+ JdbcDatabaseModule,
+ ValidationModule,
+ JsonModule,
+ CaffeineCacheModule,
+ ResilientModule,
+ MetricsModule,
+ OpenApiManagementModule,
+ UndertowHttpServerModule {
+
+ static void main(String[] args) {
+ KoraApplication.run(ApplicationGraph::graph);
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/controller/HttpExceptionHandler.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/controller/HttpExceptionHandler.java
new file mode 100644
index 0000000..5e3358c
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/controller/HttpExceptionHandler.java
@@ -0,0 +1,42 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.controller;
+
+import io.micrometer.core.instrument.config.validate.ValidationException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeoutException;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.common.Context;
+import ru.tinkoff.kora.common.Tag;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.http.common.body.HttpBody;
+import ru.tinkoff.kora.http.server.common.*;
+import ru.tinkoff.kora.json.common.JsonWriter;
+
+@Tag(HttpServerModule.class)
+@Component
+public final class HttpExceptionHandler implements HttpServerInterceptor {
+
+ private final JsonWriter errorJsonWriter;
+
+ public HttpExceptionHandler(JsonWriter errorJsonWriter) {
+ this.errorJsonWriter = errorJsonWriter;
+ }
+
+ @Override
+ public CompletionStage intercept(Context context, HttpServerRequest request, InterceptChain chain)
+ throws Exception {
+ return chain.process(context, request).exceptionally(e -> {
+ if (e instanceof HttpServerResponseException ex) {
+ return ex;
+ }
+
+ var body = HttpBody.json(errorJsonWriter.toByteArrayUnchecked(new MessageTO(e.getMessage())));
+ if (e instanceof IllegalArgumentException || e instanceof ValidationException) {
+ return HttpServerResponse.of(400, body);
+ } else if (e instanceof TimeoutException) {
+ return HttpServerResponse.of(408, body);
+ } else {
+ return HttpServerResponse.of(500, body);
+ }
+ });
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/controller/PetDelegate.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/controller/PetDelegate.java
new file mode 100644
index 0000000..ee8a82f
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/controller/PetDelegate.java
@@ -0,0 +1,79 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.controller;
+
+import static ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiResponses.*;
+
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.mapper.PetMapper;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.service.PetService;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiDelegate;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.*;
+
+@Component
+public final class PetDelegate implements PetApiDelegate {
+
+ private final PetMapper petMapper;
+ private final PetService petService;
+
+ public PetDelegate(PetMapper petMapper, PetService petService) {
+ this.petMapper = petMapper;
+ this.petService = petService;
+ }
+
+ @Override
+ public GetPetByIdApiResponse getPetById(long petId) {
+ if (petId < 0) {
+ return new GetPetByIdApiResponse.GetPetById400ApiResponse(malformedId(petId));
+ }
+
+ var pet = petService.findByID(petId);
+ if (pet.isPresent()) {
+ var body = petMapper.asDTO(pet.get());
+ return new GetPetByIdApiResponse.GetPetById200ApiResponse(body);
+ } else {
+ return new GetPetByIdApiResponse.GetPetById404ApiResponse(notFound(petId));
+ }
+ }
+
+ @Override
+ public AddPetApiResponse addPet(PetCreateTO petCreateTO) {
+ var pet = petService.add(petCreateTO);
+ var body = petMapper.asDTO(pet);
+ return new AddPetApiResponse.AddPet200ApiResponse(body);
+ }
+
+ @Override
+ public UpdatePetApiResponse updatePet(long petId, PetUpdateTO petUpdateTO) {
+ if (petId < 0) {
+ return new UpdatePetApiResponse.UpdatePet400ApiResponse(malformedId(petId));
+ }
+
+ var updated = petService.update(petId, petUpdateTO);
+ if (updated.isPresent()) {
+ var body = petMapper.asDTO(updated.get());
+ return new UpdatePetApiResponse.UpdatePet200ApiResponse(body);
+ } else {
+ return new UpdatePetApiResponse.UpdatePet404ApiResponse(notFound(petId));
+ }
+ }
+
+ @Override
+ public DeletePetApiResponse deletePet(long petId) {
+ if (petId < 0) {
+ return new DeletePetApiResponse.DeletePet400ApiResponse(malformedId(petId));
+ }
+
+ if (petService.delete(petId)) {
+ return new DeletePetApiResponse.DeletePet200ApiResponse(new MessageTO("Successfully deleted pet with ID: " + petId));
+ } else {
+ return new DeletePetApiResponse.DeletePet404ApiResponse(notFound(petId));
+ }
+ }
+
+ private static MessageTO notFound(long petId) {
+ return new MessageTO("Pet not found for ID: " + petId);
+ }
+
+ private static MessageTO malformedId(long petId) {
+ return new MessageTO("Pet malformed ID: " + petId);
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/Pet.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/Pet.java
new file mode 100644
index 0000000..1c4f907
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/Pet.java
@@ -0,0 +1,25 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+
+@Table("pets")
+public record Pet(@Id @Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Status status,
+ @Column("category_id") long categoryId) {
+
+ public enum Status {
+
+ AVAILABLE(0),
+ PENDING(10),
+ SOLD(20);
+
+ public final int code;
+
+ Status(int code) {
+ this.code = code;
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/PetCategory.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/PetCategory.java
new file mode 100644
index 0000000..98ab510
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/PetCategory.java
@@ -0,0 +1,8 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+
+@Table("categories")
+public record PetCategory(@Id long id,
+ String name) {}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/PetWithCategory.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/PetWithCategory.java
new file mode 100644
index 0000000..bc7966b
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/dao/PetWithCategory.java
@@ -0,0 +1,14 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Embedded;
+
+public record PetWithCategory(@Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Pet.Status status,
+ @Embedded("category_") PetCategory category) {
+
+ public Pet getPet() {
+ return new Pet(id, name, status, category.id());
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/mapper/PetMapper.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/mapper/PetMapper.java
new file mode 100644
index 0000000..b32b4b0
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/model/mapper/PetMapper.java
@@ -0,0 +1,15 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.model.mapper;
+
+import org.mapstruct.Mapper;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetCategory;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetWithCategory;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetTO;
+
+@Mapper
+public interface PetMapper {
+
+ PetTO asDTO(PetWithCategory pet);
+
+ CategoryTO asDTO(PetCategory category);
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/CategoryRepository.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/CategoryRepository.java
new file mode 100644
index 0000000..14a85e5
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/CategoryRepository.java
@@ -0,0 +1,22 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.repository;
+
+import java.util.Optional;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.database.jdbc.JdbcRepository;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetCategory;
+
+@Repository
+public interface CategoryRepository extends JdbcRepository {
+
+ @Query("SELECT %{return#selects} FROM %{return#table} WHERE name = :name")
+ Optional findByName(String name);
+
+ @Id
+ @Query("INSERT INTO categories(name) VALUES (:categoryName)")
+ long insert(String categoryName);
+
+ @Query("DELETE FROM categories WHERE id = :id")
+ void deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/PetRepository.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/PetRepository.java
new file mode 100644
index 0000000..af5be34
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/PetRepository.java
@@ -0,0 +1,32 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.repository;
+
+import java.util.Optional;
+import ru.tinkoff.kora.database.common.UpdateCount;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.database.jdbc.JdbcRepository;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetWithCategory;
+
+@Repository
+public interface PetRepository extends JdbcRepository {
+
+ @Query("""
+ SELECT p.id, p.name, p.status, p.category_id, c.name as category_name
+ FROM pets p
+ JOIN categories c on c.id = p.category_id
+ WHERE p.id = :id
+ """)
+ Optional findById(long id);
+
+ @Id
+ @Query("INSERT INTO %{entity#inserts -= id}")
+ long insert(Pet entity);
+
+ @Query("UPDATE %{entity#table} SET %{entity#updates} WHERE %{entity#where = @id}")
+ void update(Pet entity);
+
+ @Query("DELETE FROM pets WHERE id = :id")
+ UpdateCount deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/mapper/PetStatusParameterMapper.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/mapper/PetStatusParameterMapper.java
new file mode 100644
index 0000000..cec0871
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/mapper/PetStatusParameterMapper.java
@@ -0,0 +1,21 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.repository.mapper;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.jdbc.mapper.parameter.JdbcParameterColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.Pet;
+
+@Component
+public final class PetStatusParameterMapper implements JdbcParameterColumnMapper {
+
+ @Override
+ public void set(PreparedStatement stmt, int index, Pet.Status value) throws SQLException {
+ if (value == null) {
+ stmt.setNull(index, Types.INTEGER);
+ } else {
+ stmt.setInt(index, value.code);
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/mapper/PetStatusResultMapper.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/mapper/PetStatusResultMapper.java
new file mode 100644
index 0000000..62fb611
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/repository/mapper/PetStatusResultMapper.java
@@ -0,0 +1,25 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.repository.mapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.jdbc.mapper.result.JdbcResultColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.Pet;
+
+@Component
+public final class PetStatusResultMapper implements JdbcResultColumnMapper {
+
+ private final Pet.Status[] statuses = Pet.Status.values();
+
+ @Override
+ public Pet.Status apply(ResultSet row, int index) throws SQLException {
+ final int code = row.getInt(index);
+ for (Pet.Status status : statuses) {
+ if (code == status.code) {
+ return status;
+ }
+ }
+
+ throw new IllegalStateException("Unknown code: " + code);
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/service/PetCache.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/service/PetCache.java
new file mode 100644
index 0000000..cc9e795
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/service/PetCache.java
@@ -0,0 +1,10 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.service;
+
+import ru.tinkoff.kora.cache.annotation.Cache;
+import ru.tinkoff.kora.cache.caffeine.CaffeineCache;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetWithCategory;
+
+@Cache("pet-cache")
+public interface PetCache extends CaffeineCache {
+
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/service/PetService.java b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/service/PetService.java
new file mode 100644
index 0000000..28acd83
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/service/PetService.java
@@ -0,0 +1,94 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc.service;
+
+import java.util.Optional;
+import java.util.concurrent.ThreadLocalRandom;
+import ru.tinkoff.kora.cache.annotation.CacheInvalidate;
+import ru.tinkoff.kora.cache.annotation.CachePut;
+import ru.tinkoff.kora.cache.annotation.Cacheable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetCategory;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.model.dao.PetWithCategory;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.repository.CategoryRepository;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.repository.PetRepository;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.resilient.circuitbreaker.annotation.CircuitBreaker;
+import ru.tinkoff.kora.resilient.retry.annotation.Retry;
+import ru.tinkoff.kora.resilient.timeout.annotation.Timeout;
+
+@Component
+public class PetService {
+
+ private final PetRepository petRepository;
+ private final CategoryRepository categoryRepository;
+
+ public PetService(PetRepository petRepository, CategoryRepository categoryRepository) {
+ this.petRepository = petRepository;
+ this.categoryRepository = categoryRepository;
+ }
+
+ @Cacheable(PetCache.class)
+ @CircuitBreaker("pet")
+ @Retry("pet")
+ @Timeout("pet")
+ public Optional findByID(long petId) {
+ return petRepository.findById(petId);
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ public PetWithCategory add(PetCreateTO createTO) {
+ final long petCategoryId = categoryRepository.findByName(createTO.category().name())
+ .map(PetCategory::id)
+ .orElseGet(() -> categoryRepository.insert(createTO.category().name()));
+
+ var pet = new Pet(ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE), createTO.name(), Pet.Status.AVAILABLE,
+ petCategoryId);
+ var petId = petRepository.insert(pet);
+
+ return new PetWithCategory(petId, pet.name(), pet.status(),
+ new PetCategory(petCategoryId, createTO.category().name()));
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CachePut(value = PetCache.class, parameters = "id")
+ public Optional update(long id, PetUpdateTO updateTO) {
+ final Optional existing = petRepository.findById(id);
+ if (existing.isEmpty()) {
+ return Optional.empty();
+ }
+
+ var category = existing.get().category();
+ if (updateTO.category() != null) {
+ category = categoryRepository.findByName(updateTO.category().name()).orElseGet(() -> {
+ final long newCategoryId = categoryRepository.insert(updateTO.category().name());
+ return new PetCategory(newCategoryId, updateTO.category().name());
+ });
+ }
+
+ var status = (updateTO.status() == null)
+ ? existing.get().status()
+ : toStatus(updateTO.status());
+ var result = new PetWithCategory(existing.get().id(), updateTO.name(), status, category);
+
+ petRepository.update(result.getPet());
+ return Optional.of(result);
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CacheInvalidate(PetCache.class)
+ public boolean delete(long petId) {
+ return petRepository.deleteById(petId).value() == 1;
+ }
+
+ private static Pet.Status toStatus(PetUpdateTO.StatusEnum statusEnum) {
+ return switch (statusEnum) {
+ case AVAILABLE -> Pet.Status.AVAILABLE;
+ case PENDING -> Pet.Status.PENDING;
+ case SOLD -> Pet.Status.SOLD;
+ };
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/resources/application.conf b/kora-java-graalvm-crud-jdbc/src/main/resources/application.conf
new file mode 100644
index 0000000..b36f0c4
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/resources/application.conf
@@ -0,0 +1,60 @@
+httpServer {
+ publicApiHttpPort = 8080
+ privateApiHttpPort = 8085
+}
+
+
+db {
+ jdbcUrl = ${POSTGRES_JDBC_URL}
+ username = ${POSTGRES_USER}
+ password = ${POSTGRES_PASS}
+ maxPoolSize = 10
+ poolName = "kora"
+ initializationFailTimeout = "10s"
+}
+
+
+pet-cache {
+ maximumSize = 1000
+ expireAfterWrite = ${?CACHE_EXPIRE_WRITE}
+}
+
+
+openapi {
+ management {
+ enabled = true
+ file = "openapi/http-server.yaml"
+ swaggerui {
+ enabled = true
+ }
+ rapidoc {
+ enabled = true
+ }
+ }
+}
+
+
+resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 50
+ minimumRequiredCalls = 25
+ failureRateThreshold = 50
+ permittedCallsInHalfOpenState = 10
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+}
+
+
+logging.level {
+ "root": "WARN"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
+ "ru.tinkoff.kora.application.graph.internal.loom.VirtualThreadExecutorHolder": "DEBUG"
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/main/resources/db/migration/V1__setup-tables.sql b/kora-java-graalvm-crud-jdbc/src/main/resources/db/migration/V1__setup-tables.sql
new file mode 100644
index 0000000..3e70499
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/resources/db/migration/V1__setup-tables.sql
@@ -0,0 +1,16 @@
+CREATE TABLE IF NOT EXISTS categories
+(
+ id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR NOT NULL,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE IF NOT EXISTS pets
+(
+ id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR NOT NULL,
+ status SMALLINT NOT NULL,
+ category_id BIGINT NOT NULL REFERENCES categories(id),
+ PRIMARY KEY (id)
+);
diff --git a/kora-java-graalvm-crud-jdbc/src/main/resources/logback.xml b/kora-java-graalvm-crud-jdbc/src/main/resources/logback.xml
new file mode 100644
index 0000000..745e83f
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ UTF-8
+ %d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-jdbc/src/main/resources/openapi/http-server.yaml b/kora-java-graalvm-crud-jdbc/src/main/resources/openapi/http-server.yaml
new file mode 100644
index 0000000..8d99f49
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/main/resources/openapi/http-server.yaml
@@ -0,0 +1,315 @@
+openapi: 3.0.3
+info:
+ title: Swagger Petstore - OpenAPI 3.0
+ description: |-
+ This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
+ Swagger at [https://swagger.io](https://swagger.io).
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ version: 1.0.11
+externalDocs:
+ description: Find out more about Swagger
+ url: https://swagger.io
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: https://swagger.io
+paths:
+ /v3/pets:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: Add a new pet to the store
+ operationId: addPet
+ requestBody:
+ required: true
+ description: Create a new pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetCreateTO'
+ responses:
+ '200':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ /v3/pets/{id}:
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: id
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ nullable: false
+ minimum: 1
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: Update an existing pet by Id
+ operationId: updatePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ requestBody:
+ description: Update an existent pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetUpdateTO'
+ required: true
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: delete a pet
+ operationId: deletePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+components:
+ schemas:
+ MessageTO:
+ type: object
+ properties:
+ message:
+ type: string
+ CategoryTO:
+ required:
+ - id
+ - name
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 1
+ name:
+ type: string
+ example: Dogs
+ CategoryCreateTO:
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ type: string
+ example: Dogs
+ PetStatusTO:
+ properties:
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ PetTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 10
+ nullable: false
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryTO'
+ PetCreateTO:
+ required:
+ - name
+ - category
+ type: object
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
+ PetUpdateTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
diff --git a/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/AppContainer.java b/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/AppContainer.java
new file mode 100644
index 0000000..90192cd
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/AppContainer.java
@@ -0,0 +1,47 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.time.Duration;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.utility.DockerImageName;
+
+public final class AppContainer extends GenericContainer {
+
+ private AppContainer() {
+ super(new ImageFromDockerfile("kora-java-graalvm-crud-jdbc")
+ .withDockerfile(Paths.get("Dockerfile").toAbsolutePath()));
+ }
+
+ private AppContainer(DockerImageName image) {
+ super(image);
+ }
+
+ public static AppContainer build() {
+ final String appImage = System.getenv("IMAGE_KORA_JAVA_GRAALVM_CRUD_JDBC");
+ return (appImage != null && !appImage.isBlank())
+ ? new AppContainer(DockerImageName.parse(appImage))
+ : new AppContainer();
+ }
+
+ @Override
+ protected void configure() {
+ super.configure();
+ withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
+ withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
+ waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
+ }
+
+ public int getPort() {
+ return getMappedPort(8080);
+ }
+
+ public URI getURI() {
+ return URI.create(String.format("http://%s:%s", getHost(), getPort()));
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/PetControllerTests.java b/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/PetControllerTests.java
new file mode 100644
index 0000000..085fb59
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/PetControllerTests.java
@@ -0,0 +1,210 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import io.goodforgod.testcontainers.extensions.ContainerMode;
+import io.goodforgod.testcontainers.extensions.Network;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
+import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.Migration;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Map;
+import org.json.JSONObject;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+@TestcontainersPostgreSQL(
+ network = @Network(shared = true),
+ mode = ContainerMode.PER_RUN,
+ migration = @Migration(
+ engine = Migration.Engines.FLYWAY,
+ apply = Migration.Mode.PER_METHOD,
+ drop = Migration.Mode.PER_METHOD))
+class PetControllerTests {
+
+ private static final AppContainer container = AppContainer.build()
+ .withNetwork(org.testcontainers.containers.Network.SHARED);
+
+ @ConnectionPostgreSQL
+ private JdbcConnection connection;
+
+ @BeforeEach
+ public void setup(@ConnectionPostgreSQL JdbcConnection connection) {
+ if (!container.isRunning()) {
+ var params = connection.paramsInNetwork().orElseThrow();
+ container.withEnv(Map.of(
+ "POSTGRES_JDBC_URL", params.jdbcUrl(),
+ "POSTGRES_USER", params.username(),
+ "POSTGRES_PASS", params.password(),
+ "CACHE_EXPIRE_WRITE", "0s"));
+
+ container.start();
+ }
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ container.stop();
+ }
+
+ @Test
+ void addPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var requestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var request = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, response.statusCode(), response.body());
+
+ // then
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var responseBody = new JSONObject(response.body());
+ assertNotNull(responseBody.query("/id"));
+ assertNotEquals(0L, responseBody.query("/id"));
+ assertNotNull(responseBody.query("/status"));
+ assertEquals(requestBody.query("/name"), responseBody.query("/name"));
+ assertNotNull(responseBody.query("/category/id"));
+ assertEquals(requestBody.query("/category/name"), responseBody.query("/category/name"));
+ }
+
+ @Test
+ void getPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, getResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(createResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void updatePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var updateRequestBody = new JSONObject()
+ .put("name", "doggie2")
+ .put("status", "pending")
+ .put("category", new JSONObject()
+ .put("name", "Dogs2"));
+
+ var updateRequest = HttpRequest.newBuilder()
+ .PUT(HttpRequest.BodyPublishers.ofString(updateRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var updateResponse = httpClient.send(updateRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, updateResponse.statusCode(), updateResponse.body());
+ var updateResponseBody = new JSONObject(updateResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(updateResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void deletePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var deleteRequest = HttpRequest.newBuilder()
+ .DELETE()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var deleteResponse = httpClient.send(deleteRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, deleteResponse.statusCode(), deleteResponse.body());
+
+ // then
+ connection.assertCountsEquals(0, "pets");
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/PetServiceTests.java b/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/PetServiceTests.java
new file mode 100644
index 0000000..8ca45be
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/jdbc/PetServiceTests.java
@@ -0,0 +1,123 @@
+package ru.tinkoff.kora.example.graalvm.crud.jdbc;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.repository.CategoryRepository;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.repository.PetRepository;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.service.PetCache;
+import ru.tinkoff.kora.example.graalvm.crud.jdbc.service.PetService;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTestConfigModifier;
+import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
+import ru.tinkoff.kora.test.extension.junit5.TestComponent;
+
+@KoraAppTest(Application.class)
+class PetServiceTests implements KoraAppTestConfigModifier {
+
+ @Mock
+ @TestComponent
+ private PetCache petCache;
+ @Mock
+ @TestComponent
+ private PetRepository petRepository;
+ @Mock
+ @TestComponent
+ private CategoryRepository categoryRepository;
+
+ @TestComponent
+ private PetService petService;
+
+ @NotNull
+ @Override
+ public KoraConfigModification config() {
+ return KoraConfigModification.ofString("""
+ resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 2
+ minimumRequiredCalls = 2
+ failureRateThreshold = 100
+ permittedCallsInHalfOpenState = 1
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+ }
+ """);
+ }
+
+ @Test
+ void updatePetWithNewCategoryCreated() {
+ // given
+ mockCache();
+ mockRepository(Map.of("dog", 1L, "cat", 2L));
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog")));
+ assertEquals(1, added.id());
+ assertEquals(1, added.category().id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Optional.of(added));
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("cat")));
+ assertTrue(updated.isPresent());
+ assertEquals(1, updated.get().id());
+ assertEquals(2, updated.get().category().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ Mockito.verify(categoryRepository, Mockito.times(2)).insert(any());
+ }
+
+ @Test
+ void updatePetWithSameCategory() {
+ // given
+ mockCache();
+ mockRepository(Map.of("dog", 1L));
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog")));
+ assertEquals(1, added.id());
+ assertEquals(1, added.category().id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Optional.of(added));
+ Mockito.when(categoryRepository.findByName(any())).thenReturn(Optional.of(added.category()));
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("dog")));
+ assertTrue(updated.isPresent());
+ assertNotEquals(0, updated.get().id());
+ assertNotEquals(0, updated.get().category().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ Mockito.verify(categoryRepository).insert(any());
+ }
+
+ private void mockCache() {
+ Mockito.when(petCache.get(anyLong())).thenReturn(null);
+ Mockito.when(petCache.put(anyLong(), any())).then(invocation -> invocation.getArguments()[1]);
+ Mockito.when(petCache.get(anyCollection())).thenReturn(Collections.emptyMap());
+ }
+
+ private void mockRepository(Map categoryNameToId) {
+ categoryNameToId.forEach((k, v) -> Mockito.when(categoryRepository.insert(k)).thenReturn(v));
+ Mockito.when(categoryRepository.findByName(any())).thenReturn(Optional.empty());
+ Mockito.when(petRepository.insert(any())).thenReturn(1L);
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Optional.empty());
+ }
+}
diff --git a/kora-java-graalvm-crud-jdbc/src/test/resources/logback-test.xml b/kora-java-graalvm-crud-jdbc/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..adccdab
--- /dev/null
+++ b/kora-java-graalvm-crud-jdbc/src/test/resources/logback-test.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ UTF-8
+ %cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-r2dbc/Dockerfile b/kora-java-graalvm-crud-r2dbc/Dockerfile
new file mode 100644
index 0000000..de40743
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/Dockerfile
@@ -0,0 +1,25 @@
+FROM ghcr.io/graalvm/native-image-community:21 as builder
+
+ARG APP_DIR=/opt/application
+ARG JAR_DIR=build/libs
+WORKDIR $APP_DIR
+
+ADD $JAR_DIR/*.jar $APP_DIR/application.jar
+
+RUN native-image --no-fallback -classpath $APP_DIR/application.jar
+
+FROM ubuntu:noble-20240212 as runner
+
+ARG APP_DIR=/opt/application
+WORKDIR $APP_DIR
+
+COPY --from=builder $APP_DIR/application $APP_DIR/application
+
+ARG DOCKER_USER=app
+RUN groupadd -r $DOCKER_USER && useradd -rg $DOCKER_USER $DOCKER_USER
+RUN chmod +x application
+USER $DOCKER_USER
+
+EXPOSE 8080/tcp
+EXPOSE 8085/tcp
+CMD "/opt/application/application"
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-r2dbc/README.md b/kora-java-graalvm-crud-r2dbc/README.md
new file mode 100644
index 0000000..3b98d3b
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/README.md
@@ -0,0 +1,49 @@
+# Kora Java GraalVM CRUD R2DBC Service
+
+Пример сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
+в качестве базы данных выступает Postgres, используется кэш Caffeine, а также другие модули которые использовались бы в реальном приложении в бою.
+
+В примере использовались модули:
+- [HTTP Server](https://kora-projects.github.io/kora-docs/ru/documentation/http-server/)
+- [HTTP Server OpenAPI Generation](https://kora-projects.github.io/kora-docs/ru/documentation/openapi-codegen/)
+- [Probes](https://kora-projects.github.io/kora-docs/ru/documentation/probes/)
+- [Metrics](https://kora-projects.github.io/kora-docs/ru/documentation/metrics/)
+- [Database R2DBC](https://kora-projects.github.io/kora-docs/ru/documentation/database-r2dbc/)
+- [JSON](https://kora-projects.github.io/kora-docs/ru/documentation/json/)
+- [Resilient](https://kora-projects.github.io/kora-docs/ru/documentation/resilient/)
+- [Validation](https://kora-projects.github.io/kora-docs/ru/documentation/validation/)
+- [Cache Caffeine](https://kora-projects.github.io/kora-docs/ru/documentation/cache/#caffeine)
+
+Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_21/)
+
+## Build
+
+Собрать артефакт:
+
+```shell
+./gradlew shadowJar
+docker build -t kora-java-graalvm-crud-r2dbc .
+```
+
+### Generate
+
+Сгенерировать API для HTTP Server:
+```shell
+./gradlew openApiGenerateHttpServer
+```
+
+## Run
+
+Запустить локально:
+```shell
+./gradlew run
+```
+
+## Test
+
+Тесты используют [Testcontainers](https://java.testcontainers.org/), требуется [Docker](https://docs.docker.com/engine/install/) окружение для запуска тестов или аналогичные контейнерные окружения ([colima](https://github.com/abiosoft/colima) / итп)
+
+Протестировать локально:
+```shell
+./gradlew test
+```
diff --git a/kora-java-graalvm-crud-r2dbc/build.gradle b/kora-java-graalvm-crud-r2dbc/build.gradle
new file mode 100644
index 0000000..c8b135c
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/build.gradle
@@ -0,0 +1,182 @@
+buildscript {
+ dependencies {
+ classpath("ru.tinkoff.kora:openapi-generator:$koraVersion")
+ }
+}
+
+plugins {
+ id "java"
+ id "jacoco"
+ id "application"
+
+ id "org.openapi.generator" version "7.1.0"
+ id "com.github.johnrengelman.shadow" version "8.1.1"
+ id "org.flywaydb.flyway" version "8.4.2"
+ id "org.graalvm.buildtools.native" version "0.10.1"
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+mainClassName = "ru.tinkoff.kora.example.graalvm.crud.r2dbc.Application"
+
+sourceCompatibility = JavaVersion.VERSION_17
+targetCompatibility = JavaVersion.VERSION_17
+
+configurations {
+ koraBom
+ implementation.extendsFrom(koraBom)
+ annotationProcessor.extendsFrom(koraBom)
+}
+
+dependencies {
+ koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
+ annotationProcessor "org.mapstruct:mapstruct-processor:1.5.5.Final"
+ annotationProcessor "ru.tinkoff.kora:annotation-processors"
+ annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
+ compileOnly "io.goodforgod:graalvm-hint-annotations:1.2.0"
+ compileOnly "org.postgresql:postgresql:42.7.2"
+
+ implementation "ru.tinkoff.kora:http-server-undertow"
+ implementation "ru.tinkoff.kora:database-r2dbc"
+ implementation "ru.tinkoff.kora:micrometer-module"
+ implementation "ru.tinkoff.kora:json-module"
+ implementation "ru.tinkoff.kora:validation-module"
+ implementation "ru.tinkoff.kora:cache-caffeine"
+ implementation "ru.tinkoff.kora:resilient-kora"
+ implementation "ru.tinkoff.kora:config-hocon"
+ implementation "ru.tinkoff.kora:openapi-management"
+ implementation "ru.tinkoff.kora:logging-logback"
+
+ implementation "org.mapstruct:mapstruct:1.5.5.Final"
+ implementation "org.postgresql:r2dbc-postgresql:1.0.4.RELEASE"
+
+ testImplementation "org.json:json:20231013"
+ testImplementation "org.skyscreamer:jsonassert:1.5.1"
+ testImplementation "org.postgresql:postgresql:42.7.2"
+
+ testImplementation "org.mockito:mockito-core:5.6.0"
+ testImplementation "ru.tinkoff.kora:test-junit5"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
+ testImplementation "org.testcontainers:junit-jupiter:1.17.6"
+}
+
+openApiGenerate {
+ generatorName = "kora"
+ group = "openapi tools"
+ inputSpec = "$projectDir/src/main/resources/openapi/http-server.yaml"
+ outputDir = "$buildDir/generated/openapi"
+ apiPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.api"
+ modelPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.model"
+ invokerPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.invoker"
+ configOptions = [
+ mode : "java-reactive-server", // так же есть java-server вариация HTTP Server"а
+ enableServerValidation: "true"
+ ]
+}
+
+graalvmNative {
+ binaries {
+ main {
+ imageName = "$project.name"
+ mainClass = "$mainClassName"
+ javaLauncher = javaToolchains.launcherFor {
+ languageVersion = JavaLanguageVersion.of(21)
+ vendor = JvmVendorSpec.matching("GraalVM Community")
+ }
+ }
+ }
+ metadataRepository {
+ enabled = true
+ }
+}
+
+compileJava.dependsOn tasks.openApiGenerate
+processResources.dependsOn tasks.collectReachabilityMetadata
+test.dependsOn tasks.shadowJar
+
+//noinspection GroovyAssignabilityCheck
+run {
+ environment([
+ "POSTGRES_R2DBC_URL": "r2dbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase",
+ "POSTGRES_USER" : "$postgresUser",
+ "POSTGRES_PASS" : "$postgresPassword",
+ ])
+}
+
+test {
+ jvmArgs += [
+ "-XX:+TieredCompilation",
+ "-XX:TieredStopAtLevel=1",
+ ]
+
+ environment([
+ "": ""
+ ])
+
+ useJUnitPlatform()
+ testLogging {
+ showStandardStreams(true)
+ events("passed", "skipped", "failed")
+ exceptionFormat("full")
+ }
+
+ jacoco {
+ excludes += ["**/Application*"]
+ }
+
+ reports {
+ html.required = false
+ junitXml.required = false
+ }
+}
+
+sourceSets {
+ main {
+ java.srcDirs += "$buildDir/generated/openapi"
+ resources.srcDirs += "$buildDir/native-reachability-metadata"
+ }
+}
+
+flyway {
+ url = "jdbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase"
+ user = "$postgresUser"
+ password = "$postgresPassword"
+ locations = ["classpath:db/migration"]
+}
+
+jar.enabled = false
+shadowJar {
+ mergeServiceFiles()
+ manifest {
+ attributes "Main-Class": mainClassName
+ attributes "Implementation-Version": koraVersion
+ }
+}
+
+artifacts {
+ archives shadowJar
+}
+
+compileJava {
+ options.encoding("UTF-8")
+ options.incremental(true)
+ options.fork = true
+}
+
+check.dependsOn jacocoTestReport
+jacocoTestReport {
+ reports {
+ xml.required = true
+ html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
+ }
+}
+
+javadoc {
+ options.encoding = "UTF-8"
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption("html5", true)
+ }
+}
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-r2dbc/gradle.properties b/kora-java-graalvm-crud-r2dbc/gradle.properties
new file mode 100644
index 0000000..8e7a509
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/gradle.properties
@@ -0,0 +1,6 @@
+##### POSTGRES #####
+postgresHost=localhost
+postgresPort=5432
+postgresUser=postgres
+postgresPassword=postgres
+postgresDatabase=postgres
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/Application.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/Application.java
new file mode 100644
index 0000000..a877774
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/Application.java
@@ -0,0 +1,36 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc;
+
+import io.goodforgod.graalvm.hint.annotation.NativeImageHint;
+import io.goodforgod.graalvm.hint.annotation.ResourceHint;
+import ru.tinkoff.kora.application.graph.KoraApplication;
+import ru.tinkoff.kora.cache.caffeine.CaffeineCacheModule;
+import ru.tinkoff.kora.common.KoraApp;
+import ru.tinkoff.kora.config.hocon.HoconConfigModule;
+import ru.tinkoff.kora.database.r2dbc.R2dbcDatabaseModule;
+import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
+import ru.tinkoff.kora.json.module.JsonModule;
+import ru.tinkoff.kora.logging.logback.LogbackModule;
+import ru.tinkoff.kora.micrometer.module.MetricsModule;
+import ru.tinkoff.kora.openapi.management.OpenApiManagementModule;
+import ru.tinkoff.kora.resilient.ResilientModule;
+import ru.tinkoff.kora.validation.module.ValidationModule;
+
+@ResourceHint(include = { "openapi/http-server.yaml" })
+@NativeImageHint(name = "application", entrypoint = Application.class)
+@KoraApp
+public interface Application extends
+ HoconConfigModule,
+ LogbackModule,
+ R2dbcDatabaseModule,
+ ValidationModule,
+ JsonModule,
+ CaffeineCacheModule,
+ ResilientModule,
+ MetricsModule,
+ OpenApiManagementModule,
+ UndertowHttpServerModule {
+
+ static void main(String[] args) {
+ KoraApplication.run(ApplicationGraph::graph);
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/controller/HttpExceptionHandler.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/controller/HttpExceptionHandler.java
new file mode 100644
index 0000000..3a5f251
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/controller/HttpExceptionHandler.java
@@ -0,0 +1,42 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.controller;
+
+import io.micrometer.core.instrument.config.validate.ValidationException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeoutException;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.common.Context;
+import ru.tinkoff.kora.common.Tag;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.http.common.body.HttpBody;
+import ru.tinkoff.kora.http.server.common.*;
+import ru.tinkoff.kora.json.common.JsonWriter;
+
+@Tag(HttpServerModule.class)
+@Component
+public final class HttpExceptionHandler implements HttpServerInterceptor {
+
+ private final JsonWriter errorJsonWriter;
+
+ public HttpExceptionHandler(JsonWriter errorJsonWriter) {
+ this.errorJsonWriter = errorJsonWriter;
+ }
+
+ @Override
+ public CompletionStage intercept(Context context, HttpServerRequest request, InterceptChain chain)
+ throws Exception {
+ return chain.process(context, request).exceptionally(e -> {
+ if (e instanceof HttpServerResponseException ex) {
+ return ex;
+ }
+
+ var body = HttpBody.json(errorJsonWriter.toByteArrayUnchecked(new MessageTO(e.getMessage())));
+ if (e instanceof IllegalArgumentException || e instanceof ValidationException) {
+ return HttpServerResponse.of(400, body);
+ } else if (e instanceof TimeoutException) {
+ return HttpServerResponse.of(408, body);
+ } else {
+ return HttpServerResponse.of(500, body);
+ }
+ });
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/controller/PetDelegate.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/controller/PetDelegate.java
new file mode 100644
index 0000000..7222a13
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/controller/PetDelegate.java
@@ -0,0 +1,82 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.controller;
+
+import static ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiResponses.*;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiDelegate;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.mapper.PetMapper;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.service.PetService;
+
+@Component
+public final class PetDelegate implements PetApiDelegate {
+
+ private final PetMapper petMapper;
+ private final PetService petService;
+
+ public PetDelegate(PetMapper petMapper, PetService petService) {
+ this.petMapper = petMapper;
+ this.petService = petService;
+ }
+
+ @Override
+ public Mono getPetById(long petId) {
+ if (petId < 0) {
+ return Mono.just(new GetPetByIdApiResponse.GetPetById400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.findByID(petId)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return ((GetPetByIdApiResponse) new GetPetByIdApiResponse.GetPetById200ApiResponse(body));
+ })
+ .switchIfEmpty(Mono.fromSupplier(() -> new GetPetByIdApiResponse.GetPetById404ApiResponse(notFound(petId))));
+ }
+
+ @Override
+ public Mono addPet(PetCreateTO petCreateTO) {
+ return petService.add(petCreateTO)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return new AddPetApiResponse.AddPet200ApiResponse(body);
+ });
+ }
+
+ @Override
+ public Mono updatePet(long petId, PetUpdateTO petUpdateTO) {
+ if (petId < 0) {
+ return Mono.just(new UpdatePetApiResponse.UpdatePet400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.update(petId, petUpdateTO)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return ((UpdatePetApiResponse) new UpdatePetApiResponse.UpdatePet200ApiResponse(body));
+ })
+ .switchIfEmpty(Mono.fromSupplier(() -> new UpdatePetApiResponse.UpdatePet404ApiResponse(notFound(petId))));
+ }
+
+ @Override
+ public Mono deletePet(long petId) {
+ if (petId < 0) {
+ return Mono.just(new DeletePetApiResponse.DeletePet400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.delete(petId)
+ .map(isDeleted -> (isDeleted)
+ ? new DeletePetApiResponse.DeletePet200ApiResponse(
+ new MessageTO("Successfully deleted pet with ID: " + petId))
+ : new DeletePetApiResponse.DeletePet404ApiResponse(notFound(petId)));
+ }
+
+ private static MessageTO notFound(long petId) {
+ return new MessageTO("Pet not found for ID: " + petId);
+ }
+
+ private static MessageTO malformedId(long petId) {
+ return new MessageTO("Pet malformed ID: " + petId);
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/Pet.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/Pet.java
new file mode 100644
index 0000000..d285192
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/Pet.java
@@ -0,0 +1,25 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+
+@Table("pets")
+public record Pet(@Id @Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Status status,
+ @Column("category_id") long categoryId) {
+
+ public enum Status {
+
+ AVAILABLE(0),
+ PENDING(10),
+ SOLD(20);
+
+ public final int code;
+
+ Status(int code) {
+ this.code = code;
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/PetCategory.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/PetCategory.java
new file mode 100644
index 0000000..8cfb55f
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/PetCategory.java
@@ -0,0 +1,8 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+
+@Table("categories")
+public record PetCategory(@Id long id,
+ String name) {}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/PetWithCategory.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/PetWithCategory.java
new file mode 100644
index 0000000..eb44134
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/dao/PetWithCategory.java
@@ -0,0 +1,18 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Embedded;
+
+public record PetWithCategory(@Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Pet.Status status,
+ @Embedded("category_") PetCategory category) {
+
+ public PetWithCategory(Pet pet, PetCategory category) {
+ this(pet.id(), pet.name(), pet.status(), category);
+ }
+
+ public Pet getPet() {
+ return new Pet(id, name, status, category.id());
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/mapper/PetMapper.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/mapper/PetMapper.java
new file mode 100644
index 0000000..40effdd
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/model/mapper/PetMapper.java
@@ -0,0 +1,15 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.mapper;
+
+import org.mapstruct.Mapper;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetTO;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetCategory;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetWithCategory;
+
+@Mapper
+public interface PetMapper {
+
+ PetTO asDTO(PetWithCategory pet);
+
+ CategoryTO asDTO(PetCategory category);
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/CategoryRepository.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/CategoryRepository.java
new file mode 100644
index 0000000..a88df75
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/CategoryRepository.java
@@ -0,0 +1,23 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.database.common.UpdateCount;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.database.r2dbc.R2dbcRepository;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetCategory;
+
+@Repository
+public interface CategoryRepository extends R2dbcRepository {
+
+ @Query("SELECT %{return#selects} FROM %{return#table} WHERE name = :name")
+ Mono findByName(String name);
+
+ @Id
+ @Query("INSERT INTO categories(name) VALUES (:categoryName)")
+ Mono insert(String categoryName);
+
+ @Query("DELETE FROM categories WHERE id = :id")
+ Mono deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/PetRepository.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/PetRepository.java
new file mode 100644
index 0000000..9c18736
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/PetRepository.java
@@ -0,0 +1,32 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.database.common.UpdateCount;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.database.r2dbc.R2dbcRepository;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetWithCategory;
+
+@Repository
+public interface PetRepository extends R2dbcRepository {
+
+ @Query("""
+ SELECT p.id, p.name, p.status, p.category_id, c.name as category_name
+ FROM pets p
+ JOIN categories c on c.id = p.category_id
+ WHERE p.id = :id
+ """)
+ Mono findById(long id);
+
+ @Id
+ @Query("INSERT INTO %{entity#inserts -= id}")
+ Mono insert(Pet entity);
+
+ @Query("UPDATE %{entity#table} SET %{entity#updates} WHERE %{entity#where = @id}")
+ Mono update(Pet entity);
+
+ @Query("DELETE FROM pets WHERE id = :id")
+ Mono deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/mapper/PetStatusParameterMapper.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/mapper/PetStatusParameterMapper.java
new file mode 100644
index 0000000..958051f
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/mapper/PetStatusParameterMapper.java
@@ -0,0 +1,20 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository.mapper;
+
+import io.r2dbc.spi.Statement;
+import jakarta.annotation.Nullable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.r2dbc.mapper.parameter.R2dbcParameterColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.Pet;
+
+@Component
+public final class PetStatusParameterMapper implements R2dbcParameterColumnMapper {
+
+ @Override
+ public void apply(Statement stmt, int index, @Nullable Pet.Status value) {
+ if (value == null) {
+ stmt.bindNull(index, Integer.class);
+ } else {
+ stmt.bind(index, value.code);
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/mapper/PetStatusResultMapper.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/mapper/PetStatusResultMapper.java
new file mode 100644
index 0000000..543e2bf
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/repository/mapper/PetStatusResultMapper.java
@@ -0,0 +1,24 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository.mapper;
+
+import io.r2dbc.spi.Row;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.r2dbc.mapper.result.R2dbcResultColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.Pet;
+
+@Component
+public final class PetStatusResultMapper implements R2dbcResultColumnMapper {
+
+ private final Pet.Status[] statuses = Pet.Status.values();
+
+ @Override
+ public Pet.Status apply(Row row, String label) {
+ final int code = row.get(label, Integer.class);
+ for (Pet.Status status : statuses) {
+ if (code == status.code) {
+ return status;
+ }
+ }
+
+ throw new IllegalStateException("Unknown code: " + code);
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/service/PetCache.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/service/PetCache.java
new file mode 100644
index 0000000..3515e97
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/service/PetCache.java
@@ -0,0 +1,10 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.service;
+
+import ru.tinkoff.kora.cache.annotation.Cache;
+import ru.tinkoff.kora.cache.caffeine.CaffeineCache;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetWithCategory;
+
+@Cache("pet-cache")
+public interface PetCache extends CaffeineCache {
+
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/service/PetService.java b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/service/PetService.java
new file mode 100644
index 0000000..8a552e1
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/service/PetService.java
@@ -0,0 +1,91 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc.service;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.cache.annotation.CacheInvalidate;
+import ru.tinkoff.kora.cache.annotation.CachePut;
+import ru.tinkoff.kora.cache.annotation.Cacheable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetCategory;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.model.dao.PetWithCategory;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository.CategoryRepository;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository.PetRepository;
+import ru.tinkoff.kora.resilient.circuitbreaker.annotation.CircuitBreaker;
+import ru.tinkoff.kora.resilient.retry.annotation.Retry;
+import ru.tinkoff.kora.resilient.timeout.annotation.Timeout;
+
+@Component
+public class PetService {
+
+ private final PetRepository petRepository;
+ private final CategoryRepository categoryRepository;
+
+ public PetService(PetRepository petRepository, CategoryRepository categoryRepository) {
+ this.petRepository = petRepository;
+ this.categoryRepository = categoryRepository;
+ }
+
+ @Cacheable(PetCache.class)
+ @CircuitBreaker("pet")
+ @Retry("pet")
+ @Timeout("pet")
+ public Mono findByID(long petId) {
+ return petRepository.findById(petId);
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ public Mono add(PetCreateTO createTO) {
+ return categoryRepository.findByName(createTO.category().name())
+ .map(PetCategory::id)
+ .switchIfEmpty(categoryRepository.insert(createTO.category().name()))
+ .flatMap(categoryId -> {
+ var pet = new Pet(0, createTO.name(), Pet.Status.AVAILABLE, categoryId);
+ return petRepository.insert(pet)
+ .map(petId -> new PetWithCategory(petId, pet.name(), pet.status(),
+ new PetCategory(categoryId, createTO.category().name())));
+ });
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CachePut(value = PetCache.class, parameters = "id")
+ public Mono update(long id, PetUpdateTO updateTO) {
+ return petRepository.findById(id)
+ .flatMap(existingPet -> {
+ var categoryMono = Mono.just(existingPet.category());
+ if (updateTO.category() != null) {
+ categoryMono = categoryRepository.findByName(updateTO.category().name())
+ .switchIfEmpty(Mono.defer(() -> categoryRepository.insert(updateTO.category().name())
+ .map(newCategoryId -> new PetCategory(newCategoryId, updateTO.category().name()))));
+ }
+
+ var status = (updateTO.status() == null)
+ ? existingPet.status()
+ : toStatus(updateTO.status());
+
+ return categoryMono
+ .flatMap(category -> {
+ var result = new PetWithCategory(existingPet.id(), updateTO.name(), status, category);
+ return petRepository.update(result.getPet()).then(Mono.just(result));
+ });
+ });
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CacheInvalidate(PetCache.class)
+ public Mono delete(long petId) {
+ return petRepository.deleteById(petId).map(counter -> counter.value() == 1);
+ }
+
+ private static Pet.Status toStatus(PetUpdateTO.StatusEnum statusEnum) {
+ return switch (statusEnum) {
+ case AVAILABLE -> Pet.Status.AVAILABLE;
+ case PENDING -> Pet.Status.PENDING;
+ case SOLD -> Pet.Status.SOLD;
+ };
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/resources/application.conf b/kora-java-graalvm-crud-r2dbc/src/main/resources/application.conf
new file mode 100644
index 0000000..269f7c1
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/resources/application.conf
@@ -0,0 +1,60 @@
+httpServer {
+ publicApiHttpPort = 8080
+ privateApiHttpPort = 8085
+}
+
+
+db {
+ r2dbcUrl = ${POSTGRES_R2DBC_URL}
+ username = ${POSTGRES_USER}
+ password = ${POSTGRES_PASS}
+ maxPoolSize = 10
+ poolName = "kora"
+ initializationFailTimeout = "10s"
+}
+
+
+pet-cache {
+ maximumSize = 1000
+ expireAfterWrite = ${?CACHE_EXPIRE_WRITE}
+}
+
+
+openapi {
+ management {
+ enabled = true
+ file = "openapi/http-server.yaml"
+ swaggerui {
+ enabled = true
+ }
+ rapidoc {
+ enabled = true
+ }
+ }
+}
+
+
+resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 50
+ minimumRequiredCalls = 25
+ failureRateThreshold = 50
+ permittedCallsInHalfOpenState = 10
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+}
+
+
+logging.level {
+ "root": "WARN"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
+ "ru.tinkoff.kora.application.graph.internal.loom.VirtualThreadExecutorHolder": "DEBUG"
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/resources/db/migration/V1__setup-tables.sql b/kora-java-graalvm-crud-r2dbc/src/main/resources/db/migration/V1__setup-tables.sql
new file mode 100644
index 0000000..3e70499
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/resources/db/migration/V1__setup-tables.sql
@@ -0,0 +1,16 @@
+CREATE TABLE IF NOT EXISTS categories
+(
+ id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR NOT NULL,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE IF NOT EXISTS pets
+(
+ id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR NOT NULL,
+ status SMALLINT NOT NULL,
+ category_id BIGINT NOT NULL REFERENCES categories(id),
+ PRIMARY KEY (id)
+);
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/resources/logback.xml b/kora-java-graalvm-crud-r2dbc/src/main/resources/logback.xml
new file mode 100644
index 0000000..745e83f
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ UTF-8
+ %d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-r2dbc/src/main/resources/openapi/http-server.yaml b/kora-java-graalvm-crud-r2dbc/src/main/resources/openapi/http-server.yaml
new file mode 100644
index 0000000..8d99f49
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/main/resources/openapi/http-server.yaml
@@ -0,0 +1,315 @@
+openapi: 3.0.3
+info:
+ title: Swagger Petstore - OpenAPI 3.0
+ description: |-
+ This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
+ Swagger at [https://swagger.io](https://swagger.io).
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ version: 1.0.11
+externalDocs:
+ description: Find out more about Swagger
+ url: https://swagger.io
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: https://swagger.io
+paths:
+ /v3/pets:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: Add a new pet to the store
+ operationId: addPet
+ requestBody:
+ required: true
+ description: Create a new pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetCreateTO'
+ responses:
+ '200':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ /v3/pets/{id}:
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: id
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ nullable: false
+ minimum: 1
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: Update an existing pet by Id
+ operationId: updatePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ requestBody:
+ description: Update an existent pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetUpdateTO'
+ required: true
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: delete a pet
+ operationId: deletePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+components:
+ schemas:
+ MessageTO:
+ type: object
+ properties:
+ message:
+ type: string
+ CategoryTO:
+ required:
+ - id
+ - name
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 1
+ name:
+ type: string
+ example: Dogs
+ CategoryCreateTO:
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ type: string
+ example: Dogs
+ PetStatusTO:
+ properties:
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ PetTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 10
+ nullable: false
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryTO'
+ PetCreateTO:
+ required:
+ - name
+ - category
+ type: object
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
+ PetUpdateTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
diff --git a/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/AppContainer.java b/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/AppContainer.java
new file mode 100644
index 0000000..2071bdd
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/AppContainer.java
@@ -0,0 +1,47 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.time.Duration;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.utility.DockerImageName;
+
+public final class AppContainer extends GenericContainer {
+
+ private AppContainer() {
+ super(new ImageFromDockerfile("kora-java-graalvm-crud-r2dbc")
+ .withDockerfile(Paths.get("Dockerfile").toAbsolutePath()));
+ }
+
+ private AppContainer(DockerImageName image) {
+ super(image);
+ }
+
+ public static AppContainer build() {
+ final String appImage = System.getenv("IMAGE_KORA_JAVA_GRAALVM_CRUD_R2DBC");
+ return (appImage != null && !appImage.isBlank())
+ ? new AppContainer(DockerImageName.parse(appImage))
+ : new AppContainer();
+ }
+
+ @Override
+ protected void configure() {
+ super.configure();
+ withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
+ withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
+ waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
+ }
+
+ public int getPort() {
+ return getMappedPort(8080);
+ }
+
+ public URI getURI() {
+ return URI.create(String.format("http://%s:%s", getHost(), getPort()));
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/PetControllerTests.java b/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/PetControllerTests.java
new file mode 100644
index 0000000..61d56d1
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/PetControllerTests.java
@@ -0,0 +1,211 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import io.goodforgod.testcontainers.extensions.ContainerMode;
+import io.goodforgod.testcontainers.extensions.Network;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
+import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.Migration;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Map;
+import org.json.JSONObject;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+@TestcontainersPostgreSQL(
+ network = @Network(shared = true),
+ mode = ContainerMode.PER_RUN,
+ migration = @Migration(
+ engine = Migration.Engines.FLYWAY,
+ apply = Migration.Mode.PER_METHOD,
+ drop = Migration.Mode.PER_METHOD))
+class PetControllerTests {
+
+ private static final AppContainer container = AppContainer.build()
+ .withNetwork(org.testcontainers.containers.Network.SHARED);
+
+ @ConnectionPostgreSQL
+ private JdbcConnection connection;
+
+ @BeforeEach
+ public void setup(@ConnectionPostgreSQL JdbcConnection connection) {
+ if (!container.isRunning()) {
+ var params = connection.paramsInNetwork().orElseThrow();
+ container.withEnv(Map.of(
+ "POSTGRES_R2DBC_URL",
+ "r2dbc:postgresql://%s:%s/%s".formatted(params.host(), params.port(), params.database()),
+ "POSTGRES_USER", params.username(),
+ "POSTGRES_PASS", params.password(),
+ "CACHE_EXPIRE_WRITE", "0s"));
+
+ container.start();
+ }
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ container.stop();
+ }
+
+ @Test
+ void addPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var requestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var request = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, response.statusCode(), response.body());
+
+ // then
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var responseBody = new JSONObject(response.body());
+ assertNotNull(responseBody.query("/id"));
+ assertNotEquals(0L, responseBody.query("/id"));
+ assertNotNull(responseBody.query("/status"));
+ assertEquals(requestBody.query("/name"), responseBody.query("/name"));
+ assertNotNull(responseBody.query("/category/id"));
+ assertEquals(requestBody.query("/category/name"), responseBody.query("/category/name"));
+ }
+
+ @Test
+ void getPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, getResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(createResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void updatePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var updateRequestBody = new JSONObject()
+ .put("name", "doggie2")
+ .put("status", "pending")
+ .put("category", new JSONObject()
+ .put("name", "Dogs2"));
+
+ var updateRequest = HttpRequest.newBuilder()
+ .PUT(HttpRequest.BodyPublishers.ofString(updateRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var updateResponse = httpClient.send(updateRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, updateResponse.statusCode(), updateResponse.body());
+ var updateResponseBody = new JSONObject(updateResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(updateResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void deletePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var deleteRequest = HttpRequest.newBuilder()
+ .DELETE()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var deleteResponse = httpClient.send(deleteRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, deleteResponse.statusCode(), deleteResponse.body());
+
+ // then
+ connection.assertCountsEquals(0, "pets");
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/PetServiceTests.java b/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/PetServiceTests.java
new file mode 100644
index 0000000..47fec72
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/test/java/ru/tinkoff/kora/example/graalvm/crud/r2dbc/PetServiceTests.java
@@ -0,0 +1,127 @@
+package ru.tinkoff.kora.example.graalvm.crud.r2dbc;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+
+import java.util.Collections;
+import java.util.Map;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository.CategoryRepository;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.repository.PetRepository;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.service.PetCache;
+import ru.tinkoff.kora.example.graalvm.crud.r2dbc.service.PetService;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTestConfigModifier;
+import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
+import ru.tinkoff.kora.test.extension.junit5.TestComponent;
+
+@KoraAppTest(Application.class)
+class PetServiceTests implements KoraAppTestConfigModifier {
+
+ @Mock
+ @TestComponent
+ private PetCache petCache;
+ @Mock
+ @TestComponent
+ private PetRepository petRepository;
+ @Mock
+ @TestComponent
+ private CategoryRepository categoryRepository;
+
+ @TestComponent
+ private PetService petService;
+
+ @NotNull
+ @Override
+ public KoraConfigModification config() {
+ return KoraConfigModification.ofString("""
+ resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 2
+ minimumRequiredCalls = 2
+ failureRateThreshold = 100
+ permittedCallsInHalfOpenState = 1
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+ }
+ """);
+ }
+
+ @Test
+ void updatePetWithNewCategoryCreated() {
+ // given
+ mockCache();
+ mockRepository(Map.of("dog", 1L, "cat", 2L));
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog"))).block();
+ assertEquals(1, added.id());
+ assertEquals(1, added.category().id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.just(added));
+ Mockito.when(petRepository.update(any())).thenReturn(Mono.empty());
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("cat")))
+ .blockOptional();
+ assertTrue(updated.isPresent());
+ assertEquals(1, updated.get().id());
+ assertEquals(2, updated.get().category().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ Mockito.verify(categoryRepository, Mockito.times(2)).insert(any());
+ }
+
+ @Test
+ void updatePetWithSameCategory() {
+ // given
+ mockCache();
+ mockRepository(Map.of("dog", 1L));
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog"))).block();
+ assertEquals(1, added.id());
+ assertEquals(1, added.category().id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.just(added));
+ Mockito.when(petRepository.update(any())).thenReturn(Mono.empty());
+ Mockito.when(categoryRepository.findByName(any())).thenReturn(Mono.just(added.category()));
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("dog")))
+ .blockOptional();
+ assertTrue(updated.isPresent());
+ assertNotEquals(0, updated.get().id());
+ assertNotEquals(0, updated.get().category().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ Mockito.verify(categoryRepository).insert(any());
+ }
+
+ private void mockCache() {
+ Mockito.when(petCache.get(anyLong())).thenReturn(null);
+ Mockito.when(petCache.put(anyLong(), any())).then(invocation -> invocation.getArguments()[1]);
+ Mockito.when(petCache.get(anyCollection())).thenReturn(Collections.emptyMap());
+ }
+
+ private void mockRepository(Map categoryNameToId) {
+ categoryNameToId.forEach((k, v) -> Mockito.when(categoryRepository.insert(k)).thenReturn(Mono.just(v)));
+ Mockito.when(categoryRepository.findByName(any())).thenReturn(Mono.empty());
+ Mockito.when(petRepository.insert(any())).thenReturn(Mono.just(1L));
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.empty());
+ }
+}
diff --git a/kora-java-graalvm-crud-r2dbc/src/test/resources/logback-test.xml b/kora-java-graalvm-crud-r2dbc/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..adccdab
--- /dev/null
+++ b/kora-java-graalvm-crud-r2dbc/src/test/resources/logback-test.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ UTF-8
+ %cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-vertx/Dockerfile b/kora-java-graalvm-crud-vertx/Dockerfile
new file mode 100644
index 0000000..de40743
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/Dockerfile
@@ -0,0 +1,25 @@
+FROM ghcr.io/graalvm/native-image-community:21 as builder
+
+ARG APP_DIR=/opt/application
+ARG JAR_DIR=build/libs
+WORKDIR $APP_DIR
+
+ADD $JAR_DIR/*.jar $APP_DIR/application.jar
+
+RUN native-image --no-fallback -classpath $APP_DIR/application.jar
+
+FROM ubuntu:noble-20240212 as runner
+
+ARG APP_DIR=/opt/application
+WORKDIR $APP_DIR
+
+COPY --from=builder $APP_DIR/application $APP_DIR/application
+
+ARG DOCKER_USER=app
+RUN groupadd -r $DOCKER_USER && useradd -rg $DOCKER_USER $DOCKER_USER
+RUN chmod +x application
+USER $DOCKER_USER
+
+EXPOSE 8080/tcp
+EXPOSE 8085/tcp
+CMD "/opt/application/application"
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-vertx/README.md b/kora-java-graalvm-crud-vertx/README.md
new file mode 100644
index 0000000..2aef01a
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/README.md
@@ -0,0 +1,49 @@
+# Kora Java GraalVM CRUD Vertx Service
+
+Пример сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
+в качестве базы данных выступает Postgres, используется кэш Caffeine, а также другие модули которые использовались бы в реальном приложении в бою.
+
+В примере использовались модули:
+- [HTTP Server](https://kora-projects.github.io/kora-docs/ru/documentation/http-server/)
+- [HTTP Server OpenAPI Generation](https://kora-projects.github.io/kora-docs/ru/documentation/openapi-codegen/)
+- [Probes](https://kora-projects.github.io/kora-docs/ru/documentation/probes/)
+- [Metrics](https://kora-projects.github.io/kora-docs/ru/documentation/metrics/)
+- [Database Vertx](https://kora-projects.github.io/kora-docs/ru/documentation/database-vertx/)
+- [JSON](https://kora-projects.github.io/kora-docs/ru/documentation/json/)
+- [Resilient](https://kora-projects.github.io/kora-docs/ru/documentation/resilient/)
+- [Validation](https://kora-projects.github.io/kora-docs/ru/documentation/validation/)
+- [Cache Caffeine](https://kora-projects.github.io/kora-docs/ru/documentation/cache/#caffeine)
+
+Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_21/)
+
+## Build
+
+Собрать артефакт:
+
+```shell
+./gradlew shadowJar
+docker build -t kora-java-graalvm-crud-vertx .
+```
+
+### Generate
+
+Сгенерировать API для HTTP Server:
+```shell
+./gradlew openApiGenerateHttpServer
+```
+
+## Run
+
+Запустить локально:
+```shell
+./gradlew run
+```
+
+## Test
+
+Тесты используют [Testcontainers](https://java.testcontainers.org/), требуется [Docker](https://docs.docker.com/engine/install/) окружение для запуска тестов или аналогичные контейнерные окружения ([colima](https://github.com/abiosoft/colima) / итп)
+
+Протестировать локально:
+```shell
+./gradlew test
+```
diff --git a/kora-java-graalvm-crud-vertx/build.gradle b/kora-java-graalvm-crud-vertx/build.gradle
new file mode 100644
index 0000000..bcae7f3
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/build.gradle
@@ -0,0 +1,184 @@
+buildscript {
+ dependencies {
+ classpath("ru.tinkoff.kora:openapi-generator:$koraVersion")
+ }
+}
+
+plugins {
+ id "java"
+ id "jacoco"
+ id "application"
+
+ id "org.openapi.generator" version "7.1.0"
+ id "com.github.johnrengelman.shadow" version "8.1.1"
+ id "org.flywaydb.flyway" version "8.4.2"
+ id "org.graalvm.buildtools.native" version "0.10.1"
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+mainClassName = "ru.tinkoff.kora.example.graalvm.crud.vertx.Application"
+
+sourceCompatibility = JavaVersion.VERSION_17
+targetCompatibility = JavaVersion.VERSION_17
+
+configurations {
+ koraBom
+ implementation.extendsFrom(koraBom)
+ annotationProcessor.extendsFrom(koraBom)
+}
+
+dependencies {
+ koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
+ annotationProcessor "org.mapstruct:mapstruct-processor:1.5.5.Final"
+ annotationProcessor "ru.tinkoff.kora:annotation-processors"
+ annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
+ compileOnly "io.goodforgod:graalvm-hint-annotations:1.2.0"
+ compileOnly "org.postgresql:postgresql:42.7.2"
+
+ implementation "ru.tinkoff.kora:http-server-undertow"
+ implementation "ru.tinkoff.kora:database-vertx"
+ implementation "ru.tinkoff.kora:micrometer-module"
+ implementation "ru.tinkoff.kora:json-module"
+ implementation "ru.tinkoff.kora:validation-module"
+ implementation "ru.tinkoff.kora:cache-caffeine"
+ implementation "ru.tinkoff.kora:resilient-kora"
+ implementation "ru.tinkoff.kora:config-hocon"
+ implementation "ru.tinkoff.kora:openapi-management"
+ implementation "ru.tinkoff.kora:logging-logback"
+
+ implementation "org.mapstruct:mapstruct:1.5.5.Final"
+ implementation "io.vertx:vertx-pg-client:4.3.8"
+ implementation "com.ongres.scram:client:2.1"
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
+
+ testImplementation "org.json:json:20231013"
+ testImplementation "org.skyscreamer:jsonassert:1.5.1"
+ testImplementation "org.postgresql:postgresql:42.7.2"
+
+ testImplementation "org.mockito:mockito-core:5.6.0"
+ testImplementation "ru.tinkoff.kora:test-junit5"
+ testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.11.0"
+ testImplementation "org.testcontainers:junit-jupiter:1.17.6"
+}
+
+openApiGenerate {
+ generatorName = "kora"
+ group = "openapi tools"
+ inputSpec = "$projectDir/src/main/resources/openapi/http-server.yaml"
+ outputDir = "$buildDir/generated/openapi"
+ apiPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.api"
+ modelPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.model"
+ invokerPackage = "ru.tinkoff.kora.example.graalvm.crud.openapi.server.invoker"
+ configOptions = [
+ mode : "java-reactive-server", // так же есть java-server вариация HTTP Server"а
+ enableServerValidation: "true"
+ ]
+}
+
+graalvmNative {
+ binaries {
+ main {
+ imageName = "$project.name"
+ mainClass = "$mainClassName"
+ javaLauncher = javaToolchains.launcherFor {
+ languageVersion = JavaLanguageVersion.of(21)
+ vendor = JvmVendorSpec.matching("GraalVM Community")
+ }
+ }
+ }
+ metadataRepository {
+ enabled = true
+ }
+}
+
+compileJava.dependsOn tasks.openApiGenerate
+processResources.dependsOn tasks.collectReachabilityMetadata
+test.dependsOn tasks.shadowJar
+
+//noinspection GroovyAssignabilityCheck
+run {
+ environment([
+ "POSTGRES_VERTX_URL": ":postgresql://$postgresHost:$postgresPort/$postgresDatabase",
+ "POSTGRES_USER" : "$postgresUser",
+ "POSTGRES_PASS" : "$postgresPassword",
+ ])
+}
+
+test {
+ jvmArgs += [
+ "-XX:+TieredCompilation",
+ "-XX:TieredStopAtLevel=1",
+ ]
+
+ environment([
+ "": ""
+ ])
+
+ useJUnitPlatform()
+ testLogging {
+ showStandardStreams(true)
+ events("passed", "skipped", "failed")
+ exceptionFormat("full")
+ }
+
+ jacoco {
+ excludes += ["**/Application*"]
+ }
+
+ reports {
+ html.required = false
+ junitXml.required = false
+ }
+}
+
+sourceSets {
+ main {
+ java.srcDirs += "$buildDir/generated/openapi"
+ resources.srcDirs += "$buildDir/native-reachability-metadata"
+ }
+}
+
+flyway {
+ url = "jdbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase"
+ user = "$postgresUser"
+ password = "$postgresPassword"
+ locations = ["classpath:db/migration"]
+}
+
+jar.enabled = false
+shadowJar {
+ mergeServiceFiles()
+ manifest {
+ attributes "Main-Class": mainClassName
+ attributes "Implementation-Version": koraVersion
+ }
+}
+
+artifacts {
+ archives shadowJar
+}
+
+compileJava {
+ options.encoding("UTF-8")
+ options.incremental(true)
+ options.fork = true
+}
+
+check.dependsOn jacocoTestReport
+jacocoTestReport {
+ reports {
+ xml.required = true
+ html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
+ }
+}
+
+javadoc {
+ options.encoding = "UTF-8"
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption("html5", true)
+ }
+}
\ No newline at end of file
diff --git a/kora-java-graalvm-crud-vertx/gradle.properties b/kora-java-graalvm-crud-vertx/gradle.properties
new file mode 100644
index 0000000..8e7a509
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/gradle.properties
@@ -0,0 +1,6 @@
+##### POSTGRES #####
+postgresHost=localhost
+postgresPort=5432
+postgresUser=postgres
+postgresPassword=postgres
+postgresDatabase=postgres
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/Application.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/Application.java
new file mode 100644
index 0000000..37c675b
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/Application.java
@@ -0,0 +1,36 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx;
+
+import io.goodforgod.graalvm.hint.annotation.NativeImageHint;
+import io.goodforgod.graalvm.hint.annotation.ResourceHint;
+import ru.tinkoff.kora.application.graph.KoraApplication;
+import ru.tinkoff.kora.cache.caffeine.CaffeineCacheModule;
+import ru.tinkoff.kora.common.KoraApp;
+import ru.tinkoff.kora.config.hocon.HoconConfigModule;
+import ru.tinkoff.kora.database.vertx.VertxDatabaseModule;
+import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
+import ru.tinkoff.kora.json.module.JsonModule;
+import ru.tinkoff.kora.logging.logback.LogbackModule;
+import ru.tinkoff.kora.micrometer.module.MetricsModule;
+import ru.tinkoff.kora.openapi.management.OpenApiManagementModule;
+import ru.tinkoff.kora.resilient.ResilientModule;
+import ru.tinkoff.kora.validation.module.ValidationModule;
+
+@ResourceHint(include = { "openapi/http-server.yaml" })
+@NativeImageHint(name = "application", entrypoint = Application.class)
+@KoraApp
+public interface Application extends
+ HoconConfigModule,
+ LogbackModule,
+ VertxDatabaseModule,
+ ValidationModule,
+ JsonModule,
+ CaffeineCacheModule,
+ ResilientModule,
+ MetricsModule,
+ OpenApiManagementModule,
+ UndertowHttpServerModule {
+
+ static void main(String[] args) {
+ KoraApplication.run(ApplicationGraph::graph);
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/controller/HttpExceptionHandler.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/controller/HttpExceptionHandler.java
new file mode 100644
index 0000000..9a6fa53
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/controller/HttpExceptionHandler.java
@@ -0,0 +1,43 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.controller;
+
+import io.micrometer.core.instrument.config.validate.ValidationException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeoutException;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.common.Context;
+import ru.tinkoff.kora.common.Tag;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.http.common.body.HttpBody;
+import ru.tinkoff.kora.http.server.common.*;
+import ru.tinkoff.kora.json.common.JsonWriter;
+
+@Tag(HttpServerModule.class)
+@Component
+public final class HttpExceptionHandler implements HttpServerInterceptor {
+
+ private final JsonWriter errorJsonWriter;
+
+ public HttpExceptionHandler(JsonWriter errorJsonWriter) {
+ this.errorJsonWriter = errorJsonWriter;
+ }
+
+ @Override
+ public CompletionStage intercept(Context context, HttpServerRequest request, InterceptChain chain)
+ throws Exception {
+ return chain.process(context, request).exceptionally(e -> {
+ if (e instanceof HttpServerResponseException ex) {
+ return ex;
+ }
+
+ e.printStackTrace();
+ var body = HttpBody.json(errorJsonWriter.toByteArrayUnchecked(new MessageTO(e.getMessage())));
+ if (e instanceof IllegalArgumentException || e instanceof ValidationException) {
+ return HttpServerResponse.of(400, body);
+ } else if (e instanceof TimeoutException) {
+ return HttpServerResponse.of(408, body);
+ } else {
+ return HttpServerResponse.of(500, body);
+ }
+ });
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/controller/PetDelegate.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/controller/PetDelegate.java
new file mode 100644
index 0000000..572271d
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/controller/PetDelegate.java
@@ -0,0 +1,93 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.controller;
+
+import static ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiResponses.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.api.PetApiDelegate;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.MessageTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.mapper.PetMapper;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.service.PetService;
+
+@Component
+public final class PetDelegate implements PetApiDelegate {
+
+ private final PetMapper petMapper;
+ private final PetService petService;
+
+ public PetDelegate(PetMapper petMapper, PetService petService) {
+ this.petMapper = petMapper;
+ this.petService = petService;
+ }
+
+ @Override
+ public Mono getPetById(long petId) {
+ if (petId < 0) {
+ return Mono.just(new GetPetByIdApiResponse.GetPetById400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.findByID(petId)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return ((GetPetByIdApiResponse) new GetPetByIdApiResponse.GetPetById200ApiResponse(body));
+ })
+ .switchIfEmpty(Mono.fromSupplier(() -> new GetPetByIdApiResponse.GetPetById404ApiResponse(notFound(petId))));
+ }
+
+ @Override
+ public Mono addPet(PetCreateTO petCreateTO) {
+ Logger logger = LoggerFactory.getLogger(getClass());
+ return petService.add(petCreateTO)
+ .doOnSuccess(r -> {
+ if (r == null) {
+ logger.warn("INSERT NULL");
+ } else {
+ logger.warn("INSERT NOT NULL");
+ }
+ })
+ .doOnError(e -> { logger.warn("ERROR - " + e); })
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return new AddPetApiResponse.AddPet200ApiResponse(body);
+ });
+ }
+
+ @Override
+ public Mono updatePet(long petId, PetUpdateTO petUpdateTO) {
+ if (petId < 0) {
+ return Mono.just(new UpdatePetApiResponse.UpdatePet400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.update(petId, petUpdateTO)
+ .map(pet -> {
+ var body = petMapper.asDTO(pet);
+ return ((UpdatePetApiResponse) new UpdatePetApiResponse.UpdatePet200ApiResponse(body));
+ })
+ .switchIfEmpty(Mono.fromSupplier(() -> new UpdatePetApiResponse.UpdatePet404ApiResponse(notFound(petId))));
+ }
+
+ @Override
+ public Mono deletePet(long petId) {
+ if (petId < 0) {
+ return Mono.just(new DeletePetApiResponse.DeletePet400ApiResponse(malformedId(petId)));
+ }
+
+ return petService.delete(petId)
+ .map(isDeleted -> (isDeleted)
+ ? new DeletePetApiResponse.DeletePet200ApiResponse(
+ new MessageTO("Successfully deleted pet with ID: " + petId))
+ : new DeletePetApiResponse.DeletePet404ApiResponse(notFound(petId)));
+ }
+
+ private static MessageTO notFound(long petId) {
+ return new MessageTO("Pet not found for ID: " + petId);
+ }
+
+ private static MessageTO malformedId(long petId) {
+ return new MessageTO("Pet malformed ID: " + petId);
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/Pet.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/Pet.java
new file mode 100644
index 0000000..fc8f842
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/Pet.java
@@ -0,0 +1,25 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+
+@Table("pets")
+public record Pet(@Id @Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Status status,
+ @Column("category_id") long categoryId) {
+
+ public enum Status {
+
+ AVAILABLE(0),
+ PENDING(10),
+ SOLD(20);
+
+ public final int code;
+
+ Status(int code) {
+ this.code = code;
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/PetCategory.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/PetCategory.java
new file mode 100644
index 0000000..0dc285e
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/PetCategory.java
@@ -0,0 +1,8 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Id;
+import ru.tinkoff.kora.database.common.annotation.Table;
+
+@Table("categories")
+public record PetCategory(@Id long id,
+ String name) {}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/PetWithCategory.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/PetWithCategory.java
new file mode 100644
index 0000000..7c15099
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/dao/PetWithCategory.java
@@ -0,0 +1,14 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao;
+
+import ru.tinkoff.kora.database.common.annotation.Column;
+import ru.tinkoff.kora.database.common.annotation.Embedded;
+
+public record PetWithCategory(@Column("id") long id,
+ @Column("name") String name,
+ @Column("status") Pet.Status status,
+ @Embedded("category_") PetCategory category) {
+
+ public Pet getPet() {
+ return new Pet(id, name, status, category.id());
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/mapper/PetMapper.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/mapper/PetMapper.java
new file mode 100644
index 0000000..2372331
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/model/mapper/PetMapper.java
@@ -0,0 +1,15 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.model.mapper;
+
+import org.mapstruct.Mapper;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetTO;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetCategory;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetWithCategory;
+
+@Mapper
+public interface PetMapper {
+
+ PetTO asDTO(PetWithCategory pet);
+
+ CategoryTO asDTO(PetCategory category);
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/CategoryRepository.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/CategoryRepository.java
new file mode 100644
index 0000000..ca5a28b
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/CategoryRepository.java
@@ -0,0 +1,21 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.repository;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.database.common.UpdateCount;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.database.vertx.VertxRepository;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetCategory;
+
+@Repository
+public interface CategoryRepository extends VertxRepository {
+
+ @Query("SELECT %{return#selects} FROM %{return#table} WHERE name = :name")
+ Mono findByName(String name);
+
+ @Query("INSERT INTO categories(name) VALUES (:categoryName) RETURNING id")
+ Mono insert(String categoryName);
+
+ @Query("DELETE FROM categories WHERE id = :id")
+ Mono deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/PetRepository.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/PetRepository.java
new file mode 100644
index 0000000..e299066
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/PetRepository.java
@@ -0,0 +1,30 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.repository;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.database.common.UpdateCount;
+import ru.tinkoff.kora.database.common.annotation.Query;
+import ru.tinkoff.kora.database.common.annotation.Repository;
+import ru.tinkoff.kora.database.vertx.VertxRepository;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetWithCategory;
+
+@Repository
+public interface PetRepository extends VertxRepository {
+
+ @Query("""
+ SELECT p.id, p.name, p.status, p.category_id, c.name as category_name
+ FROM pets p
+ JOIN categories c on c.id = p.category_id
+ WHERE p.id = :id
+ """)
+ Mono findById(long id);
+
+ @Query("INSERT INTO %{entity#inserts -= id} RETURNING id")
+ Mono insert(Pet entity);
+
+ @Query("UPDATE %{entity#table} SET %{entity#updates} WHERE %{entity#where = @id}")
+ Mono update(Pet entity);
+
+ @Query("DELETE FROM pets WHERE id = :id")
+ Mono deleteById(long id);
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/mapper/PetStatusParameterMapper.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/mapper/PetStatusParameterMapper.java
new file mode 100644
index 0000000..5a273eb
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/mapper/PetStatusParameterMapper.java
@@ -0,0 +1,19 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.repository.mapper;
+
+import jakarta.annotation.Nullable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.vertx.mapper.parameter.VertxParameterColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.Pet;
+
+@Component
+public final class PetStatusParameterMapper implements VertxParameterColumnMapper {
+
+ @Override
+ public Object apply(@Nullable Pet.Status value) {
+ if (value == null) {
+ return null;
+ } else {
+ return value.code;
+ }
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/mapper/PetStatusResultMapper.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/mapper/PetStatusResultMapper.java
new file mode 100644
index 0000000..cdbfa21
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/repository/mapper/PetStatusResultMapper.java
@@ -0,0 +1,23 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.repository.mapper;
+
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.database.vertx.mapper.result.VertxResultColumnMapper;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.Pet;
+
+@Component
+public final class PetStatusResultMapper implements VertxResultColumnMapper {
+
+ private final Pet.Status[] statuses = Pet.Status.values();
+
+ @Override
+ public Pet.Status apply(io.vertx.sqlclient.Row row, int index) {
+ final int code = row.get(Integer.class, index);
+ for (Pet.Status status : statuses) {
+ if (code == status.code) {
+ return status;
+ }
+ }
+
+ throw new IllegalStateException("Unknown code: " + code);
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/service/PetCache.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/service/PetCache.java
new file mode 100644
index 0000000..2c1a4e5
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/service/PetCache.java
@@ -0,0 +1,10 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.service;
+
+import ru.tinkoff.kora.cache.annotation.Cache;
+import ru.tinkoff.kora.cache.caffeine.CaffeineCache;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetWithCategory;
+
+@Cache("pet-cache")
+public interface PetCache extends CaffeineCache {
+
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/service/PetService.java b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/service/PetService.java
new file mode 100644
index 0000000..c56b721
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/java/ru/tinkoff/kora/example/graalvm/crud/vertx/service/PetService.java
@@ -0,0 +1,91 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx.service;
+
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.cache.annotation.CacheInvalidate;
+import ru.tinkoff.kora.cache.annotation.CachePut;
+import ru.tinkoff.kora.cache.annotation.Cacheable;
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.Pet;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetCategory;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.model.dao.PetWithCategory;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.repository.CategoryRepository;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.repository.PetRepository;
+import ru.tinkoff.kora.resilient.circuitbreaker.annotation.CircuitBreaker;
+import ru.tinkoff.kora.resilient.retry.annotation.Retry;
+import ru.tinkoff.kora.resilient.timeout.annotation.Timeout;
+
+@Component
+public class PetService {
+
+ private final PetRepository petRepository;
+ private final CategoryRepository categoryRepository;
+
+ public PetService(PetRepository petRepository, CategoryRepository categoryRepository) {
+ this.petRepository = petRepository;
+ this.categoryRepository = categoryRepository;
+ }
+
+ @Cacheable(PetCache.class)
+ @CircuitBreaker("pet")
+ @Retry("pet")
+ @Timeout("pet")
+ public Mono findByID(long petId) {
+ return petRepository.findById(petId);
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ public Mono add(PetCreateTO createTO) {
+ return categoryRepository.findByName(createTO.category().name())
+ .map(PetCategory::id)
+ .switchIfEmpty(categoryRepository.insert(createTO.category().name()))
+ .flatMap(categoryId -> {
+ var pet = new Pet(0, createTO.name(), Pet.Status.AVAILABLE, categoryId);
+ return petRepository.insert(pet)
+ .map(petId -> new PetWithCategory(petId, pet.name(), pet.status(),
+ new PetCategory(categoryId, createTO.category().name())));
+ });
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CachePut(value = PetCache.class, parameters = "id")
+ public Mono update(long id, PetUpdateTO updateTO) {
+ return petRepository.findById(id)
+ .flatMap(existingPet -> {
+ var categoryMono = Mono.just(existingPet.category());
+ if (updateTO.category() != null) {
+ categoryMono = categoryRepository.findByName(updateTO.category().name())
+ .switchIfEmpty(Mono.defer(() -> categoryRepository.insert(updateTO.category().name())
+ .map(newCategoryId -> new PetCategory(newCategoryId, updateTO.category().name()))));
+ }
+
+ var status = (updateTO.status() == null)
+ ? existingPet.status()
+ : toStatus(updateTO.status());
+
+ return categoryMono
+ .flatMap(category -> {
+ var result = new PetWithCategory(existingPet.id(), updateTO.name(), status, category);
+ return petRepository.update(result.getPet()).then(Mono.just(result));
+ });
+ });
+ }
+
+ @CircuitBreaker("pet")
+ @Timeout("pet")
+ @CacheInvalidate(PetCache.class)
+ public Mono delete(long petId) {
+ return petRepository.deleteById(petId).map(counter -> counter.value() == 1);
+ }
+
+ private static Pet.Status toStatus(PetUpdateTO.StatusEnum statusEnum) {
+ return switch (statusEnum) {
+ case AVAILABLE -> Pet.Status.AVAILABLE;
+ case PENDING -> Pet.Status.PENDING;
+ case SOLD -> Pet.Status.SOLD;
+ };
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/resources/application.conf b/kora-java-graalvm-crud-vertx/src/main/resources/application.conf
new file mode 100644
index 0000000..e13d1a3
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/resources/application.conf
@@ -0,0 +1,60 @@
+httpServer {
+ publicApiHttpPort = 8080
+ privateApiHttpPort = 8085
+}
+
+
+db {
+ connectionUri = ${POSTGRES_VERTX_URL}
+ username = ${POSTGRES_USER}
+ password = ${POSTGRES_PASS}
+ maxPoolSize = 10
+ poolName = "kora"
+ initializationFailTimeout = "10s"
+}
+
+
+pet-cache {
+ maximumSize = 1000
+ expireAfterWrite = ${?CACHE_EXPIRE_WRITE}
+}
+
+
+openapi {
+ management {
+ enabled = true
+ file = "openapi/http-server.yaml"
+ swaggerui {
+ enabled = true
+ }
+ rapidoc {
+ enabled = true
+ }
+ }
+}
+
+
+resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 50
+ minimumRequiredCalls = 25
+ failureRateThreshold = 50
+ permittedCallsInHalfOpenState = 10
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+}
+
+
+logging.level {
+ "root": "WARN"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
+ "ru.tinkoff.kora.application.graph.internal.loom.VirtualThreadExecutorHolder": "DEBUG"
+}
diff --git a/kora-java-graalvm-crud-vertx/src/main/resources/db/migration/V1__setup-tables.sql b/kora-java-graalvm-crud-vertx/src/main/resources/db/migration/V1__setup-tables.sql
new file mode 100644
index 0000000..3e70499
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/resources/db/migration/V1__setup-tables.sql
@@ -0,0 +1,16 @@
+CREATE TABLE IF NOT EXISTS categories
+(
+ id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR NOT NULL,
+ PRIMARY KEY (id)
+);
+
+
+CREATE TABLE IF NOT EXISTS pets
+(
+ id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR NOT NULL,
+ status SMALLINT NOT NULL,
+ category_id BIGINT NOT NULL REFERENCES categories(id),
+ PRIMARY KEY (id)
+);
diff --git a/kora-java-graalvm-crud-vertx/src/main/resources/logback.xml b/kora-java-graalvm-crud-vertx/src/main/resources/logback.xml
new file mode 100644
index 0000000..745e83f
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ UTF-8
+ %d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-crud-vertx/src/main/resources/openapi/http-server.yaml b/kora-java-graalvm-crud-vertx/src/main/resources/openapi/http-server.yaml
new file mode 100644
index 0000000..8d99f49
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/main/resources/openapi/http-server.yaml
@@ -0,0 +1,315 @@
+openapi: 3.0.3
+info:
+ title: Swagger Petstore - OpenAPI 3.0
+ description: |-
+ This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
+ Swagger at [https://swagger.io](https://swagger.io).
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ version: 1.0.11
+externalDocs:
+ description: Find out more about Swagger
+ url: https://swagger.io
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: https://swagger.io
+paths:
+ /v3/pets:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: Add a new pet to the store
+ operationId: addPet
+ requestBody:
+ required: true
+ description: Create a new pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetCreateTO'
+ responses:
+ '200':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ /v3/pets/{id}:
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: id
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ nullable: false
+ minimum: 1
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: Update an existing pet by Id
+ operationId: updatePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ requestBody:
+ description: Update an existent pet in the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetUpdateTO'
+ required: true
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PetTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: delete a pet
+ operationId: deletePet
+ parameters:
+ - name: id
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '400':
+ description: Invalid parameters supplier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '404':
+ description: Pet not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '408':
+ description: Timeout exception
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '429':
+ description: Too many requests
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+ '500':
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageTO'
+components:
+ schemas:
+ MessageTO:
+ type: object
+ properties:
+ message:
+ type: string
+ CategoryTO:
+ required:
+ - id
+ - name
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 1
+ name:
+ type: string
+ example: Dogs
+ CategoryCreateTO:
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ type: string
+ example: Dogs
+ PetStatusTO:
+ properties:
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ PetTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 10
+ nullable: false
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryTO'
+ PetCreateTO:
+ required:
+ - name
+ - category
+ type: object
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
+ PetUpdateTO:
+ allOf:
+ - $ref: '#/components/schemas/PetStatusTO'
+ - type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ example: doggie
+ nullable: false
+ minLength: 1
+ maxLength: 50
+ category:
+ $ref: '#/components/schemas/CategoryCreateTO'
diff --git a/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/AppContainer.java b/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/AppContainer.java
new file mode 100644
index 0000000..c002c8d
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/AppContainer.java
@@ -0,0 +1,47 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.time.Duration;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.utility.DockerImageName;
+
+public final class AppContainer extends GenericContainer {
+
+ private AppContainer() {
+ super(new ImageFromDockerfile("kora-java-graalvm-crud-r2dbc")
+ .withDockerfile(Paths.get("Dockerfile").toAbsolutePath()));
+ }
+
+ private AppContainer(DockerImageName image) {
+ super(image);
+ }
+
+ public static AppContainer build() {
+ final String appImage = System.getenv("IMAGE_KORA_JAVA_GRAALVM_CRUD_R2DBC");
+ return (appImage != null && !appImage.isBlank())
+ ? new AppContainer(DockerImageName.parse(appImage))
+ : new AppContainer();
+ }
+
+ @Override
+ protected void configure() {
+ super.configure();
+ withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
+ withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
+ waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
+ }
+
+ public int getPort() {
+ return getMappedPort(8080);
+ }
+
+ public URI getURI() {
+ return URI.create(String.format("http://%s:%s", getHost(), getPort()));
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/PetControllerTests.java b/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/PetControllerTests.java
new file mode 100644
index 0000000..bed57a8
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/PetControllerTests.java
@@ -0,0 +1,210 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import io.goodforgod.testcontainers.extensions.ContainerMode;
+import io.goodforgod.testcontainers.extensions.Network;
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL;
+import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection;
+import io.goodforgod.testcontainers.extensions.jdbc.Migration;
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Map;
+import org.json.JSONObject;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+@TestcontainersPostgreSQL(
+ network = @Network(shared = true),
+ mode = ContainerMode.PER_RUN,
+ migration = @Migration(
+ engine = Migration.Engines.FLYWAY,
+ apply = Migration.Mode.PER_METHOD,
+ drop = Migration.Mode.PER_METHOD))
+class PetControllerTests {
+
+ private static final AppContainer container = AppContainer.build()
+ .withNetwork(org.testcontainers.containers.Network.SHARED);
+
+ @ConnectionPostgreSQL
+ private JdbcConnection connection;
+
+ @BeforeEach
+ public void setup(@ConnectionPostgreSQL JdbcConnection connection) {
+ if (!container.isRunning()) {
+ var params = connection.paramsInNetwork().orElseThrow();
+ container.withEnv(Map.of(
+ "POSTGRES_VERTX_URL", "postgresql://%s:%s/%s".formatted(params.host(), params.port(), params.database()),
+ "POSTGRES_USER", params.username(),
+ "POSTGRES_PASS", params.password(),
+ "CACHE_EXPIRE_WRITE", "0s"));
+
+ container.start();
+ }
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ container.stop();
+ }
+
+ @Test
+ void addPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var requestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var request = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, response.statusCode(), response.body());
+
+ // then
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var responseBody = new JSONObject(response.body());
+ assertNotNull(responseBody.query("/id"));
+ assertNotEquals(0L, responseBody.query("/id"));
+ assertNotNull(responseBody.query("/status"));
+ assertEquals(requestBody.query("/name"), responseBody.query("/name"));
+ assertNotNull(responseBody.query("/category/id"));
+ assertEquals(requestBody.query("/category/name"), responseBody.query("/category/name"));
+ }
+
+ @Test
+ void getPet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ // when
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, getResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(createResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void updatePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var updateRequestBody = new JSONObject()
+ .put("name", "doggie2")
+ .put("status", "pending")
+ .put("category", new JSONObject()
+ .put("name", "Dogs2"));
+
+ var updateRequest = HttpRequest.newBuilder()
+ .PUT(HttpRequest.BodyPublishers.ofString(updateRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var updateResponse = httpClient.send(updateRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, updateResponse.statusCode(), updateResponse.body());
+ var updateResponseBody = new JSONObject(updateResponse.body());
+
+ // then
+ var getRequest = HttpRequest.newBuilder()
+ .GET()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var getResponse = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), getResponse.body());
+
+ var getResponseBody = new JSONObject(getResponse.body());
+ JSONAssert.assertEquals(updateResponseBody.toString(), getResponseBody.toString(), JSONCompareMode.LENIENT);
+ }
+
+ @Test
+ void deletePet() throws Exception {
+ // given
+ var httpClient = HttpClient.newHttpClient();
+ var createRequestBody = new JSONObject()
+ .put("name", "doggie")
+ .put("category", new JSONObject()
+ .put("name", "Dogs"));
+
+ var createRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(createRequestBody.toString()))
+ .uri(container.getURI().resolve("/v3/pets"))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var createResponse = httpClient.send(createRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, createResponse.statusCode(), createResponse.body());
+ connection.assertCountsEquals(1, "pets");
+ connection.assertCountsEquals(1, "categories");
+ var createResponseBody = new JSONObject(createResponse.body());
+
+ // when
+ var deleteRequest = HttpRequest.newBuilder()
+ .DELETE()
+ .uri(container.getURI().resolve("/v3/pets/" + createResponseBody.query("/id")))
+ .timeout(Duration.ofSeconds(5))
+ .build();
+
+ var deleteResponse = httpClient.send(deleteRequest, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, deleteResponse.statusCode(), deleteResponse.body());
+
+ // then
+ connection.assertCountsEquals(0, "pets");
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/PetServiceTests.java b/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/PetServiceTests.java
new file mode 100644
index 0000000..104fba7
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/test/java/ru/tinkoff/kora/example/graalvm/crud/vertx/PetServiceTests.java
@@ -0,0 +1,127 @@
+package ru.tinkoff.kora.example.graalvm.crud.vertx;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+
+import java.util.Collections;
+import java.util.Map;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.CategoryCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetCreateTO;
+import ru.tinkoff.kora.example.graalvm.crud.openapi.server.model.PetUpdateTO;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.repository.CategoryRepository;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.repository.PetRepository;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.service.PetCache;
+import ru.tinkoff.kora.example.graalvm.crud.vertx.service.PetService;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
+import ru.tinkoff.kora.test.extension.junit5.KoraAppTestConfigModifier;
+import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
+import ru.tinkoff.kora.test.extension.junit5.TestComponent;
+
+@KoraAppTest(Application.class)
+class PetServiceTests implements KoraAppTestConfigModifier {
+
+ @Mock
+ @TestComponent
+ private PetCache petCache;
+ @Mock
+ @TestComponent
+ private PetRepository petRepository;
+ @Mock
+ @TestComponent
+ private CategoryRepository categoryRepository;
+
+ @TestComponent
+ private PetService petService;
+
+ @NotNull
+ @Override
+ public KoraConfigModification config() {
+ return KoraConfigModification.ofString("""
+ resilient {
+ circuitbreaker.pet {
+ slidingWindowSize = 2
+ minimumRequiredCalls = 2
+ failureRateThreshold = 100
+ permittedCallsInHalfOpenState = 1
+ waitDurationInOpenState = 15s
+ }
+ timeout.pet {
+ duration = 5000ms
+ }
+ retry.pet {
+ delay = 100ms
+ attempts = 2
+ }
+ }
+ """);
+ }
+
+ @Test
+ void updatePetWithNewCategoryCreated() {
+ // given
+ mockCache();
+ mockRepository(Map.of("dog", 1L, "cat", 2L));
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog"))).block();
+ assertEquals(1, added.id());
+ assertEquals(1, added.category().id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.just(added));
+ Mockito.when(petRepository.update(any())).thenReturn(Mono.empty());
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("cat")))
+ .blockOptional();
+ assertTrue(updated.isPresent());
+ assertEquals(1, updated.get().id());
+ assertEquals(2, updated.get().category().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ Mockito.verify(categoryRepository, Mockito.times(2)).insert(any());
+ }
+
+ @Test
+ void updatePetWithSameCategory() {
+ // given
+ mockCache();
+ mockRepository(Map.of("dog", 1L));
+
+ var added = petService.add(new PetCreateTO("dog", new CategoryCreateTO("dog"))).block();
+ assertEquals(1, added.id());
+ assertEquals(1, added.category().id());
+
+ // when
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.just(added));
+ Mockito.when(petRepository.update(any())).thenReturn(Mono.empty());
+ Mockito.when(categoryRepository.findByName(any())).thenReturn(Mono.just(added.category()));
+ var updated = petService.update(added.id(),
+ new PetUpdateTO(PetUpdateTO.StatusEnum.PENDING, "cat", new CategoryCreateTO("dog")))
+ .blockOptional();
+ assertTrue(updated.isPresent());
+ assertNotEquals(0, updated.get().id());
+ assertNotEquals(0, updated.get().category().id());
+
+ // then
+ Mockito.verify(petRepository).insert(any());
+ Mockito.verify(categoryRepository).insert(any());
+ }
+
+ private void mockCache() {
+ Mockito.when(petCache.get(anyLong())).thenReturn(null);
+ Mockito.when(petCache.put(anyLong(), any())).then(invocation -> invocation.getArguments()[1]);
+ Mockito.when(petCache.get(anyCollection())).thenReturn(Collections.emptyMap());
+ }
+
+ private void mockRepository(Map categoryNameToId) {
+ categoryNameToId.forEach((k, v) -> Mockito.when(categoryRepository.insert(k)).thenReturn(Mono.just(v)));
+ Mockito.when(categoryRepository.findByName(any())).thenReturn(Mono.empty());
+ Mockito.when(petRepository.insert(any())).thenReturn(Mono.just(1L));
+ Mockito.when(petRepository.findById(anyLong())).thenReturn(Mono.empty());
+ }
+}
diff --git a/kora-java-graalvm-crud-vertx/src/test/resources/logback-test.xml b/kora-java-graalvm-crud-vertx/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..adccdab
--- /dev/null
+++ b/kora-java-graalvm-crud-vertx/src/test/resources/logback-test.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ UTF-8
+ %cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-kafka/Dockerfile b/kora-java-graalvm-kafka/Dockerfile
new file mode 100644
index 0000000..de40743
--- /dev/null
+++ b/kora-java-graalvm-kafka/Dockerfile
@@ -0,0 +1,25 @@
+FROM ghcr.io/graalvm/native-image-community:21 as builder
+
+ARG APP_DIR=/opt/application
+ARG JAR_DIR=build/libs
+WORKDIR $APP_DIR
+
+ADD $JAR_DIR/*.jar $APP_DIR/application.jar
+
+RUN native-image --no-fallback -classpath $APP_DIR/application.jar
+
+FROM ubuntu:noble-20240212 as runner
+
+ARG APP_DIR=/opt/application
+WORKDIR $APP_DIR
+
+COPY --from=builder $APP_DIR/application $APP_DIR/application
+
+ARG DOCKER_USER=app
+RUN groupadd -r $DOCKER_USER && useradd -rg $DOCKER_USER $DOCKER_USER
+RUN chmod +x application
+USER $DOCKER_USER
+
+EXPOSE 8080/tcp
+EXPOSE 8085/tcp
+CMD "/opt/application/application"
\ No newline at end of file
diff --git a/kora-java-graalvm-kafka/README.md b/kora-java-graalvm-kafka/README.md
new file mode 100644
index 0000000..c85ab92
--- /dev/null
+++ b/kora-java-graalvm-kafka/README.md
@@ -0,0 +1,34 @@
+# Kora Java GraalVM Kafka
+
+Пример модуля Kafka в Kora.
+
+В примере использовались модули:
+- [Kafka](https://kora-projects.github.io/kora-docs/ru/documentation/kafka/)
+- [JSON](https://kora-projects.github.io/kora-docs/ru/documentation/json/)
+
+Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_21/)
+
+## Build
+
+Собрать артефакт:
+
+```shell
+./gradlew shadowJar
+docker build -t kora-java-graalvm-kafka .
+```
+
+## Run
+
+Запустить локально:
+```shell
+./gradlew run
+```
+
+## Test
+
+Тесты используют [Testcontainers](https://java.testcontainers.org/), требуется [Docker](https://docs.docker.com/engine/install/) окружение для запуска тестов или аналогичные контейнерные окружения ([colima](https://github.com/abiosoft/colima) / итп)
+
+Протестировать локально:
+```shell
+./gradlew test
+```
diff --git a/kora-java-graalvm-kafka/build.gradle b/kora-java-graalvm-kafka/build.gradle
new file mode 100644
index 0000000..13d2083
--- /dev/null
+++ b/kora-java-graalvm-kafka/build.gradle
@@ -0,0 +1,137 @@
+plugins {
+ id "java"
+ id "jacoco"
+ id "application"
+
+ id "com.github.johnrengelman.shadow" version "8.1.1"
+ id "org.graalvm.buildtools.native" version "0.10.1"
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+mainClassName = "ru.tinkoff.kora.example.graalvm.kafka.Application"
+
+sourceCompatibility = JavaVersion.VERSION_17
+targetCompatibility = JavaVersion.VERSION_17
+
+configurations {
+ koraBom
+ implementation.extendsFrom(koraBom)
+ annotationProcessor.extendsFrom(koraBom)
+}
+
+dependencies {
+ koraBom platform("ru.tinkoff.kora:kora-parent:$koraVersion")
+ annotationProcessor "ru.tinkoff.kora:annotation-processors"
+ annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
+ compileOnly "io.goodforgod:graalvm-hint-annotations:1.2.0"
+
+ implementation "ru.tinkoff.kora:http-server-undertow"
+ implementation "ru.tinkoff.kora:kafka"
+ implementation "ru.tinkoff.kora:json-module"
+ implementation "ru.tinkoff.kora:config-yaml"
+ implementation "ru.tinkoff.kora:micrometer-module"
+ implementation "ru.tinkoff.kora:logging-logback"
+
+ testImplementation "org.json:json:20231013"
+ testImplementation "org.mockito:mockito-core:5.6.0"
+ testImplementation "ru.tinkoff.kora:test-junit5"
+ testImplementation "io.goodforgod:testcontainers-extensions-kafka:0.11.0"
+ testImplementation "org.testcontainers:junit-jupiter:1.17.6"
+}
+
+graalvmNative {
+ binaries {
+ main {
+ imageName = "$project.name"
+ mainClass = "$mainClassName"
+ javaLauncher = javaToolchains.launcherFor {
+ languageVersion = JavaLanguageVersion.of(21)
+ vendor = JvmVendorSpec.matching("GraalVM Community")
+ }
+ }
+ }
+ metadataRepository {
+ enabled = true
+ }
+}
+
+processResources.dependsOn tasks.collectReachabilityMetadata
+test.dependsOn tasks.shadowJar
+
+//noinspection GroovyAssignabilityCheck
+run {
+ environment([
+ "KAFKA_BOOTSTRAP": "$kafkaBootstrapServers"
+ ])
+}
+
+test {
+ jvmArgs += [
+ "-XX:+TieredCompilation",
+ "-XX:TieredStopAtLevel=1",
+ ]
+
+ environment([
+ "": ""
+ ])
+
+ useJUnitPlatform()
+ testLogging {
+ showStandardStreams(true)
+ events("passed", "skipped", "failed")
+ exceptionFormat("full")
+ }
+
+ jacoco {
+ excludes += ["**/Application*"]
+ }
+
+ reports {
+ html.required = false
+ junitXml.required = false
+ }
+}
+
+sourceSets {
+ main {
+ resources.srcDirs += "$buildDir/native-reachability-metadata"
+ }
+}
+
+jar.enabled = false
+shadowJar {
+ mergeServiceFiles()
+ manifest {
+ attributes "Main-Class": mainClassName
+ attributes "Implementation-Version": koraVersion
+ }
+}
+
+artifacts {
+ archives shadowJar
+}
+
+compileJava {
+ options.encoding("UTF-8")
+ options.incremental(true)
+ options.fork = true
+}
+
+check.dependsOn jacocoTestReport
+jacocoTestReport {
+ reports {
+ xml.required = true
+ html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
+ }
+}
+
+javadoc {
+ options.encoding = "UTF-8"
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption("html5", true)
+ }
+}
\ No newline at end of file
diff --git a/kora-java-graalvm-kafka/gradle.properties b/kora-java-graalvm-kafka/gradle.properties
new file mode 100644
index 0000000..71fdfaa
--- /dev/null
+++ b/kora-java-graalvm-kafka/gradle.properties
@@ -0,0 +1,2 @@
+##### KAFKA #####
+kafkaBootstrapServers=localhost:9092
diff --git a/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/Application.java b/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/Application.java
new file mode 100644
index 0000000..81ceab8
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/Application.java
@@ -0,0 +1,26 @@
+package ru.tinkoff.kora.example.graalvm.kafka;
+
+import io.goodforgod.graalvm.hint.annotation.NativeImageHint;
+import ru.tinkoff.kora.application.graph.KoraApplication;
+import ru.tinkoff.kora.common.KoraApp;
+import ru.tinkoff.kora.config.yaml.YamlConfigModule;
+import ru.tinkoff.kora.http.server.undertow.UndertowModule;
+import ru.tinkoff.kora.json.module.JsonModule;
+import ru.tinkoff.kora.kafka.common.KafkaModule;
+import ru.tinkoff.kora.logging.logback.LogbackModule;
+import ru.tinkoff.kora.micrometer.module.MetricsModule;
+
+@NativeImageHint(name = "application", entrypoint = Application.class)
+@KoraApp
+public interface Application extends
+ YamlConfigModule,
+ LogbackModule,
+ JsonModule,
+ UndertowModule,
+ KafkaModule,
+ MetricsModule {
+
+ static void main(String[] args) {
+ KoraApplication.run(ApplicationGraph::graph);
+ }
+}
diff --git a/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/TaskPublisher.java b/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/TaskPublisher.java
new file mode 100644
index 0000000..30240f0
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/TaskPublisher.java
@@ -0,0 +1,14 @@
+package ru.tinkoff.kora.example.graalvm.kafka;
+
+import ru.tinkoff.kora.json.common.annotation.Json;
+import ru.tinkoff.kora.kafka.common.annotation.KafkaPublisher;
+
+@KafkaPublisher("kafka.publisher.task")
+public interface TaskPublisher {
+
+ @Json
+ record Task(String name, long code) {}
+
+ @KafkaPublisher.Topic("kafka.publisher.task")
+ void send(@Json TaskPublisher.Task value);
+}
diff --git a/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/UserListener.java b/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/UserListener.java
new file mode 100644
index 0000000..ed8a7ac
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/main/java/ru/tinkoff/kora/example/graalvm/kafka/UserListener.java
@@ -0,0 +1,24 @@
+package ru.tinkoff.kora.example.graalvm.kafka;
+
+import ru.tinkoff.kora.common.Component;
+import ru.tinkoff.kora.json.common.annotation.Json;
+import ru.tinkoff.kora.kafka.common.annotation.KafkaListener;
+
+@Component
+public final class UserListener {
+
+ @Json
+ public record User(String id, String name) {}
+
+ private final TaskPublisher taskPublisher;
+
+ public UserListener(TaskPublisher taskPublisher) {
+ this.taskPublisher = taskPublisher;
+ }
+
+ @KafkaListener("kafka.listener.user")
+ void process(@Json UserListener.User value) {
+ long code = System.currentTimeMillis();
+ taskPublisher.send(new TaskPublisher.Task(value.name() + "-" + code, code));
+ }
+}
diff --git a/kora-java-graalvm-kafka/src/main/resources/application.yaml b/kora-java-graalvm-kafka/src/main/resources/application.yaml
new file mode 100644
index 0000000..528539f
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/main/resources/application.yaml
@@ -0,0 +1,24 @@
+kafka:
+ publisher:
+ task:
+ topic: "tasks"
+ driverProperties:
+ bootstrap:
+ servers: ${KAFKA_BOOTSTRAP}
+ listener:
+ user:
+ pollTimeout: 250ms
+ topics: "users"
+ driverProperties:
+ bootstrap.servers: ${KAFKA_BOOTSTRAP}
+ group.id: "users-gi"
+ auto.offset.reset: "latest"
+ enable.auto.commit: true
+
+
+logging:
+ level:
+ root: "WARN"
+ ru.tinkoff.kora: "INFO"
+ ru.tinkoff.kora.example: "INFO"
+ ru.tinkoff.kora.application.graph.internal.loom.VirtualThreadExecutorHolder: "DEBUG"
\ No newline at end of file
diff --git a/kora-java-graalvm-kafka/src/main/resources/logback.xml b/kora-java-graalvm-kafka/src/main/resources/logback.xml
new file mode 100644
index 0000000..745e83f
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ UTF-8
+ %d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-graalvm-kafka/src/test/java/ru/tinkoff/kora/example/graalvm/kafka/AppContainer.java b/kora-java-graalvm-kafka/src/test/java/ru/tinkoff/kora/example/graalvm/kafka/AppContainer.java
new file mode 100644
index 0000000..740be19
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/test/java/ru/tinkoff/kora/example/graalvm/kafka/AppContainer.java
@@ -0,0 +1,47 @@
+package ru.tinkoff.kora.example.graalvm.kafka;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.time.Duration;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.utility.DockerImageName;
+
+public final class AppContainer extends GenericContainer {
+
+ private AppContainer() {
+ super(new ImageFromDockerfile("kora-java-graalvm-kafka")
+ .withDockerfile(Paths.get("Dockerfile").toAbsolutePath()));
+ }
+
+ private AppContainer(DockerImageName image) {
+ super(image);
+ }
+
+ public static AppContainer build() {
+ final String appImage = System.getenv("IMAGE_KORA_JAVA_GRAALVM_KAFKA");
+ return (appImage != null && !appImage.isBlank())
+ ? new AppContainer(DockerImageName.parse(appImage))
+ : new AppContainer();
+ }
+
+ @Override
+ protected void configure() {
+ super.configure();
+ withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
+ withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
+ waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
+ }
+
+ public int getPort() {
+ return getMappedPort(8080);
+ }
+
+ public URI getURI() {
+ return URI.create(String.format("http://%s:%s", getHost(), getPort()));
+ }
+}
diff --git a/kora-java-graalvm-kafka/src/test/java/ru/tinkoff/kora/example/graalvm/kafka/UserListenerTests.java b/kora-java-graalvm-kafka/src/test/java/ru/tinkoff/kora/example/graalvm/kafka/UserListenerTests.java
new file mode 100644
index 0000000..0727ae9
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/test/java/ru/tinkoff/kora/example/graalvm/kafka/UserListenerTests.java
@@ -0,0 +1,46 @@
+package ru.tinkoff.kora.example.graalvm.kafka;
+
+import io.goodforgod.testcontainers.extensions.ContainerMode;
+import io.goodforgod.testcontainers.extensions.Network;
+import io.goodforgod.testcontainers.extensions.kafka.*;
+import java.time.Duration;
+import java.util.Map;
+import java.util.UUID;
+import org.json.JSONObject;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+@TestcontainersKafka(
+ network = @Network(shared = true),
+ mode = ContainerMode.PER_RUN,
+ topics = @Topics({ "tasks", "users" }))
+class UserListenerTests {
+
+ private static final AppContainer container = AppContainer.build()
+ .withNetwork(org.testcontainers.containers.Network.SHARED);
+
+ @ConnectionKafka
+ private KafkaConnection connection;
+
+ @BeforeAll
+ public static void setup(@ConnectionKafka KafkaConnection connection) {
+ var params = connection.paramsInNetwork().orElseThrow();
+ container.withEnv(Map.of("KAFKA_BOOTSTRAP", params.bootstrapServers()));
+ container.start();
+ }
+
+ @Test
+ void userEventReceivedAndTaskEventSent() {
+ // given
+ var topicUsers = "users";
+ var topicTasks = "tasks";
+ var event = new JSONObject().put("id", UUID.randomUUID().toString()).put("name", "Ivan");
+
+ // when
+ var consumerTask = connection.subscribe(topicTasks);
+ connection.send(topicUsers, Event.ofValueAndRandomKey(event));
+
+ // then
+ consumerTask.assertReceivedEqualsInTime(1, Duration.ofSeconds(20));
+ }
+}
diff --git a/kora-java-graalvm-kafka/src/test/resources/logback-test.xml b/kora-java-graalvm-kafka/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..adccdab
--- /dev/null
+++ b/kora-java-graalvm-kafka/src/test/resources/logback-test.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ UTF-8
+ %cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kora-java-grpc-client/build.gradle b/kora-java-grpc-client/build.gradle
index 3b47b9c..01692a3 100644
--- a/kora-java-grpc-client/build.gradle
+++ b/kora-java-grpc-client/build.gradle
@@ -95,13 +95,12 @@ sourceSets {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-grpc-client/src/main/resources/application.conf b/kora-java-grpc-client/src/main/resources/application.conf
index ae7d490..1beb554 100644
--- a/kora-java-grpc-client/src/main/resources/application.conf
+++ b/kora-java-grpc-client/src/main/resources/application.conf
@@ -7,6 +7,6 @@ grpcClient {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-grpc-client/src/main/resources/logback.xml b/kora-java-grpc-client/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-grpc-client/src/main/resources/logback.xml
+++ b/kora-java-grpc-client/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-grpc-client/src/test/resources/logback-test.xml b/kora-java-grpc-client/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-grpc-client/src/test/resources/logback-test.xml
+++ b/kora-java-grpc-client/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-grpc-server/build.gradle b/kora-java-grpc-server/build.gradle
index 544a3c4..9ad7a0f 100644
--- a/kora-java-grpc-server/build.gradle
+++ b/kora-java-grpc-server/build.gradle
@@ -96,13 +96,12 @@ sourceSets {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-grpc-server/src/main/resources/application.conf b/kora-java-grpc-server/src/main/resources/application.conf
index 011482b..e502497 100644
--- a/kora-java-grpc-server/src/main/resources/application.conf
+++ b/kora-java-grpc-server/src/main/resources/application.conf
@@ -5,6 +5,6 @@ grpcServer {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-grpc-server/src/main/resources/logback.xml b/kora-java-grpc-server/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-grpc-server/src/main/resources/logback.xml
+++ b/kora-java-grpc-server/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-grpc-server/src/test/resources/logback-test.xml b/kora-java-grpc-server/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-grpc-server/src/test/resources/logback-test.xml
+++ b/kora-java-grpc-server/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-helloworld/build.gradle b/kora-java-helloworld/build.gradle
index 76853fe..0e0b2c0 100644
--- a/kora-java-helloworld/build.gradle
+++ b/kora-java-helloworld/build.gradle
@@ -74,13 +74,12 @@ test {
test.dependsOn tasks.shadowJar
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-helloworld/src/main/resources/application.conf b/kora-java-helloworld/src/main/resources/application.conf
index dc5f2e0..2828793 100644
--- a/kora-java-helloworld/src/main/resources/application.conf
+++ b/kora-java-helloworld/src/main/resources/application.conf
@@ -6,6 +6,6 @@ httpServer {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-helloworld/src/main/resources/logback.xml b/kora-java-helloworld/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-helloworld/src/main/resources/logback.xml
+++ b/kora-java-helloworld/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-helloworld/src/test/java/ru/tinkoff/kora/example/helloworld/AppContainer.java b/kora-java-helloworld/src/test/java/ru/tinkoff/kora/example/helloworld/AppContainer.java
index 196a748..311c01f 100644
--- a/kora-java-helloworld/src/test/java/ru/tinkoff/kora/example/helloworld/AppContainer.java
+++ b/kora-java-helloworld/src/test/java/ru/tinkoff/kora/example/helloworld/AppContainer.java
@@ -2,6 +2,7 @@
import java.net.URI;
import java.nio.file.Paths;
+import java.time.Duration;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
@@ -31,6 +32,7 @@ public static AppContainer build() {
protected void configure() {
super.configure();
withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer.class)));
waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200));
}
diff --git a/kora-java-helloworld/src/test/resources/logback-test.xml b/kora-java-helloworld/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-helloworld/src/test/resources/logback-test.xml
+++ b/kora-java-helloworld/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-http-client/build.gradle b/kora-java-http-client/build.gradle
index 94e924d..a72afd9 100644
--- a/kora-java-http-client/build.gradle
+++ b/kora-java-http-client/build.gradle
@@ -28,13 +28,13 @@ dependencies {
implementation "ru.tinkoff.kora:http-client-jdk"
implementation "ru.tinkoff.kora:json-module"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-mockserver:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-mockserver:0.11.0"
}
test.dependsOn tasks.shadowJar
@@ -73,13 +73,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-http-client/src/main/resources/application.conf b/kora-java-http-client/src/main/resources/application.conf
index d80236d..e2853bc 100644
--- a/kora-java-http-client/src/main/resources/application.conf
+++ b/kora-java-http-client/src/main/resources/application.conf
@@ -9,6 +9,6 @@ httpClient.default {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-http-client/src/main/resources/logback.xml b/kora-java-http-client/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-http-client/src/main/resources/logback.xml
+++ b/kora-java-http-client/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/FormHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/FormHttpClientTests.java
index c694500..00512f0 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/FormHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/FormHttpClientTests.java
@@ -3,9 +3,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@@ -22,12 +22,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class FormHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
@@ -46,14 +46,14 @@ void formEncodedHttpClient() {
.withMethod("POST")
.withHeader("content-type", "application/x-www-form-urlencoded")
.withPath("/form/encoded")
- .withBody(new StringBody("password=12345&name=Bob")))
+ .withBody(new StringBody("password=12345&name=Ivan")))
.respond(
org.mockserver.model.HttpResponse.response()
.withBody("OK"));
// then
var response = httpClient.formEncoded(new FormUrlEncoded(
- new FormUrlEncoded.FormPart("name", "Bob"),
+ new FormUrlEncoded.FormPart("name", "Ivan"),
new FormUrlEncoded.FormPart("password", "12345")));
assertEquals(200, response.code());
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/InterceptedHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/InterceptedHttpClientTests.java
index ca7d51a..3385ed9 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/InterceptedHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/InterceptedHttpClientTests.java
@@ -3,9 +3,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
@@ -13,12 +13,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class InterceptedHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/JsonHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/JsonHttpClientTests.java
index c552480..fbfcca0 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/JsonHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/JsonHttpClientTests.java
@@ -3,9 +3,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.mockserver.model.StringBody;
@@ -14,12 +14,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class JsonHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
@@ -40,11 +40,11 @@ void jsonHttpClient() {
.withBody(new StringBody("{\"id\":\"1\"}")))
.respond(
org.mockserver.model.HttpResponse.response()
- .withBody("{\"name\":\"Bob\",\"value\":100}"));
+ .withBody("{\"name\":\"Ivan\",\"value\":100}"));
// then
var response = httpClient.post(new JsonHttpClient.JsonRequest("1"));
- assertEquals("Bob", response.name());
+ assertEquals("Ivan", response.name());
assertEquals(100, response.value());
}
}
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperRequestHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperRequestHttpClientTests.java
index d1f7560..043f3c2 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperRequestHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperRequestHttpClientTests.java
@@ -4,9 +4,9 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
@@ -14,12 +14,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class MapperRequestHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperResponseHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperResponseHttpClientTests.java
index f45e4c5..b2d53a3 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperResponseHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/MapperResponseHttpClientTests.java
@@ -3,9 +3,9 @@
import static org.junit.jupiter.api.Assertions.*;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
@@ -13,12 +13,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class MapperResponseHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ParametersHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ParametersHttpClientTests.java
index 44b60e5..f7a535b 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ParametersHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ParametersHttpClientTests.java
@@ -3,9 +3,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -14,12 +14,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class ParametersHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ReactorHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ReactorHttpClientTests.java
index 44d66e3..89fcc71 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ReactorHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/ReactorHttpClientTests.java
@@ -4,9 +4,9 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import java.nio.charset.StandardCharsets;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -15,12 +15,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class ReactorHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/VoidHttpClientTests.java b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/VoidHttpClientTests.java
index e28bad6..684e797 100644
--- a/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/VoidHttpClientTests.java
+++ b/kora-java-http-client/src/test/java/ru/tinkoff/kora/example/http/client/VoidHttpClientTests.java
@@ -1,9 +1,9 @@
package ru.tinkoff.kora.example.http.client;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import java.time.Duration;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
@@ -12,12 +12,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class VoidHttpClientTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-http-client/src/test/resources/logback-test.xml b/kora-java-http-client/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-http-client/src/test/resources/logback-test.xml
+++ b/kora-java-http-client/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-http-server/build.gradle b/kora-java-http-server/build.gradle
index 5b58c1f..681ceea 100644
--- a/kora-java-http-server/build.gradle
+++ b/kora-java-http-server/build.gradle
@@ -28,7 +28,7 @@ dependencies {
implementation "ru.tinkoff.kora:http-server-undertow"
implementation "ru.tinkoff.kora:json-module"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
@@ -68,13 +68,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-http-server/src/main/resources/application.conf b/kora-java-http-server/src/main/resources/application.conf
index dc5f2e0..2828793 100644
--- a/kora-java-http-server/src/main/resources/application.conf
+++ b/kora-java-http-server/src/main/resources/application.conf
@@ -6,6 +6,6 @@ httpServer {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-http-server/src/main/resources/logback.xml b/kora-java-http-server/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-http-server/src/main/resources/logback.xml
+++ b/kora-java-http-server/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/InterceptedControllerTests.java b/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/InterceptedControllerTests.java
index a74f31a..52a3310 100644
--- a/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/InterceptedControllerTests.java
+++ b/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/InterceptedControllerTests.java
@@ -38,7 +38,8 @@ void interceptedController() throws Exception {
// then
var interceptedLogs = Awaitility.await()
.atMost(Duration.ofSeconds(30))
- .until(() -> container.getLogs().split("\n"), logs -> logs.length >= 9);
+ .until(() -> container.getLogs().split("\n"),
+ logs -> Arrays.stream(logs).anyMatch(log -> log.endsWith("Method Level Interceptor")));
assertTrue(Arrays.stream(interceptedLogs).anyMatch(log -> log.endsWith("Server Level Interceptor")));
assertTrue(Arrays.stream(interceptedLogs).anyMatch(log -> log.endsWith("Controller Level Interceptor")));
assertTrue(Arrays.stream(interceptedLogs).anyMatch(log -> log.endsWith("Method Level Interceptor")));
diff --git a/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/JsonPostControllerTests.java b/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/JsonPostControllerTests.java
index 688421c..043054d 100644
--- a/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/JsonPostControllerTests.java
+++ b/kora-java-http-server/src/test/java/ru/tinkoff/kora/example/httpserver/JsonPostControllerTests.java
@@ -23,13 +23,13 @@ void jsonPostController() throws Exception {
// then
var request = HttpRequest.newBuilder()
- .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Bob\"}"))
+ .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Ivan\"}"))
.uri(container.getURI().resolve("/json"))
.timeout(Duration.ofSeconds(5))
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode(), response.body());
- assertEquals("Hello world: Bob", response.body(), response.body());
+ assertEquals("Hello world: Ivan", response.body(), response.body());
}
}
diff --git a/kora-java-http-server/src/test/resources/logback-test.xml b/kora-java-http-server/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-http-server/src/test/resources/logback-test.xml
+++ b/kora-java-http-server/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-kafka/build.gradle b/kora-java-kafka/build.gradle
index d3523d6..4d909dc 100644
--- a/kora-java-kafka/build.gradle
+++ b/kora-java-kafka/build.gradle
@@ -34,7 +34,7 @@ dependencies {
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-kafka:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-kafka:0.11.0"
}
//noinspection GroovyAssignabilityCheck
@@ -71,13 +71,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-kafka/src/main/resources/application.conf b/kora-java-kafka/src/main/resources/application.conf
index 58b1bb8..2710c0d 100644
--- a/kora-java-kafka/src/main/resources/application.conf
+++ b/kora-java-kafka/src/main/resources/application.conf
@@ -30,6 +30,6 @@ kafka {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-kafka/src/main/resources/logback.xml b/kora-java-kafka/src/main/resources/logback.xml
index 9bc40f3..27d7854 100644
--- a/kora-java-kafka/src/main/resources/logback.xml
+++ b/kora-java-kafka/src/main/resources/logback.xml
@@ -16,6 +16,8 @@
+
+
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordExceptionListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordExceptionListenerTests.java
index 6bb20b7..679708e 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordExceptionListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordExceptionListenerTests.java
@@ -5,7 +5,6 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.kafka.*;
import java.time.Duration;
-import java.util.Timer;
import java.util.concurrent.Executors;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
@@ -23,7 +22,7 @@
@KoraAppTest(Application.class)
class AutoCommitRecordExceptionListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitRecordExceptionListenerModule.AutoCommitRecordExceptionListenerProcessTag.class)
@@ -46,7 +45,7 @@ void processed() throws InterruptedException {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordJsonListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordJsonListenerTests.java
index 2d338e2..d7101a9 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordJsonListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordJsonListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitRecordJsonListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitRecordJsonListenerModule.AutoCommitRecordJsonListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordListenerTests.java
index 733d19a..13d4624 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitRecordListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitRecordListenerModule.AutoCommitRecordListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordMapperListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordMapperListenerTests.java
index 079aa04..bd00ab4 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordMapperListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordMapperListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitRecordMapperListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitRecordMapperListenerModule.AutoCommitRecordMapperListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsListenerTests.java
index b7c3244..dce3fce 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitRecordsListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitRecordsListenerModule.AutoCommitRecordsListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsTelemetryListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsTelemetryListenerTests.java
index 83b1935..9c4e852 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsTelemetryListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitRecordsTelemetryListenerTests.java
@@ -21,7 +21,7 @@
@KoraAppTest(Application.class)
class AutoCommitRecordsTelemetryListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitRecordsTelemetryListenerModule.AutoCommitRecordsTelemetryListenerProcessTag.class)
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueExceptionListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueExceptionListenerTests.java
index da5184f..fba1eff 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueExceptionListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueExceptionListenerTests.java
@@ -3,7 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -26,7 +26,7 @@
@KoraAppTest(Application.class)
class AutoCommitValueExceptionListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitValueExceptionListenerModule.AutoCommitValueExceptionListenerProcessTag.class)
@@ -47,7 +47,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueJsonListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueJsonListenerTests.java
index 3d841d5..0c96e27 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueJsonListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueJsonListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitValueJsonListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitValueJsonListenerModule.AutoCommitValueJsonListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyHeadersListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyHeadersListenerTests.java
index cd4ff4e..0396a7f 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyHeadersListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyHeadersListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitValueKeyHeadersListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitValueKeyHeadersListenerModule.AutoCommitValueKeyHeadersListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyListenerTests.java
index b615d3a..472f23a 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueKeyListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitValueKeyListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitValueKeyListenerModule.AutoCommitValueKeyListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueListenerTests.java
index 3bb9444..721cfb6 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitValueListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitValueListenerModule.AutoCommitValueListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueMapperListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueMapperListenerTests.java
index f94190e..6eee5b5 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueMapperListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/AutoCommitValueMapperListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class AutoCommitValueMapperListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(AutoCommitValueMapperListenerModule.AutoCommitValueMapperListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/ManualCommitRecordListenerTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/ManualCommitRecordListenerTests.java
index b4e964d..b811631 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/ManualCommitRecordListenerTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/listener/ManualCommitRecordListenerTests.java
@@ -1,7 +1,7 @@
package ru.tinkoff.kora.example.kafka.listener;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.Event;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
@@ -24,7 +24,7 @@
@KoraAppTest(Application.class)
class ManualCommitRecordListenerTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@Tag(ManualCommitRecordListenerModule.ManualCommitRecordListenerProcessTag.class)
@@ -45,7 +45,7 @@ public KoraConfigModification config() {
void processed() {
// given
var topic = "my-topic-consumer";
- var event = new JSONObject().put("username", "Bob").put("code", 1);
+ var event = new JSONObject().put("username", "Ivan").put("code", 1);
// when
connection.send(topic, Event.ofValueAndRandomKey(event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerJsonPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerJsonPublisherTests.java
index 9e6811e..4b5a56e 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerJsonPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerJsonPublisherTests.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -22,7 +22,7 @@
@KoraAppTest(Application.class)
class ProducerJsonPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -43,7 +43,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new ProducerJsonPublisher.MyEvent(name, code);
publisher.send(new ProducerRecord<>(topic, event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerMapperPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerMapperPublisherTests.java
index 187bc7a..a6fa634 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerMapperPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerMapperPublisherTests.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -22,7 +22,7 @@
@KoraAppTest(Application.class)
class ProducerMapperPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -43,7 +43,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new ProducerMapperPublisher.MyEvent(name, code);
publisher.send(new ProducerRecord<>(topic, event));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerPublisherTests.java
index 1e76bac..4068394 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/ProducerPublisherTests.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -23,7 +23,7 @@
@KoraAppTest(Application.class)
class ProducerPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -45,7 +45,7 @@ void processed() throws InterruptedException {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new JSONObject().put("username", name).put("code", code);
publisher.send(new ProducerRecord<>(topic, event.toString()));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicJsonPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicJsonPublisherTests.java
index 82902d8..3ceae85 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicJsonPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicJsonPublisherTests.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -21,7 +21,7 @@
@KoraAppTest(Application.class)
class TopicJsonPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -42,7 +42,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new TopicJsonPublisher.MyEvent(name, code);
publisher.send(event);
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyHeadersPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyHeadersPublisherTests.java
index 9b8b876..45acf61 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyHeadersPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyHeadersPublisherTests.java
@@ -3,7 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -25,7 +25,7 @@
@KoraAppTest(Application.class)
class TopicKeyHeadersPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -46,7 +46,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new JSONObject().put("username", name).put("code", code);
publisher.send("1", event.toString(),
new RecordHeaders(List.of(new RecordHeader("2", "3".getBytes(StandardCharsets.UTF_8)))));
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyPublisherTests.java
index cff86a0..4e84197 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicKeyPublisherTests.java
@@ -3,7 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -21,7 +21,7 @@
@KoraAppTest(Application.class)
class TopicKeyPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -42,7 +42,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new JSONObject().put("username", name).put("code", code);
publisher.send("1", event.toString());
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicMapperPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicMapperPublisherTests.java
index dfed5a5..4eb81d3 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicMapperPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicMapperPublisherTests.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -21,7 +21,7 @@
@KoraAppTest(Application.class)
class TopicMapperPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -42,7 +42,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new TopicMapperPublisher.MyEvent(name, code);
publisher.send(event);
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicPublisherTests.java
index a063848..8ea5cd1 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TopicPublisherTests.java
@@ -4,7 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection;
+import io.goodforgod.testcontainers.extensions.kafka.ConnectionKafka;
import io.goodforgod.testcontainers.extensions.kafka.KafkaConnection;
import io.goodforgod.testcontainers.extensions.kafka.TestcontainersKafka;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
@@ -22,7 +22,7 @@
@KoraAppTest(Application.class)
class TopicPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection
+ @ConnectionKafka
private KafkaConnection connection;
@TestComponent
@@ -43,7 +43,7 @@ void processed() {
// when
var code = ThreadLocalRandom.current().nextInt(1, 100_000);
- var name = "Bob";
+ var name = "Ivan";
var event = new JSONObject().put("username", name).put("code", code);
publisher.send(event.toString());
diff --git a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TransactionalPublisherTests.java b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TransactionalPublisherTests.java
index 5e63751..b6b36ea 100644
--- a/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TransactionalPublisherTests.java
+++ b/kora-java-kafka/src/test/java/ru/tinkoff/kora/example/kafka/publisher/TransactionalPublisherTests.java
@@ -4,7 +4,6 @@
import io.goodforgod.testcontainers.extensions.ContainerMode;
import io.goodforgod.testcontainers.extensions.kafka.*;
-import io.goodforgod.testcontainers.extensions.kafka.ContainerKafkaConnection.Property;
import io.goodforgod.testcontainers.extensions.kafka.Topics;
import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;
@@ -22,7 +21,7 @@
@KoraAppTest(Application.class)
class TransactionalPublisherTests implements KoraAppTestConfigModifier {
- @ContainerKafkaConnection(properties = @Property(name = ConsumerConfig.ISOLATION_LEVEL_CONFIG, value = "read_committed"))
+ @ConnectionKafka(properties = { ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed" })
private KafkaConnection connection;
@TestComponent
diff --git a/kora-java-kafka/src/test/resources/logback-test.xml b/kora-java-kafka/src/test/resources/logback-test.xml
index 89dcf64..66bab1b 100644
--- a/kora-java-kafka/src/test/resources/logback-test.xml
+++ b/kora-java-kafka/src/test/resources/logback-test.xml
@@ -16,10 +16,12 @@
+
+
-
+
diff --git a/kora-java-openapi-generator-http-client/build.gradle b/kora-java-openapi-generator-http-client/build.gradle
index 6bafb95..66bb1eb 100644
--- a/kora-java-openapi-generator-http-client/build.gradle
+++ b/kora-java-openapi-generator-http-client/build.gradle
@@ -36,10 +36,9 @@ dependencies {
annotationProcessor "ru.tinkoff.kora:annotation-processors"
implementation "ru.tinkoff.kora:validation-module"
- implementation "ru.tinkoff.kora:validation-common"
implementation "ru.tinkoff.kora:http-client-jdk"
implementation "ru.tinkoff.kora:json-module"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
@@ -47,7 +46,7 @@ dependencies {
testImplementation "org.json:json:20231013"
testImplementation "org.skyscreamer:jsonassert:1.5.1"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-mockserver:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-mockserver:0.11.0"
}
// OpeAPI for V2
@@ -123,13 +122,12 @@ sourceSets {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-openapi-generator-http-client/src/main/resources/application.conf b/kora-java-openapi-generator-http-client/src/main/resources/application.conf
index 68eb285..5c8424e 100644
--- a/kora-java-openapi-generator-http-client/src/main/resources/application.conf
+++ b/kora-java-openapi-generator-http-client/src/main/resources/application.conf
@@ -16,6 +16,6 @@ httpClient.petV3.PetApi {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-openapi-generator-http-client/src/main/resources/logback.xml b/kora-java-openapi-generator-http-client/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-openapi-generator-http-client/src/main/resources/logback.xml
+++ b/kora-java-openapi-generator-http-client/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-openapi-generator-http-client/src/main/resources/openapi/petstoreV3.yaml b/kora-java-openapi-generator-http-client/src/main/resources/openapi/petstoreV3.yaml
index 70107f1..d4e1511 100644
--- a/kora-java-openapi-generator-http-client/src/main/resources/openapi/petstoreV3.yaml
+++ b/kora-java-openapi-generator-http-client/src/main/resources/openapi/petstoreV3.yaml
@@ -149,6 +149,7 @@ paths:
type: integer
format: int64
nullable: false
+ minimum: 1
responses:
'200':
description: successful operation
diff --git a/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV2Tests.java b/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV2Tests.java
index ac4ba93..fa62d40 100644
--- a/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV2Tests.java
+++ b/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV2Tests.java
@@ -6,9 +6,9 @@
import static org.mockserver.model.HttpResponse.response;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
@@ -25,12 +25,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class HttpClientPetV2Tests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV3Tests.java b/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV3Tests.java
index 1c2119f..7eb0e9d 100644
--- a/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV3Tests.java
+++ b/kora-java-openapi-generator-http-client/src/test/java/ru/tinkoff/kora/example/openapi/http/client/HttpClientPetV3Tests.java
@@ -6,9 +6,9 @@
import static org.mockserver.model.HttpResponse.response;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
@@ -25,12 +25,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_RUN)
+@TestcontainersMockServer(mode = ContainerMode.PER_RUN)
@KoraAppTest(Application.class)
class HttpClientPetV3Tests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-openapi-generator-http-client/src/test/resources/logback-test.xml b/kora-java-openapi-generator-http-client/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-openapi-generator-http-client/src/test/resources/logback-test.xml
+++ b/kora-java-openapi-generator-http-client/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-openapi-generator-http-server/build.gradle b/kora-java-openapi-generator-http-server/build.gradle
index 3fc4b3d..e7738b6 100644
--- a/kora-java-openapi-generator-http-server/build.gradle
+++ b/kora-java-openapi-generator-http-server/build.gradle
@@ -36,10 +36,9 @@ dependencies {
annotationProcessor "ru.tinkoff.kora:annotation-processors"
implementation "ru.tinkoff.kora:validation-module"
- implementation "ru.tinkoff.kora:validation-common"
implementation "ru.tinkoff.kora:http-server-undertow"
implementation "ru.tinkoff.kora:json-module"
- implementation "io.projectreactor:reactor-core:3.5.10" // For reactive examples (optional)
+ implementation "io.projectreactor:reactor-core:3.6.3" // For reactive examples (optional)
implementation "ru.tinkoff.kora:logging-logback"
implementation "ru.tinkoff.kora:config-hocon"
@@ -75,7 +74,7 @@ tasks.register("openApiGeneratePetV3", GenerateTask) {
modelPackage = "ru.tinkoff.kora.example.openapi.petV3.model"
invokerPackage = "ru.tinkoff.kora.example.openapi.petV3.invoker"
configOptions = [
- mode : "java-reactive-server", // так же есть java_server вариация HTTP Server'а
+ mode : "java-reactive-server", // так же есть java-server вариация HTTP Server'а
enableServerValidation: "true"
]
}
@@ -117,13 +116,12 @@ sourceSets {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-openapi-generator-http-server/src/main/resources/application.conf b/kora-java-openapi-generator-http-server/src/main/resources/application.conf
index dc5f2e0..2828793 100644
--- a/kora-java-openapi-generator-http-server/src/main/resources/application.conf
+++ b/kora-java-openapi-generator-http-server/src/main/resources/application.conf
@@ -6,6 +6,6 @@ httpServer {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-openapi-generator-http-server/src/main/resources/logback.xml b/kora-java-openapi-generator-http-server/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-openapi-generator-http-server/src/main/resources/logback.xml
+++ b/kora-java-openapi-generator-http-server/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-openapi-generator-http-server/src/main/resources/openapi/petstoreV3.yaml b/kora-java-openapi-generator-http-server/src/main/resources/openapi/petstoreV3.yaml
index 70107f1..d4e1511 100644
--- a/kora-java-openapi-generator-http-server/src/main/resources/openapi/petstoreV3.yaml
+++ b/kora-java-openapi-generator-http-server/src/main/resources/openapi/petstoreV3.yaml
@@ -149,6 +149,7 @@ paths:
type: integer
format: int64
nullable: false
+ minimum: 1
responses:
'200':
description: successful operation
diff --git a/kora-java-openapi-generator-http-server/src/test/resources/logback-test.xml b/kora-java-openapi-generator-http-server/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-openapi-generator-http-server/src/test/resources/logback-test.xml
+++ b/kora-java-openapi-generator-http-server/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-resilient/build.gradle b/kora-java-resilient/build.gradle
index 658abe7..8096906 100644
--- a/kora-java-resilient/build.gradle
+++ b/kora-java-resilient/build.gradle
@@ -68,13 +68,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-resilient/src/main/resources/application.conf b/kora-java-resilient/src/main/resources/application.conf
index b9a49f7..126fcd2 100644
--- a/kora-java-resilient/src/main/resources/application.conf
+++ b/kora-java-resilient/src/main/resources/application.conf
@@ -28,6 +28,6 @@ resilient {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-resilient/src/main/resources/logback.xml b/kora-java-resilient/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-resilient/src/main/resources/logback.xml
+++ b/kora-java-resilient/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-resilient/src/test/resources/logback-test.xml b/kora-java-resilient/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-resilient/src/test/resources/logback-test.xml
+++ b/kora-java-resilient/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-scheduling-jdk/build.gradle b/kora-java-scheduling-jdk/build.gradle
index 49e93ef..3c929a2 100644
--- a/kora-java-scheduling-jdk/build.gradle
+++ b/kora-java-scheduling-jdk/build.gradle
@@ -69,13 +69,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-scheduling-jdk/src/main/resources/application.conf b/kora-java-scheduling-jdk/src/main/resources/application.conf
index b2a4c8a..81c4c93 100644
--- a/kora-java-scheduling-jdk/src/main/resources/application.conf
+++ b/kora-java-scheduling-jdk/src/main/resources/application.conf
@@ -12,6 +12,6 @@ scheduling {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-scheduling-jdk/src/main/resources/logback.xml b/kora-java-scheduling-jdk/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-scheduling-jdk/src/main/resources/logback.xml
+++ b/kora-java-scheduling-jdk/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-scheduling-jdk/src/test/resources/logback-test.xml b/kora-java-scheduling-jdk/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-scheduling-jdk/src/test/resources/logback-test.xml
+++ b/kora-java-scheduling-jdk/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-scheduling-quartz/build.gradle b/kora-java-scheduling-quartz/build.gradle
index e7b8ad0..2153340 100644
--- a/kora-java-scheduling-quartz/build.gradle
+++ b/kora-java-scheduling-quartz/build.gradle
@@ -69,13 +69,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-scheduling-quartz/src/main/resources/application.conf b/kora-java-scheduling-quartz/src/main/resources/application.conf
index cace772..34f234d 100644
--- a/kora-java-scheduling-quartz/src/main/resources/application.conf
+++ b/kora-java-scheduling-quartz/src/main/resources/application.conf
@@ -13,6 +13,6 @@ scheduling {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-scheduling-quartz/src/main/resources/logback.xml b/kora-java-scheduling-quartz/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-scheduling-quartz/src/main/resources/logback.xml
+++ b/kora-java-scheduling-quartz/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-scheduling-quartz/src/test/resources/logback-test.xml b/kora-java-scheduling-quartz/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-scheduling-quartz/src/test/resources/logback-test.xml
+++ b/kora-java-scheduling-quartz/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-soap-client/build.gradle b/kora-java-soap-client/build.gradle
index 426ea74..7ec8ad1 100644
--- a/kora-java-soap-client/build.gradle
+++ b/kora-java-soap-client/build.gradle
@@ -42,7 +42,7 @@ dependencies {
implementation "ru.tinkoff.kora:config-hocon"
testImplementation "ru.tinkoff.kora:test-junit5"
- testImplementation "io.goodforgod:testcontainers-extensions-mockserver:0.9.6"
+ testImplementation "io.goodforgod:testcontainers-extensions-mockserver:0.11.0"
}
//noinspection GroovyAssignabilityCheck
@@ -92,13 +92,12 @@ wsdl2java {
]
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-soap-client/src/main/resources/application.conf b/kora-java-soap-client/src/main/resources/application.conf
index b3afe95..0148bf6 100644
--- a/kora-java-soap-client/src/main/resources/application.conf
+++ b/kora-java-soap-client/src/main/resources/application.conf
@@ -12,6 +12,6 @@ httpClient.default {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-soap-client/src/main/resources/logback.xml b/kora-java-soap-client/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-soap-client/src/main/resources/logback.xml
+++ b/kora-java-soap-client/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-soap-client/src/test/java/ru/tinkoff/kora/example/soap/client/SimpleServiceTests.java b/kora-java-soap-client/src/test/java/ru/tinkoff/kora/example/soap/client/SimpleServiceTests.java
index f51103a..2a456c2 100644
--- a/kora-java-soap-client/src/test/java/ru/tinkoff/kora/example/soap/client/SimpleServiceTests.java
+++ b/kora-java-soap-client/src/test/java/ru/tinkoff/kora/example/soap/client/SimpleServiceTests.java
@@ -3,9 +3,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.goodforgod.testcontainers.extensions.ContainerMode;
-import io.goodforgod.testcontainers.extensions.mockserver.ContainerMockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.MockserverConnection;
-import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockserver;
+import io.goodforgod.testcontainers.extensions.mockserver.ConnectionMockServer;
+import io.goodforgod.testcontainers.extensions.mockserver.MockServerConnection;
+import io.goodforgod.testcontainers.extensions.mockserver.TestcontainersMockServer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.mockserver.model.XmlBody;
@@ -16,12 +16,12 @@
import ru.tinkoff.kora.test.extension.junit5.KoraConfigModification;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;
-@TestcontainersMockserver(mode = ContainerMode.PER_CLASS)
+@TestcontainersMockServer(mode = ContainerMode.PER_CLASS)
@KoraAppTest(Application.class)
class SimpleServiceTests implements KoraAppTestConfigModifier {
- @ContainerMockserverConnection
- private MockserverConnection mockserverConnection;
+ @ConnectionMockServer
+ private MockServerConnection mockserverConnection;
@NotNull
@Override
diff --git a/kora-java-soap-client/src/test/resources/logback-test.xml b/kora-java-soap-client/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-soap-client/src/test/resources/logback-test.xml
+++ b/kora-java-soap-client/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-telemetry/build.gradle b/kora-java-telemetry/build.gradle
index 2871193..ca6908a 100644
--- a/kora-java-telemetry/build.gradle
+++ b/kora-java-telemetry/build.gradle
@@ -66,13 +66,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-telemetry/src/main/resources/application.conf b/kora-java-telemetry/src/main/resources/application.conf
index b2881c9..34892f1 100644
--- a/kora-java-telemetry/src/main/resources/application.conf
+++ b/kora-java-telemetry/src/main/resources/application.conf
@@ -21,6 +21,6 @@ tracing {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-telemetry/src/main/resources/logback.xml b/kora-java-telemetry/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-telemetry/src/main/resources/logback.xml
+++ b/kora-java-telemetry/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-telemetry/src/test/java/ru/tinkoff/kora/example/telemetry/AppContainer.java b/kora-java-telemetry/src/test/java/ru/tinkoff/kora/example/telemetry/AppContainer.java
index df80a21..1b6ebd1 100644
--- a/kora-java-telemetry/src/test/java/ru/tinkoff/kora/example/telemetry/AppContainer.java
+++ b/kora-java-telemetry/src/test/java/ru/tinkoff/kora/example/telemetry/AppContainer.java
@@ -2,6 +2,7 @@
import java.net.URI;
import java.nio.file.Paths;
+import java.time.Duration;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
@@ -31,6 +32,7 @@ public static AppContainer build() {
protected void configure() {
super.configure();
withExposedPorts(8080, 8085);
+ withStartupTimeout(Duration.ofSeconds(120));
withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(getClass())));
waitingFor(Wait.forHttp("/readiness")
.forPort(8085)
diff --git a/kora-java-telemetry/src/test/resources/logback-test.xml b/kora-java-telemetry/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-telemetry/src/test/resources/logback-test.xml
+++ b/kora-java-telemetry/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-java-validation/build.gradle b/kora-java-validation/build.gradle
index 9791599..535f52d 100644
--- a/kora-java-validation/build.gradle
+++ b/kora-java-validation/build.gradle
@@ -61,13 +61,12 @@ test {
}
}
-jar.enabled = true
+jar.enabled = false
shadowJar {
mergeServiceFiles()
manifest {
attributes "Main-Class": mainClassName
attributes "Implementation-Version": koraVersion
- attributes "Build-Time": java.time.OffsetDateTime.now()
}
}
diff --git a/kora-java-validation/src/main/resources/application.conf b/kora-java-validation/src/main/resources/application.conf
index b7f05da..aa7b2b0 100644
--- a/kora-java-validation/src/main/resources/application.conf
+++ b/kora-java-validation/src/main/resources/application.conf
@@ -1,5 +1,5 @@
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-java-validation/src/main/resources/logback.xml b/kora-java-validation/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-java-validation/src/main/resources/logback.xml
+++ b/kora-java-validation/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ArgumentValidatorTests.java b/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ArgumentValidatorTests.java
index 1886feb..ca6db8b 100644
--- a/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ArgumentValidatorTests.java
+++ b/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ArgumentValidatorTests.java
@@ -17,7 +17,7 @@ class ArgumentValidatorTests {
@Test
void createSuccess() {
// given
- var user = new ArgumentValidator.User("1", "Bob", "2");
+ var user = new ArgumentValidator.User("1", "Ivan", "2");
var code = "ME2";
// then
@@ -28,7 +28,7 @@ void createSuccess() {
@Test
void createFails() {
// given
- var user = new ArgumentValidator.User("1", "Bob", "2");
+ var user = new ArgumentValidator.User("1", "Ivan", "2");
var code = "2";
// then
diff --git a/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ResultValidatorTests.java b/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ResultValidatorTests.java
index f3c8b81..5f8af9a 100644
--- a/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ResultValidatorTests.java
+++ b/kora-java-validation/src/test/java/ru/tinkoff/kora/example/validation/ResultValidatorTests.java
@@ -16,7 +16,7 @@ class ResultValidatorTests {
@Test
void createSuccess() {
// given
- var name = "Bob";
+ var name = "Ivan";
var status = "2";
// then
diff --git a/kora-java-validation/src/test/resources/logback-test.xml b/kora-java-validation/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-java-validation/src/test/resources/logback-test.xml
+++ b/kora-java-validation/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-kotlin-crud/README.md b/kora-kotlin-crud/README.md
index b419e04..194d5b0 100644
--- a/kora-kotlin-crud/README.md
+++ b/kora-kotlin-crud/README.md
@@ -1,6 +1,6 @@
# Kora Kotlin CRUD Service
-Пример сервиса реализованного на Kora с HTTP [CRUD](https://appmaster.io/ru/blog/grubye-operatsii-chto-takoe-grubye-operatsii) API,
+Пример сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
в качестве базы данных выступает Postgres, используется кэш Caffeine, а также другие модули которые использовались бы в реальном приложении в бою.
В примере использовались модули:
diff --git a/kora-kotlin-crud/build.gradle.kts b/kora-kotlin-crud/build.gradle.kts
index 2eb417d..fd63aab 100644
--- a/kora-kotlin-crud/build.gradle.kts
+++ b/kora-kotlin-crud/build.gradle.kts
@@ -59,7 +59,7 @@ dependencies {
implementation("ru.tinkoff.kora:openapi-management")
implementation("ru.tinkoff.kora:logging-logback")
- implementation("org.postgresql:postgresql:42.6.0")
+ implementation("org.postgresql:postgresql:42.7.2")
implementation("org.mapstruct:mapstruct:1.5.5.Final")
testImplementation("org.json:json:20231013")
@@ -67,7 +67,7 @@ dependencies {
testImplementation("io.mockk:mockk:1.13.8")
testImplementation("ru.tinkoff.kora:test-junit5")
- testImplementation("io.goodforgod:testcontainers-extensions-postgres:0.9.6")
+ testImplementation("io.goodforgod:testcontainers-extensions-postgres:0.11.0")
testImplementation("org.testcontainers:junit-jupiter:1.17.6")
}
diff --git a/kora-kotlin-crud/src/main/resources/application.conf b/kora-kotlin-crud/src/main/resources/application.conf
index 6047c88..08f5f45 100644
--- a/kora-kotlin-crud/src/main/resources/application.conf
+++ b/kora-kotlin-crud/src/main/resources/application.conf
@@ -9,7 +9,7 @@ db {
username = ${POSTGRES_USER}
password = ${POSTGRES_PASS}
maxPoolSize = 10
- poolName = "example"
+ poolName = "kora"
initializationFailTimeout = "10s"
}
@@ -55,6 +55,6 @@ resilient {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-kotlin-crud/src/main/resources/logback.xml b/kora-kotlin-crud/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-kotlin-crud/src/main/resources/logback.xml
+++ b/kora-kotlin-crud/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-kotlin-crud/src/main/resources/openapi/http-server.yaml b/kora-kotlin-crud/src/main/resources/openapi/http-server.yaml
index 8851744..8d99f49 100644
--- a/kora-kotlin-crud/src/main/resources/openapi/http-server.yaml
+++ b/kora-kotlin-crud/src/main/resources/openapi/http-server.yaml
@@ -79,6 +79,7 @@ paths:
type: integer
format: int64
nullable: false
+ minimum: 1
responses:
'200':
description: successful operation
diff --git a/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/AppContainer.kt b/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/AppContainer.kt
index 2dfc714..7f6afc2 100644
--- a/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/AppContainer.kt
+++ b/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/AppContainer.kt
@@ -8,6 +8,7 @@ import org.testcontainers.images.builder.ImageFromDockerfile
import org.testcontainers.utility.DockerImageName
import java.net.URI
import java.nio.file.Paths
+import java.time.Duration
import java.util.concurrent.Future
class AppContainer : GenericContainer {
@@ -32,6 +33,7 @@ class AppContainer : GenericContainer {
override fun configure() {
super.configure()
withExposedPorts(8080, 8085)
+ withStartupTimeout(Duration.ofSeconds(120))
withLogConsumer(Slf4jLogConsumer(LoggerFactory.getLogger(AppContainer::class.java)))
waitingFor(Wait.forHttp("/system/readiness").forPort(8085).forStatusCode(200))
}
diff --git a/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/PetControllerTests.kt b/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/PetControllerTests.kt
index ed9ae10..bc6a0c4 100644
--- a/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/PetControllerTests.kt
+++ b/kora-kotlin-crud/src/test/kotlin/ru/tinkoff/kora/kotlin/example/crud/PetControllerTests.kt
@@ -2,10 +2,10 @@ package ru.tinkoff.kora.kotlin.example.crud
import io.goodforgod.testcontainers.extensions.ContainerMode
import io.goodforgod.testcontainers.extensions.Network
-import io.goodforgod.testcontainers.extensions.jdbc.ContainerPostgresConnection
+import io.goodforgod.testcontainers.extensions.jdbc.ConnectionPostgreSQL
import io.goodforgod.testcontainers.extensions.jdbc.JdbcConnection
import io.goodforgod.testcontainers.extensions.jdbc.Migration
-import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgres
+import io.goodforgod.testcontainers.extensions.jdbc.TestcontainersPostgreSQL
import org.json.JSONObject
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions.*
@@ -20,7 +20,7 @@ import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration
-@TestcontainersPostgres(
+@TestcontainersPostgreSQL(
network = Network(shared = true),
mode = ContainerMode.PER_RUN,
migration = Migration(
@@ -29,7 +29,7 @@ import java.time.Duration
drop = Migration.Mode.PER_METHOD
)
)
-class PetControllerTests(@ContainerPostgresConnection val connection: JdbcConnection) {
+class PetControllerTests(@ConnectionPostgreSQL val connection: JdbcConnection) {
companion object {
@@ -37,7 +37,7 @@ class PetControllerTests(@ContainerPostgresConnection val connection: JdbcConnec
@JvmStatic
@BeforeAll
- fun setup(@ContainerPostgresConnection connection: JdbcConnection) {
+ fun setup(@ConnectionPostgreSQL connection: JdbcConnection) {
val params = connection.paramsInNetwork().orElseThrow()
container.withEnv(
mapOf(
diff --git a/kora-kotlin-crud/src/test/resources/logback-test.xml b/kora-kotlin-crud/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-kotlin-crud/src/test/resources/logback-test.xml
+++ b/kora-kotlin-crud/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/kora-kotlin-helloworld/build.gradle.kts b/kora-kotlin-helloworld/build.gradle.kts
index b4184ae..dddb997 100644
--- a/kora-kotlin-helloworld/build.gradle.kts
+++ b/kora-kotlin-helloworld/build.gradle.kts
@@ -1,7 +1,7 @@
plugins {
id("application")
- kotlin("jvm") version ("1.9.22")
- id("com.google.devtools.ksp") version ("1.9.22-1.0.16")
+ kotlin("jvm") version ("1.9.10")
+ id("com.google.devtools.ksp") version ("1.9.10-1.0.13")
}
repositories {
diff --git a/kora-kotlin-helloworld/src/main/resources/application.conf b/kora-kotlin-helloworld/src/main/resources/application.conf
index dc5f2e0..2828793 100644
--- a/kora-kotlin-helloworld/src/main/resources/application.conf
+++ b/kora-kotlin-helloworld/src/main/resources/application.conf
@@ -6,6 +6,6 @@ httpServer {
logging.level {
"root": "WARN"
- "ru.tinkoff.kora": "DEBUG"
- "ru.tinkoff.kora.example": "DEBUG"
+ "ru.tinkoff.kora": "INFO"
+ "ru.tinkoff.kora.example": "INFO"
}
diff --git a/kora-kotlin-helloworld/src/main/resources/logback.xml b/kora-kotlin-helloworld/src/main/resources/logback.xml
index 7f6b209..745e83f 100644
--- a/kora-kotlin-helloworld/src/main/resources/logback.xml
+++ b/kora-kotlin-helloworld/src/main/resources/logback.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/kora-kotlin-helloworld/src/test/resources/logback-test.xml b/kora-kotlin-helloworld/src/test/resources/logback-test.xml
index a9af3b9..adccdab 100644
--- a/kora-kotlin-helloworld/src/test/resources/logback-test.xml
+++ b/kora-kotlin-helloworld/src/test/resources/logback-test.xml
@@ -16,10 +16,9 @@
-
-
+
-
+
diff --git a/settings.gradle b/settings.gradle
index 26dea0a..91990fb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,6 +3,7 @@ pluginManagement {
gradlePluginPortal()
mavenLocal()
mavenCentral()
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
}
@@ -31,5 +32,10 @@ include "kora-java-config-hocon"
include "kora-java-config-yaml"
include "kora-java-helloworld"
include "kora-java-crud"
+include "kora-java-graalvm-crud-jdbc"
+include "kora-java-graalvm-crud-r2dbc"
+include "kora-java-graalvm-crud-vertx"
+include "kora-java-graalvm-crud-cassandra"
+include "kora-java-graalvm-kafka"
include "kora-kotlin-helloworld"
include "kora-kotlin-crud"