diff --git a/src/commonMain/kotlin/com/ioki/lokalise/api/LokaliseClient.kt b/src/commonMain/kotlin/com/ioki/lokalise/api/LokaliseClient.kt index 5a589bd..92c8564 100644 --- a/src/commonMain/kotlin/com/ioki/lokalise/api/LokaliseClient.kt +++ b/src/commonMain/kotlin/com/ioki/lokalise/api/LokaliseClient.kt @@ -1,5 +1,6 @@ package com.ioki.lokalise.api +import com.ioki.lokalise.api.models.ErrorWrapper import com.ioki.lokalise.api.models.FileDownload import com.ioki.lokalise.api.models.FileUpload import com.ioki.lokalise.api.models.Projects @@ -16,6 +17,7 @@ import io.ktor.client.request.get import io.ktor.client.request.header import io.ktor.client.request.post import io.ktor.client.request.setBody +import io.ktor.client.statement.HttpResponse import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.serialization.kotlinx.json.json @@ -39,7 +41,7 @@ interface LokaliseProjects { */ suspend fun allProjects( queryParams: Map = emptyMap() - ): Projects + ): Result } interface LokaliseFiles { @@ -52,7 +54,7 @@ interface LokaliseFiles { projectId: String, format: String, bodyParams: Map = emptyMap() - ): FileDownload + ): Result /** * Upload files. @@ -64,7 +66,7 @@ interface LokaliseFiles { filename: String, langIso: String, bodyParams: Map = emptyMap() - ): FileUpload + ): Result } interface LokaliseQueuedProcesses { @@ -76,7 +78,7 @@ interface LokaliseQueuedProcesses { suspend fun retrieveProcess( projectId: String, processId: String, - ): RetrievedProcess + ): Result } /** @@ -95,7 +97,6 @@ internal fun Lokalise( fullLoggingEnabled: Boolean = false, httpClientEngine: HttpClientEngine? = null ): Lokalise { - val clientConfig: HttpClientConfig<*>.() -> Unit = { install(ContentNegotiation) { json() @@ -122,7 +123,7 @@ private class LokaliseClient( private val httpClient: HttpClient, ) : Lokalise { - override suspend fun allProjects(queryParams: Map): Projects { + override suspend fun allProjects(queryParams: Map): Result { val requestParams = queryParams .map { "${it.key}=${it.value}" } .joinToString(separator = "&") @@ -130,21 +131,21 @@ private class LokaliseClient( return httpClient .get("projects$requestParams") - .body() + .toResult() } override suspend fun downloadFiles( projectId: String, format: String, bodyParams: Map - ): FileDownload { + ): Result { val requestBody = bodyParams.toMutableMap() .apply { put("format", format) } .toRequestBody() return httpClient .post("projects/$projectId/files/download") { setBody(requestBody) } - .body() + .toResult() } override suspend fun uploadFile( @@ -153,7 +154,7 @@ private class LokaliseClient( filename: String, langIso: String, bodyParams: Map - ): FileUpload { + ): Result { val requestBody = bodyParams.toMutableMap() .apply { put("data", data) @@ -164,19 +165,23 @@ private class LokaliseClient( return httpClient .post("projects/$projectId/files/upload") { setBody(requestBody) } - .body() + .toResult() } override suspend fun retrieveProcess( projectId: String, processId: String - ): RetrievedProcess { + ): Result { return httpClient .get("projects/$projectId/processes/$processId") - .body() + .toResult() } } +private suspend inline fun HttpResponse.toResult(): Result = + if (status.value in 200..299) Result.Success(body()) + else Result.Failure(body().error) + /** * Found at * [kotlinx.serialization/issues#746](https://github.com/Kotlin/kotlinx.serialization/issues/746#issuecomment-863099397) diff --git a/src/commonMain/kotlin/com/ioki/lokalise/api/Result.kt b/src/commonMain/kotlin/com/ioki/lokalise/api/Result.kt new file mode 100644 index 0000000..4f939be --- /dev/null +++ b/src/commonMain/kotlin/com/ioki/lokalise/api/Result.kt @@ -0,0 +1,8 @@ +package com.ioki.lokalise.api + +import com.ioki.lokalise.api.models.Error + +sealed interface Result { + data class Success(val data: T) : Result + data class Failure(val error: Error) : Result +} \ No newline at end of file diff --git a/src/commonMain/kotlin/com/ioki/lokalise/api/models/Error.kt b/src/commonMain/kotlin/com/ioki/lokalise/api/models/Error.kt new file mode 100644 index 0000000..bfc02f8 --- /dev/null +++ b/src/commonMain/kotlin/com/ioki/lokalise/api/models/Error.kt @@ -0,0 +1,15 @@ +package com.ioki.lokalise.api.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class ErrorWrapper( + val error: Error, +) + +@Serializable +data class Error( + @SerialName("message") val message: String, + @SerialName("code") val code: Int +) \ No newline at end of file diff --git a/src/commonTest/kotlin/com/ioki/lokalise/api/LokaliseClientTest.kt b/src/commonTest/kotlin/com/ioki/lokalise/api/LokaliseClientTest.kt index ee40d7b..e736d12 100644 --- a/src/commonTest/kotlin/com/ioki/lokalise/api/LokaliseClientTest.kt +++ b/src/commonTest/kotlin/com/ioki/lokalise/api/LokaliseClientTest.kt @@ -1,8 +1,9 @@ package com.ioki.lokalise.api -import com.ioki.lokalise.api.stubs.projectsJson +import com.ioki.lokalise.api.stubs.errorJson import com.ioki.lokalise.api.stubs.fileDownloadJson import com.ioki.lokalise.api.stubs.fileUploadJson +import com.ioki.lokalise.api.stubs.projectsJson import com.ioki.lokalise.api.stubs.retrieveProcessJson import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.mock.MockEngine @@ -11,6 +12,7 @@ import io.ktor.client.engine.mock.toByteArray import io.ktor.http.ContentType import io.ktor.http.Headers import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode import io.ktor.http.headersOf import io.ktor.utils.io.core.String import kotlinx.coroutines.runBlocking @@ -20,6 +22,41 @@ import kotlin.test.assertTrue class LokaliseClientTest { + @Test + fun `test result value with error on allProjects but doesn't matter where`() = runLokaliseTest( + errorJson, + HttpStatusCode.NotFound + ) { lokalise, mockEngine -> + val result = lokalise.allProjects() + + assertTrue(result is Result.Failure) + assertTrue(result.error.code == 404) + assertTrue(result.error.message == "Not Found") + } + + @Test + fun `test result value without error on allProjects but doesn't matter where`() = + runLokaliseTest(projectsJson) { lokalise, mockEngine -> + val result = lokalise.allProjects() + + assertTrue(result is Result.Success) + assertTrue(result.data.projects.size == 1) + with(result.data.projects.first()) { + assertEquals( + expected = projectId, + actual = "string" + ) + assertEquals( + expected = createdAtTimestamp, + actual = 0 + ) + assertEquals( + expected = settings.branching, + actual = true + ) + } + } + @Test fun `test list all projects without params`() = runLokaliseTest(projectsJson) { lokalise, mockEngine -> lokalise.allProjects() @@ -211,28 +248,33 @@ class LokaliseClientTest { headers.contains("X-Api-Token", "sec3tT0k3n") } - private fun createLokalise( - httpClientEngine: HttpClientEngine, - token: String = "sec3tT0k3n", - ): Lokalise = Lokalise( - token = token, - fullLoggingEnabled = true, - httpClientEngine = httpClientEngine - ) - - private fun createMockEngine(content: String): MockEngine = MockEngine { _ -> - respond( - content = content, - headers = headersOf("Content-Type", ContentType.Application.Json.toString()) - ) - } - private fun runLokaliseTest( httpJsonResponse: String, + httpStatusCode: HttpStatusCode = HttpStatusCode.OK, block: suspend (Lokalise, MockEngine) -> Unit ) = runBlocking { - val engine = createMockEngine(httpJsonResponse) + val engine = createMockEngine(httpJsonResponse, httpStatusCode) val lokalise = createLokalise(engine) block(lokalise, engine) } } + +private fun createMockEngine( + content: String, + statusCode: HttpStatusCode +): MockEngine = MockEngine { _ -> + respond( + content = content, + headers = headersOf("Content-Type", ContentType.Application.Json.toString()), + status = statusCode + ) +} + +private fun createLokalise( + httpClientEngine: HttpClientEngine, + token: String = "sec3tT0k3n", +): Lokalise = Lokalise( + token = token, + fullLoggingEnabled = true, + httpClientEngine = httpClientEngine +) diff --git a/src/commonTest/kotlin/com/ioki/lokalise/api/stubs/ErrorJson.kt b/src/commonTest/kotlin/com/ioki/lokalise/api/stubs/ErrorJson.kt new file mode 100644 index 0000000..b97282a --- /dev/null +++ b/src/commonTest/kotlin/com/ioki/lokalise/api/stubs/ErrorJson.kt @@ -0,0 +1,10 @@ +package com.ioki.lokalise.api.stubs + +val errorJson = """ +{ + "error":{ + "message": "Not Found", + "code": 404 + } +} +""".trimIndent() \ No newline at end of file