add websockets work
This commit is contained in:
parent
9421edcb85
commit
61ccfe7948
@ -39,8 +39,10 @@ kotlin {
|
|||||||
implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
if ((project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true") {
|
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 {
|
} else {
|
||||||
|
api projectByName("postssystem.utils.common")
|
||||||
api projectByName("postssystem.utils.repos.common")
|
api projectByName("postssystem.utils.repos.common")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.insanusmokrassar.postssystem.core.content
|
package com.insanusmokrassar.postssystem.core.content
|
||||||
|
|
||||||
import com.insanusmokrassar.postssystem.core.utils.ByteArrayAllocator
|
import com.insanusmokrassar.postssystem.utils.common.ByteArrayAllocator
|
||||||
import com.insanusmokrassar.postssystem.core.utils.ByteArrayAllocatorSerializer
|
import com.insanusmokrassar.postssystem.utils.common.ByteArrayAllocatorSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
typealias ContentId = String
|
typealias ContentId = String
|
||||||
|
@ -3,8 +3,8 @@ package com.insanusmokrassar.postssystem.core.post.repo
|
|||||||
import com.insanusmokrassar.postssystem.core.content.ContentId
|
import com.insanusmokrassar.postssystem.core.content.ContentId
|
||||||
import com.insanusmokrassar.postssystem.core.post.PostId
|
import com.insanusmokrassar.postssystem.core.post.PostId
|
||||||
import com.insanusmokrassar.postssystem.core.post.RegisteredPost
|
import com.insanusmokrassar.postssystem.core.post.RegisteredPost
|
||||||
import com.insanusmokrassar.postssystem.core.utils.MAX_DATE
|
import com.insanusmokrassar.postssystem.utils.common.MAX_DATE
|
||||||
import com.insanusmokrassar.postssystem.core.utils.MIN_DATE
|
import com.insanusmokrassar.postssystem.utils.common.MIN_DATE
|
||||||
import com.insanusmokrassar.postssystem.utils.repos.pagination.Pagination
|
import com.insanusmokrassar.postssystem.utils.repos.pagination.Pagination
|
||||||
import com.insanusmokrassar.postssystem.utils.repos.pagination.PaginationResult
|
import com.insanusmokrassar.postssystem.utils.repos.pagination.PaginationResult
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
|
@ -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)
|
|
@ -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<RegisteredContent> = createStandardWebsocketFlow(
|
||||||
|
client,
|
||||||
|
"$baseUrl/$contentCreatedFlowRoute"
|
||||||
|
) {
|
||||||
|
standardKtorSerializer.load(RegisteredContent.serializer(), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val contentDeletedFlow: Flow<RegisteredContent> = createStandardWebsocketFlow(
|
||||||
|
client,
|
||||||
|
"$baseUrl/$contentDeletedFlowRoute"
|
||||||
|
) {
|
||||||
|
standardKtorSerializer.load(RegisteredContent.serializer(), it)
|
||||||
|
}
|
||||||
|
}
|
@ -12,8 +12,8 @@ import io.ktor.client.request.get
|
|||||||
import kotlinx.serialization.builtins.nullable
|
import kotlinx.serialization.builtins.nullable
|
||||||
|
|
||||||
class ReadContentRepoKtorClient(
|
class ReadContentRepoKtorClient(
|
||||||
private val client: HttpClient,
|
private val baseUrl: String,
|
||||||
private val baseUrl: String
|
private val client: HttpClient = HttpClient()
|
||||||
) : ReadContentRepo {
|
) : ReadContentRepo {
|
||||||
override suspend fun getContentsIds(): Set<ContentId> = client.get<ByteArray>(
|
override suspend fun getContentsIds(): Set<ContentId> = client.get<ByteArray>(
|
||||||
"$baseUrl/$getContentsIdsRoute"
|
"$baseUrl/$getContentsIdsRoute"
|
||||||
|
@ -2,6 +2,7 @@ package com.insanusmokrassar.postssystem.core.ktor.client.content
|
|||||||
|
|
||||||
import com.insanusmokrassar.postssystem.core.content.*
|
import com.insanusmokrassar.postssystem.core.content.*
|
||||||
import com.insanusmokrassar.postssystem.core.content.api.WriteContentRepo
|
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.core.ktor.registerContentRoute
|
||||||
import com.insanusmokrassar.postssystem.ktor.standardKtorSerializer
|
import com.insanusmokrassar.postssystem.ktor.standardKtorSerializer
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
@ -11,8 +12,8 @@ import kotlinx.serialization.builtins.nullable
|
|||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
|
||||||
class WriteContentRepoKtorClient(
|
class WriteContentRepoKtorClient(
|
||||||
private val client: HttpClient,
|
private val baseUrl: String,
|
||||||
private val baseUrl: String
|
private val client: HttpClient = HttpClient()
|
||||||
) : WriteContentRepo {
|
) : WriteContentRepo {
|
||||||
override suspend fun registerContent(content: Content): RegisteredContent? = client.post<ByteArray> {
|
override suspend fun registerContent(content: Content): RegisteredContent? = client.post<ByteArray> {
|
||||||
url("$baseUrl/$registerContentRoute")
|
url("$baseUrl/$registerContentRoute")
|
||||||
@ -22,7 +23,7 @@ class WriteContentRepoKtorClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteContent(id: ContentId): Boolean = client.post<ByteArray> {
|
override suspend fun deleteContent(id: ContentId): Boolean = client.post<ByteArray> {
|
||||||
url("$baseUrl/$registerContentRoute")
|
url("$baseUrl/$deleteContentRoute")
|
||||||
body = standardKtorSerializer.dump(ContentId.serializer(), id)
|
body = standardKtorSerializer.dump(ContentId.serializer(), id)
|
||||||
}.let {
|
}.let {
|
||||||
standardKtorSerializer.load(Boolean.serializer(), it)
|
standardKtorSerializer.load(Boolean.serializer(), it)
|
||||||
|
@ -6,3 +6,6 @@ const val getContentByPaginationRoute = "getContentByPagination"
|
|||||||
|
|
||||||
const val registerContentRoute = "registerContent"
|
const val registerContentRoute = "registerContent"
|
||||||
const val deleteContentRoute = "deleteContent"
|
const val deleteContentRoute = "deleteContent"
|
||||||
|
|
||||||
|
const val contentCreatedFlowRoute = "contentCreatedFlow"
|
||||||
|
const val contentDeletedFlowRoute = "contentDeletedFlow"
|
||||||
|
@ -6,7 +6,7 @@ kotlin_serialisation_runtime_version=0.20.0
|
|||||||
ktor_version=1.3.2
|
ktor_version=1.3.2
|
||||||
|
|
||||||
klockVersion=1.11.12
|
klockVersion=1.11.12
|
||||||
uuidVersion=0.1.0
|
uuidVersion=0.1.1
|
||||||
|
|
||||||
exposed_version=0.26.2
|
exposed_version=0.26.2
|
||||||
test_sqlite_version=3.32.3.2
|
test_sqlite_version=3.32.3.2
|
||||||
|
@ -45,6 +45,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
api "io.ktor:ktor-client-core:$ktor_version"
|
api "io.ktor:ktor-client-core:$ktor_version"
|
||||||
|
api "io.ktor:ktor-client-websockets:$ktor_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
@ -58,6 +59,8 @@ kotlin {
|
|||||||
implementation kotlin('stdlib-jdk8')
|
implementation kotlin('stdlib-jdk8')
|
||||||
|
|
||||||
api "io.ktor:ktor-client:$ktor_version"
|
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 {
|
jvmTest {
|
||||||
@ -70,6 +73,7 @@ kotlin {
|
|||||||
implementation kotlin('stdlib-js')
|
implementation kotlin('stdlib-js')
|
||||||
|
|
||||||
api "io.ktor:ktor-client-js:$ktor_version"
|
api "io.ktor:ktor-client-js:$ktor_version"
|
||||||
|
api "io.ktor:ktor-client-websockets-js:$ktor_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsTest {
|
jsTest {
|
||||||
|
@ -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 <reified T> createStandardWebsocketFlow(
|
||||||
|
client: HttpClient,
|
||||||
|
url: String,
|
||||||
|
crossinline conversation: suspend (ByteArray) -> T
|
||||||
|
): Flow<T> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,8 @@ kotlin {
|
|||||||
implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$kotlin_serialisation_runtime_version"
|
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 "org.jetbrains.kotlinx:kotlinx-serialization-cbor-common:$kotlin_serialisation_runtime_version"
|
||||||
|
|
||||||
|
api projectByName("postssystem.utils.common")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.ktor
|
||||||
|
|
||||||
|
object CorrectCloseException : Exception()
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
68
ktor/server/build.gradle
Normal file
68
ktor/server/build.gradle
Normal file
@ -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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
ktor/server/maven.publish.gradle
Normal file
53
ktor/server/maven.publish.gradle
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
ktor/server/publish.gradle
Normal file
55
ktor/server/publish.gradle
Normal file
@ -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
|
1
ktor/server/publish_config.kpsb
Normal file
1
ktor/server/publish_config.kpsb
Normal file
@ -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"}
|
@ -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 <T> Route.includeWebsocketHandling(
|
||||||
|
suburl: String,
|
||||||
|
flow: Flow<T>,
|
||||||
|
converter: (T) -> ByteArray
|
||||||
|
) {
|
||||||
|
webSocket(suburl) {
|
||||||
|
safely {
|
||||||
|
flow.collect {
|
||||||
|
checkReceivedAndCloseIfExists()
|
||||||
|
send(converter(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> createKtorServer(
|
||||||
|
engine: ApplicationEngineFactory<TEngine, TConfiguration>,
|
||||||
|
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)
|
39
ktor/tests/build.gradle
Normal file
39
ktor/tests/build.gradle
Normal file
@ -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"
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.ktor.tests
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
val testsScope = CoroutineScope(Dispatchers.Default)
|
@ -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<Int>(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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
rootProject.name='postssystem'
|
rootProject.name='postssystem'
|
||||||
|
|
||||||
String[] includes = [
|
String[] includes = [
|
||||||
|
':utils:common',
|
||||||
':utils:repos',
|
':utils:repos',
|
||||||
':utils:repos:common',
|
':utils:repos:common',
|
||||||
':utils:repos:exposed',
|
':utils:repos:exposed',
|
||||||
@ -9,6 +10,8 @@ String[] includes = [
|
|||||||
|
|
||||||
':ktor:common',
|
':ktor:common',
|
||||||
':ktor:client',
|
':ktor:client',
|
||||||
|
':ktor:server',
|
||||||
|
':ktor:tests',
|
||||||
|
|
||||||
':core:api',
|
':core:api',
|
||||||
':core:exposed',
|
':core:exposed',
|
||||||
|
79
utils/common/build.gradle
Normal file
79
utils/common/build.gradle
Normal file
@ -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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
utils/common/maven.publish.gradle
Normal file
64
utils/common/maven.publish.gradle
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
utils/common/publish.gradle
Normal file
55
utils/common/publish.gradle
Normal file
@ -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
|
1
utils/common/publish_config.json
Normal file
1
utils/common/publish_config.json
Normal file
@ -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"}]}}
|
@ -1,4 +1,4 @@
|
|||||||
package com.insanusmokrassar.postssystem.core.utils
|
package com.insanusmokrassar.postssystem.utils.common
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.builtins.ByteArraySerializer
|
import kotlinx.serialization.builtins.ByteArraySerializer
|
@ -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)
|
@ -1,4 +1,4 @@
|
|||||||
package com.insanusmokrassar.postssystem.core.utils
|
package com.insanusmokrassar.postssystem.utils.common
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
@ -37,11 +37,8 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('stdlib')
|
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 projectByName("postssystem.utils.common")
|
||||||
api "com.benasher44:uuid:$uuidVersion"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
|
Loading…
Reference in New Issue
Block a user