From 2ede1815726aeb385dab0b555639c8389f96628f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 14 Feb 2023 13:24:53 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E5=9E=8B=E3=82=92=E4=B8=80=E9=83=A8=E3=81=AB=E5=B0=8E=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 + .../dev/usbharu/multim/api/ApiClient.kt | 70 ++++++++++++++----- .../kotlin/dev/usbharu/multim/error/Error.kt | 25 +++++++ .../usbharu/multim/error/HttpClientError.kt | 10 +++ .../dev/usbharu/multim/error/MultiMError.kt | 5 ++ 5 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 core/src/main/kotlin/dev/usbharu/multim/error/Error.kt create mode 100644 core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt create mode 100644 core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt diff --git a/build.gradle.kts b/build.gradle.kts index f4b2f0f..536507b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,6 +73,7 @@ subprojects { "implementation"("io.ktor:ktor-client-websockets:$ktor_version") "implementation"("io.ktor:ktor-serialization-kotlinx-json:$ktor_version") "implementation"("io.github.aakira:napier:2.6.1") + "implementation"("com.michael-bull.kotlin-result:kotlin-result:1.1.16") "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.9.0") "testImplementation"("org.junit.jupiter:junit-jupiter-params:5.9.0") diff --git a/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt b/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt index b394f53..8e3de5c 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt @@ -1,9 +1,13 @@ package dev.usbharu.multim.api +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result import dev.usbharu.multim.MultiM +import dev.usbharu.multim.error.* import io.ktor.client.* import io.ktor.client.call.* -import io.ktor.client.engine.cio.* +import io.ktor.client.plugins.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* @@ -14,38 +18,66 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) { return createHttpClient(config) } - suspend inline fun post(path: String, baseUrl: String = this.baseUrl): R { - val post = client.post(baseUrl + path) - return post.body() + suspend inline fun post( + path: String, + baseUrl: String = this.baseUrl + ): Result { + val post = try { + client.post(baseUrl + path) + } catch (e: ClientRequestException) { + return Err(HttpClientClientError(e)) + } catch (e: ServerResponseException) { + return Err(HttpClientServerError(e)) + } + return runCatching { post.body() }.fold( + onSuccess = { Ok(it) }, + onFailure = { Err(ThrowableError(it)) }) } suspend inline fun post( content: T, path: String, baseUrl: String = this.baseUrl - ): R { - val post = client.post(baseUrl + path) { - contentType(ContentType.Application.Json) - setBody(content) - } - - return post.body() + ): Result { + val post = + try { + client.post(baseUrl + path) { + contentType(ContentType.Application.Json) + setBody(content) + } + } catch (e: ClientRequestException) { + return Err(HttpClientClientError(e)) + } catch (e: ServerResponseException) { + return Err(HttpClientServerError(e)) + } + return runCatching { post.body() }.fold( + onSuccess = { Ok(it) }, + onFailure = { Err(ThrowableError(it)) }) } suspend inline fun postWithoutResponse( content: T, path: String, baseUrl: String = this.baseUrl - ) { - client.post(baseUrl + path) { - contentType(ContentType.Application.Json) - setBody(content) - } - + ): Result { + return runCatching { + client.post(baseUrl + path) { + contentType(ContentType.Application.Json) + setBody(content) + } + }.fold(onSuccess = Ok(), onFailure = ThrowableError()) } - suspend fun get(path: String, block: HttpRequestBuilder.() -> Unit): HttpResponse { - return client.get(baseUrl + path, block) + suspend fun get( + path: String, + block: HttpRequestBuilder.() -> Unit + ): Result { + return runCatching { + client.get( + baseUrl + path, + block + ) + }.fold(onSuccess = { Ok(it) }, onFailure = { Err(ThrowableError(it)) }) } } diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt b/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt new file mode 100644 index 0000000..b393d5e --- /dev/null +++ b/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt @@ -0,0 +1,25 @@ +package dev.usbharu.multim.error + +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok + +abstract class Error(open val message: String) + +open class ThrowableError( + val throwable: Throwable, + override val message: String = throwable.localizedMessage +) : Error(message) + +class SimpleError(message: String) : Error(message) + +fun Ok(): (R) -> Ok { + return { Ok(it) } +} + +fun ThrowableError(): (Throwable) -> Err { + return { Err(ThrowableError(it)) } +} + +fun Error(error: (T) -> Error): (T) -> Err { + return { Err(error(it)) } +} diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt b/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt new file mode 100644 index 0000000..de40158 --- /dev/null +++ b/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt @@ -0,0 +1,10 @@ +package dev.usbharu.multim.error + +abstract class HttpClientError(throwable: Throwable, message: String = throwable.localizedMessage) : + ThrowableError(throwable, message) + +class HttpClientServerError(throwable: Throwable, message: String = throwable.localizedMessage) : + HttpClientError(throwable, message) + +class HttpClientClientError(throwable: Throwable, message: String = throwable.localizedMessage) : + HttpClientError(throwable, message) diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt new file mode 100644 index 0000000..584dee8 --- /dev/null +++ b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt @@ -0,0 +1,5 @@ +package dev.usbharu.multim.error + +class MultiMError(message: String) : Error(message) + +class MultiMHttpError() From d14191b6a13b04b12ef8b65e6fd16a6d3b399aba Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 15 Feb 2023 00:34:28 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=E5=85=AC=E9=96=8BAPI=E3=81=A7Resul?= =?UTF-8?q?t=E3=82=92=E5=88=A9=E7=94=A8=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/dev/usbharu/multim/MultiM.kt | 9 +- .../dev/usbharu/multim/api/AccountApi.kt | 20 ++- .../dev/usbharu/multim/api/NodeinfoApi.kt | 58 ++++++- .../dev/usbharu/multim/api/StatusApi.kt | 28 +-- .../dev/usbharu/multim/api/TimelineApi.kt | 10 +- .../kotlin/dev/usbharu/multim/error/Error.kt | 10 +- .../usbharu/multim/error/HttpClientError.kt | 8 +- .../dev/usbharu/multim/error/MultiMError.kt | 98 ++++++++++- .../multim/multi/MultiAccountAccountApi.kt | 117 ++++++++----- .../multim/multi/MultiAccountApiBase.kt | 36 ++-- .../multim/multi/MultiAccountStatusApi.kt | 159 ++++++++++-------- .../multim/multi/MultiAccountTimelineApi.kt | 64 ++++--- 12 files changed, 429 insertions(+), 188 deletions(-) diff --git a/core/src/main/kotlin/dev/usbharu/multim/MultiM.kt b/core/src/main/kotlin/dev/usbharu/multim/MultiM.kt index 59eccae..df1b53d 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/MultiM.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/MultiM.kt @@ -1,6 +1,10 @@ package dev.usbharu.multim +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.flatMap +import com.github.michaelbull.result.map import dev.usbharu.multim.api.NodeinfoApi +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.factory.MultiMApis import dev.usbharu.multim.factory.PlatformApiFactory import dev.usbharu.multim.multi.MultiAccountApiBase @@ -30,10 +34,9 @@ object MultiM { token: String, factory: PlatformApiFactory, httpClient: HttpClient = httpClientWithJson - ): MultiMApis { + ): Result { - val nodeinfo = NodeinfoApi(httpClient).nodeinfo(url) - return factory.factory(nodeInfo = nodeinfo, httpClient, token, url) + return NodeinfoApi(httpClient).nodeinfo(url).map{ nodeInfo -> factory.factory(nodeInfo,httpClient,token,url) } } fun createMultiAccountClients(serviceInfoList: List = listOf()): MultiAccountApiBase { diff --git a/core/src/main/kotlin/dev/usbharu/multim/api/AccountApi.kt b/core/src/main/kotlin/dev/usbharu/multim/api/AccountApi.kt index bf21b78..e6cc5ad 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/api/AccountApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/api/AccountApi.kt @@ -1,5 +1,7 @@ package dev.usbharu.multim.api +import com.github.michaelbull.result.Result +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.model.* //todo 成功したかをboolで返しているが、詳細がわからないのでしっかり返す。 @@ -9,14 +11,14 @@ interface AccountApi { account: Account, since: StatusId? = null, until: StatusId? = null - ): List + ): Result,MultiMError> - suspend fun follow(account: Account): Boolean - suspend fun unfollow(account: Account): Boolean - suspend fun profile(account: Account): Profile - suspend fun statuses(account: Account, includeRepost: Boolean = false): List - suspend fun relationships(myself: Account, other: Account): Relation - suspend fun requestCancel(account: Account): Boolean - suspend fun requestAccept(account: Account): Boolean - suspend fun requestReject(account: Account): Boolean + suspend fun follow(account: Account): Result + suspend fun unfollow(account: Account): Result + suspend fun profile(account: Account): Result + suspend fun statuses(account: Account, includeRepost: Boolean = false): Result,MultiMError> + suspend fun relationships(myself: Account, other: Account): Result + suspend fun requestCancel(account: Account): Result + suspend fun requestAccept(account: Account): Result + suspend fun requestReject(account: Account): Result } diff --git a/core/src/main/kotlin/dev/usbharu/multim/api/NodeinfoApi.kt b/core/src/main/kotlin/dev/usbharu/multim/api/NodeinfoApi.kt index 4dad217..02f8eba 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/api/NodeinfoApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/api/NodeinfoApi.kt @@ -1,13 +1,17 @@ package dev.usbharu.multim.api +import com.github.michaelbull.result.* import dev.usbharu.multim.MultiM.json +import dev.usbharu.multim.error.* import dev.usbharu.multim.model.wellknown.NodeinfoList import dev.usbharu.multim.model.wellknown.nodeinfo.NodeInfo import io.ktor.client.* import io.ktor.client.call.* +import io.ktor.client.plugins.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.request.* import io.ktor.serialization.kotlinx.json.* +import kotlin.runCatching // todo well-knwonだけでパッケージ作ったほうがいいかも class NodeinfoApi(private var httpClient: HttpClient) { @@ -20,22 +24,60 @@ class NodeinfoApi(private var httpClient: HttpClient) { } } - suspend fun wellKnownNodeinfo(url: String): NodeinfoList { - return httpClient.get("$url.well-known/nodeinfo").body() + suspend fun wellKnownNodeinfo(url: String): Result { + val get = try { + httpClient.get("$url.well-known/nodeinfo") + } catch (e: ServerResponseException) { + return Err( + MultiMHttpError( + HttpError.ServerError(e.response.status.value, e.message, e), e + ) + ) + } catch (e: ClientRequestException) { + return Err( + MultiMHttpError( + HttpError.ClientError(e.response.status.value, e.message, e), e + ) + ) + } + return runCatching { get.body() }.foldWithOk { + MultiMJsonContentTransformError( + it.localizedMessage + ) + } } - fun nodeinfoLink(nodeinfoList: NodeinfoList): NodeinfoList.NodeinfoLink { - return nodeinfoList.links.minByOrNull { it.rel.substringAfterLast("/", "0").toFloat() }!! + fun nodeinfoLink(nodeinfoList: NodeinfoList): Result { + return runCatching { + nodeinfoList.links.minByOrNull { it.rel.substringAfterLast("/", "0").toFloat() }!! + }.foldWithOk { + MultiMError(it.localizedMessage, it, ErrorType.API) + } } // todo 強制で2.0のが返ってくるのでバージョンを識別する - suspend fun nodeinfo(nodeinfoLink: NodeinfoList.NodeinfoLink): NodeInfo { + suspend fun nodeinfo(nodeinfoLink: NodeinfoList.NodeinfoLink): Result { - return httpClient.get(nodeinfoLink.href).body() + val get = try { + httpClient.get(nodeinfoLink.href) + } catch (e: ServerResponseException) { + return Err( + MultiMHttpError(e) + ) + } catch (e: ClientRequestException) { + return Err(MultiMHttpError(e)) + } + return runCatching { get.body() }.foldWithOk { + MultiMJsonContentTransformError( + it.localizedMessage + ) + } } - suspend fun nodeinfo(url: String): NodeInfo { - return nodeinfo(nodeinfoLink(wellKnownNodeinfo(url))) + suspend fun nodeinfo(url: String): Result { + return wellKnownNodeinfo(url) + .flatMap { nodeinfoLink(it) } + .flatMap { nodeinfo(it) } } } diff --git a/core/src/main/kotlin/dev/usbharu/multim/api/StatusApi.kt b/core/src/main/kotlin/dev/usbharu/multim/api/StatusApi.kt index 68ef56e..3323964 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/api/StatusApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/api/StatusApi.kt @@ -1,12 +1,14 @@ package dev.usbharu.multim.api +import com.github.michaelbull.result.Result +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.model.* interface StatusApi { - suspend fun post(status: StatusForPost): Status - suspend fun delete(id: StatusId): Boolean - suspend fun findById(id: StatusId): Status - suspend fun addReaction(id: StatusId, reaction: Reaction): Boolean + suspend fun post(status: StatusForPost): Result + suspend fun delete(id: StatusId): Result + suspend fun findById(id: StatusId): Result + suspend fun addReaction(id: StatusId, reaction: Reaction): Result /** @@ -16,13 +18,13 @@ interface StatusApi { * @param reaction 実装によって挙動が変わります。nullでも * @return */ - suspend fun removeReaction(id: StatusId, reaction: Reaction?): Boolean - suspend fun reactions(id: StatusId): Map - suspend fun replies(id: StatusId): List - suspend fun repost(id: StatusId): Status - suspend fun unRepost(id: StatusId): Boolean - suspend fun replyTo(id: StatusId, status: StatusForPost): Status - suspend fun addToBookmarks(id: StatusId): Boolean - suspend fun removeFromBookmarks(id: StatusId): Boolean - suspend fun getPreviousAndNext(id: StatusId): PreviousAndNextPosts + suspend fun removeReaction(id: StatusId, reaction: Reaction?): Result + suspend fun reactions(id: StatusId): Result,MultiMError> + suspend fun replies(id: StatusId): Result,MultiMError> + suspend fun repost(id: StatusId): Result + suspend fun unRepost(id: StatusId): Result + suspend fun replyTo(id: StatusId, status: StatusForPost): Result + suspend fun addToBookmarks(id: StatusId): Result + suspend fun removeFromBookmarks(id: StatusId): Result + suspend fun getPreviousAndNext(id: StatusId): Result } diff --git a/core/src/main/kotlin/dev/usbharu/multim/api/TimelineApi.kt b/core/src/main/kotlin/dev/usbharu/multim/api/TimelineApi.kt index b716d05..a2902a5 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/api/TimelineApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/api/TimelineApi.kt @@ -1,15 +1,17 @@ package dev.usbharu.multim.api +import com.github.michaelbull.result.Result +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.model.Status import dev.usbharu.multim.model.Timeline interface TimelineApi { - suspend fun availableTimelines(): List + suspend fun availableTimelines(): Result,MultiMError> - suspend fun listen(timeline: Timeline,callback: (List) -> Unit) + suspend fun listen(timeline: Timeline,callback: (List) -> Unit):Result // todo 返り値を詳細にする - suspend fun connect(timeline: Timeline): Boolean + suspend fun connect(timeline: Timeline): Result /** * Disconnect @@ -18,7 +20,7 @@ interface TimelineApi { * @param force 強制的に切断し、もし受信しても無視するように要求する。 * @return */ - suspend fun disconnect(timeline: Timeline, force: Boolean = false): Boolean + suspend fun disconnect(timeline: Timeline, force: Boolean = false): Result } diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt b/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt index b393d5e..f70ba2f 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/error/Error.kt @@ -2,6 +2,7 @@ package dev.usbharu.multim.error import com.github.michaelbull.result.Err import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result as MichaelbullResultResult abstract class Error(open val message: String) @@ -20,6 +21,13 @@ fun ThrowableError(): (Throwable) -> Err { return { Err(ThrowableError(it)) } } -fun Error(error: (T) -> Error): (T) -> Err { +fun Error(error: (T) -> R): (T) -> Err { return { Err(error(it)) } } + +fun Result.foldWithOk(onSuccess:(A)->Ok = Ok(), onFailure:(Throwable)->B): MichaelbullResultResult { + return fold( + onSuccess = onSuccess, + onFailure = Error(onFailure) + ) +} diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt b/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt index de40158..7dbe677 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/error/HttpClientError.kt @@ -1,10 +1,12 @@ package dev.usbharu.multim.error -abstract class HttpClientError(throwable: Throwable, message: String = throwable.localizedMessage) : +import io.ktor.client.plugins.* + +abstract class HttpClientError(throwable: ResponseException, message: String = throwable.localizedMessage) : ThrowableError(throwable, message) -class HttpClientServerError(throwable: Throwable, message: String = throwable.localizedMessage) : +class HttpClientServerError(throwable: ServerResponseException, message: String = throwable.localizedMessage) : HttpClientError(throwable, message) -class HttpClientClientError(throwable: Throwable, message: String = throwable.localizedMessage) : +class HttpClientClientError(throwable: ClientRequestException, message: String = throwable.localizedMessage) : HttpClientError(throwable, message) diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt index 584dee8..411a341 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt @@ -1,5 +1,99 @@ package dev.usbharu.multim.error -class MultiMError(message: String) : Error(message) +import io.ktor.client.plugins.* -class MultiMHttpError() +open class MultiMError( + message: String, + val throwable: Throwable? = null, + val errorType: ErrorType +) : + Error("MultiM ${errorType.message} ERROR : $message") + +class MultiMHttpError(val httpError: HttpError, throwable: Throwable? = httpError.throwable) : + MultiMError( + """ + HTTP ERROR ${httpError.code} ${httpError.errorName}. + Message : ${httpError.message} + ${throwable?.let { "Exception : ${it.message}" }} + ${httpError.throwable?.let { "Exception : ${it.message}" }} + """.trimIndent(), + throwable, + ErrorType.HTTP + ) { + + constructor(e: ClientRequestException) : this( + HttpError.ClientError( + e.response.status.value, + e.message, + e + ) + ) + + constructor(e: ServerResponseException) : this( + HttpError.ServerError( + e.response.status.value, + e.message, + e + ) + ) + constructor(httpClientClientError: HttpClientClientError) : this( + (httpClientClientError.throwable as ClientRequestException).let { + HttpError.ClientError( + it.response.status.value, + it.message, + it + ) + }, httpClientClientError.throwable + ) + + constructor(httpClientServerError: HttpClientServerError) : this( + (httpClientServerError.throwable as ServerResponseException).let { + HttpError.ClientError( + it.response.status.value, + it.message, + it + ) + }, httpClientServerError.throwable + ) +} + +open class MultiMContentTransformError( + val typeName: String, + message: String +) : + MultiMError( + """ + TypeName : $typeName + Message : $message + """.trimIndent(), null, ErrorType.CONTENT_TRANSFORM + ) + +class MultiMJsonContentTransformError( + message: String +) : MultiMContentTransformError( + "json", + message +) + + +sealed class HttpError( + val code: Int, + val message: String, + val errorName: String, + val throwable: Throwable? = null +) { + data class ClientError(val _code: Int, val _message: String, val _throwable: Throwable?) : + HttpError(_code, _message, "Client", _throwable) + + data class ServerError(val _code: Int, val _message: String, val _throwable: Throwable?) : + HttpError(_code, _message, "Server", _throwable) +} + + +enum class ErrorType(val message: String) { + API("API"), + HTTP("HTTP"), + CONTENT_TRANSFORM("Content Transform"), + ILLEGAL_ARGUMENT("Illegal Argument"), + UNKNOWN("UNKNOWN") +} diff --git a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountAccountApi.kt b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountAccountApi.kt index 1655b86..140db48 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountAccountApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountAccountApi.kt @@ -1,6 +1,10 @@ package dev.usbharu.multim.multi +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.flatMap +import com.github.michaelbull.result.map import dev.usbharu.multim.api.AccountApi +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.model.* import dev.usbharu.multim.multi.model.MultiAccountProfile import dev.usbharu.multim.multi.model.MultiAccountStatus @@ -11,105 +15,138 @@ class MultiAccountAccountApi(private val multiAccountApiBase: MultiAccountApiBas account: Account, since: StatusId?, until: StatusId? - ): List { - return getImpl2(account) { - userTimeline( - it, - since, - until - ) - }.let { it.first.map { status -> MultiAccountStatus(status, it.second) } } + ): Result, MultiMError> { + return getImpl2(account) { userTimeline(it, since, until) } + .flatMap { + it.first.map { statuses -> + statuses.map { status -> + MultiAccountStatus( + status, + it.second + ) + } + } + } } - override suspend fun follow(account: Account): Boolean { - return getImpl2(account) { follow(it) }.first + override suspend fun follow(account: Account): Result { + return getImpl2(account) { follow(it) }.map { it.first } } - suspend fun follow(account: MultiAccountData): MultiAccountData { + suspend fun follow(account: MultiAccountData): Result, MultiMError> { return getImpl(account) { follow(it) } } - override suspend fun unfollow(account: Account): Boolean { - return getImpl2(account) { unfollow(it) }.first + override suspend fun unfollow(account: Account): Result { + return getImpl2(account) { unfollow(it) }.map { it.first } } - override suspend fun profile(account: Account): Profile { - return MultiAccountProfile(getImpl2(account) { profile(it) }) + override suspend fun profile(account: Account): Result { + return getImpl2(account) { profile(it) } + .flatMap { + it.first.map { profile -> + MultiAccountProfile( + profile to it.second + ) + } + } } - override suspend fun statuses(account: Account, includeRepost: Boolean): List { + override suspend fun statuses( + account: Account, + includeRepost: Boolean + ): Result, MultiMError> { return getImpl2(account) { statuses( it, includeRepost ) - }.let { it.first.map { status -> MultiAccountStatus(status, it.second) } } + }.flatMap { + it.first.map { statuses -> + statuses.map { status: Status -> + MultiAccountStatus( + status, + it.second + ) + } + } + } } - override suspend fun relationships(myself: Account, other: Account): Relation { - return getImpl2(myself) { relationships(it, other) }.first + override suspend fun relationships( + myself: Account, + other: Account + ): Result { + return getImpl2(myself) { relationships(it, other) }.flatMap { it.first } } - override suspend fun requestCancel(account: Account): Boolean { - return getImpl2(account) { requestCancel(it) }.first + override suspend fun requestCancel(account: Account): Result { + return getImpl2(account) { requestCancel(it) }.map { it.first } } - override suspend fun requestAccept(account: Account): Boolean { - return getImpl2(account) { requestAccept(account) }.first + override suspend fun requestAccept(account: Account): Result { + return getImpl2(account) { requestAccept(account) }.map { it.first } } - override suspend fun requestReject(account: Account): Boolean { - return getImpl2(account) { requestReject(it) }.first + override suspend fun requestReject(account: Account): Result { + return getImpl2(account) { requestReject(it) }.map { it.first } } - suspend fun unfollow(account: MultiAccountData): MultiAccountData { + suspend fun unfollow(account: MultiAccountData): Result, MultiMError> { return getImpl(account) { unfollow(it) } } - suspend fun profile(account: MultiAccountData): MultiAccountData { + suspend fun profile(account: MultiAccountData): Result, MultiMError> { return getImpl(account) { profile(it) } } - suspend fun statuses(account: MultiAccountData): MultiAccountData> { + suspend fun statuses(account: MultiAccountData): Result>, MultiMError> { return getImpl(account) { statuses(it) } } suspend fun relationships( myself: MultiAccountData, other: MultiAccountData - ): MultiAccountData { + ): Result, MultiMError> { return getImpl(myself) { relationships(it, other.innerData) } } - suspend fun requestCancel(account: MultiAccountData): MultiAccountData { + suspend fun requestCancel(account: MultiAccountData): Result, MultiMError> { return getImpl(account) { requestCancel(it) } } - suspend fun requestAccept(account: MultiAccountData): MultiAccountData { + suspend fun requestAccept(account: MultiAccountData): Result, MultiMError> { return getImpl(account) { requestAccept(it) } } - suspend fun requestReject(account: MultiAccountData): MultiAccountData { + suspend fun requestReject(account: MultiAccountData): Result, MultiMError> { return getImpl(account) { requestReject(it) } } private suspend fun getImpl( apiData: MultiAccountData, - callback: suspend AccountApi.(T) -> R - ): MultiAccountData = - MultiAccountDataImpl(callback(accountApi(apiData), apiData.innerData), apiData.hashCode) + callback: suspend AccountApi.(T) -> Result + ): Result, MultiMError> { + return accountApi(apiData) + .flatMap { callback(it, apiData.innerData) } + .map { MultiAccountDataImpl(it, apiData.hashCode) } + } private suspend fun getImpl2( apiData: T, callback: suspend AccountApi.(T) -> R - ): Pair { - return callback(accountApi(apiData), apiData) to ( - (apiData as? MultiAccountData<*>)?.hashCode + ): Result, MultiMError> { + return accountApi(apiData) + .map { callback(it, apiData) } + .map { + it to ((apiData as? MultiAccountData<*>)?.hashCode ?: multiAccountApiBase.mainClientHashCode!!) + } } - private fun accountApi(id: T): AccountApi { - return multiAccountApiBase.getImpl((id as? MultiAccountData<*>)?.hashCode).accountApi + private fun accountApi(id: T): Result { + return multiAccountApiBase.getImpl((id as? MultiAccountData<*>)?.hashCode) + .map { it.accountApi } } } diff --git a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountApiBase.kt b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountApiBase.kt index 95f6446..1020f52 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountApiBase.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountApiBase.kt @@ -1,8 +1,13 @@ package dev.usbharu.multim.multi +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.map +import com.github.michaelbull.result.toResultOr import dev.usbharu.multim.MultiM import dev.usbharu.multim.ServiceInfo import dev.usbharu.multim.api.createHttpClient +import dev.usbharu.multim.error.ErrorType +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.factory.MultiMApis import dev.usbharu.multim.factory.ServiceInfoFactory @@ -15,26 +20,31 @@ class MultiAccountApiBase(val serviceList: List) { val apiClientMap = mutableMapOf() var mainClientHashCode: Int? = null - suspend fun addAccount(url: String, token: String): Int { + suspend fun addAccount(url: String, token: String): Result { val hashCode = (url + token).hashCode() - apiClientMap[hashCode] = - MultiM.createClient(url, token, factory, httpClient) - if (mainClientHashCode == null) { - mainClientHashCode = hashCode + return MultiM.createClient(url, token, factory, httpClient).map { + apiClientMap[hashCode] = it + }.map { + if (mainClientHashCode == null) { + mainClientHashCode = hashCode + } + hashCode } - return hashCode } - suspend fun addMainAccount(url: String, token: String): Int { - mainClientHashCode = - addAccount(url, token) - return mainClientHashCode as Int + suspend fun addMainAccount(url: String, token: String): Result { + + return addAccount(url, token).map { mainClientHashCode = it;it } } - fun getImpl(hashCode: Int? = mainClientHashCode): MultiMApis { + fun getImpl(hashCode: Int? = mainClientHashCode): Result { if (hashCode == null) { - return apiClientMap[mainClientHashCode]!! + return apiClientMap[mainClientHashCode].toResultOr { + MultiMError("Main Client not found : $mainClientHashCode",null, ErrorType.ILLEGAL_ARGUMENT) + } + } + return apiClientMap[hashCode].toResultOr { + MultiMError("Api Client not found : $hashCode",null,ErrorType.ILLEGAL_ARGUMENT) } - return apiClientMap[hashCode]!! } } diff --git a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountStatusApi.kt b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountStatusApi.kt index 100ec3a..951a821 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountStatusApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountStatusApi.kt @@ -1,142 +1,167 @@ package dev.usbharu.multim.multi +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.flatMap +import com.github.michaelbull.result.map import dev.usbharu.multim.api.StatusApi +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.model.* import dev.usbharu.multim.multi.model.MultiAccountReaction import dev.usbharu.multim.multi.model.MultiAccountStatus class MultiAccountStatusApi(private val multiAccountApiBase: MultiAccountApiBase) : StatusApi { - suspend fun post(status: MultiAccountData): MultiAccountData = + suspend fun post(status: MultiAccountData): Result, MultiMError> = getImpl(status) { post(it) } - suspend fun delete(id: MultiAccountData): MultiAccountData = + suspend fun delete(id: MultiAccountData): Result, MultiMError> = getImpl(id) { delete(it) } - suspend fun findById(id: MultiAccountData): MultiAccountData = + suspend fun findById(id: MultiAccountData): Result, MultiMError> = getImpl(id) { findById(it) } suspend fun addReaction( - id: MultiAccountData, - reaction: MultiAccountData - ): MultiAccountData = getImpl(id) { addReaction(it, reaction.innerData) } + id: MultiAccountData, reaction: MultiAccountData + ): Result, MultiMError> = + getImpl(id) { addReaction(it, reaction.innerData) } suspend fun removeReaction( - id: MultiAccountData, - reaction: MultiAccountData - ): MultiAccountData = getImpl(id) { removeReaction(it, reaction.innerData) } + id: MultiAccountData, reaction: MultiAccountData + ): Result, MultiMError> = + getImpl(id) { removeReaction(it, reaction.innerData) } - suspend fun reactions(id: MultiAccountData): MultiAccountData> = + suspend fun reactions(id: MultiAccountData): Result>, MultiMError> = getImpl(id) { reactions(it) } - suspend fun replies(id: MultiAccountData): MultiAccountData> = + suspend fun replies(id: MultiAccountData): Result>, MultiMError> = getImpl(id) { replies(it) } - suspend fun repost(id: MultiAccountData): MultiAccountData = + suspend fun repost(id: MultiAccountData): Result, MultiMError> = getImpl(id) { repost(it) } - suspend fun unRepost(id: MultiAccountData): MultiAccountData = + suspend fun unRepost(id: MultiAccountData): Result, MultiMError> = getImpl(id) { unRepost(it) } suspend fun replyTo( - id: MultiAccountData, - status: MultiAccountData - ): MultiAccountData = getImpl(id) { replyTo(it, status.innerData) } + id: MultiAccountData, status: MultiAccountData + ): Result, MultiMError> = getImpl(id) { replyTo(it, status.innerData) } - suspend fun addToBookmarks(id: MultiAccountData): MultiAccountData = + suspend fun addToBookmarks(id: MultiAccountData): Result, MultiMError> = getImpl(id) { addToBookmarks(it) } - suspend fun removeFromBookmarks(id: MultiAccountData): MultiAccountData = + suspend fun removeFromBookmarks(id: MultiAccountData): Result, MultiMError> = getImpl(id) { removeFromBookmarks(it) } - suspend fun getPreviousAndNext(id: MultiAccountData): MultiAccountData = + suspend fun getPreviousAndNext(id: MultiAccountData): Result, MultiMError> = getImpl(id) { getPreviousAndNext(it) } private suspend fun getImpl( - apiData: MultiAccountData, - callback: suspend StatusApi.(T) -> R - ): MultiAccountData = - MultiAccountDataImpl(callback(statusApi(apiData), apiData.innerData), apiData.hashCode) + apiData: MultiAccountData, callback: suspend StatusApi.(T) -> Result + ): Result, MultiMError> { + return statusApi(apiData).flatMap { callback(it, apiData.innerData) } + .map { MultiAccountDataImpl(it, apiData.hashCode) } + } private suspend fun getImpl2( - apiData: T, - callback: suspend StatusApi.(T) -> R - ): Pair { - return callback(statusApi(apiData), apiData) to ( - (apiData as? MultiAccountData<*>)?.hashCode - ?: multiAccountApiBase.mainClientHashCode!! - ) + apiData: T, callback: suspend StatusApi.(T) -> R + ): Result, MultiMError> { + return statusApi(apiData).map { + callback( + it, apiData + ) to ((apiData as? MultiAccountData<*>)?.hashCode + ?: multiAccountApiBase.mainClientHashCode!!) + } } - private fun statusApi(id: T) = - multiAccountApiBase.getImpl((id as? MultiAccountData<*>)?.hashCode).statusApi + private fun statusApi(id: T): Result { + return multiAccountApiBase.getImpl((id as? MultiAccountData<*>)?.hashCode) + .map { it.statusApi } + } + + + override suspend fun post(status: StatusForPost): Result { + return getImpl2(status) { post(it) }.flatMap { + it.first.map { iStatus -> + (iStatus to it.second).toMultiAccount() + } + } - override suspend fun post(status: StatusForPost): Status { - return getImpl2(status) { post(it) }.toMultiAccount() } - override suspend fun delete(id: StatusId): Boolean { - return getImpl2(id) { delete(it) }.first + override suspend fun delete(id: StatusId): Result { + return getImpl2(id) { delete(it) }.map { it.first } } - override suspend fun findById(id: StatusId): Status { - return getImpl2(id) { findById(it) }.toMultiAccount() + override suspend fun findById(id: StatusId): Result { + return getImpl2(id) { findById(it) }.flatMap { it.first.map { iStatus -> (iStatus to it.second).toMultiAccount() } } } - override suspend fun addReaction(id: StatusId, reaction: Reaction): Boolean { - return getImpl2(id) { addReaction(it, reaction) }.first + override suspend fun addReaction(id: StatusId, reaction: Reaction): Result { + return getImpl2(id) { addReaction(it, reaction) }.map { it.first } } - override suspend fun removeReaction(id: StatusId, reaction: Reaction?): Boolean { - return getImpl2(id) { removeReaction(it, reaction) }.first + override suspend fun removeReaction( + id: StatusId, reaction: Reaction? + ): Result { + return getImpl2(id) { removeReaction(it, reaction) }.map { it.first } } - override suspend fun reactions(id: StatusId): Map { + override suspend fun reactions(id: StatusId): Result, MultiMError> { return getImpl2(id) { reactions(it) }.let { - it.first.map { (key, value) -> - MultiAccountReaction( - key, - it.second - ) to value - }.toMap() + it.flatMap { + it.first.map { map -> + map.map { (key, value) -> + MultiAccountReaction( + key, it.second + ) to value + }.toMap() + } + } } } - override suspend fun replies(id: StatusId): List { - return getImpl2(id) { replies(it) }.let { - it.first.map { status -> - MultiAccountStatus( - status, - it.second - ) + override suspend fun replies(id: StatusId): Result, MultiMError> { + return getImpl2(id) { replies(it) }.flatMap { + it.first.map { statusList -> + statusList.map { status -> + MultiAccountStatus( + status, it.second + ) + } } } } - override suspend fun repost(id: StatusId): Status { - return getImpl2(id) { repost(it) }.toMultiAccount() + override suspend fun repost(id: StatusId): Result { + return getImpl2(id) { repost(it) }.flatMap { it.first.map { iStatus -> (iStatus to it.second).toMultiAccount() } } } - override suspend fun unRepost(id: StatusId): Boolean { - return getImpl2(id) { unRepost(it) }.first + override suspend fun unRepost(id: StatusId): Result { + return getImpl2(id) { unRepost(it) }.map { it.first } } - override suspend fun replyTo(id: StatusId, status: StatusForPost): Status { - return getImpl2(status) { replyTo(id, it) }.toMultiAccount() + override suspend fun replyTo(id: StatusId, status: StatusForPost): Result { + return getImpl2(status) { + replyTo( + id, it + ) + }.flatMap { it.first.map { iStatus -> (iStatus to it.second).toMultiAccount() } } } - override suspend fun addToBookmarks(id: StatusId): Boolean { - return getImpl2(id) { addToBookmarks(it) }.first + override suspend fun addToBookmarks(id: StatusId): Result { + return getImpl2(id) { addToBookmarks(it) }.map { it.first } } - override suspend fun removeFromBookmarks(id: StatusId): Boolean { - return getImpl2(id) { removeFromBookmarks(it) }.first + override suspend fun removeFromBookmarks(id: StatusId): Result { + return getImpl2(id) { removeFromBookmarks(it) }.map { it.first } } - override suspend fun getPreviousAndNext(id: StatusId): PreviousAndNextPosts { + override suspend fun getPreviousAndNext(id: StatusId): Result { TODO("Not yet implemented") } private fun Pair.toMultiAccount(): MultiAccountStatus { return MultiAccountStatus(first, second) } + + } diff --git a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountTimelineApi.kt b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountTimelineApi.kt index fe6f6f7..652bd50 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountTimelineApi.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/multi/MultiAccountTimelineApi.kt @@ -1,76 +1,90 @@ package dev.usbharu.multim.multi +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.flatMap +import com.github.michaelbull.result.map import dev.usbharu.multim.api.TimelineApi +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.model.Status import dev.usbharu.multim.model.Timeline import dev.usbharu.multim.multi.model.MultiAccountStatus class MultiAccountTimelineApi(private val multiAccountApiBase: MultiAccountApiBase) : TimelineApi { - suspend fun availableTimelines(id: MultiAccountData<*>): MultiAccountData> { + suspend fun availableTimelines(id: MultiAccountData<*>): Result>, MultiMError> { return getImpl(id) { availableTimelines() } } - suspend fun availableTimelines(hashCode: Int): MultiAccountData> { - return MultiAccountDataImpl>( - multiAccountApiBase.getImpl(hashCode).timelineApi.availableTimelines(), - hashCode - ) + suspend fun availableTimelines(hashCode: Int): Result>, MultiMError> { + return multiAccountApiBase.getImpl(hashCode) + .flatMap { it.timelineApi.availableTimelines() } + .map { MultiAccountDataImpl(it, hashCode) } } @Suppress("UNCHECKED_CAST") suspend fun listen( timeline: MultiAccountData, callback: MultiAccountData<(List) -> Unit> - ): MultiAccountData { + ): Result, MultiMError> { return getImpl(timeline) { listen(it, callback.innerData as (List) -> Unit) } } - suspend fun connect(timeline: MultiAccountData): MultiAccountData { + suspend fun connect(timeline: MultiAccountData): Result, MultiMError> { return getImpl(timeline) { connect(it) } } suspend fun disconnect( timeline: MultiAccountData, force: Boolean = false - ): MultiAccountData { + ): Result, MultiMError> { return getImpl(timeline) { disconnect(it, force) } } private suspend fun getImpl( apiData: MultiAccountData, - callback: suspend TimelineApi.(T) -> R - ): MultiAccountData = - MultiAccountDataImpl(callback(timelineApi(apiData), apiData.innerData), apiData.hashCode) + callback: suspend TimelineApi.(T) -> Result + ): Result, MultiMError> { + return timelineApi(apiData) + .flatMap { callback(it, apiData.innerData) } + .map { MultiAccountDataImpl(it, apiData.hashCode) } + } private suspend fun getImpl2( apiData: T, callback: suspend TimelineApi.(T) -> R - ): Pair { - return callback(timelineApi(apiData), apiData) to ( - (apiData as? MultiAccountData<*>)?.hashCode - ?: multiAccountApiBase.mainClientHashCode!!) + ): Result, MultiMError> { + val timelineApi = timelineApi(apiData) + return timelineApi.map { + callback(it, apiData) to ( + (apiData as? MultiAccountData<*>)?.hashCode + ?: multiAccountApiBase.mainClientHashCode!!) + } } - private fun timelineApi(id: T) = - multiAccountApiBase.getImpl((id as? MultiAccountData<*>)?.hashCode).timelineApi + private fun timelineApi(id: T): Result { + return multiAccountApiBase.getImpl((id as? MultiAccountData<*>)?.hashCode) + .map { it.timelineApi } + } - override suspend fun availableTimelines(): List { + override suspend fun availableTimelines(): Result, MultiMError> { TODO("Not yet implemented") } - override suspend fun listen(timeline: Timeline, callback: (List) -> Unit) { - return getImpl2(timeline) { listen(it, callback) }.first + override suspend fun listen( + timeline: Timeline, + callback: (List) -> Unit + ): Result { + return getImpl2(timeline) { listen(it, callback) }.map { it.first } } - override suspend fun connect(timeline: Timeline): Boolean { - return getImpl2(timeline){connect(it)}.first + override suspend fun connect(timeline: Timeline): Result { + return getImpl2(timeline) { connect(it) }.map { it.first } } - override suspend fun disconnect(timeline: Timeline, force: Boolean): Boolean { - return getImpl2(timeline){disconnect(it,force)}.first + override suspend fun disconnect(timeline: Timeline, force: Boolean): Result { + return getImpl2(timeline) { disconnect(it, force) }.map { it.first } } } From 67011b795e0847c8bf986f76bb1827be14d708ca Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:18:14 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20ApiClient=E3=81=AE=E5=9F=BA?= =?UTF-8?q?=E5=BA=95=E3=82=AF=E3=83=A9=E3=82=B9=E3=81=AEAPI=E3=81=AE?= =?UTF-8?q?=E8=BF=94=E3=82=8A=E5=80=A4=E3=81=AE=E5=9E=8B=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E3=80=81API=E5=90=8D=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/dev/usbharu/multim/api/ApiClient.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt b/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt index 8e3de5c..e9f5fa1 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt @@ -18,10 +18,10 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) { return createHttpClient(config) } - suspend inline fun post( + suspend inline fun postEmpty( path: String, baseUrl: String = this.baseUrl - ): Result { + ): Result { val post = try { client.post(baseUrl + path) } catch (e: ClientRequestException) { @@ -38,7 +38,7 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) { content: T, path: String, baseUrl: String = this.baseUrl - ): Result { + ): Result { val post = try { client.post(baseUrl + path) { @@ -59,7 +59,7 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) { content: T, path: String, baseUrl: String = this.baseUrl - ): Result { + ): Result { return runCatching { client.post(baseUrl + path) { contentType(ContentType.Application.Json) @@ -71,7 +71,7 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) { suspend fun get( path: String, block: HttpRequestBuilder.() -> Unit - ): Result { + ): Result { return runCatching { client.get( baseUrl + path, From dce969dc5353111ca7069d36a8dfebd6207a3807 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:19:09 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20Misskey=20v12=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=E3=81=AEAPI=E3=82=92Result=E3=81=AB=E7=A7=BB=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/multim/error/MultiMError.kt | 31 ++++ .../dev/usbharu/multim/misskey/v12/api/Ap.kt | 15 +- .../usbharu/multim/misskey/v12/api/Drive.kt | 87 +++++---- .../multim/misskey/v12/api/Following.kt | 45 +++-- .../usbharu/multim/misskey/v12/api/Miauth.kt | 14 +- .../usbharu/multim/misskey/v12/api/Notes.kt | 165 +++++++++++------- .../usbharu/multim/misskey/v12/api/Users.kt | 15 +- 7 files changed, 251 insertions(+), 121 deletions(-) diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt index 411a341..41121d0 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt @@ -1,5 +1,8 @@ package dev.usbharu.multim.error +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result import io.ktor.client.plugins.* open class MultiMError( @@ -36,6 +39,7 @@ class MultiMHttpError(val httpError: HttpError, throwable: Throwable? = httpErro e ) ) + constructor(httpClientClientError: HttpClientClientError) : this( (httpClientClientError.throwable as ClientRequestException).let { HttpError.ClientError( @@ -97,3 +101,30 @@ enum class ErrorType(val message: String) { ILLEGAL_ARGUMENT("Illegal Argument"), UNKNOWN("UNKNOWN") } + +fun Result.mapMultiMError(): Result { + return when (this) { + is Ok -> this + is Err -> { + when (this.error) { + is HttpClientServerError -> { + Err(MultiMHttpError(this.error.throwable as ServerResponseException)) + } + + is HttpClientClientError -> { + Err(MultiMHttpError(this.error.throwable as ClientRequestException)) + } + + else -> { + Err(MultiMError(this.error.message, this.error.throwable, ErrorType.UNKNOWN)) + } + } + } + } +} + +fun mapMultiMError(result: Result): Result { + return result.mapMultiMError() +} + +typealias MultiMResult = Result diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Ap.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Ap.kt index 1c9c542..6cd3867 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Ap.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Ap.kt @@ -1,11 +1,22 @@ package dev.usbharu.multim.misskey.v12.api +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.mapError +import dev.usbharu.multim.error.* import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.ApShowRequest import dev.usbharu.multim.misskey.v12.model.ApShowResponse +import io.ktor.client.plugins.* class Ap(val client: MisskeyApiClient) { - suspend fun show(apShowRequest: ApShowRequest): ApShowResponse { - return client.post(apShowRequest, "api/ap/show") + suspend fun show(apShowRequest: ApShowRequest): Result { + return client.post(apShowRequest, "api/ap/show").mapError { + val multiMError: MultiMError = when (it) { + is HttpClientServerError -> MultiMHttpError(it.throwable as ServerResponseException) + is HttpClientClientError -> MultiMHttpError(it.throwable as ClientRequestException) + else -> MultiMError("Api Client Error", it.throwable, ErrorType.UNKNOWN) + } + multiMError + } } } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt index 7c52081..40f3d48 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt @@ -1,5 +1,8 @@ package dev.usbharu.multim.misskey.v12.api +import com.github.michaelbull.result.Result +import dev.usbharu.multim.error.MultiMError +import dev.usbharu.multim.error.mapMultiMError import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.* import io.ktor.client.call.* @@ -8,82 +11,104 @@ import io.ktor.http.* class Drive(val client: MisskeyApiClient) { - suspend fun drive(): DriveResponse { - return client.post("api/drive") + suspend fun drive(): Result { + return client.postEmpty("api/drive").mapMultiMError() } - suspend fun files(filesRequest: DriveFilesRequest): DriveFilesResponse { - return client.post(filesRequest, "api/drive/files") + suspend fun files(filesRequest: DriveFilesRequest): Result { + return client.post(filesRequest, "api/drive/files") + .mapMultiMError() } - suspend fun folders(foldersRequest: DriveFoldersRequest): DriveFoldersResponse { - return client.post(foldersRequest, "api/drive/folders") + suspend fun folders(foldersRequest: DriveFoldersRequest): Result { + return client.post( + foldersRequest, + "api/drive/folders" + ).mapMultiMError() } inner class Files { - suspend fun attachedNotes(attachedNotesRequest: DriveFilesAttachedNotesRequest): DriveFilesAttachedNotesResponse { - return client.post(attachedNotesRequest, "api/drive/files/attached-notes") + suspend fun attachedNotes(attachedNotesRequest: DriveFilesAttachedNotesRequest): Result { + return client.post( + attachedNotesRequest, + "api/drive/files/attached-notes" + ).mapMultiMError() } - suspend fun checkExistence(checkExistenceRequest: DriveFilesCheckExistenceRequest): DriveFilesCheckExistenceResponse { - return client.post(checkExistenceRequest, "api/drive/files/check-existence") + suspend fun checkExistence(checkExistenceRequest: DriveFilesCheckExistenceRequest): Result { + return client.post( + checkExistenceRequest, + "api/drive/files/check-existence" + ).mapMultiMError() } + //TODO Result型対応 suspend fun create(createRequest: DriveFilesCreateRequest): DriveFilesCreateResponse { return client.client.submitFormWithBinaryData( client.baseUrl + "api/drive/files/create", formData = formData { - append("\"i\"",client.token) - append("\"file\"",createRequest.file, Headers.build { - append(HttpHeaders.ContentDisposition,"filename=${createRequest.name}") + append("\"i\"", client.token) + append("\"file\"", createRequest.file, Headers.build { + append(HttpHeaders.ContentDisposition, "filename=${createRequest.name}") }) }).body() // return client.post(createRequest, "api/drive/files/create") } - suspend fun delete(deleteRequest: DriveFilesDeleteRequest) { + suspend fun delete(deleteRequest: DriveFilesDeleteRequest): Result { return client.postWithoutResponse(deleteRequest, "api/drive/files/delete") + .mapMultiMError() } - suspend fun findByHash(findByHashRequest: DriveFilesFindByHashRequest): DriveFilesFindByHashResponse { - return client.post(findByHashRequest, "api/drive/files/find-by-hash") + suspend fun findByHash(findByHashRequest: DriveFilesFindByHashRequest): Result { + return mapMultiMError(client.post(findByHashRequest, "api/drive/files/find-by-hash")) } - suspend fun find(findRequest: DriveFilesFindRequest): DriveFilesFindResponse { - return client.post(findRequest, "api/drive/files/find") + suspend fun find(findRequest: DriveFilesFindRequest): Result { + return mapMultiMError(client.post(findRequest, "api/drive/files/find")) } - suspend fun show(showRequest: DriveFilesShowRequestByUrl): DriveFilesShowResponse { - return client.post(showRequest, "api/drive/files/show") + suspend fun show(showRequest: DriveFilesShowRequestByUrl): Result { + return mapMultiMError(client.post(showRequest, "api/drive/files/show")) } - suspend fun show(showRequest: DriveFilesShowRequestByFileId): DriveFilesShowResponse { - return client.post(showRequest, "api/drive/files/show") + suspend fun show(showRequest: DriveFilesShowRequestByFileId): Result { + return client.post( + showRequest, + "api/drive/files/show" + ).mapMultiMError() } - suspend fun update(updateRequest: DriveFilesUpdateRequest): DriveFilesUpdateResponse { - return client.post(updateRequest, "api/drive/files/update") + suspend fun update(updateRequest: DriveFilesUpdateRequest): Result { + return client.post( + updateRequest, + "api/drive/files/update" + ).mapMultiMError() } - suspend fun uploadFromUrl(uploadFromUrlRequest: DriveFilesUploadFromUrlRequest) { + suspend fun uploadFromUrl(uploadFromUrlRequest: DriveFilesUploadFromUrlRequest): Result { return client.postWithoutResponse( uploadFromUrlRequest, "api/drive/files/upload-from-url" - ) + ).mapMultiMError() } } inner class Folders { - suspend fun create(foldersCreateRequest: DriveFoldersCreateRequest): DriveFoldersCreateResponse { - return client.post(foldersCreateRequest, "api/drive/folders/create") + suspend fun create(foldersCreateRequest: DriveFoldersCreateRequest): Result { + return client.post( + foldersCreateRequest, + "api/drive/folders/create" + ).mapMultiMError() } - suspend fun delete(foldersDeleteRequest: DriveFoldersDeleteRequest) { + suspend fun delete(foldersDeleteRequest: DriveFoldersDeleteRequest): Result { return client.postWithoutResponse(foldersDeleteRequest, "api/drive/folders/delete") + .mapMultiMError() } - suspend fun find(foldersFindRequest: DriveFoldersFindRequest): DriveFoldersFindResponse { - return client.post(foldersFindRequest, "api/drive/folders/find") + suspend fun find(foldersFindRequest: DriveFoldersFindRequest): Result { + return client.post(foldersFindRequest, "api/drive/folders/find").mapMultiMError() } } } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Following.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Following.kt index a35a245..c27680e 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Following.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Following.kt @@ -1,45 +1,58 @@ package dev.usbharu.multim.misskey.v12.api +import com.github.michaelbull.result.Result +import dev.usbharu.multim.error.MultiMError +import dev.usbharu.multim.error.mapMultiMError import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.* class Following(val client: MisskeyApiClient) { - suspend fun create(followingCreateRequest: FollowingCreateRequest): FollowingCreateResponse { - return client.post(followingCreateRequest, "api/following/create") + suspend fun create(followingCreateRequest: FollowingCreateRequest): Result { + return client.post( + followingCreateRequest, + "api/following/create" + ).mapMultiMError() } - suspend fun delete(followingDeleteRequest: FollowingDeleteRequest): FollowingDeleteResponse { - return client.post(followingDeleteRequest, "api/following/delete") + suspend fun delete(followingDeleteRequest: FollowingDeleteRequest): Result { + return client.post( + followingDeleteRequest, + "api/following/delete" + ).mapMultiMError() } - suspend fun invalidate(followingInvalidateRequest: FollowingInvalidateRequest): FollowingInvalidateResponse { - return client.post(followingInvalidateRequest, "api/following/invalidate") + suspend fun invalidate(followingInvalidateRequest: FollowingInvalidateRequest): Result { + return client.post( + followingInvalidateRequest, + "api/following/invalidate" + ).mapMultiMError() } inner class Requests { - suspend fun accept(followingRequestsAcceptRequest: FollowingRequestsAcceptRequest) { - client.postWithoutResponse( + suspend fun accept(followingRequestsAcceptRequest: FollowingRequestsAcceptRequest): Result { + return client.postWithoutResponse( followingRequestsAcceptRequest, "api/following/requests/list" - ) + ).mapMultiMError() } - suspend fun cancel(followingRequestsCancelRequest: FollowingRequestsCancelRequest) { - client.postWithoutResponse( + suspend fun cancel(followingRequestsCancelRequest: FollowingRequestsCancelRequest): Result { + return client.postWithoutResponse( followingRequestsCancelRequest, "api/following/requests/cancel" - ) + ).mapMultiMError() } - suspend fun list(): FollowingRequestsListResponse { - return client.post("api/following/requests/list") + suspend fun list(): Result { + return client.postEmpty("api/following/requests/list") + .mapMultiMError() } - suspend fun reject(followingRequestsRejectRequest: FollowingRequestsRejectRequest) { + suspend fun reject(followingRequestsRejectRequest: FollowingRequestsRejectRequest): Result { return client.postWithoutResponse( followingRequestsRejectRequest, "api/following/requests/reject" - ) + ).mapMultiMError() } } } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Miauth.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Miauth.kt index 90fa608..6bd3187 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Miauth.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Miauth.kt @@ -1,5 +1,9 @@ package dev.usbharu.multim.misskey.v12.api +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.map +import dev.usbharu.multim.error.MultiMError +import dev.usbharu.multim.error.mapMultiMError import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.MiauthCheckRequest import dev.usbharu.multim.misskey.v12.model.MiauthCheckResponse @@ -9,7 +13,7 @@ import java.util.* class Miauth(val client: MisskeyApiClient) { - suspend fun auth(): String { + suspend fun auth(): Result { val body = client.get(client.baseUrl + "/miauth/" + UUID.randomUUID() + "/") { url { @@ -17,12 +21,12 @@ class Miauth(val client: MisskeyApiClient) { parameters.append("name", "multim-test") parameters.append("permission", "read:account,write:notes") } - }.bodyAsText() - return body + }.map { it.bodyAsText() } + return body.mapMultiMError() } - suspend fun check(params: MiauthCheckRequest): MiauthCheckResponse { - return client.post("", "/api/miauth/${params.sessionId}/check") + suspend fun check(params: MiauthCheckRequest): Result { + return client.postEmpty("", "/api/miauth/${params.sessionId}/check").mapMultiMError() } } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Notes.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Notes.kt index 5ef6657..8f941ed 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Notes.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Notes.kt @@ -1,148 +1,191 @@ package dev.usbharu.multim.misskey.v12.api +import com.github.michaelbull.result.Result +import dev.usbharu.multim.error.MultiMError +import dev.usbharu.multim.error.MultiMResult +import dev.usbharu.multim.error.mapMultiMError import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.* +import dev.usbharu.multim.misskey.v12.model.components.Note class Notes(val client: MisskeyApiClient) { - suspend fun globalTimeline(globalTimelineRequest: NotesGlobalTimelineRequest = NotesGlobalTimelineRequest()): NotesGlobalTimelineResponse { - return client.post(globalTimelineRequest, "api/notes/global-timeline") + suspend fun globalTimeline(globalTimelineRequest: NotesGlobalTimelineRequest = NotesGlobalTimelineRequest()): Result { + return client.post( + globalTimelineRequest, + "api/notes/global-timeline" + ).mapMultiMError() } - suspend fun hybridTimeline(hybridTimelineRequest: NotesHybridTimelineRequest = NotesHybridTimelineRequest()): NotesHybridTimelineResponse { - return client.post(hybridTimelineRequest, "api/notes/hybrid-timeline") + suspend fun hybridTimeline(hybridTimelineRequest: NotesHybridTimelineRequest = NotesHybridTimelineRequest()): Result { + return client.post( + hybridTimelineRequest, + "api/notes/hybrid-timeline" + ).mapMultiMError() } - suspend fun localTimeline(localTimelineRequest: NotesLocalTimelineRequest = NotesLocalTimelineRequest()): NotesLocalTimelineResponse { - return client.post(localTimelineRequest, "api/notes/local-timeline") + suspend fun localTimeline(localTimelineRequest: NotesLocalTimelineRequest = NotesLocalTimelineRequest()): Result { + return client.post( + localTimelineRequest, + "api/notes/local-timeline" + ).mapMultiMError() } - suspend fun show(showRequest: NotesShowRequest): NotesShowResponse { - return client.post(showRequest, "api/notes/show") + suspend fun show(showRequest: NotesShowRequest): Result { + return client.post(showRequest, "api/notes/show") + .mapMultiMError() } - suspend fun create(createRequest: NotesCreateRequest): NotesCreateResponse { - return client.post(createRequest, "api/notes/create") + suspend fun create(createRequest: NotesCreateRequest): Result { + return client.post( + createRequest, + "api/notes/create" + ).mapMultiMError() } suspend fun delete(deleteRequest: NotesDeleteRequest) { - client.postWithoutResponse(deleteRequest, "api/notes/delete") + client.postWithoutResponse(deleteRequest, "api/notes/delete").mapMultiMError() } - suspend fun featured(featuredRequest: NotesFeaturedRequest = NotesFeaturedRequest()): NotesFeaturedResponse { - return client.post(featuredRequest, "api/notes/featured") + suspend fun featured(featuredRequest: NotesFeaturedRequest = NotesFeaturedRequest()): Result { + return client.post( + featuredRequest, + "api/notes/featured" + ).mapMultiMError() } - suspend fun mentions(mentionsRequest: NotesMentionsRequest): NotesMentionsResponse { - return client.post(mentionsRequest, "api/notes/mentions") + suspend fun mentions(mentionsRequest: NotesMentionsRequest): Result { + return client.post( + mentionsRequest, + "api/notes/mentions" + ).mapMultiMError() } - suspend fun children(childrenRequest: NotesChildrenRequest): NotesChildrenResponse { - return client.post(childrenRequest, "api/notes/children") + suspend fun children(childrenRequest: NotesChildrenRequest): Result { + return client.post( + childrenRequest, + "api/notes/children" + ).mapMultiMError() } - suspend fun notes(notesRequest: NotesNotesRequest): NotesNotesResponse { - return client.post(notesRequest, "api/notes") + suspend fun notes(notesRequest: NotesNotesRequest): Result { + return client.post(notesRequest, "api/notes") + .mapMultiMError() } - suspend fun conversation(conversationRequest: NotesConversationRequest): NotesConversationResponse { - return client.post(conversationRequest, "api/notes/conversation") + suspend fun conversation(conversationRequest: NotesConversationRequest): Result { + return client.post( + conversationRequest, + "api/notes/conversation" + ).mapMultiMError() } - suspend fun reactions(reactionsRequest: NotesReactionsRequest): NotesReactionsResponse { - return client.post(reactionsRequest, "api/notes/reactions") + suspend fun reactions(reactionsRequest: NotesReactionsRequest): Result { + return client.post( + reactionsRequest, + "api/notes/reactions" + ).mapMultiMError() } - suspend fun renotes(renoteRequest: NotesRenoteRequest): NotesRenoteResponse { - return client.post(renoteRequest, "api/notes/renotes") + suspend fun renotes(renoteRequest: NotesRenoteRequest): Result { + return client.post( + renoteRequest, + "api/notes/renotes" + ).mapMultiMError() } - suspend fun replies(repliesRequest: NotesRepliesRequest): NotesRepliesResponse { - return client.post(repliesRequest, "api/notes/replies") + suspend fun replies(repliesRequest: NotesRepliesRequest): Result { + return client.post( + repliesRequest, + "api/notes/replies" + ).mapMultiMError() } - suspend fun searchByTag(searchByTagRequest: NotesSearchByTagRequest): NotesSearchByTagResponse { - return client.post(searchByTagRequest, "api/notes/search-by-tag") + suspend fun searchByTag(searchByTagRequest: NotesSearchByTagRequest): Result { + return client.post( + searchByTagRequest, + "api/notes/search-by-tag" + ).mapMultiMError() } - suspend fun search(searchRequest: NotesSearchRequest): NotesSearchResponse { - return client.post(searchRequest, "api/notes/search") + suspend fun search(searchRequest: NotesSearchRequest): Result { + return client.post(searchRequest, "api/notes/search").mapMultiMError() } - suspend fun state(stateRequest: NotesStateRequest): NotesStateResponse { - return client.post(stateRequest, "api/notes/state") + suspend fun state(stateRequest: NotesStateRequest): Result { + return client.post(stateRequest, "api/notes/state").mapMultiMError() } - suspend fun timeline(timelineRequest: NotesTimelineRequest): NotesTimelineResponse { - return client.post(timelineRequest, "api/notes/timeline") + suspend fun timeline(timelineRequest: NotesTimelineRequest): Result { + return client.post(timelineRequest, "api/notes/timeline").mapMultiMError() } - suspend fun translate(translateRequest: NotesTranslateRequest): NotesTranslateResponse { - return client.post(translateRequest, "api/notes/translate") + suspend fun translate(translateRequest: NotesTranslateRequest): MultiMResult { + return client.post(translateRequest, "api/notes/translate").mapMultiMError() } - suspend fun unrenote(unrenoteRequest: NotesUnrenoteRequest) { - return client.postWithoutResponse(unrenoteRequest, "api/notes/unrenote") + suspend fun unrenote(unrenoteRequest: NotesUnrenoteRequest): Result { + return client.postWithoutResponse(unrenoteRequest, "api/notes/unrenote").mapMultiMError() } - suspend fun userListTimeline(userListTimelineRequest: NotesUserListTimelineRequest): NotesUserListTimelineResponse { - return client.post(userListTimelineRequest, "api/notes/user-list-timeline") + suspend fun userListTimeline(userListTimelineRequest: NotesUserListTimelineRequest): MultiMResult { + return client.post>(userListTimelineRequest, "api/notes/user-list-timeline").mapMultiMError() } inner class Polls { - suspend fun recommendation(pollsRecommendationRequest: PollsRecommendationRequest): PollsRecommendationResponse { - return client.post(pollsRecommendationRequest, "api/notes/polls/recommendation") + suspend fun recommendation(pollsRecommendationRequest: PollsRecommendationRequest): MultiMResult { + return client.post>(pollsRecommendationRequest, "api/notes/polls/recommendation").mapMultiMError() } - suspend fun vote(voteRequest: NotesPollsVoteRequest): NotesPollsVoteRequest { - return client.post(voteRequest, "api/notes/polls/vote") + suspend fun vote(voteRequest: NotesPollsVoteRequest): MultiMResult { + return client.post(voteRequest, "api/notes/polls/vote").mapMultiMError() } } inner class ThreadMuting { - suspend fun create(threadMutingCreateRequest: NotesThreadMutingCreateRequest) { + suspend fun create(threadMutingCreateRequest: NotesThreadMutingCreateRequest): Result { return client.postWithoutResponse( threadMutingCreateRequest, "api/notes/thread-muting/create" - ) + ).mapMultiMError() } - suspend fun delete(threadMutingDeleteRequest: NotesThreadMutingDeleteRequest) { + suspend fun delete(threadMutingDeleteRequest: NotesThreadMutingDeleteRequest): Result { return client.postWithoutResponse( threadMutingDeleteRequest, "api/notes/thread-muting/delete" - ) + ).mapMultiMError() } } inner class Watching { - suspend fun create(watchingCreateRequest: NotesWatchingCreateRequest) { - return client.postWithoutResponse(watchingCreateRequest, "api/notes/watching/create") + suspend fun create(watchingCreateRequest: NotesWatchingCreateRequest): Result { + return client.postWithoutResponse(watchingCreateRequest, "api/notes/watching/create").mapMultiMError() } - suspend fun delete(watchingDeleteRequest: NotesWatchingDeleteRequest) { - return client.postWithoutResponse(watchingDeleteRequest, "api/notes/watching/delete") + suspend fun delete(watchingDeleteRequest: NotesWatchingDeleteRequest): Result { + return client.postWithoutResponse(watchingDeleteRequest, "api/notes/watching/delete").mapMultiMError() } } inner class Favorites { - suspend fun create(favoritesCreateRequest: NotesFavoritesCreateRequest) { - return client.postWithoutResponse(favoritesCreateRequest, "api/notes/favorites/create") + suspend fun create(favoritesCreateRequest: NotesFavoritesCreateRequest): Result { + return client.postWithoutResponse(favoritesCreateRequest, "api/notes/favorites/create").mapMultiMError() } - suspend fun delete(favoritesDeleteRequest: NotesFavoritesDeleteRequest) { - return client.postWithoutResponse(favoritesDeleteRequest, "api/notes/favorites/delete") + suspend fun delete(favoritesDeleteRequest: NotesFavoritesDeleteRequest): Result { + return client.postWithoutResponse(favoritesDeleteRequest, "api/notes/favorites/delete").mapMultiMError() } } inner class Reaction { - suspend fun create(reactionCreateRequest: NotesReactionCreateRequest) { - return client.postWithoutResponse(reactionCreateRequest, "api/notes/reactions/create") + suspend fun create(reactionCreateRequest: NotesReactionCreateRequest): Result { + return client.postWithoutResponse(reactionCreateRequest, "api/notes/reactions/create").mapMultiMError() } - suspend fun delete(reactionDeleteRequest: NotesReactionDeleteRequest) { - return client.postWithoutResponse(reactionDeleteRequest, "api/notes/reactions/delete") + suspend fun delete(reactionDeleteRequest: NotesReactionDeleteRequest): Result { + return client.postWithoutResponse(reactionDeleteRequest, "api/notes/reactions/delete").mapMultiMError() } } } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Users.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Users.kt index 9f2a34e..51d721b 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Users.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Users.kt @@ -1,18 +1,21 @@ package dev.usbharu.multim.misskey.v12.api +import dev.usbharu.multim.error.MultiMResult +import dev.usbharu.multim.error.mapMultiMError import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.* +import dev.usbharu.multim.misskey.v12.model.components.Note class Users(val client: MisskeyApiClient) { - suspend fun relation(relationRequest: UsersRelationRequest): UsersRelationResponse { - return client.post(relationRequest, "api/users/relation") + suspend fun relation(relationRequest: UsersRelationRequest): MultiMResult { + return client.post(relationRequest, "api/users/relation").mapMultiMError() } - suspend fun notes(notesRequest: UsersNotesRequest): UsersNotesResponse { - return client.post(notesRequest, "api/users/notes") + suspend fun notes(notesRequest: UsersNotesRequest): MultiMResult { + return client.post>(notesRequest, "api/users/notes").mapMultiMError() } - suspend fun show(showRequest: UsersShowRequest): UsersShowResponse { - return client.post(showRequest, "api/users/show") + suspend fun show(showRequest: UsersShowRequest): MultiMResult { + return client.post(showRequest, "api/users/show").mapMultiMError() } } From 6e05e44fd6685025e695c88f3e06b7c252e9b9ee Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 15 Feb 2023 13:00:19 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E5=85=B1=E9=80=9AAPI=E3=80=81Missk?= =?UTF-8?q?ey=E5=AE=9F=E8=A3=85=E3=82=92Result=E3=81=AB=E7=A7=BB=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/multim/error/MultiMError.kt | 9 +- .../usbharu/multim/misskey/v12/api/Drive.kt | 6 +- .../v12/common/api/MisskeyAccountApi.kt | 115 +++++++++++------- .../v12/common/api/MisskeyStatusApi.kt | 107 +++++++++------- .../v12/common/api/MisskeyTimelineApi.kt | 20 +-- 5 files changed, 149 insertions(+), 108 deletions(-) diff --git a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt index 41121d0..e7091fb 100644 --- a/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt +++ b/core/src/main/kotlin/dev/usbharu/multim/error/MultiMError.kt @@ -99,6 +99,7 @@ enum class ErrorType(val message: String) { HTTP("HTTP"), CONTENT_TRANSFORM("Content Transform"), ILLEGAL_ARGUMENT("Illegal Argument"), + NOT_IMPL("Not yet implement"), UNKNOWN("UNKNOWN") } @@ -123,8 +124,8 @@ fun Result.mapMultiMError(): Result { } } -fun mapMultiMError(result: Result): Result { - return result.mapMultiMError() -} - typealias MultiMResult = Result + +fun TODO(throwable: Throwable? = runCatching { kotlin.TODO() }.exceptionOrNull()): Err { + return Err(MultiMError("Not yet Implement.",throwable,ErrorType.NOT_IMPL)) +} diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt index 40f3d48..fd94a9a 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/api/Drive.kt @@ -61,15 +61,15 @@ class Drive(val client: MisskeyApiClient) { } suspend fun findByHash(findByHashRequest: DriveFilesFindByHashRequest): Result { - return mapMultiMError(client.post(findByHashRequest, "api/drive/files/find-by-hash")) + return client.post(findByHashRequest, "api/drive/files/find-by-hash").mapMultiMError() } suspend fun find(findRequest: DriveFilesFindRequest): Result { - return mapMultiMError(client.post(findRequest, "api/drive/files/find")) + return client.post(findRequest, "api/drive/files/find").mapMultiMError() } suspend fun show(showRequest: DriveFilesShowRequestByUrl): Result { - return mapMultiMError(client.post(showRequest, "api/drive/files/show")) + return client.post(showRequest, "api/drive/files/show").mapMultiMError() } suspend fun show(showRequest: DriveFilesShowRequestByFileId): Result { diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyAccountApi.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyAccountApi.kt index 503ed2c..0b9cb0e 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyAccountApi.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyAccountApi.kt @@ -1,6 +1,13 @@ package dev.usbharu.multim.misskey.v12.common.api +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.map import dev.usbharu.multim.api.AccountApi +import dev.usbharu.multim.error.ErrorType +import dev.usbharu.multim.error.MultiMError +import dev.usbharu.multim.error.TODO import dev.usbharu.multim.misskey.v12.api.MisskeyApis import dev.usbharu.multim.misskey.v12.common.* import dev.usbharu.multim.misskey.v12.converter.misskey.v12.NoteConverter.toStatus @@ -16,7 +23,7 @@ class MisskeyAccountApi(val misskeyApis: MisskeyApis) : AccountApi { account: Account, since: StatusId?, until: StatusId? - ): List { + ): Result, MultiMError> { if (account is MisskeyAccount && since is MisskeyStatusId? && until is MisskeyStatusId?) { return misskeyApis.users.notes( UsersNotesRequest( @@ -24,101 +31,115 @@ class MisskeyAccountApi(val misskeyApis: MisskeyApis) : AccountApi { sinceId = since?.id, untilId = until?.id ) - ).map { it.toStatus() } + ).map { + it.map { note -> + note.toStatus() + } + } } else { - TODO() + return TODO() } } - override suspend fun follow(account: Account): Boolean { - if (account is MisskeyAccount) { + override suspend fun follow(account: Account): Result { + return if (account is MisskeyAccount) { misskeyApis.following.create(FollowingCreateRequest(account.id)) - return true + Ok(Unit) } else { TODO() } } - override suspend fun unfollow(account: Account): Boolean { + override suspend fun unfollow(account: Account): Result { return if (account is MisskeyAccount) { misskeyApis.following.delete(FollowingDeleteRequest(account.id)) - true + Ok(Unit) } else { // サーバーが認知していないアカウントのフォローを辞めることはできないのでfalse - false + Err(MultiMError("Can not unfollow", null, ErrorType.API)) } } - override suspend fun profile(account: Account): Profile { + override suspend fun profile(account: Account): Result { if (account is MisskeyAccount) { - return when (val show = misskeyApis.users.show(UsersShowRequest(account.id))) { - //todo converterで書く - is MeDetailed -> MisskeyProfile( - account, - account.isBot ?: false, - MisskeyContent(show.description ?: ""), - show.followingCount, - show.followersCount, - ) - is UserDetailedNotMe -> MisskeyProfile( - account, - account.isBot ?: false, - MisskeyContent( - show.description ?: "" - ), - show.followingCount, - show.followersCount - ) - is UserLite -> throw IllegalStateException() - else -> throw IllegalStateException("else") + val show = misskeyApis.users.show(UsersShowRequest(account.id)) + return show.map { + when (it) { + //todo converterで書く + is MeDetailed -> MisskeyProfile( + account, + account.isBot ?: false, + MisskeyContent(it.description ?: ""), + it.followingCount, + it.followersCount, + ) + + is UserDetailedNotMe -> MisskeyProfile( + account, + account.isBot ?: false, + MisskeyContent( + it.description ?: "" + ), + it.followingCount, + it.followersCount + ) + + is UserLite -> throw IllegalStateException() + } } } - TODO("Not yet implemented") + return TODO() } - override suspend fun statuses(account: Account, includeRepost: Boolean): List { - if (account is MisskeyAccount) { - return misskeyApis.users.notes( + override suspend fun statuses( + account: Account, + includeRepost: Boolean + ): Result, MultiMError> { + return if (account is MisskeyAccount) { + misskeyApis.users.notes( UsersNotesRequest( userId = account.id, includeMyRenotes = includeRepost ) - ).map { it.toStatus() } + ).map { it.map { note -> note.toStatus() } } } else { TODO() } } - override suspend fun relationships(myself: Account, other: Account): Relation { + override suspend fun relationships( + myself: Account, + other: Account + ): Result { if (myself is MisskeyAccount && other is MisskeyAccount) { return misskeyApis.users.relation(UsersRelationRequest(other.id)) - .toRelation(myself, other) + .map { it.toRelation(myself, other) } } - TODO() + return TODO() } - override suspend fun requestCancel(account: Account): Boolean { + override suspend fun requestCancel(account: Account): Result { if (account is MisskeyAccount) { misskeyApis.following.Requests().cancel(FollowingRequestsCancelRequest(account.id)) - return true + return Ok(Unit) } - TODO("Not yet implemented") + return TODO() } - override suspend fun requestAccept(account: Account): Boolean { - if (account is MisskeyAccount) { + override suspend fun requestAccept(account: Account): Result { + return if (account is MisskeyAccount) { misskeyApis.following.Requests().accept(FollowingRequestsAcceptRequest(account.id)) - return true + Ok(Unit) } else { TODO() } } - override suspend fun requestReject(account: Account): Boolean { - if (account is MisskeyAccount) { + override suspend fun requestReject(account: Account): Result { + return if (account is MisskeyAccount) { misskeyApis.following.Requests().reject(FollowingRequestsRejectRequest(account.id)) - return true + Ok(Unit) } else { TODO() } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyStatusApi.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyStatusApi.kt index 79f54bb..9275e2e 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyStatusApi.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyStatusApi.kt @@ -1,6 +1,11 @@ package dev.usbharu.multim.misskey.v12.common.api +import com.github.michaelbull.result.* import dev.usbharu.multim.api.StatusApi +import dev.usbharu.multim.error.ErrorType +import dev.usbharu.multim.error.MultiMError +import dev.usbharu.multim.error.MultiMResult +import dev.usbharu.multim.error.TODO import dev.usbharu.multim.misskey.v12.api.MisskeyApis import dev.usbharu.multim.misskey.v12.common.MisskeyReaction import dev.usbharu.multim.misskey.v12.common.MisskeyStatusId @@ -17,34 +22,37 @@ import dev.usbharu.multim.model.* * @constructor Create empty Misskey status api */ class MisskeyStatusApi(private val misskeyApis: MisskeyApis) : StatusApi { - override suspend fun post(status: StatusForPost): Status { + override suspend fun post(status: StatusForPost): Result { + + return misskeyApis.notes.create(NotesCreateRequest(text = status.content.text)) + .map { it.createdNote.toStatus() } - val create = misskeyApis.notes.create(NotesCreateRequest(text = status.content.text)) - return create.createdNote.toStatus() } - override suspend fun delete(id: StatusId): Boolean { + override suspend fun delete(id: StatusId): Result { if (id is MisskeyStatusId) { misskeyApis.notes.delete(NotesDeleteRequest(id.id)) - return true + return Ok(Unit) } - return false + return TODO() } - override suspend fun findById(id: StatusId): Status { - return misskeyApis.notes.show(NotesShowRequest(id.fetchId().id)).toStatus() + override suspend fun findById(id: StatusId): Result { + return id.fetchId() + .flatMap { misskeyApis.notes.show(NotesShowRequest(it.id)) } + .map { it.toStatus() } } - override suspend fun addReaction(id: StatusId, reaction: Reaction): Boolean { + override suspend fun addReaction(id: StatusId, reaction: Reaction): Result { if (reaction is MisskeyReaction && id is MisskeyStatusId) { misskeyApis.notes.Reaction().create( NotesReactionCreateRequest( noteId = id.id, reaction = reaction.toLocal().name ) ) - return true + return Ok(Unit) } - return false + return TODO() } /** @@ -54,77 +62,84 @@ class MisskeyStatusApi(private val misskeyApis: MisskeyApis) : StatusApi { * @param reaction 無視される * @return */ - override suspend fun removeReaction(id: StatusId, reaction: Reaction?): Boolean { + override suspend fun removeReaction( + id: StatusId, + reaction: Reaction? + ): Result { return if (id is MisskeyStatusId) { misskeyApis.notes.Reaction().delete(NotesReactionDeleteRequest(id.id)) - true + Ok(Unit) } else { //サーバーが認知していない投稿のリアクションを消せるわけないので何もしない - false + Err(MultiMError("id is Not misskey id", null, ErrorType.API)) } } - override suspend fun reactions(id: StatusId): Map { - val show = misskeyApis.notes.show(NotesShowRequest(id.fetchId().id)) - return show.reactions.toReactions(show) + override suspend fun reactions(id: StatusId): Result, MultiMError> { + return id.fetchId().flatMap { misskeyApis.notes.show(NotesShowRequest(it.id)) }.map { it.reactions.toReactions(it) } } - override suspend fun repost(id: StatusId): Status { - return misskeyApis.notes.create(NotesCreateRequest(renoteId = id.fetchId().id)).createdNote.toStatus() + override suspend fun repost(id: StatusId): Result { + return id.fetchId() + .flatMap { misskeyApis.notes.create(NotesCreateRequest(renoteId = it.id)) } + .map { it.createdNote.toStatus() } } - override suspend fun unRepost(id: StatusId): Boolean { + override suspend fun unRepost(id: StatusId): Result { return if (id is MisskeyStatusId) { misskeyApis.notes.unrenote(NotesUnrenoteRequest(id.id)) - true + Ok(Unit) } else { //サーバーが認知していない投稿の再投稿を消せるわけないので何もしない - false + Err(MultiMError("id is Not Misskey id",null,ErrorType.API)) } } - override suspend fun replyTo(id: StatusId, status: StatusForPost): Status { - return misskeyApis.notes.create( - NotesCreateRequest( - replyId = id.fetchId().id, text = status.content.text + override suspend fun replyTo(id: StatusId, status: StatusForPost): Result { + return id.fetchId().flatMap { + misskeyApis.notes.create( + NotesCreateRequest( + replyId = it.id, + text = status.content.text + ) ) - ).createdNote.toStatus() + }.map { it.createdNote.toStatus() } } - override suspend fun addToBookmarks(id: StatusId): Boolean { - misskeyApis.notes.Favorites().create(NotesFavoritesCreateRequest(id.fetchId().id)) - return true + override suspend fun addToBookmarks(id: StatusId): Result { + return id.fetchId() + .flatMap { misskeyApis.notes.Favorites().create(NotesFavoritesCreateRequest(it.id)) } } - override suspend fun removeFromBookmarks(id: StatusId): Boolean { + override suspend fun removeFromBookmarks(id: StatusId): Result { return if (id is MisskeyStatusId) { misskeyApis.notes.Favorites().delete(NotesFavoritesDeleteRequest(id.id)) - true + Ok(Unit) } else { // サーバーが認知していない投稿のブックマークを消せるわけないので何もしない - false + Err(MultiMError("id is Not Misskey id",null,ErrorType.API)) } } - override suspend fun getPreviousAndNext(id: StatusId): PreviousAndNextPosts { - TODO("よくわからないので未実装") //よくわからんのでとりあえず未実装 多分user-timelineからリノートを取り除いたらできる + override suspend fun getPreviousAndNext(id: StatusId): Result { + return TODO() //よくわからんのでとりあえず未実装 多分user-timelineからリノートを取り除いたらできる } - override suspend fun replies(id: StatusId): List { - - return misskeyApis.notes.children(NotesChildrenRequest(id.fetchId().id)) - .map { it.toStatus() } - + override suspend fun replies(id: StatusId): Result, MultiMError> { + return id.fetchId().flatMap { misskeyApis.notes.children(NotesChildrenRequest(it.id)) } + .map { it.map { note -> note.toStatus() } } } - private suspend fun StatusId.fetchId(): MisskeyStatusId { + private suspend fun StatusId.fetchId(): MultiMResult { if (this is MisskeyStatusId) { - return this + return Ok(this) } - val show = misskeyApis.ap.show(ApShowRequest(getUrl())) - if (show is ApShowResponse.TypeNote) { - return show.note.toStatus().id + return misskeyApis.ap.show(ApShowRequest(getUrl())).flatMap { + if (it is ApShowResponse.TypeNote) { + Ok(it.note.toStatus().id) + }else { + Err(MultiMError("The format of the Id($this ${this.getUrl()}) is different.",null,ErrorType.API)) + } } - throw IllegalArgumentException("The format of the Id is different.") } } diff --git a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyTimelineApi.kt b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyTimelineApi.kt index 89410f8..c14b82f 100644 --- a/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyTimelineApi.kt +++ b/impl/misskey/src/main/kotlin/dev/usbharu/multim/misskey/v12/common/api/MisskeyTimelineApi.kt @@ -1,6 +1,9 @@ package dev.usbharu.multim.misskey.v12.common.api +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result import dev.usbharu.multim.api.TimelineApi +import dev.usbharu.multim.error.MultiMError import dev.usbharu.multim.misskey.v12.api.MisskeyApis import dev.usbharu.multim.misskey.v12.common.MisskeyTimeline import dev.usbharu.multim.misskey.v12.converter.misskey.v12.NoteConverter.toStatus @@ -14,18 +17,18 @@ import dev.usbharu.multim.model.Timeline import java.util.* class MisskeyTimelineApi(private val misskeyApis: MisskeyApis) : TimelineApi { - override suspend fun availableTimelines(): List { + override suspend fun availableTimelines(): Result, MultiMError> { fun uuid() = UUID.randomUUID().toString() - return listOf( + return Ok(listOf( MisskeyTimeline(uuid(), "homeTimeline", Body.Channel.HOME_TIMELINE), MisskeyTimeline(uuid(), "hybridTimeline", Body.Channel.HYBRID_TIMELINE), MisskeyTimeline(uuid(), "localTimeline", Body.Channel.LOCAL_TIMELINE), MisskeyTimeline(uuid(), "globalTimeline", Body.Channel.GLOBAL_TIMELINE) - ) + )) } - override suspend fun listen(timeline: Timeline, callback: (List) -> Unit) { + override suspend fun listen(timeline: Timeline, callback: (List) -> Unit): Result { if (timeline is MisskeyTimeline) { misskeyApis.timeline.connectChannel( ConnectRequest( @@ -40,9 +43,10 @@ class MisskeyTimelineApi(private val misskeyApis: MisskeyApis) : TimelineApi { } } } + return Ok(Unit) } - override suspend fun connect(timeline: Timeline): Boolean { + override suspend fun connect(timeline: Timeline): Result { if (timeline is MisskeyTimeline) { misskeyApis.timeline.connectChannel( ConnectRequest( @@ -53,10 +57,10 @@ class MisskeyTimelineApi(private val misskeyApis: MisskeyApis) : TimelineApi { ) ) } - return true + return Ok(Unit) } - override suspend fun disconnect(timeline: Timeline, force: Boolean): Boolean { + override suspend fun disconnect(timeline: Timeline, force: Boolean): Result { if (timeline is MisskeyTimeline) { misskeyApis.timeline.disconnectChannel( DisconnectRequest( @@ -66,6 +70,6 @@ class MisskeyTimelineApi(private val misskeyApis: MisskeyApis) : TimelineApi { ) ) } - return true + return Ok(Unit) } } From 3bc078f2b1ee7ef9077077961ec74aef12898dc1 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:40:07 +0900 Subject: [PATCH 6/7] =?UTF-8?q?test:=20=E6=9A=AB=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=83=93=E3=83=AB=E3=83=89=E9=80=9A=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../multim/misskey/v12/api/TimelineTest.kt | 9 +- .../dev/usbharu/multim/v12/api/NotesTest.kt | 144 ++++++++++++------ .../v12/common/api/MisskeyStatusApiTest.kt | 5 +- 3 files changed, 103 insertions(+), 55 deletions(-) diff --git a/impl/misskey/src/test/kotlin/dev/usbharu/multim/misskey/v12/api/TimelineTest.kt b/impl/misskey/src/test/kotlin/dev/usbharu/multim/misskey/v12/api/TimelineTest.kt index 6f3a7be..5982aea 100644 --- a/impl/misskey/src/test/kotlin/dev/usbharu/multim/misskey/v12/api/TimelineTest.kt +++ b/impl/misskey/src/test/kotlin/dev/usbharu/multim/misskey/v12/api/TimelineTest.kt @@ -1,5 +1,6 @@ package dev.usbharu.multim.misskey.v12.api +import com.github.michaelbull.result.get import dev.usbharu.multim.api.createHttpClient import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient import dev.usbharu.multim.misskey.v12.model.NotesCreateRequest @@ -48,9 +49,11 @@ class TimelineTestE2E { } delay(1000) repeat(10) { - createdNotes.add( - notes.create(NotesCreateRequest(text = "このノートはMultiMのテストで作成され、Streaming APIのテストで使用されます。#$uuid ")).createdNote - ) + notes.create(NotesCreateRequest(text = "このノートはMultiMのテストで作成され、Streaming APIのテストで使用されます。#$uuid ")).get()?.createdNote?.let { it1 -> + createdNotes.add( + it1 + ) + } } delay(2000) timeline.disconnectChannel(DisconnectRequest(DisconnectRequest.Body(uuid))) diff --git a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt index 0759bb7..d25a948 100644 --- a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt +++ b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt @@ -5,6 +5,7 @@ package dev.usbharu.multim.v12.api import MisskeyTestUtil.createFakeNote import MisskeyTestUtil.createMockHttpClient import MisskeyTestUtil.json +import com.github.michaelbull.result.* import dev.usbharu.multim.api.createHttpClient import dev.usbharu.multim.misskey.v12.api.Drive import dev.usbharu.multim.misskey.v12.api.Notes @@ -101,8 +102,8 @@ class NotesTest { createMockHttpClient(content = json.encodeToString(NotesCreateResponse(note))) ) ) - val create = notes.create(NotesCreateRequest(text = "gold")) - assertEquals(note, create.createdNote) + val create = notes.create(NotesCreateRequest(text = "gold")).get() + assertEquals(note, create?.createdNote) } @Test @@ -223,14 +224,15 @@ class NotesTestE2E { visibility = NotesCreateRequest.Visibility.HOME, text = "このノートはMultim のテストで作成されました。${this@NotesTestE2E::class} create Test" ) - ) - println(created.createdNote) + ).get() + println(created?.createdNote) } @Test fun createWithFile() = runTest { - val encode : ByteArray = withContext(Dispatchers.IO) { - NotesTestE2E::class.java.classLoader.getResourceAsStream("notes/create/files/note_with_file_test.jpg")!!.readBytes() + val encode: ByteArray = withContext(Dispatchers.IO) { + NotesTestE2E::class.java.classLoader.getResourceAsStream("notes/create/files/note_with_file_test.jpg")!! + .readBytes() } val driveFile = drive.Files().create(DriveFilesCreateRequest(file = encode)) @@ -239,9 +241,9 @@ class NotesTestE2E { text = "このノートはMultiMのテストで作成され、ファイル添付のテストで使用されます。", fileIds = setOf(driveFile.id) ) - ) + ).get() - assertEquals(driveFile, createdNote.createdNote.files?.firstOrNull()) + assertEquals(driveFile, createdNote?.createdNote?.files?.firstOrNull()) } @Test @@ -252,21 +254,25 @@ class NotesTestE2E { text = "このノートはMultiMのテストで作成され、投票付きノートの作成で使用されます。 ${this@NotesTestE2E::class} create note with poll", poll = poll ) - ) - assertEquals(poll.choices.map { it },create.createdNote.poll?.choices?.map { it.text }); + ).get() + assertEquals(poll.choices.map { it }, create?.createdNote?.poll?.choices?.map { it.text }); } @Test fun delete() = runTest { - val deleteNote = notes.create( + val create = notes.create( NotesCreateRequest( visibility = NotesCreateRequest.Visibility.HOME, text = "このノートはMultim のテストで作成され、削除される予定です。 ${this@NotesTestE2E::class} delete Test" ) ) - notes.delete(NotesDeleteRequest(deleteNote.createdNote.id)) + val deleteNote = when (create) { + is Ok -> create.value.createdNote.id + is Err -> fail(create.error.message, create.error.throwable) + } + notes.delete(NotesDeleteRequest(deleteNote)) assertThrows { - notes.show(NotesShowRequest(deleteNote.createdNote.id)) //消せていたら失敗する + notes.show(NotesShowRequest(deleteNote)) //消せていたら失敗する } } @@ -298,19 +304,29 @@ class NotesTestE2E { fun favoritesCreate() = runTest { val create = notes.create(NotesCreateRequest(text = "このノートはMultim のテストで作成され、お気に入り登録のテストで使用されます。 ${this@NotesTestE2E::class} favorites create test")) - notes.Favorites().create(NotesFavoritesCreateRequest(create.createdNote.id)) - assertTrue(notes.state(NotesStateRequest(create.createdNote.id)).isFavorited) - + .get() + create?.createdNote?.id?.let { NotesFavoritesCreateRequest(it) } + ?.let { notes.Favorites().create(it) } + assertTrue(create?.createdNote?.id?.let { NotesStateRequest(it) } + ?.let { notes.state(it).get()?.isFavorited } == true) +//todo ネストがやばいことになってるのでnullが返ってきた時点で失敗にする } @Test fun favoritesDelete() = runTest { - val create = + val result = notes.create(NotesCreateRequest(text = "このノートはMultim のテストで作成され、お気に入り削除のテストで使用されます。 ${this@NotesTestE2E::class} favorites delete test")) + val create = + when (result) { + is Ok -> result.value + is Err -> fail(result.error.message, result.error.throwable) + } notes.Favorites().create(NotesFavoritesCreateRequest(create.createdNote.id)) - assertTrue(notes.state(NotesStateRequest(create.createdNote.id)).isFavorited) + assertTrue(notes.state(NotesStateRequest(create.createdNote.id)).get()?.isFavorited == true) notes.Favorites().delete(NotesFavoritesDeleteRequest(create.createdNote.id)) - assertFalse(notes.state(NotesStateRequest(create.createdNote.id)).isFavorited) + assertFalse( + notes.state(NotesStateRequest(create.createdNote.id)).get()?.isFavorited == true + ) } @Test @@ -329,9 +345,12 @@ class NotesTestE2E { fun renotes() = runTest { val create = notes.create(NotesCreateRequest(text = "このノートはMultim のテストで作成され、リノートのテストで使用されます。 ${this@NotesTestE2E::class} renotes test")) - val notesCreateResponse = notes.create(NotesCreateRequest(renoteId = create.createdNote.id)) - val renotes = notes.renotes(NotesRenoteRequest(create.createdNote.id)) - assertEquals(listOf(notesCreateResponse.createdNote),renotes) + .get() + val notesCreateResponse = + notes.create(NotesCreateRequest(renoteId = create?.createdNote?.id)).get() + val renotes = create?.createdNote?.id?.let { NotesRenoteRequest(it) } + ?.let { notes.renotes(it) }?.get() + assertEquals(listOf(notesCreateResponse?.createdNote), renotes) println(renotes) } @@ -339,22 +358,24 @@ class NotesTestE2E { fun replies() = runTest { val root = notes.create(NotesCreateRequest(text = "このノートはMultim のテストで作成され、返信取得のテストで使用されます。 ${this@NotesTestE2E::class} replies test")) + .get() val reply1 = notes.create( NotesCreateRequest( text = "返信1 このノートはMultimのテストで作成され、返信取得のテストで使用されます。 ${this@NotesTestE2E::class} replies test", - replyId = root.createdNote.id + replyId = root?.createdNote?.id ) - ) + ).get() val reply2 = notes.create( NotesCreateRequest( text = "返信2 このノートはMultimのテストで作成され、返信取得のテストで使用されます。 ${this@NotesTestE2E::class} replies test", - replyId = root.createdNote.id + replyId = root?.createdNote?.id ) - ) - val replies = notes.replies(NotesRepliesRequest(root.createdNote.id)) + ).get() + val replies = root?.createdNote?.id?.let { NotesRepliesRequest(it) } + ?.let { notes.replies(it) }?.get() assertEquals( - listOf(reply1.createdNote, reply2.createdNote).sortedBy { note -> note.id }, - replies.sortedBy { note -> note.id }) + listOf(reply1?.createdNote, reply2?.createdNote).sortedBy { note -> note?.id }, + replies?.sortedBy { note -> note.id }) } @Test @@ -362,9 +383,10 @@ class NotesTestE2E { val tag = UUID.randomUUID().toString() val tagedNote = notes.create(NotesCreateRequest(text = "#$tag このノートはMultimのテストで作成され、タグ検索のテストで使用されます。 ${this@NotesTestE2E::class} search by tag test")) - val searchByTag = notes.searchByTag(NotesSearchByTagRequest(tag)) - org.assertj.core.api.Assertions.assertThat(searchByTag).isNotEmpty - assertTrue(searchByTag.contains(tagedNote.createdNote)) + .get() + val searchByTag = notes.searchByTag(NotesSearchByTagRequest(tag)).get() + org.assertj.core.api.Assertions.assertThat(searchByTag)?.isNotEmpty + assertTrue(searchByTag?.contains(tagedNote?.createdNote) == true) println(searchByTag) } @@ -372,20 +394,35 @@ class NotesTestE2E { fun threadMutingCreate() = runTest { val create = notes.create(NotesCreateRequest(text = "このノートはMultimのテストで作成され、スレッドミュートのテストで使用されます。 ${this@NotesTestE2E::class} thread mute create test")) - assertFalse(notes.state(NotesStateRequest(create.createdNote.id)).isMutedThread) - notes.ThreadMuting().create(NotesThreadMutingCreateRequest(create.createdNote.id)) - assertTrue(notes.state(NotesStateRequest(create.createdNote.id)).isMutedThread) + .get() + assertFalse(create?.createdNote?.id?.let { NotesStateRequest(it) } + ?.let { notes.state(it).get()?.isMutedThread } == true) + create?.createdNote?.id?.let { NotesThreadMutingCreateRequest(it) } + ?.let { notes.ThreadMuting().create(it) } + assertTrue(create?.createdNote?.id?.let { NotesStateRequest(it) } + ?.let { notes.state(it).get()?.isMutedThread } == true) } @Test fun threadMutingDelete() = runTest { val create = notes.create(NotesCreateRequest(text = "このノートはMultimのテストで作成され、スレッドミュート解除のテストで使用されます。 ${this@NotesTestE2E::class} thread mute delete test")) - assertFalse(notes.state(NotesStateRequest(create.createdNote.id)).isMutedThread) - notes.ThreadMuting().create(NotesThreadMutingCreateRequest(create.createdNote.id)) - assertTrue(notes.state(NotesStateRequest(create.createdNote.id)).isMutedThread) - notes.ThreadMuting().delete(NotesThreadMutingDeleteRequest(create.createdNote.id)) - assertFalse(notes.state(NotesStateRequest(create.createdNote.id)).isMutedThread) + .get() + create?.createdNote?.id?.let { NotesStateRequest(it) }?.let { + notes.state(it).get()?.isMutedThread?.let { + assertFalse( + it + ) + } + } + create?.createdNote?.id?.let { NotesThreadMutingCreateRequest(it) } + ?.let { notes.ThreadMuting().create(it) } + assertTrue(create?.createdNote?.id?.let { NotesStateRequest(it) } + ?.let { notes.state(it).get()?.isMutedThread } == true) + create?.createdNote?.id?.let { NotesThreadMutingDeleteRequest(it) } + ?.let { notes.ThreadMuting().delete(it) } + assertFalse(create?.createdNote?.id?.let { NotesStateRequest(it) } + ?.let { notes.state(it).get()?.isMutedThread } == true) } @Test @@ -399,12 +436,19 @@ class NotesTestE2E { fun unrenote() = runBlocking { val create = notes.create(NotesCreateRequest(text = "このノートはMultimのテストで作成され、リノート取り消しのテストで使用されます。 ${this@NotesTestE2E::class} unrenote test")) + .get() delay(1000) - val renoted = notes.create(NotesCreateRequest(renoteId = create.createdNote.id)) + val renoted = notes.create(NotesCreateRequest(renoteId = create?.createdNote?.id)).get() delay(1000) - notes.unrenote(NotesUnrenoteRequest(create.createdNote.id)) + create?.createdNote?.id?.let { NotesUnrenoteRequest(it) }?.let { notes.unrenote(it) } delay(1000) - assertThrows { notes.show(NotesShowRequest(renoted.createdNote.id)) } + assertThrows { + renoted?.createdNote?.id?.let { + NotesShowRequest( + it + ) + }?.let { notes.show(it) } + } } @@ -417,21 +461,21 @@ class NotesTestE2E { @Test fun watchingCreate() = runTest { - val state = notes.state(NotesStateRequest("9ad9fr34tu")) - if (state.isWatching) { + val state = notes.state(NotesStateRequest("9ad9fr34tu")).get() + if (state?.isWatching == true) { notes.Watching().delete(NotesWatchingDeleteRequest("9ad9fr34tu")) } - notes.Watching().create(NotesWatchingCreateRequest("9ad9fr34tu")) - assertTrue(notes.state(NotesStateRequest("9ad9fr34tu")).isWatching) + notes.Watching().create(NotesWatchingCreateRequest("9ad9fr34tu")).get() + assertTrue(notes.state(NotesStateRequest("9ad9fr34tu")).get()?.isWatching == true) } @Test fun watchingDelete() = runTest { - val state = notes.state(NotesStateRequest("9ad9fr34tu")) - if (!state.isWatching) { + val state = notes.state(NotesStateRequest("9ad9fr34tu")).get() + if (state?.isWatching == false) { notes.Watching().create(NotesWatchingCreateRequest("9ad9fr34tu")) } notes.Watching().delete(NotesWatchingDeleteRequest("9ad9fr34tu")) - assertFalse(notes.state(NotesStateRequest("9ad9fr34tu")).isWatching) + assertFalse(notes.state(NotesStateRequest("9ad9fr34tu")).get()?.isWatching == false) } } diff --git a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/common/api/MisskeyStatusApiTest.kt b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/common/api/MisskeyStatusApiTest.kt index 286a928..57bc509 100644 --- a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/common/api/MisskeyStatusApiTest.kt +++ b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/common/api/MisskeyStatusApiTest.kt @@ -1,6 +1,7 @@ package dev.usbharu.multim.v12.common.api import MisskeyTestUtil +import com.github.michaelbull.result.get import dev.usbharu.multim.misskey.v12.api.MisskeyApis import dev.usbharu.multim.misskey.v12.common.MisskeyStatusId import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient @@ -44,7 +45,7 @@ class MisskeyStatusApiTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun findByIdTest() = runTest { - val findById = misskeyStatusApi.findById(MisskeyStatusId("9a65528e5z", "https://lcalhost")) - println(findById.content.text) + val findById = misskeyStatusApi.findById(MisskeyStatusId("9a65528e5z", "https://lcalhost")).get() + println(findById?.content?.text) } } From 6ea2c452da1d2bdd1e177c9e27677317370dd2dc Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:30:27 +0900 Subject: [PATCH 7/7] =?UTF-8?q?test:=20=E6=9A=AB=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E9=80=9A=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/multim/v12/api/ApTest.kt | 5 ++-- .../dev/usbharu/multim/v12/api/NotesTest.kt | 24 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/ApTest.kt b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/ApTest.kt index 4247b14..39dbe0d 100644 --- a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/ApTest.kt +++ b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/ApTest.kt @@ -6,6 +6,7 @@ import MisskeyTestUtil.checkAuth import MisskeyTestUtil.createFakeNoteToString import MisskeyTestUtil.createMockHttpClient import MisskeyTestUtil.json +import com.github.michaelbull.result.get import dev.usbharu.multim.api.createHttpClient import dev.usbharu.multim.misskey.v12.api.Ap import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient @@ -50,7 +51,7 @@ class ApTest { val misskeyApiClient = MisskeyApiClient( "aaaaaaaa", "https://localhost", createMockHttpClient(typeUser) ) - val show = Ap(misskeyApiClient).show(ApShowRequest("https://localhost/test/IN7OFhht")) + val show = Ap(misskeyApiClient).show(ApShowRequest("https://localhost/test/IN7OFhht")).get() assertEquals(json.decodeFromString(typeUser), show) } @@ -67,7 +68,7 @@ class ApTest { val misskeyApiClient = MisskeyApiClient( "W7Xw8F", "https://localhost", createMockHttpClient(typeNote) ) - val show = Ap(misskeyApiClient).show(ApShowRequest("https://localhost/test/C56WI")) + val show = Ap(misskeyApiClient).show(ApShowRequest("https://localhost/test/C56WI")).get() assertEquals(json.decodeFromString(typeNote), show) } } diff --git a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt index d25a948..792b4f2 100644 --- a/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt +++ b/impl/misskey/src/test/kotlin/dev/usbharu/multim/v12/api/NotesTest.kt @@ -42,7 +42,7 @@ class NotesTest { ) ) val globalTimeline = - notes.globalTimeline(globalTimelineRequest = NotesGlobalTimelineRequest()) + notes.globalTimeline(globalTimelineRequest = NotesGlobalTimelineRequest()).get() assertEquals(expectNoteArray, globalTimeline) } @@ -55,7 +55,7 @@ class NotesTest { createMockHttpClient(content = json.encodeToString(expectNoteArray)) ) ) - val hybridTimeline = notes.hybridTimeline(NotesHybridTimelineRequest()) + val hybridTimeline = notes.hybridTimeline(NotesHybridTimelineRequest()).get() assertEquals(expectNoteArray, hybridTimeline) } @@ -71,7 +71,7 @@ class NotesTest { ) ) ) - val localTimeline = notes.localTimeline(NotesLocalTimelineRequest()) + val localTimeline = notes.localTimeline(NotesLocalTimelineRequest()).get() assertEquals(expectNoteArray, localTimeline) } @@ -88,7 +88,7 @@ class NotesTest { ) ) ) - val note = notes.show(NotesShowRequest("mLvakn")) + val note = notes.show(NotesShowRequest("mLvakn")).get() assertEquals(expectedNote, note) } @@ -195,25 +195,25 @@ class NotesTestE2E { @Test fun globalTimeline() = runTest { - val globalTimeline = notes.globalTimeline(NotesGlobalTimelineRequest()) + val globalTimeline = notes.globalTimeline(NotesGlobalTimelineRequest()).get() println(globalTimeline) } @Test fun hybridTimeline() = runTest { - val hybridTimeline = notes.hybridTimeline(NotesHybridTimelineRequest()) + val hybridTimeline = notes.hybridTimeline(NotesHybridTimelineRequest()).get() println(hybridTimeline) } @Test fun localTimeline() = runTest { - val localTimeline = notes.localTimeline(NotesLocalTimelineRequest()) + val localTimeline = notes.localTimeline(NotesLocalTimelineRequest()).get() println(localTimeline) } @Test fun show() = runTest { - val show = notes.show(NotesShowRequest("9ack8wxw3c")) + val show = notes.show(NotesShowRequest("9ack8wxw3c")).get() println(show) } @@ -271,9 +271,9 @@ class NotesTestE2E { is Err -> fail(create.error.message, create.error.throwable) } notes.delete(NotesDeleteRequest(deleteNote)) - assertThrows { - notes.show(NotesShowRequest(deleteNote)) //消せていたら失敗する - } + assertInstanceOf(Err::class.java,notes.show(NotesShowRequest(deleteNote))) + //消せていたら失敗する + } @Test @@ -476,6 +476,6 @@ class NotesTestE2E { notes.Watching().create(NotesWatchingCreateRequest("9ad9fr34tu")) } notes.Watching().delete(NotesWatchingDeleteRequest("9ad9fr34tu")) - assertFalse(notes.state(NotesStateRequest("9ad9fr34tu")).get()?.isWatching == false) + assertFalse(notes.state(NotesStateRequest("9ad9fr34tu")).get()?.isWatching == true) } }