diff --git a/api/src/main/kotlin/dev/msfjarvis/claw/api/ShioriApi.kt b/api/src/main/kotlin/dev/msfjarvis/claw/api/ShioriApi.kt index 7b8305db..ac6e7dd8 100644 --- a/api/src/main/kotlin/dev/msfjarvis/claw/api/ShioriApi.kt +++ b/api/src/main/kotlin/dev/msfjarvis/claw/api/ShioriApi.kt @@ -7,6 +7,7 @@ package dev.msfjarvis.claw.api import android.annotation.SuppressLint +import com.slack.eithernet.ApiResult import dev.msfjarvis.claw.model.shiori.AuthRequest import dev.msfjarvis.claw.model.shiori.AuthResponse import dev.msfjarvis.claw.model.shiori.Bookmark @@ -23,30 +24,32 @@ import retrofit2.http.PUT private const val SESSION_ID_HEADER = "X-Session-Id" interface ShioriApi { - @POST("/api/login") suspend fun login(@Body body: AuthRequest): AuthResponse + @POST("/api/login") suspend fun login(@Body body: AuthRequest): ApiResult @SuppressLint("RetrofitUsage") // POST without a body is apparently fine? @POST("/api/logout") - suspend fun logout(@Header(SESSION_ID_HEADER) sessionId: String) + suspend fun logout(@Header(SESSION_ID_HEADER) sessionId: String): ApiResult @GET("/api/bookmarks") - suspend fun getBookmarks(@Header(SESSION_ID_HEADER) sessionId: String): BookmarksResponse + suspend fun getBookmarks( + @Header(SESSION_ID_HEADER) sessionId: String, + ): ApiResult @POST("/api/bookmarks") suspend fun addBookmark( @Header(SESSION_ID_HEADER) sessionId: String, @Body bookmarkRequest: BookmarkRequest, - ): Bookmark + ): ApiResult @PUT("/api/bookmarks") suspend fun editBookmark( @Header(SESSION_ID_HEADER) sessionId: String, @Body bookmark: EditedBookmark, - ): Bookmark + ): ApiResult @HTTP(method = "DELETE", path = "/api/bookmarks", hasBody = true) suspend fun deleteBookmark( @Header(SESSION_ID_HEADER) sessionId: String, @Body ids: List, - ): Int + ): ApiResult } diff --git a/api/src/main/kotlin/dev/msfjarvis/claw/api/injection/RetrofitModule.kt b/api/src/main/kotlin/dev/msfjarvis/claw/api/injection/RetrofitModule.kt index 9146b911..424b5929 100644 --- a/api/src/main/kotlin/dev/msfjarvis/claw/api/injection/RetrofitModule.kt +++ b/api/src/main/kotlin/dev/msfjarvis/claw/api/injection/RetrofitModule.kt @@ -16,6 +16,7 @@ import dagger.multibindings.IntKey import dagger.multibindings.IntoMap import dev.msfjarvis.claw.api.LobstersApi import dev.msfjarvis.claw.api.LobstersSearchApi +import dev.msfjarvis.claw.api.ShioriApi import dev.msfjarvis.claw.api.converters.CSRFTokenConverter import dev.msfjarvis.claw.api.converters.SearchConverter import javax.inject.Qualifier @@ -72,6 +73,8 @@ object RetrofitModule { @Provides fun provideSearchApi(@SearchApi retrofit: Retrofit): LobstersSearchApi = retrofit.create() + @Provides fun provideShioriApi(retrofit: Retrofit): ShioriApi = retrofit.create() + @Provides @IntKey(0) @IntoMap diff --git a/api/src/test/kotlin/dev/msfjarvis/claw/api/ShioriApiTest.kt b/api/src/test/kotlin/dev/msfjarvis/claw/api/ShioriApiTest.kt index dfca3e1a..72873fe2 100644 --- a/api/src/test/kotlin/dev/msfjarvis/claw/api/ShioriApiTest.kt +++ b/api/src/test/kotlin/dev/msfjarvis/claw/api/ShioriApiTest.kt @@ -8,11 +8,17 @@ package dev.msfjarvis.claw.api import com.google.common.truth.Truth.assertThat import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import com.slack.eithernet.ApiResultCallAdapterFactory +import com.slack.eithernet.ApiResultConverterFactory +import com.slack.eithernet.successOrNull import dev.msfjarvis.claw.model.shiori.AuthRequest import dev.msfjarvis.claw.model.shiori.AuthResponse +import dev.msfjarvis.claw.model.shiori.Bookmark import dev.msfjarvis.claw.model.shiori.BookmarkRequest +import dev.msfjarvis.claw.model.shiori.BookmarksResponse import dev.msfjarvis.claw.model.shiori.EditedBookmark import dev.msfjarvis.claw.model.shiori.Tag +import dev.msfjarvis.claw.util.TestUtils.assertIs import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json @@ -31,7 +37,10 @@ class ShioriApiTest { @BeforeEach fun setUp() { container.start() - runBlocking { credentials = api.login(AuthRequest(USER, PASSWORD)) } + runBlocking { + credentials = + requireNotNull(api.login(AuthRequest(USER, PASSWORD)).successOrNull()) { "Login failed" } + } } @AfterEach @@ -41,7 +50,8 @@ class ShioriApiTest { @Test fun getBookmarks() = runTest { - val response = api.getBookmarks(credentials.session) + val response = api.getBookmarks(credentials.session).successOrNull() + assertIs(response) assertThat(response.page).isEqualTo(1) assertThat(response.bookmarks).isEmpty() } @@ -49,21 +59,24 @@ class ShioriApiTest { @Test fun addBookmark() = runTest { val response = - api.addBookmark( - credentials.session, - BookmarkRequest( - "https://example.com", - false, - 0, - emptyList(), - "Example Domain", - """ + api + .addBookmark( + credentials.session, + BookmarkRequest( + "https://example.com", + false, + 0, + emptyList(), + "Example Domain", + """ This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. """ - .trimIndent(), + .trimIndent(), + ) ) - ) + .successOrNull() + assertIs(response) assertThat(response.url).isEqualTo("https://example.com") assertThat(response.title).isEqualTo("Example Domain") assertThat(response.excerpt) @@ -80,21 +93,24 @@ class ShioriApiTest { @Test fun editBookmark() = runTest { val response = - api.addBookmark( - credentials.session, - BookmarkRequest( - "https://example.com", - false, - 0, - emptyList(), - "Example Domain", - """ + api + .addBookmark( + credentials.session, + BookmarkRequest( + "https://example.com", + false, + 0, + emptyList(), + "Example Domain", + """ This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. """ - .trimIndent(), + .trimIndent(), + ) ) - ) + .successOrNull() + assertIs(response) assertThat(response.tags).isEmpty() val newBookmark = EditedBookmark( @@ -103,7 +119,8 @@ class ShioriApiTest { title = response.title, tags = listOf(Tag("examples")), ) - val edited = api.editBookmark(credentials.session, newBookmark) + val edited = api.editBookmark(credentials.session, newBookmark).successOrNull() + assertIs(edited) assertThat(edited.tags).isNotEmpty() assertThat(edited.tags).containsExactly(Tag("examples")) } @@ -111,22 +128,25 @@ class ShioriApiTest { @Test fun deleteBookmark() = runTest { val response = - api.addBookmark( - credentials.session, - BookmarkRequest( - "https://example.com", - false, - 0, - emptyList(), - "Example Domain", - """ + api + .addBookmark( + credentials.session, + BookmarkRequest( + "https://example.com", + false, + 0, + emptyList(), + "Example Domain", + """ This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. """ - .trimIndent(), + .trimIndent(), + ) ) - ) - val count = api.deleteBookmark(credentials.session, listOf(response.id)) + .successOrNull() + assertIs(response) + val count = api.deleteBookmark(credentials.session, listOf(response.id)).successOrNull() assertThat(count).isEqualTo(1) } @@ -144,7 +164,9 @@ class ShioriApiTest { get() = Retrofit.Builder() .baseUrl("http://${container.host}:${container.firstMappedPort}") + .addConverterFactory(ApiResultConverterFactory) .addConverterFactory(json.asConverterFactory(MediaType.get("application/json"))) + .addCallAdapterFactory(ApiResultCallAdapterFactory) .build() .create() }