Skip to content

Commit

Permalink
Kotlin template simplified and updated
Browse files Browse the repository at this point in the history
  • Loading branch information
GoodforGod committed Oct 27, 2024
1 parent 3a0921f commit a7c76c6
Show file tree
Hide file tree
Showing 20 changed files with 117 additions and 228 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

# Шаблон приложения Kora Kotlin CRUD

Пример Kotlin сервиса реализованного на Kora с HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API,
в качестве базы данных выступает Postgres, используется кэш Caffeine, а также другие модули которые использовались бы в реальном приложении в бою.
Шаблон для быстрого старта нового проекта на Kotlin и Kora с базовым настроенным HTTP [CRUD](https://github.com/swagger-api/swagger-petstore) API для одной сущности.
В качестве базы данных выступает Postgres, используется кэш Caffeine,
а также другие модули которые использовались бы в реальном приложении в бою.

В примере использовались модули:
В шаблоне используются модули:
- [HTTP сервер](https://kora-projects.github.io/kora-docs/ru/documentation/http-server/)
- [OpenAPI HTTP серверная генерация](https://kora-projects.github.io/kora-docs/ru/documentation/openapi-codegen/)
- [Пробы](https://kora-projects.github.io/kora-docs/ru/documentation/probes/)
Expand All @@ -18,7 +19,7 @@

## Build

Собрать классы (может потребоваться запустить 2 раза из-за APT):
Собрать классы (может потребоваться запустить 2 раза из-за Kotlin APT & KSP):

```shell
./gradlew classes
Expand All @@ -37,13 +38,27 @@
./gradlew openApiGenerateHttpServer
```

### Image

Собрать образ приложения:
```shell
docker build -t kora-kotlin-crud .
```

## Run

Запустить локально:
```shell
./gradlew run
```

## Migration

Миграции вызываются с помощью Flyway Gradle Plugin:
```shell
./gradlew flywayMigrate
```

## Test

Тесты используют [Testcontainers](https://java.testcontainers.org/), требуется [Docker](https://docs.docker.com/engine/install/) окружение для запуска тестов или аналогичные контейнерные окружения ([colima](https://github.com/abiosoft/colima) / итп)
Expand Down
74 changes: 41 additions & 33 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.google.devtools.ksp.gradle.KspTask
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask

Expand All @@ -21,11 +22,6 @@ plugins {
group = property("groupId")!!
version = property("koraVersion")!!

application {
applicationName = "application"
mainClass.set("ru.tinkoff.kora.kotlin.crud.ApplicationKt")
}

kotlin {
jvmToolchain { languageVersion.set(JavaLanguageVersion.of(17)) }
sourceSets.main { kotlin.srcDir("build/generated/openapi") }
Expand All @@ -37,13 +33,13 @@ kotlin {
val koraBom: Configuration by configurations.creating
configurations {
ksp.get().extendsFrom(koraBom)
compileOnly.get().extendsFrom(koraBom)
api.get().extendsFrom(koraBom)
implementation.get().extendsFrom(koraBom)
}

repositories {
mavenCentral()
maven("https://oss.sonatype.org/content/repositories/snapshots")
}

dependencies {
Expand Down Expand Up @@ -73,8 +69,31 @@ dependencies {

testImplementation("io.mockk:mockk:1.13.8")
testImplementation("ru.tinkoff.kora:test-junit5")
testImplementation("io.goodforgod:testcontainers-extensions-postgres:0.11.0")
testImplementation("org.testcontainers:junit-jupiter:1.17.6")
testImplementation("io.goodforgod:testcontainers-extensions-postgres:0.12.0")
testImplementation("org.testcontainers:junit-jupiter:1.19.8")
}

application {
applicationName = "application"
mainClass.set("ru.tinkoff.kora.kotlin.crud.ApplicationKt")
applicationDefaultJvmArgs = listOf("-Dfile.encoding=UTF-8")
}

tasks.distTar {
archiveFileName.set("application.tar")
}

val postgresHost: String by project
val postgresPort: String by project
val postgresDatabase: String by project
val postgresUser: String by project
val postgresPassword: String by project
tasks.withType<JavaExec> {
environment(
"POSTGRES_JDBC_URL" to "jdbc:postgresql://${postgresHost}:${postgresPort}/${postgresDatabase}",
"POSTGRES_USER" to postgresUser,
"POSTGRES_PASS" to postgresPassword,
)
}

tasks.register("openApiGenerateHttpServer", GenerateTask::class) {
Expand All @@ -101,38 +120,20 @@ tasks.withType<KspTask> {
tasks.withType<KotlinCompile>().configureEach {
dependsOn(tasks.named("openApiGenerateHttpServer"))
}
tasks.named("test") {

tasks.test {
dependsOn("distTar")
}

val postgresHost: String by project
val postgresPort: String by project
val postgresDatabase: String by project
val postgresUser: String by project
val postgresPassword: String by project
tasks.withType<JavaExec> {
environment(
"POSTGRES_JDBC_URL" to "jdbc:postgresql://${postgresHost}:${postgresPort}/${postgresDatabase}",
"POSTGRES_USER" to postgresUser,
"POSTGRES_PASS" to postgresPassword,
jvmArgs(
"-XX:+TieredCompilation",
"-XX:TieredStopAtLevel=1",
)
}

flyway {
url = "jdbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase"
user = postgresUser
password = postgresPassword
locations = arrayOf("classpath:db/migration")
}

tasks.distTar {
archiveFileName.set("application.tar")
}

tasks.test {
useJUnitPlatform()
testLogging {
showStandardStreams = true
events("passed", "skipped", "failed")
exceptionFormat = TestExceptionFormat.FULL
}

reports {
Expand All @@ -147,3 +148,10 @@ tasks.jacocoTestReport {
html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
}
}

flyway {
url = "jdbc:postgresql://$postgresHost:$postgresPort/$postgresDatabase"
user = postgresUser
password = postgresPassword
locations = arrayOf("classpath:db/migration")
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
groupId=ru.tinkoff.kora
koraVersion=1.1.9
koraVersion=1.1.11


##### GRADLE #####
Expand Down
1 change: 0 additions & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
maven("https://oss.sonatype.org/content/repositories/snapshots")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.tinkoff.kora.kotlin.example.crud.controller
package ru.tinkoff.kora.kotlin.crud.controller

import io.micrometer.core.instrument.config.validate.ValidationException
import ru.tinkoff.kora.common.Component
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package ru.tinkoff.kora.kotlin.example.crud.controller
package ru.tinkoff.kora.kotlin.crud.controller

import ru.tinkoff.kora.common.Component
import ru.tinkoff.kora.kotlin.crud.model.PetMapper
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.api.PetApiDelegate
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.api.PetApiResponses
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.model.MessageTO
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.model.PetCreateTO
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.model.PetUpdateTO
import ru.tinkoff.kora.kotlin.example.crud.model.PetMapper
import ru.tinkoff.kora.kotlin.example.crud.model.PetWithCategory
import ru.tinkoff.kora.kotlin.example.crud.service.PetService
import ru.tinkoff.kora.kotlin.crud.service.PetService

@Component
class PetDelegate(
Expand All @@ -23,16 +22,16 @@ class PetDelegate(

val pet = petService.findByID(id)
if (pet != null) {
val body = petMapper.petWithCategoryToPetTO(pet)
val body = petMapper.asDTO(pet)
return PetApiResponses.GetPetByIdApiResponse.GetPetById200ApiResponse(body)
} else {
return PetApiResponses.GetPetByIdApiResponse.GetPetById404ApiResponse(notFound(id))
}
}

override fun addPet(petCreateTO: PetCreateTO): PetApiResponses.AddPetApiResponse {
val pet: PetWithCategory = petService.add(petCreateTO)
val body = petMapper.petWithCategoryToPetTO(pet)
val pet = petService.add(petCreateTO)
val body = petMapper.asDTO(pet)
return PetApiResponses.AddPetApiResponse.AddPet200ApiResponse(body)
}

Expand All @@ -43,7 +42,7 @@ class PetDelegate(

val updated = petService.update(id, petUpdateTO)
if (updated != null) {
val body = petMapper.petWithCategoryToPetTO(updated)
val body = petMapper.asDTO(updated)
return PetApiResponses.UpdatePetApiResponse.UpdatePet200ApiResponse(body)
} else {
return PetApiResponses.UpdatePetApiResponse.UpdatePet404ApiResponse(notFound(id))
Expand All @@ -57,7 +56,7 @@ class PetDelegate(

return if (petService.delete(id)) {
PetApiResponses.DeletePetApiResponse.DeletePet200ApiResponse(
MessageTO("Successfully deleted pet with ID: $id")
MessageTO("Successfully deleted Pet with ID: $id")
)
} else {
PetApiResponses.DeletePetApiResponse.DeletePet404ApiResponse(notFound(id))
Expand Down
20 changes: 1 addition & 19 deletions src/main/java/ru/tinkoff/kora/kotlin/crud/model/DAO.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ru.tinkoff.kora.kotlin.example.crud.model
package ru.tinkoff.kora.kotlin.crud.model

import ru.tinkoff.kora.database.common.annotation.Column
import ru.tinkoff.kora.database.common.annotation.Embedded
import ru.tinkoff.kora.database.common.annotation.Id
import ru.tinkoff.kora.database.common.annotation.Table

Expand All @@ -10,27 +9,10 @@ data class Pet(
@field:Column("id") @field:Id val id: Long,
@field:Column("name") val name: String,
@field:Column("status") val status: Status,
@field:Column("category_id") val categoryId: Long
) {
enum class Status(val code: Int) {
AVAILABLE(0),
PENDING(10),
SOLD(20)
}
}

@Table("categories")
data class PetCategory(
@field:Id val id: Long,
val name: String
)

data class PetWithCategory(
@field:Column("id") val id: Long,
@field:Column("name") val name: String,
@field:Column("status") val status: Pet.Status,
@field:Embedded("category_") val category: PetCategory
) {

fun getPet(): Pet = Pet(id, name, status, category.id)
}
7 changes: 2 additions & 5 deletions src/main/java/ru/tinkoff/kora/kotlin/crud/model/PetMapper.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package ru.tinkoff.kora.kotlin.example.crud.model
package ru.tinkoff.kora.kotlin.crud.model

import org.mapstruct.Mapper
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.model.CategoryTO
import ru.tinkoff.kora.kotlin.crud.openapi.http.server.model.PetTO

@Mapper
interface PetMapper {

fun petWithCategoryToPetTO(pet: PetWithCategory): PetTO

fun petCategoryToCategoryTO(category: PetCategory): CategoryTO
fun asDTO(pet: Pet): PetTO
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package ru.tinkoff.kora.kotlin.example.crud.repository
package ru.tinkoff.kora.kotlin.crud.repository

import ru.tinkoff.kora.common.Component
import ru.tinkoff.kora.database.jdbc.mapper.parameter.JdbcParameterColumnMapper
import ru.tinkoff.kora.database.jdbc.mapper.result.JdbcResultColumnMapper
import ru.tinkoff.kora.kotlin.example.crud.model.Pet
import ru.tinkoff.kora.kotlin.crud.model.Pet
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
package ru.tinkoff.kora.kotlin.example.crud.repository
package ru.tinkoff.kora.kotlin.crud.repository

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.kotlin.example.crud.model.Pet
import ru.tinkoff.kora.kotlin.example.crud.model.PetWithCategory
import ru.tinkoff.kora.kotlin.crud.model.Pet

@Repository
interface PetRepository : 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
"""
)
fun findById(id: Long): PetWithCategory?
@Query("SELECT %{return#selects} FROM %{return#table} WHERE id = :id")
fun findById(id: Long): Pet?

@Id
@Query("INSERT INTO %{entity#inserts -= id}")
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/ru/tinkoff/kora/kotlin/crud/service/Cache.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package ru.tinkoff.kora.kotlin.example.crud.service
package ru.tinkoff.kora.kotlin.crud.service

import ru.tinkoff.kora.cache.annotation.Cache
import ru.tinkoff.kora.cache.caffeine.CaffeineCache
import ru.tinkoff.kora.kotlin.example.crud.model.PetWithCategory
import ru.tinkoff.kora.kotlin.crud.model.Pet

@Cache("pet-cache")
interface PetCache : CaffeineCache<Long, PetWithCategory>
interface PetCache : CaffeineCache<Long, Pet>
Loading

0 comments on commit a7c76c6

Please sign in to comment.