add writable parts of server and client
This commit is contained in:
parent
a3cc3e8537
commit
3e7a2c1f0d
@ -32,6 +32,7 @@ dependencies {
|
|||||||
api project(":ClientServerCommon")
|
api project(":ClientServerCommon")
|
||||||
|
|
||||||
api "io.ktor:ktor-client:$ktor_version"
|
api "io.ktor:ktor-client:$ktor_version"
|
||||||
|
api "io.ktor:ktor-client-websockets:$ktor_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.core.client
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.features.websocket.ws
|
||||||
|
import io.ktor.http.HttpMethod
|
||||||
|
import io.ktor.http.cio.websocket.Frame
|
||||||
|
import io.ktor.http.cio.websocket.readText
|
||||||
|
import kotlinx.coroutines.InternalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
|
|
||||||
|
class EventFlow<T>(
|
||||||
|
private val client: HttpClient,
|
||||||
|
private val host: String,
|
||||||
|
private val port: Int,
|
||||||
|
private val path: String,
|
||||||
|
private val dataConverter: suspend (String) -> T
|
||||||
|
) : Flow<T> {
|
||||||
|
@InternalCoroutinesApi
|
||||||
|
override suspend fun collect(collector: FlowCollector<T>) {
|
||||||
|
client.ws(
|
||||||
|
method = HttpMethod.Get,
|
||||||
|
host = host,
|
||||||
|
port = port,
|
||||||
|
path = path
|
||||||
|
) {
|
||||||
|
var frame = incoming.receiveOrClosed()
|
||||||
|
while (!frame.isClosed) {
|
||||||
|
val frameVal = frame.value
|
||||||
|
when (frameVal) {
|
||||||
|
is Frame.Text -> {
|
||||||
|
collector.emit(
|
||||||
|
dataConverter(frameVal.readText())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,46 @@
|
|||||||
package com.insanusmokrassar.postssystem.core.client
|
package com.insanusmokrassar.postssystem.core.client
|
||||||
|
|
||||||
import com.insanusmokrassar.postssystem.core.api.WritePostsAPI
|
import com.insanusmokrassar.postssystem.core.api.WritePostsAPI
|
||||||
|
import com.insanusmokrassar.postssystem.core.clientserver.common.*
|
||||||
|
import com.insanusmokrassar.postssystem.core.clientserver.common.models.UpdatePostRequest
|
||||||
import com.insanusmokrassar.postssystem.core.post.*
|
import com.insanusmokrassar.postssystem.core.post.*
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.post
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
class WritableHttpPostsAPI(
|
class WritableHttpPostsAPI(
|
||||||
|
private val hostUrl: String,
|
||||||
|
private val port: Int,
|
||||||
|
private val client: HttpClient
|
||||||
) : WritePostsAPI {
|
) : WritePostsAPI {
|
||||||
override val postCreatedFlow: Flow<Post>
|
private inline fun postEventFlow(url: String) = EventFlow(
|
||||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
client,
|
||||||
override val postDeletedFlow: Flow<Post>
|
hostUrl,
|
||||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
port,
|
||||||
override val postUpdatedFlow: Flow<Post>
|
"/$url"
|
||||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
) {
|
||||||
|
Json.plain.parse(SimplePost.serializer(), it)
|
||||||
override suspend fun createPost(content: PostContents): Post? {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deletePost(id: PostId): Boolean {
|
override val postCreatedFlow: Flow<Post> = postEventFlow(eventPostsCreatedAddress)
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
override val postDeletedFlow: Flow<Post> = postEventFlow(eventPostsDeletedAddress)
|
||||||
|
override val postUpdatedFlow: Flow<Post> = postEventFlow(eventPostsUpdatedAddress)
|
||||||
|
|
||||||
|
override suspend fun createPost(content: PostContents): Post? = client.post<SimplePost>(
|
||||||
|
createPostAddress
|
||||||
|
) {
|
||||||
|
body = content
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updatePostContent(postId: PostId, content: PostContents): Boolean {
|
override suspend fun deletePost(id: PostId): Boolean = client.post(deletePostAddress) {
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
body = id
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updatePostContent(
|
||||||
|
postId: PostId,
|
||||||
|
content: PostContents
|
||||||
|
): Boolean = client.post(createPostAddress) {
|
||||||
|
body = UpdatePostRequest(postId, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,6 +5,14 @@ const val getPostsByContentIdAddress = "core/posts/get/content_id"
|
|||||||
const val getPostsByDatesAddress = "core/posts/get/dates"
|
const val getPostsByDatesAddress = "core/posts/get/dates"
|
||||||
const val getPostsByPaginationAddress = "core/posts/get/pagination"
|
const val getPostsByPaginationAddress = "core/posts/get/pagination"
|
||||||
|
|
||||||
|
const val eventPostsCreatedAddress = "core/posts/event/created"
|
||||||
|
const val eventPostsUpdatedAddress = "core/posts/event/updated"
|
||||||
|
const val eventPostsDeletedAddress = "core/posts/event/deleted"
|
||||||
|
|
||||||
|
const val createPostAddress = "core/posts/create"
|
||||||
|
const val updatePostAddress = "core/posts/update"
|
||||||
|
const val deletePostAddress = "core/posts/delete"
|
||||||
|
|
||||||
//class ReadPaths(
|
//class ReadPaths(
|
||||||
// val baseAddress: String
|
// val baseAddress: String
|
||||||
//) {
|
//) {
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.core.clientserver.common.models
|
||||||
|
|
||||||
|
import com.insanusmokrassar.postssystem.core.post.PostContents
|
||||||
|
import com.insanusmokrassar.postssystem.core.post.PostId
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdatePostRequest(
|
||||||
|
val id: PostId,
|
||||||
|
val content: PostContents
|
||||||
|
)
|
@ -1,7 +1,5 @@
|
|||||||
package com.insanusmokrassar.postssystem.core.content
|
package com.insanusmokrassar.postssystem.core.content
|
||||||
|
|
||||||
import org.joda.time.DateTime
|
|
||||||
|
|
||||||
typealias ContentId = String
|
typealias ContentId = String
|
||||||
|
|
||||||
interface Content {
|
interface Content {
|
||||||
|
@ -33,6 +33,7 @@ dependencies {
|
|||||||
|
|
||||||
api "io.ktor:ktor-server:$ktor_version"
|
api "io.ktor:ktor-server:$ktor_version"
|
||||||
api "io.ktor:ktor-server-core:$ktor_version"
|
api "io.ktor:ktor-server-core:$ktor_version"
|
||||||
|
api "io.ktor:ktor-websockets:$ktor_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
|
@ -8,8 +8,10 @@ import io.ktor.http.ContentType
|
|||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.response.respond
|
import io.ktor.response.respond
|
||||||
import io.ktor.response.respondText
|
import io.ktor.response.respondText
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.internal.ArrayListSerializer
|
import kotlinx.serialization.internal.ArrayListSerializer
|
||||||
|
import kotlinx.serialization.internal.NullableSerializer
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
|
||||||
@ -27,11 +29,12 @@ internal val Post.asSimplePost
|
|||||||
else -> SimplePost(id, content, meta)
|
else -> SimplePost(id, content, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun <T> ApplicationCall.answer(
|
@InternalSerializationApi
|
||||||
|
internal suspend fun <T : Any> ApplicationCall.answer(
|
||||||
serializer: KSerializer<T>,
|
serializer: KSerializer<T>,
|
||||||
answerObject: T
|
answerObject: T?
|
||||||
) = respondText(ContentType.Application.Json) {
|
) = respondText(ContentType.Application.Json) {
|
||||||
Json.plain.stringify(serializer, answerObject)
|
Json.plain.stringify(NullableSerializer(serializer), answerObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun ApplicationCall.answerBadRequest(
|
internal suspend fun ApplicationCall.answerBadRequest(
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.core.server
|
||||||
|
|
||||||
|
import com.insanusmokrassar.postssystem.core.api.WritePostsAPI
|
||||||
|
import com.insanusmokrassar.postssystem.core.clientserver.common.*
|
||||||
|
import com.insanusmokrassar.postssystem.core.clientserver.common.models.UpdatePostRequest
|
||||||
|
import com.insanusmokrassar.postssystem.core.post.*
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.http.cio.websocket.Frame
|
||||||
|
import io.ktor.request.receiveOrNull
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.post
|
||||||
|
import io.ktor.websocket.webSocket
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
|
import kotlinx.serialization.internal.BooleanSerializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
private inline fun Route.createWebsocket(path: String, flow: Flow<Post>) {
|
||||||
|
webSocket("/$path") {
|
||||||
|
flow.collect {
|
||||||
|
val simplePost = it.asSimplePost
|
||||||
|
outgoing.send(Frame.Text(Json.plain.stringify(SimplePost.serializer(), simplePost)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
fun Route.includePostsCoreWriteModules(
|
||||||
|
writePostsAPI: WritePostsAPI
|
||||||
|
) {
|
||||||
|
createWebsocket(eventPostsCreatedAddress, writePostsAPI.postCreatedFlow)
|
||||||
|
createWebsocket(eventPostsDeletedAddress, writePostsAPI.postDeletedFlow)
|
||||||
|
createWebsocket(eventPostsUpdatedAddress, writePostsAPI.postUpdatedFlow)
|
||||||
|
|
||||||
|
post(createPostAddress) {
|
||||||
|
call.receiveOrNull<PostContents>() ?.also { contents ->
|
||||||
|
val post = writePostsAPI.createPost(contents)
|
||||||
|
|
||||||
|
call.answer(SimplePost.serializer(), post ?.asSimplePost)
|
||||||
|
} ?: call.answerBadRequest("Contents (List of Content)")
|
||||||
|
}
|
||||||
|
|
||||||
|
post(deletePostAddress) {
|
||||||
|
call.receiveOrNull<PostId>() ?.also { postId ->
|
||||||
|
call.answer(BooleanSerializer, writePostsAPI.deletePost(postId))
|
||||||
|
} ?: call.answerBadRequest("Post Id (String)")
|
||||||
|
}
|
||||||
|
post(deletePostAddress) {
|
||||||
|
call.receiveOrNull<UpdatePostRequest>() ?.also { (postId, contents) ->
|
||||||
|
call.answer(BooleanSerializer, writePostsAPI.updatePostContent(postId, contents))
|
||||||
|
} ?: call.answerBadRequest("Post Id (String)")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user