diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/common/serialization/JsonExt.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/common/serialization/JsonExt.kt index ee8b7ff..4e6a368 100644 --- a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/common/serialization/JsonExt.kt +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/common/serialization/JsonExt.kt @@ -6,6 +6,7 @@ import io.rsocket.kotlin.payload.Payload import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream @@ -17,6 +18,10 @@ internal fun T.encodeToJson(serializationStrategy: SerializationStrategy) return ByteReadPacket(json.encodeToString(serializationStrategy, this).encodeToByteArray()) } +internal inline fun ByteArray.decodeFromJson(): T { + return json.decodeFromString(String(this)) +} + @OptIn(ExperimentalSerializationApi::class) internal fun Payload.decodeFromJson(deserializationStrategy: DeserializationStrategy): T { return json.decodeFromStream(deserializationStrategy, data.inputStream()) diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/FilesCommandsRegistry.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/FilesCommandsRegistry.kt index 5b18e95..4918e7e 100644 --- a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/FilesCommandsRegistry.kt +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/FilesCommandsRegistry.kt @@ -2,10 +2,12 @@ package io.timemates.api.rsocket.files.commands import io.timemates.api.rsocket.common.commands.RSocketCommandsBuilderScope import io.timemates.sdk.files.requests.GetFileBytesRequest +import io.timemates.sdk.files.requests.UploadFileRequest /** * RSocket commands related to timers. */ internal fun RSocketCommandsBuilderScope.files() { GetFileCommand associatedWith GetFileBytesRequest + UploadFileCommand associatedWith UploadFileRequest } \ No newline at end of file diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/GetFileCommand.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/GetFileCommand.kt index 9115fb6..c4f6066 100644 --- a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/GetFileCommand.kt +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/GetFileCommand.kt @@ -1,22 +1,22 @@ package io.timemates.api.rsocket.files.commands import io.rsocket.kotlin.RSocket -import io.rsocket.kotlin.payload.buildPayload import io.timemates.api.rsocket.common.commands.RSocketCommand -import io.timemates.api.rsocket.common.serialization.encodeToJson -import io.timemates.api.rsocket.files.requests.GetFileRequest +import io.timemates.api.rsocket.common.ext.requestStream +import io.timemates.api.rsocket.common.serialization.decodeFromJson +import io.timemates.api.rsocket.files.requests.RSocketGetFileRequest +import io.timemates.api.rsocket.files.types.sdk import io.timemates.sdk.files.requests.GetFileBytesRequest -import kotlinx.serialization.serializer +import kotlinx.coroutines.flow.first internal object GetFileCommand : RSocketCommand { override suspend fun execute(rSocket: RSocket, input: GetFileBytesRequest): GetFileBytesRequest.Result { - return rSocket.requestResponse( - buildPayload { - data(GetFileRequest(input.fileId.string).encodeToJson(serializer())) - } + return rSocket.requestStream( + route = "files.get", + data = RSocketGetFileRequest(input.fileId.string), ).let { result -> -// GetFileBytesRequest.Result(result.data) - TODO() + val metadata = result.first().decodeFromJson() + GetFileBytesRequest.Result(metadata.fileType.sdk(), result) } } } \ No newline at end of file diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/UploadFileCommand.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/UploadFileCommand.kt new file mode 100644 index 0000000..452b632 --- /dev/null +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/commands/UploadFileCommand.kt @@ -0,0 +1,43 @@ +package io.timemates.api.rsocket.files.commands + +import io.ktor.utils.io.core.ByteReadPacket +import io.rsocket.kotlin.ExperimentalMetadataApi +import io.rsocket.kotlin.RSocket +import io.rsocket.kotlin.metadata.RoutingMetadata +import io.rsocket.kotlin.metadata.compositeMetadata +import io.rsocket.kotlin.payload.Payload +import io.rsocket.kotlin.payload.buildPayload +import io.timemates.api.rsocket.common.commands.RSocketCommand +import io.timemates.api.rsocket.common.serialization.decodeFromJson +import io.timemates.api.rsocket.common.serialization.encodeToJson +import io.timemates.api.rsocket.files.requests.RSocketUploadFileRequest +import io.timemates.api.rsocket.files.types.serializable +import io.timemates.sdk.common.constructor.createOrThrow +import io.timemates.sdk.files.requests.UploadFileRequest +import io.timemates.sdk.files.types.value.FileId +import kotlinx.coroutines.flow.last +import kotlinx.coroutines.flow.map +import kotlinx.serialization.serializer + +internal object UploadFileCommand : RSocketCommand { + @OptIn(ExperimentalMetadataApi::class) + override suspend fun execute(rSocket: RSocket, input: UploadFileRequest): UploadFileRequest.Result { + return rSocket.requestChannel( + initPayload = buildPayload { + data( + RSocketUploadFileRequest.Metadata( + fileName = input.fileName.string, + fileType = input.fileType.serializable(), + ).encodeToJson(serializer()) + ) + compositeMetadata { + add(RoutingMetadata("files.upload")) + } + }, + payloads = input.bytes.map { Payload(data = ByteReadPacket(it)) } + ).let { result -> + val serialized = result.last().decodeFromJson(serializer()) + UploadFileRequest.Result(fileId = FileId.createOrThrow(serialized.fileId)) + } + } +} \ No newline at end of file diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/GetFileRequest.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/RSocketGetFileRequest.kt similarity index 64% rename from rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/GetFileRequest.kt rename to rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/RSocketGetFileRequest.kt index 8858a0f..878f02f 100644 --- a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/GetFileRequest.kt +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/RSocketGetFileRequest.kt @@ -1,18 +1,22 @@ package io.timemates.api.rsocket.files.requests +import io.timemates.api.rsocket.common.markers.RSocketRequest import io.timemates.api.rsocket.files.types.SerializableFileType +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -internal data class GetFileRequest( +internal data class RSocketGetFileRequest( val fileId: String, -) { +) : RSocketRequest { @Serializable sealed interface Response { + @SerialName("metadata") data class Metadata( val fileType: SerializableFileType, ) : Response + @SerialName("chunk") @JvmInline value class Chunk(val bytes: ByteArray) : Response } diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/UploadFileRequest.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/RSocketUploadFileRequest.kt similarity index 90% rename from rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/UploadFileRequest.kt rename to rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/RSocketUploadFileRequest.kt index ccc8809..d92091b 100644 --- a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/UploadFileRequest.kt +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/requests/RSocketUploadFileRequest.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.serialization.Serializable @Serializable -internal data class UploadFileRequest( +internal data class RSocketUploadFileRequest( val fileType: SerializableFileType, val bytes: Flow, ) { diff --git a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/types/SerializableFileType.kt b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/types/SerializableFileType.kt index 0c6f69e..a5ff0f8 100644 --- a/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/types/SerializableFileType.kt +++ b/rsocket-engine/src/commonMain/kotlin/io/timemates/api/rsocket/files/types/SerializableFileType.kt @@ -1,5 +1,6 @@ package io.timemates.api.rsocket.files.types +import io.timemates.sdk.files.types.FileType import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -7,4 +8,12 @@ import kotlinx.serialization.Serializable internal enum class SerializableFileType { @SerialName("image") IMAGE, +} + +internal fun SerializableFileType.sdk(): FileType = when (this) { + SerializableFileType.IMAGE -> FileType.IMAGE +} + +internal fun FileType.serializable(): SerializableFileType = when (this) { + FileType.IMAGE -> SerializableFileType.IMAGE } \ No newline at end of file diff --git a/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/GetFileBytesRequest.kt b/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/GetFileBytesRequest.kt index d5faea7..e95b864 100644 --- a/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/GetFileBytesRequest.kt +++ b/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/GetFileBytesRequest.kt @@ -2,6 +2,7 @@ package io.timemates.sdk.files.requests import io.timemates.sdk.common.types.TimeMatesEntity import io.timemates.sdk.common.types.TimeMatesRequest +import io.timemates.sdk.files.types.FileType import io.timemates.sdk.files.types.value.FileId import kotlinx.coroutines.flow.Flow @@ -11,6 +12,7 @@ public data class GetFileBytesRequest( public companion object Key : TimeMatesRequest.Key public data class Result( + val fileType: FileType, val bytes: Flow ) : TimeMatesEntity() diff --git a/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/UploadFileRequest.kt b/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/UploadFileRequest.kt index 2d2d9bb..36c13a7 100644 --- a/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/UploadFileRequest.kt +++ b/sdk/src/commonMain/kotlin/io/timemates/sdk/files/requests/UploadFileRequest.kt @@ -3,6 +3,7 @@ package io.timemates.sdk.files.requests import io.timemates.sdk.authorization.types.value.AccessHash import io.timemates.sdk.common.types.AuthorizedTimeMatesRequest import io.timemates.sdk.common.types.TimeMatesEntity +import io.timemates.sdk.common.types.TimeMatesRequest import io.timemates.sdk.files.types.FileType import io.timemates.sdk.files.types.value.FileId import io.timemates.sdk.files.types.value.FileName @@ -14,5 +15,9 @@ public data class UploadFileRequest( val fileName: FileName, val fileType: FileType, ) : AuthorizedTimeMatesRequest() { + public companion object Key : TimeMatesRequest.Key + public data class Result(val fileId: FileId) : TimeMatesEntity() + + override val requestKey: Key get() = Key } \ No newline at end of file