diff --git a/authorization/src/main/kotlin/org/modelix/authorization/KtorAuthUtils.kt b/authorization/src/main/kotlin/org/modelix/authorization/KtorAuthUtils.kt index ed89e8e51d..65b959efb3 100644 --- a/authorization/src/main/kotlin/org/modelix/authorization/KtorAuthUtils.kt +++ b/authorization/src/main/kotlin/org/modelix/authorization/KtorAuthUtils.kt @@ -30,7 +30,8 @@ import io.ktor.server.auth.parseAuthorizationHeader import io.ktor.server.auth.principal import io.ktor.server.request.header import io.ktor.server.routing.Route -import io.ktor.util.pipeline.PipelineContext +import io.ktor.server.routing.RoutingContext +import io.ktor.server.routing.intercept import org.modelix.authorization.permissions.PermissionEvaluator import org.modelix.authorization.permissions.PermissionParts import java.time.Instant @@ -84,7 +85,7 @@ fun ApplicationCall.checkPermission(resource: KeycloakResource, scope: KeycloakS } } -fun PipelineContext<*, ApplicationCall>.checkPermission(permissionParts: PermissionParts) { +fun RoutingContext.checkPermission(permissionParts: PermissionParts) { call.checkPermission(permissionParts) } @@ -140,7 +141,7 @@ fun ApplicationCall.jwtFromHeaders(): DecodedJWT? { fun ApplicationCall.jwt() = principal()?.jwt ?: jwtFromHeaders() -fun PipelineContext.getUserName(): String? { +fun RoutingContext.getUserName(): String? { return call.getUserName() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2da63ee325..559c011ced 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ docker-compose = { id = "com.avast.gradle.docker-compose" , version = "0.17.11" [versions] kotlin = "2.0.21" kotlinCoroutines="1.9.0" -ktor="2.3.12" +ktor="3.0.2" kotlinHtml="0.8.0" kotlinSerialization="1.7.3" ignite="2.16.0" diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 114ad92d97..51d0093705 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -60,13 +60,6 @@ abab@^2.0.6: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -abort-controller@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -336,11 +329,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -643,13 +631,6 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -855,11 +836,6 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - tslib@^1.11.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -907,11 +883,6 @@ w3c-xmlserializer@^3.0.0: dependencies: xml-name-validator "^4.0.0" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -937,14 +908,6 @@ whatwg-url@^11.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" diff --git a/model-client/src/jvmMain/kotlin/org/modelix/model/client/RestWebModelClient.kt b/model-client/src/jvmMain/kotlin/org/modelix/model/client/RestWebModelClient.kt index e062ac4fec..2b30fcd263 100644 --- a/model-client/src/jvmMain/kotlin/org/modelix/model/client/RestWebModelClient.kt +++ b/model-client/src/jvmMain/kotlin/org/modelix/model/client/RestWebModelClient.kt @@ -42,6 +42,7 @@ import io.ktor.util.reflect.TypeInfo import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.charsets.Charset import io.ktor.utils.io.core.readText +import io.ktor.utils.io.readRemaining import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -68,25 +69,6 @@ import java.net.URLEncoder import java.nio.charset.StandardCharsets import java.util.LinkedList import java.util.concurrent.atomic.AtomicInteger -import kotlin.collections.ArrayList -import kotlin.collections.HashMap -import kotlin.collections.Iterable -import kotlin.collections.LinkedHashMap -import kotlin.collections.List -import kotlin.collections.Map -import kotlin.collections.MutableList -import kotlin.collections.MutableMap -import kotlin.collections.Set -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.collections.emptyList -import kotlin.collections.emptySet -import kotlin.collections.filter -import kotlin.collections.forEach -import kotlin.collections.iterator -import kotlin.collections.minus -import kotlin.collections.plus -import kotlin.collections.set import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -172,8 +154,8 @@ class RestWebModelClient @JvmOverloads constructor( contentType: ContentType, charset: Charset, typeInfo: TypeInfo, - value: Any, - ): OutgoingContent { + value: Any?, + ): OutgoingContent? { return TextContent(value.toString(), contentType) } }, diff --git a/model-server-api/src/commonTest/kotlin/org/modelix/model/server/api/v2/VersionDeltaStreamV2Test.kt b/model-server-api/src/commonTest/kotlin/org/modelix/model/server/api/v2/VersionDeltaStreamV2Test.kt index aa57594bf0..2219f73b8b 100644 --- a/model-server-api/src/commonTest/kotlin/org/modelix/model/server/api/v2/VersionDeltaStreamV2Test.kt +++ b/model-server-api/src/commonTest/kotlin/org/modelix/model/server/api/v2/VersionDeltaStreamV2Test.kt @@ -17,8 +17,8 @@ package org.modelix.model.server.api.v2 import io.ktor.util.cio.use -import io.ktor.util.toByteArray import io.ktor.utils.io.ByteChannel +import io.ktor.utils.io.toByteArray import io.ktor.utils.io.writeFully import io.ktor.utils.io.writeStringUtf8 import kotlinx.coroutines.flow.emptyFlow diff --git a/model-server/src/main/kotlin/org/modelix/model/server/Main.kt b/model-server/src/main/kotlin/org/modelix/model/server/Main.kt index 9defd9de4a..0ed986d412 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/Main.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/Main.kt @@ -22,14 +22,13 @@ import io.ktor.http.HttpStatusCode import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application import io.ktor.server.application.ApplicationCall -import io.ktor.server.application.call import io.ktor.server.application.install +import io.ktor.server.engine.connector import io.ktor.server.engine.embeddedServer import io.ktor.server.http.content.staticResources import io.ktor.server.netty.Netty -import io.ktor.server.netty.NettyApplicationEngine -import io.ktor.server.plugins.callloging.CallLogging -import io.ktor.server.plugins.callloging.processingTimeMillis +import io.ktor.server.plugins.calllogging.CallLogging +import io.ktor.server.plugins.calllogging.processingTimeMillis import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.plugins.cors.routing.CORS import io.ktor.server.plugins.forwardedheaders.ForwardedHeaders @@ -41,7 +40,6 @@ import io.ktor.server.request.path import io.ktor.server.resources.Resources import io.ktor.server.response.respondText import io.ktor.server.routing.IgnoreTrailingSlash -import io.ktor.server.routing.Routing import io.ktor.server.routing.get import io.ktor.server.routing.routing import io.ktor.server.websocket.WebSockets @@ -81,9 +79,9 @@ import java.io.File import java.io.FileReader import java.io.IOException import java.nio.charset.StandardCharsets -import java.time.Duration import java.util.Properties import javax.sql.DataSource +import kotlin.time.Duration.Companion.seconds object Main { private val LOG = LoggerFactory.getLogger(Main::class.java) @@ -182,12 +180,10 @@ object Main { val modelReplicationServer = ModelReplicationServer(repositoriesManager) val metricsApi = MetricsApiImpl() - val configureNetty: NettyApplicationEngine.Configuration.() -> Unit = { - this.responseWriteTimeoutSeconds = cmdLineArgs.responseWriteTimeoutSeconds - } - - val ktorServer: NettyApplicationEngine = embeddedServer(Netty, port = port, configure = configureNetty) { - install(Routing) + val ktorServer = embeddedServer(Netty, configure = { + connector { this.port = port } + responseWriteTimeoutSeconds = cmdLineArgs.responseWriteTimeoutSeconds + }) { install(ModelixAuthorization) { permissionSchema = ModelServerPermissionSchema.SCHEMA } @@ -209,8 +205,8 @@ object Main { // https://opensource.zalando.com/restful-api-guidelines/#136 install(IgnoreTrailingSlash) install(WebSockets) { - pingPeriod = Duration.ofSeconds(30) - timeout = Duration.ofSeconds(30) + pingPeriod = 30.seconds + timeout = 30.seconds maxFrameSize = Long.MAX_VALUE masking = false } diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/AboutApiImpl.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/AboutApiImpl.kt index 59c9c115e9..02a8b23508 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/AboutApiImpl.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/AboutApiImpl.kt @@ -16,17 +16,15 @@ package org.modelix.model.server.handlers -import io.ktor.server.application.ApplicationCall -import io.ktor.server.application.call import io.ktor.server.response.respond -import io.ktor.util.pipeline.PipelineContext +import io.ktor.server.routing.RoutingContext import org.modelix.model.server.MODELIX_VERSION /** * Responding information about the model server. */ object AboutApiImpl : AboutApi() { - override suspend fun PipelineContext.getAboutInformationV1() { + override suspend fun RoutingContext.getAboutInformationV1() { val about = AboutV1(MODELIX_VERSION) call.respond(about) } diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/HealthApiImpl.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/HealthApiImpl.kt index 94805e9b4f..ea103c42a4 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/HealthApiImpl.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/HealthApiImpl.kt @@ -18,10 +18,8 @@ package org.modelix.model.server.handlers import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode -import io.ktor.server.application.ApplicationCall -import io.ktor.server.application.call import io.ktor.server.response.respondText -import io.ktor.util.pipeline.PipelineContext +import io.ktor.server.routing.RoutingContext import org.modelix.model.server.handlers.KeyValueLikeModelServer.Companion.PROTECTED_PREFIX import org.modelix.model.server.store.StoreManager @@ -31,7 +29,7 @@ class HealthApiImpl( private val stores: StoreManager get() = repositoriesManager.stores - override suspend fun PipelineContext.getHealth() { + override suspend fun RoutingContext.getHealth() { if (isHealthy()) { call.respondText(text = "healthy", contentType = ContentType.Text.Plain, status = HttpStatusCode.OK) } else { diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/IdsApiImpl.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/IdsApiImpl.kt index 77621aa7b3..27c4512a6e 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/IdsApiImpl.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/IdsApiImpl.kt @@ -17,12 +17,10 @@ package org.modelix.model.server.handlers import io.ktor.server.application.Application -import io.ktor.server.application.ApplicationCall -import io.ktor.server.application.call import io.ktor.server.plugins.origin import io.ktor.server.response.respondText +import io.ktor.server.routing.RoutingContext import io.ktor.server.routing.routing -import io.ktor.util.pipeline.PipelineContext import org.modelix.authorization.getUserName import org.modelix.authorization.requiresLogin @@ -33,7 +31,7 @@ class IdsApiImpl( private val repositoriesManager: IRepositoriesManager, ) : IdsApi() { - override suspend fun PipelineContext.getServerId() { + override suspend fun RoutingContext.getServerId() { // Currently, the server ID is initialized in KeyValueLikeModelServer eagerly on startup. // Should KeyValueLikeModelServer be removed or change, // RepositoriesManager#maybeInitAndGetSeverId will initialize the server ID lazily on the first request. @@ -44,11 +42,11 @@ class IdsApiImpl( call.respondText(serverId) } - override suspend fun PipelineContext.getUserId() { + override suspend fun RoutingContext.getUserId() { call.respondText(call.getUserName() ?: call.request.origin.remoteHost) } - override suspend fun PipelineContext.generateClientId() { + override suspend fun RoutingContext.generateClientId() { call.respondText(repositoriesManager.getStoreManager().getGlobalStoreClient().generateId("clientId").toString()) } diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/KeyValueLikeModelServer.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/KeyValueLikeModelServer.kt index 4b00c0357e..198a38d038 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/KeyValueLikeModelServer.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/KeyValueLikeModelServer.kt @@ -25,6 +25,7 @@ import io.ktor.server.resources.get import io.ktor.server.resources.post import io.ktor.server.resources.put import io.ktor.server.response.respondText +import io.ktor.server.routing.RoutingContext import io.ktor.server.routing.routing import io.ktor.util.pipeline.PipelineContext import kotlinx.coroutines.runBlocking @@ -219,7 +220,7 @@ class KeyValueLikeModelServer( return sorted } - fun collect(rootKey: String, callContext: CallContext?): JSONArray { + fun collect(rootKey: String, routingContext: RoutingContext?): JSONArray { val result = JSONArray() val processed: MutableSet = HashSet() val pending: MutableSet = HashSet() @@ -227,8 +228,8 @@ class KeyValueLikeModelServer( while (pending.isNotEmpty()) { val keys: List = ArrayList(pending) pending.clear() - if (callContext != null) { - keys.forEach { callContext.checkKeyPermission(it, EPermissionType.READ) } + if (routingContext != null) { + keys.forEach { routingContext.checkKeyPermission(it, EPermissionType.READ) } } val values = stores.getGlobalStoreClient().getAll(keys) for (i in keys.indices) { @@ -260,7 +261,7 @@ class KeyValueLikeModelServer( return result } - private suspend fun CallContext.putEntries(newEntries: Map) { + private suspend fun RoutingContext.putEntries(newEntries: Map) { val referencedKeys: MutableSet = HashSet() for ((key, value) in newEntries) { checkKeyPermission(key, EPermissionType.WRITE) @@ -332,7 +333,7 @@ class KeyValueLikeModelServer( } } - private suspend fun CallContext.respondValue(key: String, value: String?) { + private suspend fun RoutingContext.respondValue(key: String, value: String?) { if (value == null) { throw HttpException(HttpStatusCode.NotFound, details = "key '$key' not found") } else { @@ -341,7 +342,7 @@ class KeyValueLikeModelServer( } @Throws(IOException::class) - private fun CallContext.checkKeyPermission(key: String, type: EPermissionType) { + private fun RoutingContext.checkKeyPermission(key: String, type: EPermissionType) { val isWrite = type == EPermissionType.WRITE switchKeyType( key = key, diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/MetricsApiImpl.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/MetricsApiImpl.kt index 8aac1465e8..23b498d1f7 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/MetricsApiImpl.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/MetricsApiImpl.kt @@ -17,13 +17,11 @@ package org.modelix.model.server.handlers import io.ktor.server.application.Application -import io.ktor.server.application.ApplicationCall -import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.metrics.micrometer.MicrometerMetrics import io.ktor.server.response.respond +import io.ktor.server.routing.RoutingContext import io.ktor.server.routing.routing -import io.ktor.util.pipeline.PipelineContext import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics @@ -70,7 +68,7 @@ class MetricsApiImpl : MetricsApi() { } } - override suspend fun PipelineContext.getMetrics() { + override suspend fun RoutingContext.getMetrics() { call.respond(appMicrometerRegistry.scrape()) } } diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt index 7d507375e7..2542410be8 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt @@ -27,9 +27,9 @@ import io.ktor.server.response.respond import io.ktor.server.response.respondBytesWriter import io.ktor.server.response.respondText import io.ktor.server.response.respondTextWriter +import io.ktor.server.routing.RoutingContext import io.ktor.server.routing.routing import io.ktor.util.cio.use -import io.ktor.util.pipeline.PipelineContext import io.ktor.utils.io.ByteWriteChannel import io.ktor.utils.io.close import io.ktor.utils.io.readUTF8Line @@ -101,7 +101,7 @@ class ModelReplicationServer( private fun repositoryId(paramValue: String?) = RepositoryId(checkNotNull(paramValue) { "Parameter 'repository' not available" }) - override suspend fun PipelineContext.getRepositories() { + override suspend fun RoutingContext.getRepositories() { call.respondText( repositoriesManager.getRepositories() .filter { call.hasPermission(ModelServerPermissionSchema.repository(it).list) } @@ -109,7 +109,7 @@ class ModelReplicationServer( ) } - override suspend fun PipelineContext.getRepositoryBranches(repository: String) { + override suspend fun RoutingContext.getRepositoryBranches(repository: String) { call.respondText( repositoriesManager .getBranchNames(repositoryId(repository)) @@ -118,7 +118,7 @@ class ModelReplicationServer( ) } - override suspend fun PipelineContext.getRepositoryBranchDelta( + override suspend fun RoutingContext.getRepositoryBranchDelta( repository: String, branch: String, lastKnown: String?, @@ -129,7 +129,7 @@ class ModelReplicationServer( call.respondDelta(RepositoryId(repository), versionHash, lastKnown) } - override suspend fun PipelineContext.getRepositoryBranchV1( + override suspend fun RoutingContext.getRepositoryBranchV1( repository: String, branch: String, lastKnown: String?, @@ -140,7 +140,7 @@ class ModelReplicationServer( call.respond(BranchV1(branch, versionHash)) } - override suspend fun PipelineContext.deleteRepositoryBranch( + override suspend fun RoutingContext.deleteRepositoryBranch( repository: String, branch: String, ) { @@ -161,7 +161,7 @@ class ModelReplicationServer( call.respond(HttpStatusCode.NoContent) } - override suspend fun PipelineContext.getRepositoryBranchHash( + override suspend fun RoutingContext.getRepositoryBranchHash( repository: String, branch: String, ) { @@ -171,7 +171,7 @@ class ModelReplicationServer( call.respondText(versionHash) } - override suspend fun PipelineContext.initializeRepository( + override suspend fun RoutingContext.initializeRepository( repository: String, useRoleIds: Boolean?, legacyGlobalStorage: Boolean?, @@ -186,7 +186,7 @@ class ModelReplicationServer( call.respondDelta(RepositoryId(repository), initialVersion.getContentHash(), null) } - override suspend fun PipelineContext.deleteRepository(repository: String) { + override suspend fun RoutingContext.deleteRepository(repository: String) { checkPermission(ModelServerPermissionSchema.repository(repository).delete) val foundAndDeleted = repositoriesManager.removeRepository(repositoryId(repository)) @@ -197,7 +197,7 @@ class ModelReplicationServer( } } - override suspend fun PipelineContext.postRepositoryBranch( + override suspend fun RoutingContext.postRepositoryBranch( repository: String, branch: String, ) { @@ -210,7 +210,7 @@ class ModelReplicationServer( call.respondDelta(RepositoryId(repository), mergedHash, deltaFromClient.versionHash) } - override suspend fun PipelineContext.pollRepositoryBranch( + override suspend fun RoutingContext.pollRepositoryBranch( repository: String, branch: String, lastKnown: String?, @@ -221,7 +221,7 @@ class ModelReplicationServer( call.respondDelta(RepositoryId(repository), newVersionHash, lastKnown) } - override suspend fun PipelineContext.postRepositoryObjectsGetAll(repository: String) { + override suspend fun RoutingContext.postRepositoryObjectsGetAll(repository: String) { checkPermission(ModelServerPermissionSchema.repository(repository).objects.read) val channel = call.receiveChannel() val keys = hashSetOf() @@ -244,7 +244,7 @@ class ModelReplicationServer( } } - override suspend fun PipelineContext.pollRepositoryBranchHash( + override suspend fun RoutingContext.pollRepositoryBranchHash( repository: String, branch: String, lastKnown: String?, @@ -256,7 +256,7 @@ class ModelReplicationServer( call.respondText(newVersionHash) } - override suspend fun PipelineContext.getRepositoryVersionHash( + override suspend fun RoutingContext.getRepositoryVersionHash( versionHash: String, repository: String, lastKnown: String?, @@ -268,7 +268,7 @@ class ModelReplicationServer( call.respondDelta(RepositoryId(repository), versionHash, lastKnown) } - override suspend fun PipelineContext.postRepositoryBranchQuery( + override suspend fun RoutingContext.postRepositoryBranchQuery( repository: String, branchName: String, ) { @@ -311,7 +311,7 @@ class ModelReplicationServer( } } - override suspend fun PipelineContext.postRepositoryVersionHashQuery( + override suspend fun RoutingContext.postRepositoryVersionHashQuery( versionHash: String, repository: String, ) { @@ -322,7 +322,7 @@ class ModelReplicationServer( ModelQLServer.handleCall(call, branch.getRootNode(), branch.getArea()) } - override suspend fun PipelineContext.putRepositoryObjects(repository: String) { + override suspend fun RoutingContext.putRepositoryObjects(repository: String) { checkPermission(ModelServerPermissionSchema.repository(parameter("repository")).objects.add) val channel = call.receiveChannel() @@ -350,7 +350,7 @@ class ModelReplicationServer( } @Deprecated("deprecated flag is set in the OpenAPI specification") - override suspend fun PipelineContext.getVersionHash( + override suspend fun RoutingContext.getVersionHash( versionHash: String, lastKnown: String?, ) { @@ -460,7 +460,7 @@ private fun Map.checkValuesNotNull(lazyMessage: (K) -> Any): Map -private fun PipelineContext.parameter(name: String): String { +private fun RoutingContext.parameter(name: String): String { return call.parameter(name) } diff --git a/model-server/src/main/resources/openapi/templates/api.mustache b/model-server/src/main/resources/openapi/templates/api.mustache index 4d47a3c227..74055390f4 100644 --- a/model-server/src/main/resources/openapi/templates/api.mustache +++ b/model-server/src/main/resources/openapi/templates/api.mustache @@ -16,7 +16,7 @@ import io.ktor.server.resources.head import io.ktor.server.resources.patch {{/featureResources}} import io.ktor.server.routing.* -import io.ktor.util.pipeline.PipelineContext +import io.ktor.server.routing.RoutingContext {{#imports}}import {{import}} {{/imports}} @@ -42,7 +42,7 @@ abstract class {{classname}} { {{#isDeprecated}} @Deprecated("deprecated flag is set in the OpenAPI specification") {{/isDeprecated}} - abstract suspend fun PipelineContext.{{operationId}}{{#lambda.titlecase}}{{key}}{{/lambda.titlecase}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) + abstract suspend fun RoutingContext.{{operationId}}{{#lambda.titlecase}}{{key}}{{/lambda.titlecase}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) {{/entrySet}} {{/x-modelix-media-type-handlers}} @@ -62,7 +62,7 @@ abstract class {{classname}} { {{#isDeprecated}} @Deprecated("deprecated flag is set in the OpenAPI specification") {{/isDeprecated}} - abstract suspend fun PipelineContext.{{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) + abstract suspend fun RoutingContext.{{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) {{/x-modelix-media-type-handlers}} {{/vendorExtensions}} diff --git a/model-server/src/test/kotlin/org/modelix/model/server/ModelServerTestUtil.kt b/model-server/src/test/kotlin/org/modelix/model/server/ModelServerTestUtil.kt index 8fd69d88f3..18b6f88160 100644 --- a/model-server/src/test/kotlin/org/modelix/model/server/ModelServerTestUtil.kt +++ b/model-server/src/test/kotlin/org/modelix/model/server/ModelServerTestUtil.kt @@ -20,6 +20,9 @@ import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.application.pluginOrNull +import io.ktor.server.engine.EmbeddedServer +import io.ktor.server.engine.connector +import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty import io.ktor.server.netty.NettyApplicationEngine import io.ktor.server.plugins.contentnegotiation.ContentNegotiation @@ -29,7 +32,6 @@ import io.ktor.server.testing.ApplicationTestBuilder import io.ktor.server.websocket.WebSockets import kotlinx.coroutines.runBlocking import org.modelix.authorization.ModelixAuthorization -import org.modelix.authorization.installAuthentication import org.modelix.model.client2.ModelClientV2 import org.modelix.model.server.Main.installStatusPages import org.modelix.model.server.handlers.Paths.registerJsonTypes @@ -68,11 +70,13 @@ fun Application.installDefaultServerPlugins(unitTestMode: Boolean = true) { */ fun runWithNettyServer( setupBlock: (application: Application) -> Unit, - testBlock: suspend (server: NettyApplicationEngine) -> Unit, + testBlock: suspend (server: EmbeddedServer) -> Unit, ) { - val nettyServer: NettyApplicationEngine = io.ktor.server.engine.embeddedServer(Netty, port = 0) { - installAuthentication(unitTestMode = true) - installDefaultServerPlugins() + val nettyServer = embeddedServer(Netty, configure = { + connector { this.port = 0 } + responseWriteTimeoutSeconds = 30 + }) { + installDefaultServerPlugins(unitTestMode = true) setupBlock(this) } diff --git a/model-server/src/test/kotlin/org/modelix/model/server/PullPerformanceTest.kt b/model-server/src/test/kotlin/org/modelix/model/server/PullPerformanceTest.kt index c7faa88c85..0c3fbbbd8c 100644 --- a/model-server/src/test/kotlin/org/modelix/model/server/PullPerformanceTest.kt +++ b/model-server/src/test/kotlin/org/modelix/model/server/PullPerformanceTest.kt @@ -19,7 +19,6 @@ package org.modelix.model.server import io.ktor.server.testing.ApplicationTestBuilder import io.ktor.server.testing.testApplication import kotlinx.coroutines.coroutineScope -import org.modelix.authorization.installAuthentication import org.modelix.model.api.IChildLink import org.modelix.model.api.IConceptReference import org.modelix.model.api.INode @@ -41,8 +40,7 @@ class PullPerformanceTest { val storeClientWithStatistics = StoreClientWithStatistics(InMemoryStoreClient()) val repositoriesManager = RepositoriesManager(storeClientWithStatistics) application { - installAuthentication(unitTestMode = true) - installDefaultServerPlugins() + installDefaultServerPlugins(unitTestMode = true) ModelReplicationServer(repositoriesManager).init(this) KeyValueLikeModelServer(repositoriesManager).init(this) IdsApiImpl(repositoriesManager).init(this) diff --git a/model-server/src/test/kotlin/org/modelix/model/server/handlers/ModelReplicationServerTest.kt b/model-server/src/test/kotlin/org/modelix/model/server/handlers/ModelReplicationServerTest.kt index 7cfbb10bf3..08e7b29e76 100644 --- a/model-server/src/test/kotlin/org/modelix/model/server/handlers/ModelReplicationServerTest.kt +++ b/model-server/src/test/kotlin/org/modelix/model/server/handlers/ModelReplicationServerTest.kt @@ -43,6 +43,8 @@ import io.ktor.serialization.kotlinx.KotlinxSerializationConverter import io.ktor.serialization.kotlinx.json.DefaultJson import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application +import io.ktor.server.application.port +import io.ktor.server.engine.EmbeddedServer import io.ktor.server.netty.NettyApplicationEngine import io.ktor.server.testing.ApplicationTestBuilder import io.ktor.server.testing.testApplication @@ -55,7 +57,6 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEmpty import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout -import org.modelix.authorization.installAuthentication import org.modelix.model.api.IConceptReference import org.modelix.model.client2.ModelClientV2 import org.modelix.model.client2.readVersionDelta @@ -97,8 +98,7 @@ class ModelReplicationServerTest { block: suspend ApplicationTestBuilder.(scope: CoroutineScope, fixture: Fixture) -> Unit, ) = testApplication { application { - installAuthentication(unitTestMode = true) - installDefaultServerPlugins() + installDefaultServerPlugins(unitTestMode = true) fixture.modelReplicationServer.init(this) IdsApiImpl(fixture.repositoriesManager).init(this) } @@ -287,8 +287,8 @@ class ModelReplicationServerTest { } repositoriesManager.createRepository(repositoryId, null) - suspend fun createClient(server: NettyApplicationEngine): HttpClient { - val port = server.resolvedConnectors().first().port + fun createClient(server: EmbeddedServer): HttpClient { + val port = server.environment.config.port return HttpClient(CIO) { defaultRequest { url("http://localhost:$port") @@ -301,7 +301,7 @@ class ModelReplicationServerTest { val modelReplicationServer = ModelReplicationServer(faultyRepositoriesManager) val setupBlock = { application: Application -> modelReplicationServer.init(application) } - val testBlock: suspend (server: NettyApplicationEngine) -> Unit = { server -> + val testBlock: suspend (server: EmbeddedServer) -> Unit = { server -> withTimeout(10.seconds) { val client = createClient(server) // Act