diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d83afa7..e588fb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,11 @@ jobs: distribution: corretto java-version: 17 cache: gradle + - uses: google-github-actions/setup-gcloud@v1 + + - name: start database + run: | + make init_spanner_emulator - name: backend test run: | diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a7bcbce --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +GOOGLE_CLOUD_PROJECT_ID="gsync" + +.PHONY: init_spanner_emulator +init_spanner_emulator: + docker-compose up -d + gcloud config configurations create emulator || true + gcloud config set auth/disable_credentials true + gcloud config set project ${GOOGLE_CLOUD_PROJECT_ID} + gcloud config set api_endpoint_overrides/spanner http://localhost:9020/ + gcloud spanner instances create gsync --config=emulator-config --description="gsync" --nodes=1 + gcloud spanner databases create gsync --instance=gsync + +.PHONY: test +test: + ./gradlew test + +.PHONY: lint +lint: + ./gradlew spotlessCheck + +.PHONY: format +format: + ./gradlew spotlessApply + +.PHONY: check_dependencies +check_dependencies: + ./gradlew dependencyUpdates -Drevision=release diff --git a/README.md b/README.md index f26cc04..c985f5b 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,14 @@ This component provides only reusable features that can be used across various g * Java OpenJDK 17 * Kotlin 1.9 * Quarkus 3.4 -* TiDB 7.1 +* Cloud Spanner ### Running the application in dev mode You can run your application in dev mode that enables live coding. ```shell -docker compose up -d +make init_spanner_emulator ./gradlew quarkusDev ``` @@ -66,7 +66,7 @@ Or, if you don't have GraalVM installed, you can run the native executable build [Gradle Versions Plugin](https://github.com/ben-manes/gradle-versions-plugin) checks outdated dependencies. ```shell -$ ./gradlew dependencyUpdates -Drevision=release +make check_dependencies ``` ### Run code formatter @@ -74,5 +74,5 @@ $ ./gradlew dependencyUpdates -Drevision=release This codebase is formatted by [ktlint](https://github.com/pinterest/ktlint). ```shell -./gradlew spotlessApply +make format ``` diff --git a/build.gradle.kts b/build.gradle.kts index db55330..898fd99 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ val quarkusPlatformArtifactId: String by project val quarkusPlatformVersion: String by project dependencies { + // Quarkus implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")) implementation("io.quarkus:quarkus-kotlin") implementation("io.quarkus:quarkus-resteasy-reactive-jackson") @@ -44,6 +45,12 @@ dependencies { implementation("io.quarkus:quarkus-config-yaml") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + // GCP + implementation("io.quarkiverse.googlecloudservices:quarkus-google-cloud-spanner:2.5.0") + + // utils + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2") + // test testImplementation("io.quarkus:quarkus-junit5") testImplementation("io.github.dvgaba:easy-random-core:6.2.0") diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..8c70dde --- /dev/null +++ b/compose.yaml @@ -0,0 +1,6 @@ +services: + spanner: + build: ./docker/spanner + ports: + - "9010:9010" + - "9020:9020" diff --git a/docker/spanner/Dockerfile b/docker/spanner/Dockerfile new file mode 100644 index 0000000..64c44f5 --- /dev/null +++ b/docker/spanner/Dockerfile @@ -0,0 +1 @@ +FROM gcr.io/cloud-spanner-emulator/emulator \ No newline at end of file diff --git a/src/main/kotlin/net/averak/gsync/infrastructure/json/JsonUtils.kt b/src/main/kotlin/net/averak/gsync/infrastructure/json/JsonUtils.kt new file mode 100644 index 0000000..e5b79e8 --- /dev/null +++ b/src/main/kotlin/net/averak/gsync/infrastructure/json/JsonUtils.kt @@ -0,0 +1,42 @@ +package net.averak.gsync.infrastructure.json + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.KotlinFeature +import com.fasterxml.jackson.module.kotlin.KotlinModule + +class JsonUtils { + + companion object { + + private val objectMapper = ObjectMapper() + .registerModule( + KotlinModule.Builder() + .withReflectionCacheSize(512) + .configure(KotlinFeature.NullToEmptyCollection, false) + .configure(KotlinFeature.NullToEmptyMap, false) + .configure(KotlinFeature.NullIsSameAsDefault, false) + .configure(KotlinFeature.SingletonSupport, false) + .configure(KotlinFeature.StrictNullChecks, false) + .build() + ) + .registerModule(JavaTimeModule()) + .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true) + + @JvmStatic + fun encode(value: Any?): String { + return if (value == null) { + "{}" + } else { + objectMapper.writeValueAsString(value) + } + } + + @JvmStatic + fun decode(json: String, clazz: Class): T { + return objectMapper.readValue(json, clazz) + } + + } +} diff --git a/src/main/kotlin/net/averak/gsync/infrastructure/spanner/SpannerClient.kt b/src/main/kotlin/net/averak/gsync/infrastructure/spanner/SpannerClient.kt new file mode 100644 index 0000000..a43fc4f --- /dev/null +++ b/src/main/kotlin/net/averak/gsync/infrastructure/spanner/SpannerClient.kt @@ -0,0 +1,9 @@ +package net.averak.gsync.infrastructure.spanner + +import com.google.cloud.spanner.Spanner +import jakarta.inject.Singleton + +@Singleton +class SpannerClient( + private val spanner: Spanner, +) \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index e69de29..22d0e17 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -0,0 +1,16 @@ +quarkus: + application: + name: "gsync" + version: "1.0.0-SNAPSHOT" + datasource: + db-kind: spanner + jdbc: + url: jdbc:cloudspanner:/projects/${quarkus.google.cloud.project-id}/instances/${quarkus.google.cloud.instance-id}/databases/${quarkus.google.cloud.database-id} + driver: com.google.cloud.spanner.jdbc.JdbcDriver + google: + cloud: + project-id: ${GOOGLE_CLOUD_PROJECT_ID:gsync} + instance-id: ${GOOGLE_CLOUD_SPANNER_INSTANCE_ID:gsync} + database-id: ${GOOGLE_CLOUD_SPANNER_DATABASE_NAME:gsync} + spanner: + emulator-host: http://localhost:9020 diff --git a/src/test/groovy/net/averak/gsync/adapter/handler/controller/AbstractController_IT.groovy b/src/test/groovy/net/averak/gsync/adapter/handler/controller/AbstractController_IT.groovy new file mode 100644 index 0000000..e97bce7 --- /dev/null +++ b/src/test/groovy/net/averak/gsync/adapter/handler/controller/AbstractController_IT.groovy @@ -0,0 +1,6 @@ +package net.averak.gsync.adapter.handler.controller + +import net.averak.gsync.AbstractSpec + +class AbstractController_IT extends AbstractSpec { +} diff --git a/src/test/groovy/net/averak/gsync/AbstractSpec.groovy b/src/test/kotlin/net/averak/gsync/AbstractSpec.kt similarity index 67% rename from src/test/groovy/net/averak/gsync/AbstractSpec.groovy rename to src/test/kotlin/net/averak/gsync/AbstractSpec.kt index 93fe2ae..cef9bf4 100644 --- a/src/test/groovy/net/averak/gsync/AbstractSpec.groovy +++ b/src/test/kotlin/net/averak/gsync/AbstractSpec.kt @@ -4,5 +4,4 @@ import io.quarkus.test.junit.QuarkusTest import spock.lang.Specification @QuarkusTest -abstract class AbstractSpec extends Specification { -} +abstract class AbstractSpec : Specification() \ No newline at end of file diff --git a/src/test/groovy/net/averak/gsync/TestConfig.groovy b/src/test/kotlin/net/averak/gsync/TestConfig.kt similarity index 82% rename from src/test/groovy/net/averak/gsync/TestConfig.groovy rename to src/test/kotlin/net/averak/gsync/TestConfig.kt index ecfdee3..91d1217 100644 --- a/src/test/groovy/net/averak/gsync/TestConfig.groovy +++ b/src/test/kotlin/net/averak/gsync/TestConfig.kt @@ -3,5 +3,4 @@ package net.averak.gsync import jakarta.enterprise.context.ApplicationScoped @ApplicationScoped -class TestConfig { -} +class TestConfig \ No newline at end of file diff --git a/src/test/kotlin/net/averak/gsync/testutils/JsonUtils.kt b/src/test/kotlin/net/averak/gsync/testutils/JsonUtils.kt deleted file mode 100644 index 8df04d5..0000000 --- a/src/test/kotlin/net/averak/gsync/testutils/JsonUtils.kt +++ /dev/null @@ -1,29 +0,0 @@ -package net.averak.gsync.testutils - -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule - -class JsonUtils { - - companion object { - - private val objectMapper = ObjectMapper() - .registerModule(JavaTimeModule()) - .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true) - - @JvmStatic - fun toJson(value: Any?): String { - return if (value == null) { - "{}" - } else { - objectMapper.writeValueAsString(value) - } - } - - @JvmStatic - fun fromJson(json: String, clazz: Class): T { - return objectMapper.readValue(json, clazz) - } - } -} diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml new file mode 100644 index 0000000..e69de29