Skip to content

Commit b4e396b

Browse files
committed
feat(model-server): migrate to resource based routing based on OpenAPI spec
1 parent aef23ca commit b4e396b

File tree

12 files changed

+1793
-148
lines changed

12 files changed

+1793
-148
lines changed

api/public.yaml

Lines changed: 1365 additions & 0 deletions
Large diffs are not rendered by default.

gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ dokka = {id = "org.jetbrains.dokka", version = "1.9.10"}
1919
node = {id = "com.github.node-gradle.node", version = "7.0.1"}
2020
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.4" }
2121
npm-publish = { id = "dev.petuska.npm.publish", version = "3.4.1" }
22+
openapi-generator = {id = "org.openapi.generator", version.ref = "openapi"}
23+
#kotlin-plugin-allopen = { id = "org.jetbrains.kotlin.plugin.allopen", version.ref = "kotlin" }
2224

2325
[versions]
2426
kotlin = "1.9.21"
@@ -30,6 +32,7 @@ ignite="2.15.0"
3032
apacheCxf="3.6.2"
3133
node="18.17.1"
3234
modelixBuildtools="1.2.0"
35+
openapi = "7.0.1"
3336

3437
[libraries]
3538

@@ -57,6 +60,7 @@ ktor-server-sessions = { group = "io.ktor", name = "ktor-server-sessions", versi
5760
ktor-server-status-pages = { group = "io.ktor", name = "ktor-server-status-pages", version.ref = "ktor" }
5861
ktor-server-test-host = { group = "io.ktor", name = "ktor-server-test-host", version.ref = "ktor" }
5962
ktor-server-websockets = { group = "io.ktor", name = "ktor-server-websockets", version.ref = "ktor" }
63+
ktor-server-resources = { group = "io.ktor", name = "ktor-server-resources", version.ref = "ktor" }
6064

6165
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
6266
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }

model-server/build.gradle.kts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ plugins {
99
id("org.jetbrains.kotlin.jvm")
1010
id("com.github.johnrengelman.shadow") version "8.1.1"
1111
kotlin("plugin.serialization")
12+
alias(libs.plugins.openapi.generator)
13+
// alias(libs.plugins.kotlin.plugin.allopen)
1214
}
1315

1416
description = "Model Server offering access to model storage"
@@ -48,6 +50,7 @@ dependencies {
4850
implementation(libs.ktor.server.forwarded.header)
4951
implementation(libs.ktor.server.websockets)
5052
implementation(libs.ktor.server.content.negotiation)
53+
implementation(libs.ktor.server.resources)
5154
implementation(libs.ktor.serialization.json)
5255

5356
implementation(libs.bundles.ignite)
@@ -64,6 +67,9 @@ dependencies {
6467
testImplementation(libs.ktor.server.test.host)
6568
testImplementation(kotlin("test"))
6669
testImplementation(project(":modelql-untyped"))
70+
71+
// implementation("jakarta.ws.rs:jakarta.ws.rs-api:2.1.6")
72+
// implementation("jakarta.annotation:jakarta.annotation-api:1.3.5")
6773
}
6874

6975
tasks.test {
@@ -184,3 +190,81 @@ spotless {
184190
'\n'*/
185191
}
186192
}
193+
194+
// OpenAPI integration
195+
val basePackage = project.group.toString()
196+
val openAPIgenerationPath = "$buildDir/generated/openapi"
197+
198+
// We let the Gradle OpenAPI generator plugin build data classes and API interfaces based on the provided
199+
// OpenAPI specification. That way, the code is forced to stay in sync with the API specification.
200+
openApiGenerate {
201+
generatorName.set("kotlin-server")
202+
inputSpec.set(layout.projectDirectory.file("../api/public.yaml").toString())
203+
outputDir.set(openAPIgenerationPath)
204+
packageName.set(basePackage)
205+
packageName.set(basePackage)
206+
apiPackage.set(basePackage)
207+
modelPackage.set(basePackage)
208+
// WARNING: there are patched mustache files used!
209+
templateDir.set("$projectDir/src/main/resources/openapi/templates")
210+
configOptions.set(
211+
mapOf(
212+
"library" to "ktor",
213+
"omitGradleWrapper" to "true",
214+
"featureResources" to "true",
215+
"featureAutoHead" to "false",
216+
"featureCompression" to "false",
217+
"featureHSTS" to "false",
218+
"featureMetrics" to "false",
219+
),
220+
)
221+
globalProperties.putAll(
222+
mapOf(
223+
// "debugOpenAPI" to "true",
224+
// "debugModels" to "true",
225+
// "debugSupportingFiles" to "true",
226+
// "debugOperations" to "true",
227+
228+
// "models" to "",
229+
// "apis" to "",
230+
// "supportingFiles" to "",
231+
// "apiTests" to "false",
232+
// "modelTests" to "false",
233+
// "modelDocs" to "false",
234+
),
235+
)
236+
}
237+
238+
// Ensure that the OpenAPI generator runs before starting to compile
239+
tasks.named("build") {
240+
dependsOn("openApiGenerate")
241+
}
242+
tasks.named("processResources") {
243+
dependsOn("openApiGenerate")
244+
}
245+
tasks.named("compileKotlin") {
246+
dependsOn("openApiGenerate")
247+
}
248+
tasks.named("runKtlintCheckOverMainSourceSet") {
249+
dependsOn("openApiGenerate")
250+
}
251+
252+
// do not apply ktlint on the generated files
253+
ktlint {
254+
filter {
255+
exclude {
256+
it.file.toPath().toAbsolutePath().startsWith(openAPIgenerationPath)
257+
}
258+
// exclude("$openAPIgenerationPath/src/main/kotlin/**")
259+
// exclude("$openAPIgenerationPath/**")
260+
// exclude("**/generated/**")
261+
}
262+
}
263+
264+
// allOpen {
265+
// annotation("javax.ws.rs.Path")
266+
// annotation("javax.enterprise.context.ApplicationScoped")
267+
// }
268+
269+
// add openAPI generated artifacts to the sourceSets
270+
java.sourceSets.getByName("main").java.srcDir(file("$openAPIgenerationPath/src/main/kotlin"))

model-server/src/main/kotlin/org/modelix/model/server/Main.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import io.ktor.server.netty.NettyApplicationEngine
2929
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
3030
import io.ktor.server.plugins.cors.routing.CORS
3131
import io.ktor.server.plugins.forwardedheaders.ForwardedHeaders
32+
import io.ktor.server.resources.Resources
3233
import io.ktor.server.response.respondText
34+
import io.ktor.server.routing.IgnoreTrailingSlash
3335
import io.ktor.server.routing.Routing
3436
import io.ktor.server.routing.get
3537
import io.ktor.server.routing.routing
@@ -159,6 +161,8 @@ object Main {
159161
install(Routing)
160162
installAuthentication(unitTestMode = !KeycloakUtils.isEnabled())
161163
install(ForwardedHeaders)
164+
install(Resources)
165+
install(IgnoreTrailingSlash)
162166
install(WebSockets) {
163167
pingPeriod = Duration.ofSeconds(30)
164168
timeout = Duration.ofSeconds(30)

0 commit comments

Comments
 (0)