diff --git a/core/api/build.gradle b/core/api/build.gradle index 4371f0c4..539c431e 100644 --- a/core/api/build.gradle +++ b/core/api/build.gradle @@ -39,8 +39,10 @@ kotlin { implementation kotlin('stdlib') if ((project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true") { - api "com.insanusmokrassar:postssystem.utils.repos:$core_version" + api "com.insanusmokrassar:postssystem.utils.common:$core_version" + api "com.insanusmokrassar:postssystem.utils.repos.common:$core_version" } else { + api projectByName("postssystem.utils.common") api projectByName("postssystem.utils.repos.common") } } diff --git a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt index e0dcde58..c6a85c55 100644 --- a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt +++ b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt @@ -1,7 +1,7 @@ package com.insanusmokrassar.postssystem.core.content -import com.insanusmokrassar.postssystem.core.utils.ByteArrayAllocator -import com.insanusmokrassar.postssystem.core.utils.ByteArrayAllocatorSerializer +import com.insanusmokrassar.postssystem.utils.common.ByteArrayAllocator +import com.insanusmokrassar.postssystem.utils.common.ByteArrayAllocatorSerializer import kotlinx.serialization.Serializable typealias ContentId = String diff --git a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/post/repo/ReadPostsRepo.kt b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/post/repo/ReadPostsRepo.kt index f189e419..d6e0007c 100644 --- a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/post/repo/ReadPostsRepo.kt +++ b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/post/repo/ReadPostsRepo.kt @@ -3,8 +3,8 @@ package com.insanusmokrassar.postssystem.core.post.repo import com.insanusmokrassar.postssystem.core.content.ContentId import com.insanusmokrassar.postssystem.core.post.PostId import com.insanusmokrassar.postssystem.core.post.RegisteredPost -import com.insanusmokrassar.postssystem.core.utils.MAX_DATE -import com.insanusmokrassar.postssystem.core.utils.MIN_DATE +import com.insanusmokrassar.postssystem.utils.common.MAX_DATE +import com.insanusmokrassar.postssystem.utils.common.MIN_DATE import com.insanusmokrassar.postssystem.utils.repos.pagination.Pagination import com.insanusmokrassar.postssystem.utils.repos.pagination.PaginationResult import com.soywiz.klock.DateTime diff --git a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/DateTimeUtils.kt b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/DateTimeUtils.kt deleted file mode 100644 index 7a888a9e..00000000 --- a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/DateTimeUtils.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.insanusmokrassar.postssystem.core.utils - -import com.soywiz.klock.DateTime - -internal val MIN_DATE = DateTime(0) -internal val MAX_DATE = DateTime(Long.MAX_VALUE) diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt new file mode 100644 index 00000000..daf2dc02 --- /dev/null +++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt @@ -0,0 +1,38 @@ +package com.insanusmokrassar.postssystem.core.ktor.client.content + +import com.insanusmokrassar.postssystem.core.content.RegisteredContent +import com.insanusmokrassar.postssystem.core.content.api.* +import com.insanusmokrassar.postssystem.core.ktor.contentCreatedFlowRoute +import com.insanusmokrassar.postssystem.core.ktor.contentDeletedFlowRoute +import com.insanusmokrassar.postssystem.ktor.client.createStandardWebsocketFlow +import com.insanusmokrassar.postssystem.ktor.standardKtorSerializer +import io.ktor.client.HttpClient +import kotlinx.coroutines.flow.Flow + +class ContentRepoKtorClient( + baseUrl: String, + client: HttpClient = HttpClient() +) : ContentRepo, + ReadContentRepo by ReadContentRepoKtorClient( + baseUrl, + client + ), + WriteContentRepo by WriteContentRepoKtorClient( + baseUrl, + client + ) { + + override val contentCreatedFlow: Flow = createStandardWebsocketFlow( + client, + "$baseUrl/$contentCreatedFlowRoute" + ) { + standardKtorSerializer.load(RegisteredContent.serializer(), it) + } + + override val contentDeletedFlow: Flow = createStandardWebsocketFlow( + client, + "$baseUrl/$contentDeletedFlowRoute" + ) { + standardKtorSerializer.load(RegisteredContent.serializer(), it) + } +} diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt index c117dd56..347a8c9d 100644 --- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt +++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt @@ -12,8 +12,8 @@ import io.ktor.client.request.get import kotlinx.serialization.builtins.nullable class ReadContentRepoKtorClient( - private val client: HttpClient, - private val baseUrl: String + private val baseUrl: String, + private val client: HttpClient = HttpClient() ) : ReadContentRepo { override suspend fun getContentsIds(): Set = client.get( "$baseUrl/$getContentsIdsRoute" diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt index 1c10b109..d79b2395 100644 --- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt +++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt @@ -2,6 +2,7 @@ package com.insanusmokrassar.postssystem.core.ktor.client.content import com.insanusmokrassar.postssystem.core.content.* import com.insanusmokrassar.postssystem.core.content.api.WriteContentRepo +import com.insanusmokrassar.postssystem.core.ktor.deleteContentRoute import com.insanusmokrassar.postssystem.core.ktor.registerContentRoute import com.insanusmokrassar.postssystem.ktor.standardKtorSerializer import io.ktor.client.HttpClient @@ -11,8 +12,8 @@ import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer class WriteContentRepoKtorClient( - private val client: HttpClient, - private val baseUrl: String + private val baseUrl: String, + private val client: HttpClient = HttpClient() ) : WriteContentRepo { override suspend fun registerContent(content: Content): RegisteredContent? = client.post { url("$baseUrl/$registerContentRoute") @@ -22,7 +23,7 @@ class WriteContentRepoKtorClient( } override suspend fun deleteContent(id: ContentId): Boolean = client.post { - url("$baseUrl/$registerContentRoute") + url("$baseUrl/$deleteContentRoute") body = standardKtorSerializer.dump(ContentId.serializer(), id) }.let { standardKtorSerializer.load(Boolean.serializer(), it) diff --git a/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/Routes.kt b/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/Routes.kt index 2cec58e0..f5ba141d 100644 --- a/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/Routes.kt +++ b/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/Routes.kt @@ -6,3 +6,6 @@ const val getContentByPaginationRoute = "getContentByPagination" const val registerContentRoute = "registerContent" const val deleteContentRoute = "deleteContent" + +const val contentCreatedFlowRoute = "contentCreatedFlow" +const val contentDeletedFlowRoute = "contentDeletedFlow" diff --git a/gradle.properties b/gradle.properties index 5f2e131e..a6d7d61f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin_serialisation_runtime_version=0.20.0 ktor_version=1.3.2 klockVersion=1.11.12 -uuidVersion=0.1.0 +uuidVersion=0.1.1 exposed_version=0.26.2 test_sqlite_version=3.32.3.2 diff --git a/ktor/client/build.gradle b/ktor/client/build.gradle index 55421d07..d129348b 100644 --- a/ktor/client/build.gradle +++ b/ktor/client/build.gradle @@ -45,6 +45,7 @@ kotlin { } api "io.ktor:ktor-client-core:$ktor_version" + api "io.ktor:ktor-client-websockets:$ktor_version" } } commonTest { @@ -58,6 +59,8 @@ kotlin { implementation kotlin('stdlib-jdk8') api "io.ktor:ktor-client:$ktor_version" + api "io.ktor:ktor-client-websockets-jvm:$ktor_version" + api "io.ktor:ktor-client-okhttp:$ktor_version" } } jvmTest { @@ -70,6 +73,7 @@ kotlin { implementation kotlin('stdlib-js') api "io.ktor:ktor-client-js:$ktor_version" + api "io.ktor:ktor-client-websockets-js:$ktor_version" } } jsTest { diff --git a/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/FlowsWebsockets.kt b/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/FlowsWebsockets.kt new file mode 100644 index 00000000..036cbc6b --- /dev/null +++ b/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/FlowsWebsockets.kt @@ -0,0 +1,45 @@ +package com.insanusmokrassar.postssystem.ktor.client + +import com.insanusmokrassar.postssystem.ktor.* +import com.insanusmokrassar.postssystem.utils.common.safely +import io.ktor.client.HttpClient +import io.ktor.client.features.websocket.ws +import io.ktor.client.request.url +import io.ktor.http.HttpMethod +import io.ktor.http.cio.websocket.* +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.isActive + +inline fun createStandardWebsocketFlow( + client: HttpClient, + url: String, + crossinline conversation: suspend (ByteArray) -> T +): Flow { + val correctedUrl = url.asCorrectWebSocketUrl + + return channelFlow { + val producerScope = this + safely( + { + producerScope.close() + throw it + } + ) { + client.ws( + correctedUrl + ) { + while (true) { + when (val received = incoming.receive()) { + is Frame.Binary -> producerScope.send( + conversation(received.readBytes()) + ) + else -> { + producerScope.close() + return@ws + } + } + } + } + } + } +} diff --git a/ktor/common/build.gradle b/ktor/common/build.gradle index f0dfaebc..e4f37a32 100644 --- a/ktor/common/build.gradle +++ b/ktor/common/build.gradle @@ -39,6 +39,8 @@ kotlin { implementation kotlin('stdlib') api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$kotlin_serialisation_runtime_version" api "org.jetbrains.kotlinx:kotlinx-serialization-cbor-common:$kotlin_serialisation_runtime_version" + + api projectByName("postssystem.utils.common") } } commonTest { diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/CorrectCloseException.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/CorrectCloseException.kt new file mode 100644 index 00000000..7e3077cd --- /dev/null +++ b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/CorrectCloseException.kt @@ -0,0 +1,3 @@ +package com.insanusmokrassar.postssystem.ktor + +object CorrectCloseException : Exception() diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/CorrectWebsocketUrl.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/CorrectWebsocketUrl.kt new file mode 100644 index 00000000..336d7b30 --- /dev/null +++ b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/CorrectWebsocketUrl.kt @@ -0,0 +1,14 @@ +package com.insanusmokrassar.postssystem.ktor + +private val schemaRegex = Regex("[^:]*//") + +val String.asCorrectWebSocketUrl: String + get() = if (startsWith("ws")) { + this + } else { + if (contains("://")) { + replace(schemaRegex, "ws://") + } else { + "ws://$this" + } + } diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/WebSockets.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/WebSockets.kt new file mode 100644 index 00000000..fc6f6446 --- /dev/null +++ b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/WebSockets.kt @@ -0,0 +1,7 @@ +package com.insanusmokrassar.postssystem.ktor + +const val clientWebsocketHelloMessage = "Start getting of updates" +const val serverWebsocketHelloMessage = "Accepted" + +const val serverWebsocketNewMessageMessage = "NewMessage" +const val websocketFinalizationMessage = "Final" diff --git a/ktor/server/build.gradle b/ktor/server/build.gradle new file mode 100644 index 00000000..21dd6bd4 --- /dev/null +++ b/ktor/server/build.gradle @@ -0,0 +1,68 @@ +buildscript { + repositories { + mavenLocal() + jcenter() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version" + } +} + +plugins { + id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version" + id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" +} + +project.version = "$core_version" +project.group = "com.insanusmokrassar" + +apply from: "publish.gradle" + +repositories { + mavenLocal() + jcenter() + mavenCentral() + maven { url "https://kotlin.bintray.com/kotlinx" } +} + +kotlin { + jvm() + + sourceSets { + commonMain { + dependencies { + implementation kotlin('stdlib') + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version" + + api projectByName("postssystem.ktor.common") + } + } + commonTest { + dependencies { + implementation kotlin('test-common') + implementation kotlin('test-annotations-common') + } + } + + jvmMain { + dependencies { + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialisation_runtime_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-properties:$kotlin_serialisation_runtime_version" + + api "io.ktor:ktor-server:$ktor_version" + api "io.ktor:ktor-server-host-common:$ktor_version" + api "io.ktor:ktor-server-netty:$ktor_version" + api "io.ktor:ktor-websockets:$ktor_version" + } + } + jvmTest { + dependencies { + implementation kotlin('test-junit') + } + } + } +} diff --git a/ktor/server/maven.publish.gradle b/ktor/server/maven.publish.gradle new file mode 100644 index 00000000..50ee129b --- /dev/null +++ b/ktor/server/maven.publish.gradle @@ -0,0 +1,53 @@ +apply plugin: 'maven-publish' + +task javadocsJar(type: Jar) { + classifier = 'javadoc' +} + +afterEvaluate { + project.publishing.publications.all { + // rename artifacts + groupId "${project.group}" + if (it.name.contains('kotlinMultiplatform')) { + artifactId = "${project.name}" + } else { + artifactId = "${project.name}-$name" + } + } +} + +publishing { + publications.all { + artifact javadocsJar + + pom { + description = "Common utils for all exposed modules" + name = "PostsSystem Exposed commons" + url = "https://git.insanusmokrassar.com/PostsSystem/Core/" + + scm { + developerConnection = "scm:git:[fetch=]https://git.insanusmokrassar.com/PostsSystem/Core/.git[push=]https://git.insanusmokrassar.com/PostsSystem/Core/.git" + url = "https://git.insanusmokrassar.com/PostsSystem/Core/.git" + } + + developers { + + developer { + id = "InsanusMokrassar" + name = "Ovsiannikov Aleksei" + email = "ovsyannikov.alexey95@gmail.com" + } + + } + + licenses { + + license { + name = "Apache Software License 2.0" + url = "https://git.insanusmokrassar.com/PostsSystem/Core/src/master/LICENSE" + } + + } + } + } +} \ No newline at end of file diff --git a/ktor/server/publish.gradle b/ktor/server/publish.gradle new file mode 100644 index 00000000..b8cf4528 --- /dev/null +++ b/ktor/server/publish.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.jfrog.bintray' + +apply from: "maven.publish.gradle" + +bintray { + user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') + key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') + filesSpec { + from "${buildDir}/publications/" + eachFile { + String directorySubname = it.getFile().parentFile.name + if (it.getName() == "module.json") { + if (directorySubname == "kotlinMultiplatform") { + it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module") + } else { + it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module") + } + } else { + if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") { + it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom") + } else { + it.exclude() + } + } + } + into "${project.group}".replace(".", "/") + } + pkg { + repo = "InsanusMokrassar" + name = "${project.name}" + vcsUrl = "https://github.com/PostsSystem/PostsSystemCore" + licenses = ["Apache-2.0"] + version { + name = "${project.version}" + released = new Date() + vcsTag = "${project.version}" + gpg { + sign = true + passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase') + } + } + } +} + +bintrayUpload.doFirst { + publications = publishing.publications.collect { + if (it.name.contains('kotlinMultiplatform')) { + null + } else { + it.name + } + } - null +} + +bintrayUpload.dependsOn publishToMavenLocal \ No newline at end of file diff --git a/ktor/server/publish_config.kpsb b/ktor/server/publish_config.kpsb new file mode 100644 index 00000000..2bd43e2b --- /dev/null +++ b/ktor/server/publish_config.kpsb @@ -0,0 +1 @@ +{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}","packageVcs":"https://github.com/PostsSystem/PostsSystemCore"},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.insanusmokrassar.com/PostsSystem/Core/src/master/LICENSE"}],"mavenConfig":{"name":"PostsSystem Exposed commons","description":"Common utils for all exposed modules","url":"https://git.insanusmokrassar.com/PostsSystem/Core/","vcsUrl":"https://git.insanusmokrassar.com/PostsSystem/Core/.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]},"type":"Multiplatform"} \ No newline at end of file diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FlowsWebsocket.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FlowsWebsocket.kt new file mode 100644 index 00000000..dd4a04b5 --- /dev/null +++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FlowsWebsocket.kt @@ -0,0 +1,32 @@ +package com.insanusmokrassar.postssystem.ktor.server + +import com.insanusmokrassar.postssystem.ktor.* +import com.insanusmokrassar.postssystem.utils.common.safely +import io.ktor.http.cio.websocket.* +import io.ktor.routing.Route +import io.ktor.routing.route +import io.ktor.websocket.webSocket +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect + +private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() { + if (incoming.poll() != null) { + close() + throw CorrectCloseException + } +} + +fun Route.includeWebsocketHandling( + suburl: String, + flow: Flow, + converter: (T) -> ByteArray +) { + webSocket(suburl) { + safely { + flow.collect { + checkReceivedAndCloseIfExists() + send(converter(it)) + } + } + } +} diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/StartServer.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/StartServer.kt new file mode 100644 index 00000000..5d81a1f7 --- /dev/null +++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/StartServer.kt @@ -0,0 +1,28 @@ +package com.insanusmokrassar.postssystem.ktor.server + +import io.ktor.application.Application +import io.ktor.server.engine.* +import io.ktor.server.netty.Netty +import kotlin.random.Random + +fun createKtorServer( + engine: ApplicationEngineFactory, + host: String = "localhost", + port: Int = Random.nextInt(1024, 65535), + block: Application.() -> Unit +): TEngine { + val env = applicationEngineEnvironment { + module(block) + connector { + this@connector.host = host + this@connector.port = port + } + } + return embeddedServer(engine, env) +} + +fun createKtorServer( + host: String = "localhost", + port: Int = Random.nextInt(1024, 65535), + block: Application.() -> Unit +) = createKtorServer(Netty, host, port, block) diff --git a/ktor/tests/build.gradle b/ktor/tests/build.gradle new file mode 100644 index 00000000..dc551665 --- /dev/null +++ b/ktor/tests/build.gradle @@ -0,0 +1,39 @@ +buildscript { + repositories { + mavenLocal() + jcenter() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +plugins { + id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" +} + +project.version = "$core_version" +project.group = "com.insanusmokrassar" + +apply plugin: "java-library" +apply plugin: "kotlin" + +repositories { + mavenLocal() + jcenter() + mavenCentral() + maven { url "https://kotlin.bintray.com/kotlinx" } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + api projectByName("postssystem.ktor.client") + api projectByName("postssystem.ktor.server") + + testImplementation "org.jetbrains.kotlin:kotlin-test" + testImplementation "org.jetbrains.kotlin:kotlin-test-junit" +} diff --git a/ktor/tests/src/test/kotlin/com/insanusmokrassar/postssystem/ktor/tests/TestsScope.kt b/ktor/tests/src/test/kotlin/com/insanusmokrassar/postssystem/ktor/tests/TestsScope.kt new file mode 100644 index 00000000..b9f51276 --- /dev/null +++ b/ktor/tests/src/test/kotlin/com/insanusmokrassar/postssystem/ktor/tests/TestsScope.kt @@ -0,0 +1,6 @@ +package com.insanusmokrassar.postssystem.ktor.tests + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +val testsScope = CoroutineScope(Dispatchers.Default) diff --git a/ktor/tests/src/test/kotlin/com/insanusmokrassar/postssystem/ktor/tests/WebsocketsTest.kt b/ktor/tests/src/test/kotlin/com/insanusmokrassar/postssystem/ktor/tests/WebsocketsTest.kt new file mode 100644 index 00000000..04ca92d8 --- /dev/null +++ b/ktor/tests/src/test/kotlin/com/insanusmokrassar/postssystem/ktor/tests/WebsocketsTest.kt @@ -0,0 +1,72 @@ +package com.insanusmokrassar.postssystem.ktor.tests + +import com.insanusmokrassar.postssystem.ktor.client.createStandardWebsocketFlow +import com.insanusmokrassar.postssystem.ktor.server.createKtorServer +import com.insanusmokrassar.postssystem.ktor.server.includeWebsocketHandling +import com.insanusmokrassar.postssystem.ktor.standardKtorSerializer +import io.ktor.application.install +import io.ktor.application.log +import io.ktor.client.HttpClient +import io.ktor.http.cio.websocket.pingPeriod +import io.ktor.http.cio.websocket.timeout +import io.ktor.routing.route +import io.ktor.routing.routing +import io.ktor.websocket.WebSockets +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.* +import kotlinx.serialization.builtins.serializer +import org.junit.Test +import java.time.Duration +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class WebsocketsTest { + @Test + fun testCommonWebsocketFunctionality() { + val port = 60000 + val suburl = "test" + val dataFlowChannel = Channel(Channel.UNLIMITED) + val dataFlow = dataFlowChannel.consumeAsFlow() + val serverUrl = "127.0.0.1:$port" + val server = createKtorServer(host = "127.0.0.1", port = port) { + install(WebSockets) + routing { + includeWebsocketHandling(suburl, dataFlow) { + standardKtorSerializer.dump(Int.serializer(), it) + } + } + }.also { + it.start(false) + } + runBlocking { + delay(100L) + } + val client = HttpClient { + install(io.ktor.client.features.websocket.WebSockets) + } + val incomingWebsocketFlow = createStandardWebsocketFlow( + client, + "$serverUrl/$suburl" + ) { + standardKtorSerializer.load(Int.serializer(), it) + } + + var currentlyCheckingData: Int? = null + incomingWebsocketFlow.onEach { + assertEquals(currentlyCheckingData, it) + currentlyCheckingData = null + }.launchIn(CoroutineScope(testsScope.coroutineContext + SupervisorJob())) + runBlocking { + (0 until 100).asFlow().collect { + currentlyCheckingData = it + dataFlowChannel.send(it) + while (currentlyCheckingData != null) { + delay(10L) + } + assertNull(currentlyCheckingData) + } + } + server.stop(1000L, 1000L) + } +} diff --git a/settings.gradle b/settings.gradle index d3948ba0..f8d45ee0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ rootProject.name='postssystem' String[] includes = [ + ':utils:common', ':utils:repos', ':utils:repos:common', ':utils:repos:exposed', @@ -9,6 +10,8 @@ String[] includes = [ ':ktor:common', ':ktor:client', + ':ktor:server', + ':ktor:tests', ':core:api', ':core:exposed', diff --git a/utils/common/build.gradle b/utils/common/build.gradle new file mode 100644 index 00000000..6f8071c1 --- /dev/null +++ b/utils/common/build.gradle @@ -0,0 +1,79 @@ +buildscript { + repositories { + mavenLocal() + jcenter() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version" + } +} + +plugins { + id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version" + id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" +} + +project.version = "$core_version" +project.group = "com.insanusmokrassar" + +apply from: "publish.gradle" + +repositories { + mavenLocal() + jcenter() + mavenCentral() + maven { url "https://kotlin.bintray.com/kotlinx" } +} + +kotlin { + jvm() + js() + + sourceSets { + commonMain { + dependencies { + implementation kotlin('stdlib') + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$kotlin_serialisation_runtime_version" + + api "com.soywiz.korlibs.klock:klock:$klockVersion" + api "com.benasher44:uuid:$uuidVersion" + } + } + commonTest { + dependencies { + implementation kotlin('test-common') + implementation kotlin('test-annotations-common') + } + } + jvmMain { + dependencies { + implementation kotlin('stdlib-jdk8') + api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialisation_runtime_version" + } + } + jvmTest { + dependencies { + implementation kotlin('test-junit') + } + } + jsMain { + dependencies { + implementation kotlin('stdlib-js') + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$kotlin_coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$kotlin_serialisation_runtime_version" + } + } + jsTest { + dependencies { + implementation kotlin('test-js') + implementation kotlin('test-junit') + } + } + } +} diff --git a/utils/common/maven.publish.gradle b/utils/common/maven.publish.gradle new file mode 100644 index 00000000..b81b338a --- /dev/null +++ b/utils/common/maven.publish.gradle @@ -0,0 +1,64 @@ +apply plugin: 'maven-publish' + +task javadocsJar(type: Jar) { + classifier = 'javadoc' +} + +afterEvaluate { + project.publishing.publications.all { + // rename artifacts + groupId "${project.group}" + if (it.name.contains('kotlinMultiplatform')) { + artifactId = "${project.name}" + } else { + artifactId = "${project.name}-$name" + } + } +} + +publishing { + publications.all { + artifact javadocsJar + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + + description "Core library for PostsSystem" + name "PostsSystem Core" + url "https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore" + + scm { + developerConnection "scm:git:[fetch=]https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git[push=]https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git" + url "https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git" + } + + developers { + + developer { + id "InsanusMokrassar" + name "Ovsiannikov Aleksei" + email "ovsyannikov.alexey95@gmail.com" + } + + + developer { + id "mi-ast" + name "Michail Astafiev" + email "astaf65@gmail.com" + } + + } + + licenses { + + license { + name "Apache Software License 2.0" + url "https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore/src/master/LICENSE" + } + + } + } + } + } +} \ No newline at end of file diff --git a/utils/common/publish.gradle b/utils/common/publish.gradle new file mode 100644 index 00000000..b8cf4528 --- /dev/null +++ b/utils/common/publish.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.jfrog.bintray' + +apply from: "maven.publish.gradle" + +bintray { + user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') + key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') + filesSpec { + from "${buildDir}/publications/" + eachFile { + String directorySubname = it.getFile().parentFile.name + if (it.getName() == "module.json") { + if (directorySubname == "kotlinMultiplatform") { + it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module") + } else { + it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module") + } + } else { + if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") { + it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom") + } else { + it.exclude() + } + } + } + into "${project.group}".replace(".", "/") + } + pkg { + repo = "InsanusMokrassar" + name = "${project.name}" + vcsUrl = "https://github.com/PostsSystem/PostsSystemCore" + licenses = ["Apache-2.0"] + version { + name = "${project.version}" + released = new Date() + vcsTag = "${project.version}" + gpg { + sign = true + passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase') + } + } + } +} + +bintrayUpload.doFirst { + publications = publishing.publications.collect { + if (it.name.contains('kotlinMultiplatform')) { + null + } else { + it.name + } + } - null +} + +bintrayUpload.dependsOn publishToMavenLocal \ No newline at end of file diff --git a/utils/common/publish_config.json b/utils/common/publish_config.json new file mode 100644 index 00000000..13f4058c --- /dev/null +++ b/utils/common/publish_config.json @@ -0,0 +1 @@ +{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}","packageVcs":"https://github.com/PostsSystem/PostsSystemCore"},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore/src/master/LICENSE"}],"mavenConfig":{"name":"PostsSystem Core","description":"Core library for PostsSystem","url":"https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore","vcsUrl":"https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"mi-ast","name":"Michail Astafiev","eMail":"astaf65@gmail.com"}]}} \ No newline at end of file diff --git a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt b/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/ByteArrayAllocator.kt similarity index 92% rename from core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt rename to utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/ByteArrayAllocator.kt index 9daedf84..4ce5f986 100644 --- a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt +++ b/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/ByteArrayAllocator.kt @@ -1,4 +1,4 @@ -package com.insanusmokrassar.postssystem.core.utils +package com.insanusmokrassar.postssystem.utils.common import kotlinx.serialization.* import kotlinx.serialization.builtins.ByteArraySerializer diff --git a/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/DateTimeUtils.kt b/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/DateTimeUtils.kt new file mode 100644 index 00000000..87084bd2 --- /dev/null +++ b/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/DateTimeUtils.kt @@ -0,0 +1,6 @@ +package com.insanusmokrassar.postssystem.utils.common + +import com.soywiz.klock.DateTime + +val MIN_DATE = DateTime(0) +val MAX_DATE = DateTime(Long.MAX_VALUE) diff --git a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/HandleSafely.kt b/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/HandleSafely.kt similarity index 92% rename from core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/HandleSafely.kt rename to utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/HandleSafely.kt index 22b38904..85d804e4 100644 --- a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/HandleSafely.kt +++ b/utils/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/common/HandleSafely.kt @@ -1,4 +1,4 @@ -package com.insanusmokrassar.postssystem.core.utils +package com.insanusmokrassar.postssystem.utils.common import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.supervisorScope diff --git a/utils/repos/common/build.gradle b/utils/repos/common/build.gradle index 6f8071c1..1b83a6f6 100644 --- a/utils/repos/common/build.gradle +++ b/utils/repos/common/build.gradle @@ -37,11 +37,8 @@ kotlin { commonMain { dependencies { implementation kotlin('stdlib') - api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version" - api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$kotlin_serialisation_runtime_version" - api "com.soywiz.korlibs.klock:klock:$klockVersion" - api "com.benasher44:uuid:$uuidVersion" + api projectByName("postssystem.utils.common") } } commonTest {