Skip to content

Commit

Permalink
Version 0.12.0 (#42)
Browse files Browse the repository at this point in the history
- [kotlinx.coroutines 0.24.0](https://github.com/Kotlin/kotlinx.coroutines/releases/tag/0.24.0)
- Compiled against Kotlin 1.2.60
- Reformat code with official Kotlin style guide
- Gradle 4.9
- Bintray plugin 1.8.4
  • Loading branch information
gildor committed Aug 4, 2018
1 parent a219173 commit 2583e6c
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 65 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# CHANGELOG

## Version 0.12.0 (2017-08-04)

- [kotlinx.coroutines 0.24.0](https://github.com/Kotlin/kotlinx.coroutines/releases/tag/0.24.0)
- Compiled against Kotlin 1.2.60

## Version 0.11.0 (2017-06-12)

- [kotlinx.coroutines 0.23.1](https://github.com/Kotlin/kotlinx.coroutines/releases/)
- [kotlinx.coroutines 0.23.1](https://github.com/Kotlin/kotlinx.coroutines/releases/tag/0.23.1)
- Compiled against Kotlin 1.2.41

Thanks to [Thomas Schmidt](https://github.com/bohsen) for contribution
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@

This is a small library that provides the [Kotlin Coroutines](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md) [suspending](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#suspending-functions) extension `Call.await()` for [Retrofit 2](https://github.com/square/retrofit)

Based on [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) implementation
Based on [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) implementation.

This branch uses Kotlin experimental package `kotlin.coroutines.experimental` (pre-1.3).

Migration to package stable `kotlin.coroutines` package is planned and work in progress.

## Download
Download the [JAR](https://bintray.com/gildor/maven/kotlin-coroutines-retrofit#files/ru/gildor/coroutines/kotlin-coroutines-retrofit):

Gradle:

```groovy
compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.11.0'
compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.12.0'
```

Maven:
Maven:getOrThrow

```xml
<dependency>
<groupId>ru.gildor.coroutines</groupId>
<artifactId>kotlin-coroutines-retrofit</artifactId>
<version>0.11.0</version>
<version>0.12.0</version>
</dependency>
```

Expand All @@ -49,7 +53,7 @@ In case of an HTTP error or an invocation exception `await()` throws an exceptio

```kotlin
// You can use retrofit suspended extension inside any coroutine block
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
try {
// Wait (suspend) for result
val user: User = api.getUser("username").await()
Expand All @@ -76,7 +80,7 @@ In case of an invocation exception `awaitResponse()` throws an exception

```kotlin
// You can use retrofit suspended extension inside any coroutine block
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
try {
// Wait (suspend) for response
val response: Response<User> = api.getUser("username").awaitResponse()
Expand All @@ -100,7 +104,7 @@ fun Call<T>.awaitResult(): Result<T>
```

```kotlin
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
// Wait (suspend) for Result
val result: Result<User> = api.getUser("username").awaitResult()
// Check result type
Expand All @@ -118,7 +122,7 @@ fun main(args: Array<String>) = runBlocking {
Also, `Result` has a few handy extension functions that allow to avoid `when` block matching:

```kotlin
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
val result: User = api.getUser("username").awaitResult()

//Return value for success or null for any http error or exception
Expand All @@ -138,7 +142,7 @@ All `Result` classes also implemented one or both interfaces: `ResponseResult` a
You can use them for access to shared properties of different classes from `Result`

```kotlin
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
val result: User = api.getUser("username").awaitResult()

//Result.Ok and Result.Error both implement ResponseResult
Expand All @@ -162,7 +166,7 @@ extensions `.await()` and `.awaitResult()` are available only for
non-nullable `Call<Body>` or platform `Call<Body!>` body types:

```kotlin
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
val user: Call<User> = api.getUser("username")
val userOrNull: Call<User?> = api.getUserOrNull("username")

Expand Down Expand Up @@ -194,7 +198,7 @@ You can do that by wrapping calls with `kotlinx.coroutines` [async()](https://ko


```kotlin
fun main(args: Array<String>) = runBlocking {
fun main(args: Array<String>): Unit = runBlocking {
val users = listOf("user1", "user2", "user3")
.map { username ->
// Pass any coroutine context that fits better for your case
Expand Down
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper

plugins {
id("org.jetbrains.kotlin.jvm") version "1.2.41"
id("com.jfrog.bintray") version "1.7.3"
id("org.jetbrains.kotlin.jvm") version "1.2.60"
id("com.jfrog.bintray") version "1.8.4"
jacoco
`maven-publish`
id("org.jetbrains.dokka") version "0.9.16"
}

group = "ru.gildor.coroutines"
version = "0.11.0"
version = "0.12.0"
description = "Provides Kotlin Coroutines suspendable await() extensions for Retrofit Call"

repositories {
Expand All @@ -34,7 +34,7 @@ java {

dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib")
compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.1")
compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.24.0")
compile("com.squareup.retrofit2:retrofit:2.4.0")
testCompile("junit:junit:4.12")
}
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# suppress inspection "UnusedProperty" for whole file
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
39 changes: 28 additions & 11 deletions src/main/kotlin/ru/gildor/coroutines/retrofit/CallAwait.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2018 Andrey Mischenko
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ru.gildor.coroutines.retrofit

import kotlinx.coroutines.experimental.CancellableContinuation
Expand All @@ -8,7 +24,7 @@ import retrofit2.HttpException
import retrofit2.Response

/**
* Suspend extension that allows suspend [Call] inside coroutine.
* Suspend extension that allows suspend [Call] inside of a coroutine.
*
* @return Result of request or throw exception
*/
Expand All @@ -20,7 +36,7 @@ public suspend fun <T : Any> Call<T>.await(): T {
val body = response.body()
if (body == null) {
continuation.resumeWithException(
NullPointerException("Response body is null: $response")
NullPointerException("Response body is null: $response")
)
} else {
continuation.resume(body)
Expand Down Expand Up @@ -68,23 +84,24 @@ public suspend fun <T : Any?> Call<T>.awaitResponse(): Response<T> {
* Suspend extension that allows suspend [Call] inside coroutine.
*
* @return sealed class [Result] object that can be
* casted to [Result.Ok] (success) or [Result.Error] (HTTP error) and [Result.Exception] (other errors)
* casted to [Result.Ok] (success) or [Result.Error] (HTTP error)
* and [Result.Exception] (other errors)
*/
public suspend fun <T : Any> Call<T>.awaitResult(): Result<T> {
return suspendCancellableCoroutine { continuation ->
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>?, response: Response<T>) {
continuation.resume(
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Result.Exception(NullPointerException("Response body is null"))
} else {
Result.Ok(body, response.raw())
}
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Result.Exception(NullPointerException("Response body is null"))
} else {
Result.Error(HttpException(response), response.raw())
Result.Ok(body, response.raw())
}
} else {
Result.Error(HttpException(response), response.raw())
}
)
}

Expand Down
32 changes: 23 additions & 9 deletions src/main/kotlin/ru/gildor/coroutines/retrofit/Result.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2018 Andrey Mischenko
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ru.gildor.coroutines.retrofit

import okhttp3.Response
Expand All @@ -12,8 +28,8 @@ public sealed class Result<out T : Any> {
* Successful result of request without errors
*/
public class Ok<out T : Any>(
public val value: T,
override val response: Response
public val value: T,
override val response: Response
) : Result<T>(), ResponseResult {
override fun toString() = "Result.Ok{value=$value, response=$response}"
}
Expand All @@ -22,8 +38,8 @@ public sealed class Result<out T : Any> {
* HTTP error
*/
public class Error(
override val exception: HttpException,
override val response: Response
override val exception: HttpException,
override val response: Response
) : Result<Nothing>(), ErrorResult, ResponseResult {
override fun toString() = "Result.Error{exception=$exception}"
}
Expand All @@ -33,7 +49,7 @@ public sealed class Result<out T : Any> {
* exception occurred creating the request or processing the response
*/
public class Exception(
override val exception: Throwable
override val exception: Throwable
) : Result<Nothing>(), ErrorResult {
override fun toString() = "Result.Exception{$exception}"
}
Expand All @@ -57,14 +73,12 @@ public interface ErrorResult {
/**
* Returns [Result.Ok.value] or `null`
*/
public fun <T : Any> Result<T>.getOrNull(): T? =
if (this is Result.Ok) this.value else null
public fun <T : Any> Result<T>.getOrNull(): T? = (this as? Result.Ok)?.value

/**
* Returns [Result.Ok.value] or [default]
*/
public fun <T : Any> Result<T>.getOrDefault(default: T): T =
getOrNull() ?: default
public fun <T : Any> Result<T>.getOrDefault(default: T): T = getOrNull() ?: default

/**
* Returns [Result.Ok.value] or throw [throwable] or [ErrorResult.exception]
Expand Down
38 changes: 30 additions & 8 deletions src/test/kotlin/ru/gildor/coroutines/retrofit/CallAwaitTest.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
/*
* Copyright 2018 Andrey Mischenko
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ru.gildor.coroutines.retrofit

import kotlinx.coroutines.experimental.CoroutineScope
import kotlinx.coroutines.experimental.Unconfined
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.runBlocking
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Test
import retrofit2.Call
import retrofit2.HttpException
Expand Down Expand Up @@ -232,12 +253,12 @@ class CallAwaitTest {
}

private fun <T> checkRequestCancelWithException(
block: suspend (Call<String>) -> T
block: suspend (Call<String>) -> T
) = testBlocking {
val request = MockedCall(
ok = DONE,
autoStart = false,
cancelException = IllegalStateException()
ok = DONE,
autoStart = false,
cancelException = IllegalStateException()
)
val async = async(coroutineContext, block = { block(request) })
//We shouldn't crash on cancel exception
Expand All @@ -252,8 +273,8 @@ class CallAwaitTest {

private fun <T> checkJobCancelWithException(block: suspend (Call<String>) -> T) = testBlocking {
val request = MockedCall<String>(
exception = IllegalArgumentException(),
autoStart = false
exception = IllegalArgumentException(),
autoStart = false
)
val result = async(coroutineContext) {
block(request)
Expand All @@ -264,7 +285,7 @@ class CallAwaitTest {
}

private fun <T> checkJobCancel(
block: suspend (Call<String>) -> T
block: suspend (Call<String>) -> T
) = testBlocking {
val request = MockedCall(DONE, autoStart = false)
val async = async(coroutineContext) { block(request) }
Expand All @@ -277,3 +298,4 @@ class CallAwaitTest {
private fun testBlocking(block: suspend CoroutineScope.() -> Unit) {
runBlocking(Unconfined, block)
}

Loading

0 comments on commit 2583e6c

Please sign in to comment.