我实际上正在开发一个具有 Kotlin Springboot 后端的项目,其中使用 OpenApi 生成的 Api(在 yaml 中),我想将其插入客户端 Android Jetpack Compose。
我想要做的是从我的后端项目生成 openApi 客户端作为 Jar Kotlin 依赖项,并将其导入到我的客户端项目中。
这是实现“客户端生成/jar打包/maven本地发布”阶段的后端gradle构建:
后端客户端生成:
tasks.register("generateKotlinClient", org.openapitools.generator.gradle.plugin.tasks.GenerateTask::class) {
generatorName.set("kotlin")
inputSpec.set("$rootDir/src/main/resources/api/api.yaml")
outputDir.set(layout.buildDirectory.dir("generated/grimoire-server-client").get().asFile.path)
invokerPackage.set("com.com.corbz.grimoire.backend.client")
packageName.set("com.com.corbz.grimoire.backend.client")
apiPackage.set("com.com.corbz.grimoire.backend.client.api")
modelPackage.set("com.com.corbz.grimoire.backend.client.model")
generateApiDocumentation.set(false)
generateModelDocumentation.set(false)
generateApiTests.set(false)
generateModelTests.set(false)
configOptions.set(
mapOf(
"sourceFolder" to "."
)
)
}
tasks.register("packageClientJar", Jar::class) {
group = "build"
val generatedClientDir = layout.buildDirectory.dir("generated/grimoire-server-client/com").get().asFile
from(generatedClientDir)
archiveBaseName.set("grimoire-backend-client")
archiveVersion.set("1.0.0")
destinationDirectory.set(layout.buildDirectory.dir("libs").get().asFile)
}
publishing {
publications {
create<MavenPublication>("grimoireBackendClient") {
groupId = "com.corbz"
artifactId = "grimoire-backend-client"
version = "1.0.0"
artifact(tasks.named("packageClientJar").get()) {
builtBy(tasks.named("packageClientJar"))
}
}
}
repositories {
maven {
name = "local"
url = uri("${System.getProperty("user.home")}/.m2/repository")
}
}
}
它为我生成一个包含包文件夹和清单文件夹的 jar。我的 Android 项目由主 build.gradle.kts /setting.gradle.kts 组织,我的代码位于具有自己的 build.gradle.kts /setting.gradle.kts 的应用程序包中。在应用程序包的 build.gradle.kts 中,我的库是这样导入的(我没有将所有文件与所有依赖项放在一起,但这就是我导入客户端依赖项的方式):
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.corbz.grmoire.android"
compileSdk = 35
defaultConfig {
applicationId = "com.corbz.grmoire.android"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
}
dependencies {
// Business dependencies --------------------------
implementation(libs.grimoire.backend.client)
implementation(libs.moshi)
implementation(libs.moshi.kotlin)
// ------------------------------------------------
}
由于我不明白该库是否被识别,我可以像这样导入包:
package com.corbz.grmoire.android.config
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import com.corbz.grimoire.backend.client.model.*
@HiltAndroidApp
class GrimoireApplication: Application() {
}
但是,如果我尝试从此包中导入例如 GrimoireDto,则无法识别它。这是从 openApi 客户端依赖项为我的 GrimoireDto 生成的类 (kt):
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport"
)
package com.com.corbz.grimoire.backend.client.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
*
*
* @param code Unique identifier for the grimoire.
* @param creationDate The creation date of the grimoire.
* @param lastUpdateDate The last update date of the grimoire.
*/
data class GrimoireDto (
/* Unique identifier for the grimoire. */
@Json(name = "code")
val code: kotlin.String,
/* The creation date of the grimoire. */
@Json(name = "creationDate")
val creationDate: java.time.OffsetDateTime,
/* The last update date of the grimoire. */
@Json(name = "lastUpdateDate")
val lastUpdateDate: java.time.OffsetDateTime
) {
}
我不明白为什么这个 Kotlin 类不能导入,因为它是一个 .kt 文件,对我来说该类没有任何问题,我还尝试导入子依赖项(例如客户端的 mochi)以使所有依赖项都正常工作但没有任何效果。 我什至尝试使用 android 生成器生成(对我来说它应该与 kotlin 多平台一起使用,但它是“为了科学”)但问题仍然是一样的。
您能帮忙找出我的生成/导入出了什么问题吗?我想导入这个客户端以避免客户端调用后端的手动编码。
提前感谢大家!
经过一番搜索和尝试,我发现它可以解决我的问题。正如在某些情况下建议的那样,我所做的就是更改我的项目结构以进行根构建,并将应用程序模块(包含源、构建定义、测试)和客户端模块(客户端构建和 jar包装):
这是客户端 build.gradle.kts :
plugins {
// Inherited
kotlin("jvm")
id("org.openapi.generator")
id("maven-publish")
}
group = "com.corbz.grimoire"
version = "0.0.2-SNAPSHOT"
repositories {
mavenCentral()
}
sourceSets {
main {
java.srcDir("src/main/generated-client")
}
}
/**
* Build Processing Workflow
*/
task("processGeneratedFilesClient") {
val generatedDir = file("$projectDir/src/main/generated-client")
println("GeneratedDir to clean : $generatedDir")
if (generatedDir.exists()) {
println("Cleaning up existing generated files...")
delete(generatedDir)
}
}
// Ajout d'une tâche pour s'assurer que les sources sont générées avant la compilation
tasks.compileKotlin {
dependsOn(tasks.openApiGenerate)
}
// Backend Client Generation
openApiGenerate {
generatorName.set("kotlin")
inputSpec.set("$rootDir/api/api.yaml") // Chemin vers ton fichier YAML
outputDir.set("$projectDir/src/main/generated-client")
invokerPackage.set("com.corbz.grimoire.backend.client")
packageName.set("com.corbz.grimoire.backend.client")
apiPackage.set("com.corbz.grimoire.backend.client.api")
modelPackage.set("com.corbz.grimoire.backend.client.model")
generateApiDocumentation.set(false)
generateModelDocumentation.set(false)
generateApiTests.set(false)
generateModelTests.set(false)
configOptions.set(mapOf("sourceFolder" to ""))
}
publishing {
publications {
create<MavenPublication>("grimoireBackendClientJar") {
groupId = project.group.toString()
artifactId = "grimoire-backend-client"
version = project.version.toString()
from(components["java"])
}
}
repositories {
mavenLocal()
}
}
dependencies {
implementation("com.squareup.moshi:moshi-kotlin:1.15.2")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
}
这是应用程序构建:
plugins {
kotlin("plugin.spring") version "2.1.0"
id("org.springframework.boot") version "3.3.5"
id("io.spring.dependency-management") version "1.1.6"
// Inherited
kotlin("jvm")
id("org.openapi.generator")
}
group = "com.corbz.grimoire.backend.app"
version = "0.0.1-SNAPSHOT"
kotlin {
jvmToolchain(23)
}
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
// Open Api Generation
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.springdoc:springdoc-openapi-starter-common:2.2.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Spring Ecosystem
implementation("org.springframework.boot:spring-boot-starter-web")
// database
implementation("org.springframework.boot:spring-boot-starter-data-mongodb") // Documents / Consistences negligeables
implementation("org.springframework.boot:spring-boot-starter-data-jpa") // Utilisateurs / Consistences fortes
implementation("org.postgresql:postgresql:42.7.4")
implementation("de.flapdoodle.embed:de.flapdoodle.embed.mongo.spring25x:4.18.0") //TODO : Pas sûr d'en avoir besoin
implementation("org.jetbrains.kotlin:kotlin-reflect") //TODO: idem
// Security
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
// Unit testing
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// Integration testing
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.testcontainers:testcontainers:1.17.6")
testImplementation("org.testcontainers:postgresql:1.20.4")
testImplementation("org.testcontainers:mongodb:1.17.6")
testImplementation("org.testcontainers:junit-jupiter:1.17.6")
}
sourceSets {
main {
java.srcDir("src/main/generated-api") // Ajouter le dossier 'generated' comme répertoire source
}
}
/**
* Build Processing Workflow
*/
task("processGeneratedFilesApi") {
// Nettoyer le dossier 'generated' s'il existe
val generatedDir = file("$projectDir/src/main/generated-api")
println("GeneratedDir to clean : $generatedDir")
if (generatedDir.exists()) {
println("Cleaning up existing generated $projectDir files...")
delete(generatedDir) // Supprimer les fichiers existants dans le dossier 'generated'
}
}
tasks.compileKotlin {
dependsOn(tasks.openApiGenerate)
}
/**
* API Generation
*/
//Server Api Generation
openApiGenerate {
generatorName.set("kotlin-spring")
inputSpec.set("$rootDir/api/api.yaml") // Chemin vers ton fichier YAML
outputDir.set("$projectDir/src/main/generated-api") // Dossier où tu veux que le code généré soit placé
apiPackage.set("com.corbz.grimoire.api")
modelPackage.set("com.corbz.grimoire.api.model")
configOptions.set(
mapOf(
"interfaceOnly" to "true",
"skipDefaultInterface" to "true", // Force l'implémentation des APIs
"useSpringBoot3" to "true", // Utilisation de jakarta vs javax,
"sourceFolder" to "", // Place le package au bon niveau,
"gradleBuildFile" to "false",
"documentationProvider" to "none"
)
)
}
/**
* Testing tasks
*/
tasks.withType<Test> {
useJUnitPlatform()
}
这允许我将客户端构建为 jar 依赖项,以便作为依赖项集成到其他项目中