Skip to content

Commit

Permalink
Merge pull request #8 from nomisRev/rework-gradle
Browse files Browse the repository at this point in the history
Add temp workaround for MessageStreamEvent
  • Loading branch information
nomisRev authored Jun 7, 2024
2 parents 542ea8b + 5d2bd76 commit 7a4839b
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 118 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugins {
kotlin("multiplatform") version "2.0.0" apply false
alias(libs.plugins.multiplatform) apply false
alias(libs.plugins.publish)
}
21 changes: 9 additions & 12 deletions generation/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

plugins {
kotlin("multiplatform")
kotlin("plugin.serialization") version "2.0.0"
kotlin("plugin.power-assert") version "2.0.0"
id(libs.plugins.multiplatform.get().pluginId)
alias(libs.plugins.serialization)
alias(libs.plugins.assert)
id(libs.plugins.publish.get().pluginId)
}

@Suppress("OPT_IN_USAGE")
Expand All @@ -29,18 +30,14 @@ kotlin {
kotlin.srcDir(project.file("build/generated/openapi/src/commonMain/kotlin"))

dependencies {
// implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.3.5")
implementation("net.pearx.kasechange:kasechange:1.4.1")
implementation("com.squareup.okio:okio:3.9.0")
implementation(project(":core"))
// for build debugging example
implementation("io.exoquery:pprint-kotlin-kmp:2.0.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
api(libs.kasechange)
api(libs.okio)
api(projects.parser)
}
}
commonTest {
dependencies {
implementation(kotlin("test"))
implementation(libs.test)
}
}
// jsMain {
Expand All @@ -50,7 +47,7 @@ kotlin {
// }
commonTest {
dependencies {
implementation("com.squareup.okio:okio-fakefilesystem:3.9.0")
implementation(libs.okio.fakefilesystem)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions generation/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
POM_NAME=openapi-kt-generator
POM_DESCRIPTION=OpenAPI Kotlin Generator
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,88 @@ import okio.Path
import okio.Path.Companion.toPath

public fun FileSystem.generateClient(
pathSpec: String,
`package`: String = "io.github.nomisrev.openapi",
modelPackage: String = "$`package`.models",
generationPath: String =
"../example/build/generated/openapi/src/commonMain/kotlin/${`package`.replace(".", "/")}"
pathSpec: String,
`package`: String = "io.github.nomisrev.openapi",
modelPackage: String = "$`package`.models",
generationPath: String =
"../example/build/generated/openapi/src/commonMain/kotlin/${`package`.replace(".", "/")}"
) {
fun file(name: String, imports: Set<String>, code: String) {
write(Path("$generationPath/models/$name.kt")) {
writeUtf8("${"package $modelPackage"}\n")
writeUtf8("\n")
if (imports.isNotEmpty()) {
writeUtf8("${imports.joinToString("\n") { "import $it" }}\n")
writeUtf8("\n")
}
writeUtf8("$code\n")
}
fun file(name: String, imports: Set<String>, code: String) {
write(Path("$generationPath/models/$name.kt")) {
writeUtf8("${"package $modelPackage"}\n")
writeUtf8("\n")
if (imports.isNotEmpty()) {
writeUtf8("${imports.joinToString("\n") { "import $it" }}\n")
writeUtf8("\n")
}
writeUtf8("$code\n")
}
}

deleteRecursively(Path(generationPath), false)
runCatching { createDirectories(Path("$generationPath/models"), mustCreate = false) }
val rawSpec = read(Path(pathSpec)) { readUtf8() }
val openAPI = OpenAPI.fromJson(rawSpec)
file("predef", emptySet(), ModelPredef)
openAPI.models().forEach { model ->
val strategy = DefaultNamingStrategy
val content = template { toPropertyCode(model, strategy) }
val name = strategy.typeName(model)
if (name in setOf("MessageStreamEvent", "RunStepStreamEvent", "RunStreamEvent", "AssistantStreamEvent")) Unit
else file(name, content.imports, content.code)
deleteRecursively(Path(generationPath), false)
runCatching { createDirectories(Path("$generationPath/models"), mustCreate = false) }
val rawSpec = read(Path(pathSpec)) { readUtf8() }
val openAPI = OpenAPI.fromJson(rawSpec)
file("predef", emptySet(), ModelPredef)
openAPI.models().forEach { model ->
val strategy = DefaultNamingStrategy
val content = template { toPropertyCode(model, strategy) }
val name = strategy.typeName(model)
// if (name in setOf("MessageStreamEvent", "RunStepStreamEvent", "RunStreamEvent", "AssistantStreamEvent")) Unit
// else
file(name, content.imports, content.code)
}

val routes = openAPI
.routes()
.groupBy { route ->
route.path.takeWhile { it != '{' }.dropLastWhile { it == '/' }
}.mapValues { (path, routes) -> extracted(path, routes) }
.forEach { (path, structure) ->
val strategy = DefaultNamingStrategy
}
}

private fun extracted(path: String, routes: List<Route>): Structure {
val split = regex.split(path, limit = 2)
return if (split.size == 2) Structure.Nested(split[1], extracted(split[1], routes))
else Structure.Value(routes)
}

private val regex = "^(.+?)/".toRegex()

/**
* Structure that helps us define the structure of the routes in OpenAPI.
* We want the generated API to look like the URLs, and their OpenAPI Specification.
*
* So we generate the API according to the structure of the OpenAPI Specification.
* A top-level interface `info.title`, or custom name.
*
* And within the interface all operations are available as their URL, with operationId.
* An example for `OpenAI` `/chat/completion` with operationId `createChatCompletion`.
*
* interface OpenAI {
* val chat: Chat
* }
*
* interface Chat {
* val completions: Completions
* }
*
* interface Completions {
* fun createChatCompletion(): CreateChatCompletionResponse
* }
*
* This requires us to split paths in a sane way,
* such that we can follow the structure of the specification.
*/
sealed interface Structure {
data class Value(val route: List<Route>) : Structure
data class Nested(
val name: String,
val route: Structure
) : Structure
}

private fun Path(path: String): Path =
path.toPath()
path.toPath()
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package io.github.nomisrev.openapi

import io.exoquery.fansi.Str
import io.github.nomisrev.openapi.Schema.Type
import io.github.nomisrev.openapi.http.MediaType
import io.github.nomisrev.openapi.http.Method
import io.github.nomisrev.openapi.http.StatusCode
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.Serializable

data class Route(
val operation: Operation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ interface OpenAPIInterceptor {
Type.Basic.String ->
if (schema.format == "binary") Model.Binary
else Primitive.String(schema, schema.default?.toString())

Type.Basic.Null -> TODO("Schema.Type.Basic.Null")
}.let { primitive(context, schema, basic, it) }

Expand Down Expand Up @@ -386,23 +387,40 @@ interface OpenAPIInterceptor {
}
}

/**
* TODO
* This needs a rock solid implementation,
* and should be super easy to override from Gradle.
* This is what we use to generate names for inline schemas,
* most of the time we can get away with other information,
* but not always.
*/
private fun OpenAPISyntax.generateName(
context: NamingContext.TopLevelSchema,
schema: Schema
): NamingContext.TopLevelSchema =
when (val type = schema.type) {
is Type.Array -> TODO()
Type.Basic.Array -> {
val inner =
requireNotNull(schema.items) { "Array type requires items to be defined." }
inner.get().type
TODO()
}

Type.Basic.Object -> context.copy(name = "CaseJson")
Type.Basic.Number -> context.copy(name = "CaseDouble")
Type.Basic.Boolean -> context.copy(name = "CaseBool")
Type.Basic.Integer -> context.copy(name = "CaseInt")
Type.Basic.Object -> {
// TODO OpenAI specific
context.copy(
name = schema.properties
?.firstNotNullOfOrNull { (key, value) ->
if (key == "event") value.get().enum else null
}?.singleOrNull() ?: TODO()
)
}

is Type.Array -> TODO()
Type.Basic.Number -> TODO()
Type.Basic.Boolean -> TODO()
Type.Basic.Integer -> TODO()
Type.Basic.Null -> TODO()
Type.Basic.String -> when (val enum = schema.enum) {
null -> context.copy(name = "CaseString")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import io.github.nomisrev.openapi.AdditionalProperties.Allowed
import io.github.nomisrev.openapi.Model.Collection
import io.github.nomisrev.openapi.http.Method

public fun OpenAPI.routes(): List<Route> =
OpenAPITransformer(this).routes()

public fun OpenAPI.models(): Set<Model> =
with(OpenAPITransformer(this)) {
operationModels() + schemas()
Expand Down Expand Up @@ -61,7 +64,6 @@ private class OpenAPITransformer(
)
}

// TODO can we re-share logic with models?
fun Operation.input(): List<Route.Input> =
parameters.map { p ->
val param = p.get()
Expand Down Expand Up @@ -129,9 +131,9 @@ private class OpenAPITransformer(

override fun Schema.toModel(context: NamingContext): Model =
when {
anyOf != null -> toAnyOf(context, this, anyOf ?: emptyList())
anyOf != null -> toAnyOf(context, this, anyOf!!)
oneOf != null && oneOf?.size == 1 -> asObject(context)
oneOf != null -> toOneOf(context, this, oneOf ?: emptyList())
oneOf != null -> toOneOf(context, this, oneOf !!)
allOf != null -> TODO("allOf")
enum != null -> toEnum(
context,
Expand Down
17 changes: 17 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,20 @@ kotlin.code.style=official
kotlin.js.compiler=ir
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
org.gradle.parallel=true

SONATYPE_HOST=S01
#RELEASE_SIGNING_ENABLED=true

POM_URL=https://github.com/nomisrev/OpenAPI-kt/

POM_LICENSE_NAME=The Apache Software License, Version 2.0
POM_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENSE_DIST=repo

POM_SCM_URL=https://github.com/nomisrev/OpenAPI-kt/
POM_SCM_CONNECTION=scm:git:git://github.com/nomisRev/OpenAPI-kt.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://[email protected]/nomisRev/OpenAPI-kt.git

POM_DEVELOPER_ID=nomisRev
POM_DEVELOPER_NAME=Simon Vergauwen
POM_DEVELOPER_URL=https://github.com/nomisRev/
26 changes: 18 additions & 8 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@
dokka = "1.9.10"
kotlin = "2.0.0"
kover = "0.7.4"
knit = "0.5.0"
spotless="6.22.0"
power-assert = "0.13.0"
publish="0.28.0"
json="1.6.3"
okio="3.9.0"
kasechange="1.4.1"

[libraries]
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
test = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "json" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
okio-fakefilesystem = { module = "com.squareup.okio:okio-fakefilesystem", version.ref = "okio" }
kasechange = { module = "net.pearx.kasechange:kasechange", version.ref = "kasechange" }

[plugins]
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
assert = { id = "org.jetbrains.kotlin.plugin.power-assert", version.ref = "kotlin" }
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest-plugin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-assert = { id = "org.jetbrains.kotlin.plugin.power-assert", version.ref = "kotlin" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
knit = { id = "org.jetbrains.kotlinx.knit", version.ref = "knit" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
publish = { id = "com.vanniktech.maven.publish", version.ref="publish" }
37 changes: 19 additions & 18 deletions parser/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization") version "1.9.23"
id(libs.plugins.multiplatform.get().pluginId)
alias(libs.plugins.serialization)
id(libs.plugins.publish.get().pluginId)
}

kotlin {
explicitApi()
explicitApi()


jvm()
macosArm64()
linuxX64()
jvm()
macosArm64()
linuxX64()

sourceSets {
commonMain {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}
}
jvmTest {
dependencies {
implementation(kotlin("test"))
}
}
sourceSets {
commonMain {
dependencies {
api(libs.json)
}
}
jvmTest {
dependencies {
implementation(libs.test)
}
}
}
}

tasks.withType<Test> {
useJUnitPlatform()
useJUnitPlatform()
}
4 changes: 2 additions & 2 deletions parser/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
POM_NAME=openapi-kt-generator
POM_DESCRIPTION=OpenAPI Kotlin Generator
POM_NAME=openapi-kt-parser
POM_DESCRIPTION=OpenAPI Kotlin Parser
Loading

0 comments on commit 7a4839b

Please sign in to comment.