Skip to content

Commit fbfe55a

Browse files
authored
BUGFIX: K1 vs K2 AutoCloseable incompatibility (#52)
1 parent da045eb commit fbfe55a

File tree

9 files changed

+80
-43
lines changed

9 files changed

+80
-43
lines changed

example/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,5 +216,6 @@ gradle-app.setting
216216

217217
#Kotlin
218218
.kotlin
219+
kotlin-js-store
219220

220221
# End of https://www.toptal.com/developers/gitignore/api/macos,windows,gradle,kotlin,java,intellij+all

example/build.gradle.kts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ openApiConfig { spec("OpenAI", file("openai.yaml")) {
1010

1111
kotlin {
1212
jvm()
13+
js {
14+
browser()
15+
nodejs()
16+
}
17+
iosArm64()
18+
macosArm64()
19+
linuxX64()
20+
mingwX64()
21+
1322
sourceSets {
1423
commonMain {
1524
dependencies {
@@ -19,5 +28,35 @@ kotlin {
1928
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
2029
}
2130
}
31+
val jvmMain by getting {
32+
dependencies {
33+
api("io.ktor:ktor-client-cio:2.3.6")
34+
}
35+
}
36+
val jsMain by getting {
37+
dependencies {
38+
api("io.ktor:ktor-client-js:2.3.6")
39+
}
40+
}
41+
val iosArm64Main by getting {
42+
dependencies {
43+
implementation("io.ktor:ktor-client-cio:2.3.6")
44+
}
45+
}
46+
val linuxX64Main by getting {
47+
dependencies {
48+
implementation("io.ktor:ktor-client-cio:2.3.6")
49+
}
50+
}
51+
val macosArm64Main by getting {
52+
dependencies {
53+
implementation("io.ktor:ktor-client-cio:2.3.6")
54+
}
55+
}
56+
val mingwX64Main by getting {
57+
dependencies {
58+
implementation("io.ktor:ktor-client-winhttp:2.3.6")
59+
}
60+
}
2261
}
2362
}

example/src/commonMain/kotlin/io/github/nomisrev/example/test.kt

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import io.github.nomisrev.openai.OpenAI
88
import io.ktor.client.*
99
import io.ktor.client.plugins.*
1010
import io.ktor.client.plugins.contentnegotiation.*
11+
import io.ktor.client.request.*
1112
import io.ktor.http.*
1213
import io.ktor.serialization.kotlinx.json.*
1314
import kotlinx.serialization.json.Json
1415

1516
suspend fun main() {
16-
val ai = OpenAI(configuredClient())
17+
val ai = OpenAI(configuredClient("MY_API_KEY"))
1718
ai.chat.completions.createChatCompletion(
1819
CreateChatCompletionRequest(
1920
listOf(
@@ -26,28 +27,23 @@ suspend fun main() {
2627
)
2728
}
2829

29-
private fun configuredClient(): HttpClient = HttpClient {
30+
private fun configuredClient(
31+
token: String,
32+
org: String? = null
33+
): HttpClient = HttpClient {
34+
defaultRequest {
35+
url("https://api.openai.com/v1/")
36+
org?.let { headers.append("OpenAI-Organization", it) }
37+
bearerAuth(token)
38+
}
3039
install(ContentNegotiation) {
3140
json(
3241
Json {
3342
ignoreUnknownKeys = true
34-
prettyPrint = false
3543
isLenient = true
3644
@Suppress("OPT_IN_USAGE")
3745
explicitNulls = false
38-
classDiscriminator = "_type_"
3946
}
4047
)
4148
}
42-
install(HttpTimeout) {
43-
requestTimeoutMillis = 45_000
44-
connectTimeoutMillis = 45_000
45-
socketTimeoutMillis = 45_000
46-
}
47-
install(HttpRequestRetry) {
48-
maxRetries = 5
49-
retryIf { _, response -> !response.status.isSuccess() }
50-
retryOnExceptionIf { _, _ -> true }
51-
delayMillis { retry -> retry * 1000L }
52-
}
5349
}

generation/src/main/kotlin/io/github/nomisrev/openapi/APIs.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import com.squareup.kotlinpoet.MemberSpecHolder
1111
import com.squareup.kotlinpoet.ParameterSpec
1212
import com.squareup.kotlinpoet.TypeSpec
1313
import com.squareup.kotlinpoet.TypeSpecHolder
14-
import com.squareup.kotlinpoet.asClassName
1514
import com.squareup.kotlinpoet.asTypeName
1615
import com.squareup.kotlinpoet.withIndent
1716
import io.github.nomisrev.openapi.NamingContext.Named
1817
import io.github.nomisrev.openapi.NamingContext.Nested
1918
import io.ktor.http.*
19+
import io.ktor.utils.io.core.*
2020

2121
fun configure(defaults: Boolean) =
2222
ParameterSpec(
@@ -63,12 +63,17 @@ private fun Root.addInterface() {
6363
}
6464
val type =
6565
TypeSpec.interfaceBuilder(className())
66-
.addSuperinterface(AutoCloseable::class)
66+
.addSuperinterface(autoCloseable())
6767
.addProperties(properties)
6868
.build()
6969
addType(type)
7070
}
7171

72+
context(OpenAPIContext)
73+
fun autoCloseable(): ClassName =
74+
if (isK2) ClassName("kotlin", "AutoCloseable")
75+
else ClassName("io.ktor.utils.io.core", "Closeable")
76+
7277
context(OpenAPIContext, FileSpec.Builder)
7378
private fun Root.smartConstructor() {
7479
val className = className()
@@ -96,7 +101,7 @@ private fun Root.implementation() {
96101
addType(
97102
TypeSpec.classBuilder(className.postfix("Ktor"))
98103
.addModifiers(KModifier.PRIVATE)
99-
.addSuperinterfaces(listOf(className, AutoCloseable::class.asClassName()))
104+
.addSuperinterfaces(listOf(className, autoCloseable()))
100105
.apiConstructor()
101106
.addProperties(properties)
102107
.addFunction(

generation/src/main/kotlin/io/github/nomisrev/openapi/Main.kt

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,12 @@ import kotlin.io.path.Path
44
import okio.FileSystem
55
import okio.Path.Companion.toPath
66

7-
fun main() {
8-
generate(
9-
GenerationConfig(
10-
"openai-api.yaml",
11-
"generation/build/geneated",
12-
"io.github.nomisrev.openapi",
13-
"OpenAI"
14-
)
15-
)
16-
}
17-
187
data class GenerationConfig(
198
val path: String,
209
val output: String,
2110
val `package`: String,
22-
val name: String
11+
val name: String,
12+
val isK2: Boolean
2313
)
2414

2515
@JvmOverloads
@@ -32,7 +22,7 @@ fun generate(config: GenerationConfig, fileSystem: FileSystem = FileSystem.SYSTE
3222
"yml" -> OpenAPI.fromYaml(rawSpec)
3323
else -> throw IllegalArgumentException("Unsupported file extension: $extension")
3424
}
35-
with(OpenAPIContext(config.`package`)) {
25+
with(OpenAPIContext(config)) {
3626
val root = openAPI.root(config.name)
3727
val models = openAPI.models()
3828
val modelFileSpecs = models.toFileSpecs()

generation/src/main/kotlin/io/github/nomisrev/openapi/OpenAPIContext.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ import java.util.concurrent.atomic.AtomicReference
55

66
interface OpenAPIContext : Naming, APIInterceptor {
77
val `package`: String
8+
val isK2: Boolean
89

910
fun addAdditionalFileSpec(fileSpec: FileSpec)
1011

1112
fun additionalFiles(): List<FileSpec>
1213
}
1314

1415
fun OpenAPIContext(
15-
`package`: String,
16-
interceptor: APIInterceptor = APIInterceptor.openAIStreaming(`package`)
16+
config: GenerationConfig,
17+
interceptor: APIInterceptor = APIInterceptor.openAIStreaming(config.`package`)
1718
): OpenAPIContext =
18-
object : OpenAPIContext, Naming by Naming(`package`), APIInterceptor by interceptor {
19-
override val `package`: String = `package`
19+
object : OpenAPIContext, Naming by Naming(config.`package`), APIInterceptor by interceptor {
20+
override val `package`: String = config.`package`
21+
override val isK2: Boolean = config.isK2
2022
private val files = AtomicReference<List<FileSpec>>(emptyList())
2123

2224
override fun additionalFiles(): List<FileSpec> = files.get()

parser/src/commonMain/kotlin/io/github/nomisrev/openapi/OpenAPI.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,8 @@ public data class OpenAPI(
2424
@EncodeDefault(ALWAYS) public val openapi: String = "3.1.0",
2525
/** Provides metadata about the API. The metadata can be used by the clients if needed. */
2626
public val info: Info,
27-
/**
28-
* An array of Server Objects, which provide connectivity information to a target server. If the
29-
* servers property is not provided, or is an empty array, the default value would be a 'Server'
30-
* object with an url value of @/@.
31-
*/
32-
// Should this be a set??
33-
public val servers: List<Server> = emptyList(),
27+
/** An array of Server Objects, which provide connectivity information to a target server. */
28+
public val servers: List<Server> = listOf(Server(url = "/")),
3429
/** The available paths and operations for the API. */
3530
public val paths: Map<String, PathItem> = emptyMap(),
3631
/**

plugin/src/main/java/io/github/nomisrev/openapi/plugin/GenerateClientAction.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ abstract class GenerateClientAction : WorkAction<GenerateClientAction.Parameters
1515
path = parameters.file.get().asFile.path,
1616
output = parameters.output.get().asFile.path,
1717
`package` = parameters.packageName.get() ?: "io.github.nomisrev.openapi",
18-
name = parameters.name.get()
18+
name = parameters.name.get(),
19+
isK2 = parameters.k2.get()
1920
)
2021
)
2122
}
@@ -25,5 +26,7 @@ abstract class GenerateClientAction : WorkAction<GenerateClientAction.Parameters
2526
val file: RegularFileProperty
2627
val packageName: Property<String>
2728
val output: DirectoryProperty
29+
// isK2 results in runtime Gradle error..
30+
val k2: Property<Boolean>
2831
}
2932
}

plugin/src/main/java/io/github/nomisrev/openapi/plugin/GenerateClientTask.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.gradle.api.tasks.OutputDirectory
1010
import org.gradle.api.tasks.TaskAction
1111
import org.gradle.api.tasks.options.Option
1212
import org.gradle.workers.WorkerExecutor
13+
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
1314

1415
@CacheableTask
1516
abstract class GenerateClientTask : DefaultTask() {
@@ -31,12 +32,17 @@ abstract class GenerateClientTask : DefaultTask() {
3132
val workQueue = getWorkerExecutor().noIsolation()
3233
val specPath = requireNotNull(spec.orNull) { "No OpenAPI Config found" }
3334
require(specPath.isNotEmpty()) { "No OpenAPI Config found" }
35+
val isK2 =
36+
(project.extensions.getByName("kotlin") as KotlinProjectExtension)
37+
.coreLibrariesVersion
38+
.startsWith("2")
3439
specPath.forEach { spec ->
3540
workQueue.submit(GenerateClientAction::class.java) { parameters ->
3641
parameters.name.set(spec.name)
3742
parameters.packageName.set(spec.packageName)
3843
parameters.file.set(spec.file)
3944
parameters.output.set(project.output)
45+
parameters.k2.set(isK2)
4046
}
4147
}
4248
}

0 commit comments

Comments
 (0)