diff --git a/api/EitherNet.api b/api/eithernet.api similarity index 93% rename from api/EitherNet.api rename to api/eithernet.api index ce964fb..d3cd025 100644 --- a/api/EitherNet.api +++ b/api/eithernet.api @@ -1,3 +1,9 @@ +public final class com/slack/eithernet/AnnotationsKt { + public static final fun errorType ([Ljava/lang/annotation/Annotation;)Lkotlin/Pair; + public static final fun statusCode ([Ljava/lang/annotation/Annotation;)Lkotlin/Pair; + public static final fun toType (Lcom/slack/eithernet/ResultType;)Ljava/lang/reflect/Type; +} + public final class com/slack/eithernet/ApiException : java/lang/Exception { public fun (Ljava/lang/Object;)V public final fun getError ()Ljava/lang/Object; diff --git a/build.gradle.kts b/build.gradle.kts index 550d846..08f7986 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,19 +14,19 @@ * limitations under the License. */ import io.gitlab.arturbosch.detekt.Detekt +import java.net.URL import org.jetbrains.dokka.gradle.DokkaTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.net.URL plugins { - kotlin("jvm") version "1.6.10" + kotlin("jvm") version "1.6.21" `java-test-fixtures` - id("org.jetbrains.dokka") version "1.6.10" - id("com.google.devtools.ksp") version "1.6.10-1.0.2" - id("com.diffplug.spotless") version "6.2.0" - id("com.vanniktech.maven.publish") version "0.18.0" - id("io.gitlab.arturbosch.detekt") version "1.18.1" - id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.8.0" + id("org.jetbrains.dokka") version "1.6.21" + id("com.google.devtools.ksp") version "1.6.21-1.0.6" + id("com.diffplug.spotless") version "6.7.1" + id("com.vanniktech.maven.publish") version "0.20.0" + id("io.gitlab.arturbosch.detekt") version "1.20.0" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.10.0" } repositories { @@ -35,28 +35,19 @@ repositories { } pluginManager.withPlugin("java") { - configure { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } + configure { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } } - project.tasks.withType().configureEach { - options.release.set(8) - } + project.tasks.withType().configureEach { options.release.set(8) } } tasks.withType().configureEach { val taskName = name kotlinOptions { jvmTarget = "1.8" - val argsList = mutableListOf( - "-progressive", - "-Xopt-in=kotlin.RequiresOptIn" - ) + val argsList = mutableListOf("-progressive", "-opt-in=kotlin.RequiresOptIn") if (taskName == "compileTestKotlin") { - argsList += "-Xopt-in=kotlin.ExperimentalStdlibApi" - argsList += "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi" + argsList += "-opt-in=kotlin.ExperimentalStdlibApi" + argsList += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi" // Enable new jvmdefault behavior // https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/ argsList += "-Xjvm-default=all" @@ -66,21 +57,15 @@ tasks.withType().configureEach { } } -tasks.withType().configureEach { - jvmTarget = "1.8" -} +tasks.withType().configureEach { jvmTarget = "1.8" } -kotlin { - explicitApi() -} +kotlin { explicitApi() } tasks.named("dokkaHtml") { outputDirectory.set(rootDir.resolve("docs/0.x")) dokkaSourceSets.configureEach { skipDeprecated.set(true) - externalDocumentationLink { - url.set(URL("https://square.github.io/retrofit/2.x/retrofit/")) - } + externalDocumentationLink { url.set(URL("https://square.github.io/retrofit/2.x/retrofit/")) } } } @@ -90,35 +75,31 @@ spotless { trimTrailingWhitespace() endWithNewline() } - val ktlintVersion = "0.41.0" - val ktlintUserData = mapOf("indent_size" to "2", "continuation_indent_size" to "2") + val ktfmtVersion = "0.38" kotlin { target("**/*.kt") - ktlint(ktlintVersion).userData(ktlintUserData) + ktfmt(ktfmtVersion).googleStyle() trimTrailingWhitespace() endWithNewline() licenseHeaderFile("spotless/spotless.kt") targetExclude("**/spotless.kt") } kotlinGradle { - ktlint(ktlintVersion).userData(ktlintUserData) + ktfmt(ktfmtVersion).googleStyle() trimTrailingWhitespace() endWithNewline() - licenseHeaderFile("spotless/spotless.kt", "(import|plugins|buildscript|dependencies|pluginManagement|rootProject)") + licenseHeaderFile( + "spotless/spotless.kt", + "(import|plugins|buildscript|dependencies|pluginManagement|rootProject)" + ) } } -apiValidation { - // Exclude directly instantiated annotations - // https://github.com/Kotlin/binary-compatibility-validator/issues/71 - ignoredClasses.add("com/slack/eithernet/AnnotationsKt\$annotationImpl\$com_slack_eithernet_ResultType\$0") - ignoredClasses.add("com/slack/eithernet/AnnotationsKt\$annotationImpl\$com_slack_eithernet_StatusCode\$0") -} - val moshiVersion = "1.12.0" val retrofitVersion = "2.9.0" val okhttpVersion = "4.9.0" val coroutinesVersion = "1.6.0" + dependencies { implementation("com.squareup.retrofit2:retrofit:$retrofitVersion") diff --git a/gradle.properties b/gradle.properties index 439e234..1eea599 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,12 +13,7 @@ kapt.include.compile.classpath=false # https://youtrack.jetbrains.com/issue/KT-45545#focus=Comments-27-4862682.0-0 # Adds exports for GJF in spotless # https://github.com/diffplug/spotless/issues/834 -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \ - --add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ - --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ - --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ - --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ - --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 POM_NAME=EitherNet POM_ARTIFACT_ID=eithernet @@ -36,4 +31,6 @@ POM_LICENCE_DIST=repo POM_DEVELOPER_ID=slackhq POM_DEVELOPER_NAME=Slack Technologies, LLC POM_DEVELOPER_URL=https://github.com/slackhq -SONATYPE_STAGING_PROFILE=com.slack \ No newline at end of file +SONATYPE_STAGING_PROFILE=com.slack +SONATYPE_HOST=DEFAULT +RELEASE_SIGNING_ENABLED=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..41d9927 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102..aa991fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 3d2cae2..2ac1ab0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,4 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -rootProject.name = "eithernet" // This is important because Gradle module metadata uses this in the pom artifact id of test fixtures! +rootProject.name = + "eithernet" // This is important because Gradle module metadata uses this in the pom artifact id + // of test fixtures! diff --git a/src/main/java/com/slack/eithernet/Annotations.kt b/src/main/java/com/slack/eithernet/Annotations.kt index 47d1bde..ee6e485 100644 --- a/src/main/java/com/slack/eithernet/Annotations.kt +++ b/src/main/java/com/slack/eithernet/Annotations.kt @@ -22,9 +22,9 @@ import java.lang.reflect.WildcardType /** * Returns a [Pair] of a [StatusCode] and subset of these annotations without that [StatusCode] - * instance. This should be used in a custom Retrofit [retrofit2.Converter.Factory] to know what - * the given status code of a non-2xx response was when decoding the error body. This can be useful - * for contextually decoding error bodies based on the status code. + * instance. This should be used in a custom Retrofit [retrofit2.Converter.Factory] to know what the + * given status code of a non-2xx response was when decoding the error body. This can be useful for + * contextually decoding error bodies based on the status code. * * ``` * override fun responseBodyConverter( @@ -50,8 +50,8 @@ public fun Array.statusCode(): Pair.errorType(): Pair return nextAnnotations(ResultType::class.java) } -private fun Array.nextAnnotations(type: Class): Pair>? { +private fun Array.nextAnnotations( + type: Class +): Pair>? { var nextIndex = 0 val theseAnnotations = this var resultType: A? = null @@ -147,9 +149,8 @@ internal fun createResultType(type: Type): ResultType { is ParameterizedType -> { ownerType = type.ownerType ?: Nothing::class.java rawType = Types.getRawType(type) - typeArgs = Array(type.actualTypeArguments.size) { i -> - createResultType(type.actualTypeArguments[i]) - } + typeArgs = + Array(type.actualTypeArguments.size) { i -> createResultType(type.actualTypeArguments[i]) } } is WildcardType -> return createResultType(Util.removeSubtypeWildcard(type)) else -> error("Unrecognized type: $type") @@ -168,9 +169,10 @@ private fun createResultType( rawType: Class<*>, typeArgs: Array, isArray: Boolean -): ResultType = ResultType( - rawType = rawType.kotlin, - typeArgs = typeArgs, - ownerType = ownerType.kotlin, - isArray = isArray -) +): ResultType = + ResultType( + rawType = rawType.kotlin, + typeArgs = typeArgs, + ownerType = ownerType.kotlin, + isArray = isArray + ) diff --git a/src/main/java/com/slack/eithernet/ApiException.kt b/src/main/java/com/slack/eithernet/ApiException.kt index fdb9d26..0648a84 100644 --- a/src/main/java/com/slack/eithernet/ApiException.kt +++ b/src/main/java/com/slack/eithernet/ApiException.kt @@ -18,6 +18,7 @@ package com.slack.eithernet /** * Represents a generic API error from a given endpoint. * - * @see [ApiResult.Failure.ApiFailure] for full documentation for how this [error] property is used and its caveats. + * @see [ApiResult.Failure.ApiFailure] for full documentation for how this [error] property is used + * and its caveats. */ public class ApiException(public val error: Any?) : Exception() diff --git a/src/main/java/com/slack/eithernet/ApiResult.kt b/src/main/java/com/slack/eithernet/ApiResult.kt index 6514bf2..fdbd7a4 100644 --- a/src/main/java/com/slack/eithernet/ApiResult.kt +++ b/src/main/java/com/slack/eithernet/ApiResult.kt @@ -21,6 +21,11 @@ import com.slack.eithernet.ApiResult.Failure.HttpFailure import com.slack.eithernet.ApiResult.Failure.NetworkFailure import com.slack.eithernet.ApiResult.Failure.UnknownFailure import com.slack.eithernet.ApiResult.Success +import java.io.IOException +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.util.Collections.unmodifiableMap +import kotlin.reflect.KClass import okhttp3.Request import okhttp3.ResponseBody import retrofit2.Call @@ -29,20 +34,15 @@ import retrofit2.Callback import retrofit2.Converter import retrofit2.Response import retrofit2.Retrofit -import java.io.IOException -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.util.Collections.unmodifiableMap -import kotlin.reflect.KClass /** * Represents a result from a traditional HTTP API. [ApiResult] has two sealed subtypes: [Success] - * and [Failure]. [Success] is typed to [T] with no error type and [Failure] is typed to [E] with - * no success type. + * and [Failure]. [Success] is typed to [T] with no error type and [Failure] is typed to [E] with no + * success type. * * [Failure] in turn is represented by four sealed subtypes of its own: [Failure.NetworkFailure], - * [Failure.ApiFailure], [Failure.HttpFailure], and [Failure.UnknownFailure]. This allows for - * simple handling of results through a consistent, non-exceptional flow via sealed `when` branches. + * [Failure.ApiFailure], [Failure.HttpFailure], and [Failure.UnknownFailure]. This allows for simple + * handling of results through a consistent, non-exceptional flow via sealed `when` branches. * * ``` * when (val result = myApi.someEndpoint()) { @@ -56,16 +56,14 @@ import kotlin.reflect.KClass * } * ``` * - * Usually, user code for this could just simply show a generic error message for a [Failure] - * case, but a sealed class is exposed for more specific error messaging. + * Usually, user code for this could just simply show a generic error message for a [Failure] case, + * but a sealed class is exposed for more specific error messaging. */ public sealed interface ApiResult { /** A successful result with the data available in [response]. */ - public class Success internal constructor( - public val value: T, - tags: Map, Any> - ) : ApiResult { + public class Success + internal constructor(public val value: T, tags: Map, Any>) : ApiResult { /** Extra metadata associated with the result such as original requests, responses, etc. */ internal val tags: Map, Any> = unmodifiableMap(tags.toMap()) @@ -85,10 +83,9 @@ public sealed interface ApiResult { * non-recoverable and should be used as signal or logging before attempting to gracefully * degrade or retry. */ - public class NetworkFailure internal constructor( - public val error: IOException, - tags: Map, Any> - ) : Failure { + public class NetworkFailure + internal constructor(public val error: IOException, tags: Map, Any>) : + Failure { /** Extra metadata associated with the result such as original requests, responses, etc. */ internal val tags: Map, Any> = unmodifiableMap(tags.toMap()) @@ -105,10 +102,9 @@ public sealed interface ApiResult { * to be a non-recoverable and should be used as signal or logging before attempting to * gracefully degrade or retry. */ - public class UnknownFailure internal constructor( - public val error: Throwable, - tags: Map, Any> - ) : Failure { + public class UnknownFailure + internal constructor(public val error: Throwable, tags: Map, Any>) : + Failure { /** Extra metadata associated with the result such as original requests, responses, etc. */ internal val tags: Map, Any> = unmodifiableMap(tags.toMap()) @@ -125,11 +121,9 @@ public sealed interface ApiResult { * @property code The HTTP status code. * @property error An optional [error][E]. This would be from the error body of the response. */ - public class HttpFailure internal constructor( - public val code: Int, - public val error: E?, - tags: Map, Any> - ) : Failure { + public class HttpFailure + internal constructor(public val code: Int, public val error: E?, tags: Map, Any>) : + Failure { /** Extra metadata associated with the result such as original requests, responses, etc. */ internal val tags: Map, Any> = unmodifiableMap(tags.toMap()) @@ -141,18 +135,16 @@ public sealed interface ApiResult { } /** - * An API failure. This indicates a 2xx response where [ApiException] was thrown - * during response body conversion. + * An API failure. This indicates a 2xx response where [ApiException] was thrown during response + * body conversion. * - * An [ApiException], the [error] property will be best-effort populated with the - * value of the [ApiException.error] property. + * An [ApiException], the [error] property will be best-effort populated with the value of the + * [ApiException.error] property. * * @property error An optional [error][E]. */ - public class ApiFailure internal constructor( - public val error: E?, - tags: Map, Any> - ) : Failure { + public class ApiFailure + internal constructor(public val error: E?, tags: Map, Any>) : Failure { /** Extra metadata associated with the result such as original requests, responses, etc. */ internal val tags: Map, Any> = unmodifiableMap(tags.toMap()) @@ -187,7 +179,8 @@ public sealed interface ApiResult { public fun apiFailure(error: E? = null): ApiFailure = ApiFailure(error, emptyMap()) /** Returns a new [NetworkFailure] with given [error]. */ - public fun networkFailure(error: IOException): NetworkFailure = NetworkFailure(error, emptyMap()) + public fun networkFailure(error: IOException): NetworkFailure = + NetworkFailure(error, emptyMap()) /** Returns a new [UnknownFailure] with given [error]. */ public fun unknownFailure(error: Throwable): UnknownFailure = UnknownFailure(error, emptyMap()) @@ -205,12 +198,12 @@ public sealed interface ApiResult { } /** - * A custom [Converter.Factory] for [ApiResult] responses. This creates a delegating adapter for the underlying type - * of the result, and wraps successful results in a new [ApiResult]. + * A custom [Converter.Factory] for [ApiResult] responses. This creates a delegating adapter for the + * underlying type of the result, and wraps successful results in a new [ApiResult]. * - * When delegating to a converter for the `Success` type, a [ResultType] annotation is added to - * the forwarded annotations to allow for a downstream adapter to potentially contextually decode - * the result and throw an [ApiException] with a decoded error type. + * When delegating to a converter for the `Success` type, a [ResultType] annotation is added to the + * forwarded annotations to allow for a downstream adapter to potentially contextually decode the + * result and throw an [ApiException] with a decoded error type. */ public object ApiResultConverterFactory : Converter.Factory() { @@ -225,11 +218,8 @@ public object ApiResultConverterFactory : Converter.Factory() { val errorType = type.actualTypeArguments[1] val errorResultType: Annotation = createResultType(errorType) val nextAnnotations = annotations + errorResultType - val delegateConverter = retrofit.nextResponseBodyConverter( - this, - successType, - nextAnnotations - ) + val delegateConverter = + retrofit.nextResponseBodyConverter(this, successType, nextAnnotations) return ApiResultConverter(delegateConverter) } @@ -263,12 +253,7 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { } val decodeErrorBody = annotations.any { it is DecodeErrorBody } - return ApiResultCallAdapter( - retrofit, - apiResultType, - decodeErrorBody, - annotations - ) + return ApiResultCallAdapter(retrofit, apiResultType, decodeErrorBody, annotations) } private class ApiResultCallAdapter( @@ -289,10 +274,7 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { callback.onResponse( call, Response.success( - ApiFailure( - error = t.error, - tags = mapOf(Request::class to call.request()) - ) + ApiFailure(error = t.error, tags = mapOf(Request::class to call.request())) ) ) } @@ -300,10 +282,7 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { callback.onResponse( call, Response.success( - NetworkFailure( - error = t, - tags = mapOf(Request::class to call.request()) - ), + NetworkFailure(error = t, tags = mapOf(Request::class to call.request())), ) ) } @@ -311,10 +290,7 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { callback.onResponse( call, Response.success( - UnknownFailure( - error = t, - tags = mapOf(Request::class to call.request()) - ), + UnknownFailure(error = t, tags = mapOf(Request::class to call.request())), ) ) } @@ -326,14 +302,14 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { response: Response>, ) { if (response.isSuccessful) { - // Repackage the initial result with new tags with this call's request + response - val tags = mapOf( - okhttp3.Response::class to response.raw() - ) - val withTag = when (val result = response.body()) { - is Success -> result.withTags(result.tags + tags) - else -> null - } + // Repackage the initial result with new tags with this call's request + + // response + val tags = mapOf(okhttp3.Response::class to response.raw()) + val withTag = + when (val result = response.body()) { + is Success -> result.withTags(result.tags + tags) + else -> null + } callback.onResponse(call, Response.success(withTag)) } else { var errorBody: Any? = null @@ -346,24 +322,24 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { val statusCode = createStatusCode(response.code()) val nextAnnotations = annotations + statusCode @Suppress("TooGenericExceptionCaught") - errorBody = try { - retrofit.responseBodyConverter(errorType, nextAnnotations) - .convert(responseBody) - } catch (e: Throwable) { - @Suppress("UNCHECKED_CAST") - callback.onResponse( - call, - Response.success( - UnknownFailure( - error = e, - tags = mapOf( - okhttp3.Response::class to response.raw() + errorBody = + try { + retrofit + .responseBodyConverter(errorType, nextAnnotations) + .convert(responseBody) + } catch (e: Throwable) { + @Suppress("UNCHECKED_CAST") + callback.onResponse( + call, + Response.success( + UnknownFailure( + error = e, + tags = mapOf(okhttp3.Response::class to response.raw()) ) ) ) - ) - return - } + return + } } } @Suppress("UNCHECKED_CAST") @@ -373,9 +349,7 @@ public object ApiResultCallAdapterFactory : CallAdapter.Factory() { HttpFailure( code = response.code(), error = errorBody, - tags = mapOf( - okhttp3.Response::class to response.raw() - ) + tags = mapOf(okhttp3.Response::class to response.raw()) ) ) ) diff --git a/src/main/java/com/slack/eithernet/DecodeErrorBody.kt b/src/main/java/com/slack/eithernet/DecodeErrorBody.kt index 42a2ca2..4c8e59b 100644 --- a/src/main/java/com/slack/eithernet/DecodeErrorBody.kt +++ b/src/main/java/com/slack/eithernet/DecodeErrorBody.kt @@ -24,6 +24,4 @@ import kotlin.annotation.AnnotationTarget.FUNCTION * * This API should be considered read-only. */ -@Target(FUNCTION) -@Retention(RUNTIME) -public annotation class DecodeErrorBody +@Target(FUNCTION) @Retention(RUNTIME) public annotation class DecodeErrorBody diff --git a/src/main/java/com/slack/eithernet/InternalEitherNetApi.kt b/src/main/java/com/slack/eithernet/InternalEitherNetApi.kt index d56dbf0..3057d04 100644 --- a/src/main/java/com/slack/eithernet/InternalEitherNetApi.kt +++ b/src/main/java/com/slack/eithernet/InternalEitherNetApi.kt @@ -23,15 +23,16 @@ import kotlin.annotation.AnnotationTarget.PROPERTY import kotlin.annotation.AnnotationTarget.TYPEALIAS /** - * Marks declarations that are **internal** in EitherNet API, which means that they should not be used outside of - * `com.slack.eithernet`, because their signatures and semantics will change between future releases without any - * warnings and without providing any migration aids. + * Marks declarations that are **internal** in EitherNet API, which means that they should not be + * used outside of `com.slack.eithernet`, because their signatures and semantics will change between + * future releases without any warnings and without providing any migration aids. */ @Retention(BINARY) @Target(CLASS, FUNCTION, TYPEALIAS, PROPERTY) @RequiresOptIn( level = ERROR, - message = "This is an internal EitherNet API that " + - "should not be used from outside of EitherNet. No compatibility guarantees are provided." + message = + "This is an internal EitherNet API that " + + "should not be used from outside of EitherNet. No compatibility guarantees are provided." ) public annotation class InternalEitherNetApi diff --git a/src/main/java/com/slack/eithernet/StatusCode.kt b/src/main/java/com/slack/eithernet/StatusCode.kt index 569ff2e..23afb79 100644 --- a/src/main/java/com/slack/eithernet/StatusCode.kt +++ b/src/main/java/com/slack/eithernet/StatusCode.kt @@ -18,10 +18,9 @@ package com.slack.eithernet import kotlin.annotation.AnnotationRetention.RUNTIME /** - * Represents a status code in a 4xx or 5xx response. Retrieve it from Retrofit annotations - * via [statusCode]. + * Represents a status code in a 4xx or 5xx response. Retrieve it from Retrofit annotations via + * [statusCode]. * * This API should be considered read-only. */ -@Retention(RUNTIME) -public annotation class StatusCode(public val value: Int) +@Retention(RUNTIME) public annotation class StatusCode(public val value: Int) diff --git a/src/main/java/com/slack/eithernet/Tags.kt b/src/main/java/com/slack/eithernet/Tags.kt index f8a876c..42c683a 100644 --- a/src/main/java/com/slack/eithernet/Tags.kt +++ b/src/main/java/com/slack/eithernet/Tags.kt @@ -14,6 +14,7 @@ * limitations under the License. */ @file:Suppress("UNCHECKED_CAST") + package com.slack.eithernet import com.slack.eithernet.ApiResult.Failure.ApiFailure @@ -21,32 +22,27 @@ import com.slack.eithernet.ApiResult.Failure.HttpFailure import com.slack.eithernet.ApiResult.Failure.NetworkFailure import com.slack.eithernet.ApiResult.Failure.UnknownFailure import com.slack.eithernet.ApiResult.Success +import kotlin.reflect.KClass import okhttp3.Request import okhttp3.Response -import kotlin.reflect.KClass /* * Common tags added automatically to different ApiResult types. */ -/** - * Returns the tag attached with [T] as a key, or null if no tag is attached with that - * key. - */ +/** Returns the tag attached with [T] as a key, or null if no tag is attached with that key. */ public inline fun ApiResult<*, *>.tag(): T? = tag(T::class) -/** - * Returns the tag attached with [klass] as a key, or null if no tag is attached with that - * key. - */ +/** Returns the tag attached with [klass] as a key, or null if no tag is attached with that key. */ public fun ApiResult<*, *>.tag(klass: KClass): T? { - val tags = when (this) { - is ApiFailure -> tags - is HttpFailure -> tags - is NetworkFailure -> tags - is UnknownFailure -> tags - is Success -> tags - } + val tags = + when (this) { + is ApiFailure -> tags + is HttpFailure -> tags + is NetworkFailure -> tags + is UnknownFailure -> tags + is Success -> tags + } return tags[klass] as? T } diff --git a/src/test/kotlin/com/slack/eithernet/ApiResultTest.kt b/src/test/kotlin/com/slack/eithernet/ApiResultTest.kt index 9b1094c..38f3757 100644 --- a/src/test/kotlin/com/slack/eithernet/ApiResultTest.kt +++ b/src/test/kotlin/com/slack/eithernet/ApiResultTest.kt @@ -21,6 +21,9 @@ import com.slack.eithernet.ApiResult.Failure.HttpFailure import com.slack.eithernet.ApiResult.Failure.NetworkFailure import com.slack.eithernet.ApiResult.Failure.UnknownFailure import com.slack.eithernet.ApiResult.Success +import java.io.IOException +import java.lang.reflect.Type +import kotlin.annotation.AnnotationRetention.RUNTIME import kotlinx.coroutines.test.runTest import okhttp3.RequestBody import okhttp3.ResponseBody @@ -36,37 +39,32 @@ import retrofit2.Retrofit import retrofit2.converter.scalars.ScalarsConverterFactory import retrofit2.create import retrofit2.http.GET -import java.io.IOException -import java.lang.reflect.Type -import kotlin.annotation.AnnotationRetention.RUNTIME class ApiResultTest { - @get:Rule - val server = MockWebServer() + @get:Rule val server = MockWebServer() private lateinit var service: TestApi @Before fun before() { - val retrofit = Retrofit.Builder() - .baseUrl(server.url("/")) - .addConverterFactory(ApiResultConverterFactory) - .addCallAdapterFactory(ApiResultCallAdapterFactory) - .addConverterFactory(UnitConverterFactory) - .addConverterFactory(ErrorConverterFactory) - .addConverterFactory(ScalarsConverterFactory.create()) - .validateEagerly(true) - .build() + val retrofit = + Retrofit.Builder() + .baseUrl(server.url("/")) + .addConverterFactory(ApiResultConverterFactory) + .addCallAdapterFactory(ApiResultCallAdapterFactory) + .addConverterFactory(UnitConverterFactory) + .addConverterFactory(ErrorConverterFactory) + .addConverterFactory(ScalarsConverterFactory.create()) + .validateEagerly(true) + .build() service = retrofit.create() } @Test fun success() = runTest { - val response = MockResponse() - .setResponseCode(200) - .setBody("Response!") + val response = MockResponse().setResponseCode(200).setBody("Response!") server.enqueue(response) val result = service.testEndpoint() @@ -80,9 +78,7 @@ class ApiResultTest { @Test fun successWithUnit() = runTest { - val response = MockResponse() - .setResponseCode(200) - .setBody("Ignored!") + val response = MockResponse().setResponseCode(200).setBody("Ignored!") server.enqueue(response) val result = service.unitEndpoint() @@ -92,9 +88,7 @@ class ApiResultTest { @Test fun failureWithUnit() = runTest { - val response = MockResponse() - .setResponseCode(404) - .setBody("Ignored errors!") + val response = MockResponse().setResponseCode(404).setBody("Ignored errors!") server.enqueue(response) val result = service.unitEndpoint() @@ -109,8 +103,7 @@ class ApiResultTest { @Test fun apiHttpFailure() = runTest { - val response = MockResponse() - .setResponseCode(404) + val response = MockResponse().setResponseCode(404) server.enqueue(response) val result = service.testEndpoint() @@ -121,8 +114,7 @@ class ApiResultTest { @Test fun apiHttpFailure_5xx() = runTest { - val response = MockResponse() - .setResponseCode(500) + val response = MockResponse().setResponseCode(500) server.enqueue(response) val result = service.testEndpoint() @@ -133,9 +125,7 @@ class ApiResultTest { @Test fun apiHttpFailure_withBody() = runTest { - val response = MockResponse() - .setResponseCode(404) - .setBody("Custom errors for all") + val response = MockResponse().setResponseCode(404).setBody("Custom errors for all") server.enqueue(response) val result = service.testEndpointWithErrorBody() @@ -146,9 +136,7 @@ class ApiResultTest { @Test fun apiHttpFailure_withBodyEncodingIssue() = runTest { - val response = MockResponse() - .setResponseCode(404) - .setBody("Custom errors for all") + val response = MockResponse().setResponseCode(404).setBody("Custom errors for all") server.enqueue(response) val result = service.badEndpointWithErrorBody() @@ -164,8 +152,7 @@ class ApiResultTest { @Test fun apiHttpFailure_withBody_missingBody() = runTest { - val response = MockResponse() - .setResponseCode(404) + val response = MockResponse().setResponseCode(404) server.enqueue(response) val result = service.testEndpointWithErrorBody() @@ -177,9 +164,7 @@ class ApiResultTest { @Test fun apiFailure() = runTest { val errorMessage = "${ErrorConverterFactory.ERROR_MARKER}This is an error message." - val response = MockResponse() - .setResponseCode(200) - .setBody(errorMessage) + val response = MockResponse().setResponseCode(200).setBody(errorMessage) server.enqueue(response) val result = service.testEndpoint() @@ -193,9 +178,7 @@ class ApiResultTest { @Test fun apiFailure_customMarker() = runTest { val errorMessage = "${ErrorConverterFactory.ERROR_MARKER}The rest of this is ignored." - val response = MockResponse() - .setResponseCode(200) - .setBody(errorMessage) + val response = MockResponse().setResponseCode(200).setBody(errorMessage) server.enqueue(response) val result = service.customErrorTypeEndpoint() @@ -206,9 +189,7 @@ class ApiResultTest { @Test fun apiFailure_unknownErrorType() = runTest { val errorMessage = "${ErrorConverterFactory.ERROR_MARKER}The rest of this is ignored." - val response = MockResponse() - .setResponseCode(200) - .setBody(errorMessage) + val response = MockResponse().setResponseCode(200).setBody(errorMessage) server.enqueue(response) val result = service.unknownErrorTypeEndpoint() @@ -238,15 +219,10 @@ class ApiResultTest { @Test fun unknownFailure() = runTest { // Triggers an encoding failure - server.enqueue( - MockResponse() - .setResponseCode(200) - .setBody("") - ) + server.enqueue(MockResponse().setResponseCode(200).setBody("")) val result = service.badEndpoint() assertThat(result).isInstanceOf(UnknownFailure::class.java) - assertThat((result as UnknownFailure).error) - .isInstanceOf(BadEndpointException::class.java) + assertThat((result as UnknownFailure).error).isInstanceOf(BadEndpointException::class.java) } @Test @@ -295,33 +271,28 @@ class ApiResultTest { } interface TestApi { - @GET("/") - suspend fun testEndpoint(): ApiResult + @GET("/") suspend fun testEndpoint(): ApiResult - @DecodeErrorBody - @GET("/") - suspend fun testEndpointWithErrorBody(): ApiResult + @DecodeErrorBody @GET("/") suspend fun testEndpointWithErrorBody(): ApiResult @BadEndpoint @DecodeErrorBody @GET("/") suspend fun badEndpointWithErrorBody(): ApiResult - @GET("/") - suspend fun unitEndpoint(): ApiResult + @GET("/") suspend fun unitEndpoint(): ApiResult - @GET("/") - suspend fun customErrorTypeEndpoint(): ApiResult + @GET("/") suspend fun customErrorTypeEndpoint(): ApiResult - @GET("/") - suspend fun unknownErrorTypeEndpoint(): ApiResult + @GET("/") suspend fun unknownErrorTypeEndpoint(): ApiResult - @BadEndpoint - @GET("/") - suspend fun badEndpoint(): ApiResult + @BadEndpoint @GET("/") suspend fun badEndpoint(): ApiResult } - /** Just here for testing. In a real endpoint this would be handled by something like MoshiConverterFactory. */ + /** + * Just here for testing. In a real endpoint this would be handled by something like + * MoshiConverterFactory. + */ object UnitConverterFactory : Converter.Factory() { override fun responseBodyConverter( type: Type, @@ -355,8 +326,7 @@ class ApiResultTest { MARKER } - @Retention(RUNTIME) - annotation class BadEndpoint + @Retention(RUNTIME) annotation class BadEndpoint class BadEndpointException : RuntimeException() @@ -388,9 +358,7 @@ class ApiResultTest { throw NotImplementedError("Test only") } - class ResponseBodyConverter( - private val errorType: Type - ) : Converter { + class ResponseBodyConverter(private val errorType: Type) : Converter { override fun convert(value: ResponseBody): String { val text = value.string() if (text.startsWith(ERROR_MARKER)) { diff --git a/src/test/kotlin/com/slack/eithernet/EitherNetControllersTest.kt b/src/test/kotlin/com/slack/eithernet/EitherNetControllersTest.kt index d4f060f..31564cd 100644 --- a/src/test/kotlin/com/slack/eithernet/EitherNetControllersTest.kt +++ b/src/test/kotlin/com/slack/eithernet/EitherNetControllersTest.kt @@ -22,17 +22,17 @@ import com.slack.eithernet.ApiResult.Success import com.slack.eithernet.test.ApiValidator import com.slack.eithernet.test.enqueue import com.slack.eithernet.test.newEitherNetController +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.full.hasAnnotation +import kotlin.test.assertFailsWith +import kotlin.test.fail import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout import org.junit.Test -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.full.hasAnnotation -import kotlin.test.assertFailsWith -import kotlin.test.fail class EitherNetControllersTest { @@ -41,9 +41,7 @@ class EitherNetControllersTest { val testApi = newEitherNetController() val api = testApi.api - testApi.enqueue(PandaApi::getPandas) { - ApiResult.success("Po") - } + testApi.enqueue(PandaApi::getPandas) { ApiResult.success("Po") } val result = api.getPandas() check(result is Success) @@ -116,14 +114,16 @@ class EitherNetControllersTest { newEitherNetController() fail() } catch (e: IllegalStateException) { - assertThat(e).hasMessageThat().contains( - """ + assertThat(e) + .hasMessageThat() + .contains( + """ Service errors found for BadApi - Function missingApiResult must return ApiResult for EitherNet to work. - Function missingSlackEndpoint is missing @SlackEndpoint annotation. - Function missingSuspend must be a suspend function for EitherNet to work. """.trimIndent() - ) + ) } } @@ -153,11 +153,7 @@ class EitherNetControllersTest { } assertFailsWith { - runBlocking { - withTimeout(1000) { - api.getPandas() - } - } + runBlocking { withTimeout(1000) { api.getPandas() } } } } @@ -167,14 +163,10 @@ class EitherNetControllersTest { val api = testApi.api val expected = Exception() - testApi.enqueue(PandaApi::getPandas) { - throw expected - } + testApi.enqueue(PandaApi::getPandas) { throw expected } try { - runBlocking { - api.getPandas() - } + runBlocking { api.getPandas() } fail() } catch (e: Exception) { assertThat(e).isSameInstanceAs(expected) @@ -194,7 +186,8 @@ class EitherNetControllersTest { testApi.assertNoMoreQueuedResults() fail() } catch (e: AssertionError) { - assertThat(e).hasMessageThat() + assertThat(e) + .hasMessageThat() .isEqualTo( """ Found unprocessed ApiResults: @@ -204,9 +197,7 @@ class EitherNetControllersTest { } // Now process the result - runBlocking { - api.getPandas() - } + runBlocking { api.getPandas() } // Now it successfully asserts testApi.assertNoMoreQueuedResults() @@ -214,40 +205,30 @@ class EitherNetControllersTest { // Cover for inherited APIs interface BaseApi { - @SlackEndpoint - suspend fun getPandas(): ApiResult + @SlackEndpoint suspend fun getPandas(): ApiResult } interface PandaApi : BaseApi { - @SlackEndpoint - suspend fun getPandasWithParams(count: Int): ApiResult + @SlackEndpoint suspend fun getPandasWithParams(count: Int): ApiResult } interface AnotherApi { - @SlackEndpoint - suspend fun getPandas(): ApiResult + @SlackEndpoint suspend fun getPandas(): ApiResult } interface BadApi { suspend fun missingSlackEndpoint(): ApiResult - @SlackEndpoint - fun missingSuspend(): ApiResult + @SlackEndpoint fun missingSuspend(): ApiResult - @SlackEndpoint - suspend fun missingApiResult(): String + @SlackEndpoint suspend fun missingApiResult(): String - fun defaultMethodIsSkipped() { - } + fun defaultMethodIsSkipped() {} - @JvmSynthetic - fun syntheticMethodIsSkipped() { - } + @JvmSynthetic fun syntheticMethodIsSkipped() {} companion object { - @JvmStatic - fun staticMethodsIsSkipped() { - } + @JvmStatic fun staticMethodsIsSkipped() {} } } } diff --git a/src/test/kotlin/com/slack/eithernet/ResultTypeTest.kt b/src/test/kotlin/com/slack/eithernet/ResultTypeTest.kt index 8cf39db..bafdcf5 100644 --- a/src/test/kotlin/com/slack/eithernet/ResultTypeTest.kt +++ b/src/test/kotlin/com/slack/eithernet/ResultTypeTest.kt @@ -16,42 +16,34 @@ package com.slack.eithernet import com.google.common.truth.Truth.assertThat -import org.junit.Test import java.lang.reflect.Type import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.reflect.KType import kotlin.reflect.javaType import kotlin.reflect.typeOf +import org.junit.Test -@Retention(RUNTIME) -annotation class SampleAnnotation +@Retention(RUNTIME) annotation class SampleAnnotation @SampleAnnotation class ResultTypeTest { - @Test - fun classType() = testType() + @Test fun classType() = testType() - @Test - fun parameterizedType() = testType>() + @Test fun parameterizedType() = testType>() @Test fun parameterizedTypeWithOwner() { - val typeWithOwner = Types.newParameterizedTypeWithOwner( - ResultTypeTest::class.java, - A::class.java, - B::class.java - ) + val typeWithOwner = + Types.newParameterizedTypeWithOwner(ResultTypeTest::class.java, A::class.java, B::class.java) val annotation = createResultType(typeWithOwner) val created = annotation.toType() created.assertEqualTo(typeWithOwner) } - @Test - fun enumType() = testType() + @Test fun enumType() = testType() - @Test - fun array() = testType>() + @Test fun array() = testType>() @Test fun wildcard() { @@ -63,9 +55,10 @@ class ResultTypeTest { @Test fun errorType_present() { - val annotations = Array(4) { - ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) - } + val annotations = + Array(4) { + ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) + } val resultTypeAnnotation = createResultType(String::class.java) annotations[0] = resultTypeAnnotation val (resultType, nextAnnotations) = annotations.errorType() ?: error("No annotation found") @@ -75,17 +68,19 @@ class ResultTypeTest { @Test fun errorType_absent() { - val annotations = Array(4) { - ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) - } + val annotations = + Array(4) { + ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) + } assertThat(annotations.errorType()).isNull() } @Test fun statusCode_present() { - val annotations = Array(4) { - ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) - } + val annotations = + Array(4) { + ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) + } val statusCodeAnnotation = createStatusCode(404) annotations[0] = statusCodeAnnotation val (statusCode, nextAnnotations) = annotations.statusCode() ?: error("No annotation found") @@ -95,9 +90,10 @@ class ResultTypeTest { @Test fun statusCode_absent() { - val annotations = Array(4) { - ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) - } + val annotations = + Array(4) { + ResultTypeTest::class.java.getAnnotation(SampleAnnotation::class.java) + } assertThat(annotations.statusCode()).isNull() } diff --git a/src/testFixtures/java/com/slack/eithernet/test/ApiValidation.kt b/src/testFixtures/java/com/slack/eithernet/test/ApiValidation.kt index 856be46..8e0de1e 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/ApiValidation.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/ApiValidation.kt @@ -30,13 +30,9 @@ fun interface ApiValidator { * * If any errors are found, add a detailed description to [errors]. */ - fun validate( - apiClass: KClass<*>, - function: KFunction<*>, - errors: MutableList - ) + fun validate(apiClass: KClass<*>, function: KFunction<*>, errors: MutableList) } @OptIn(ExperimentalEitherNetApi::class) -internal fun loadValidators(): Set = ServiceLoader.load(ApiValidator::class.java) - .toSet() +internal fun loadValidators(): Set = + ServiceLoader.load(ApiValidator::class.java).toSet() diff --git a/src/testFixtures/java/com/slack/eithernet/test/EitherNetController.kt b/src/testFixtures/java/com/slack/eithernet/test/EitherNetController.kt index 390bbb6..4ef03d8 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/EitherNetController.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/EitherNetController.kt @@ -21,7 +21,8 @@ import kotlin.reflect.KFunction import kotlin.reflect.jvm.javaMethod /** - * This is a helper API returned by [newEitherNetController] to help with testing EitherNet endpoints. + * This is a helper API returned by [newEitherNetController] to help with testing EitherNet + * endpoints. * * Simple usage looks something like this: * ``` @@ -64,8 +65,7 @@ inline fun EitherNetControll "Given function ${ref.javaMethod?.declaringClass}.${ref.name} is not a member of target API $apiClass." } val key = createEndpointKey(ref.javaMethod!!) - @OptIn(InternalEitherNetApi::class) - unsafeEnqueue(key, resultBody) + @OptIn(InternalEitherNetApi::class) unsafeEnqueue(key, resultBody) } /** Enqueues a scalar [result] instance. */ diff --git a/src/testFixtures/java/com/slack/eithernet/test/EitherNetControllers.kt b/src/testFixtures/java/com/slack/eithernet/test/EitherNetControllers.kt index 55637da..9853f36 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/EitherNetControllers.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/EitherNetControllers.kt @@ -45,10 +45,11 @@ fun newEitherNetController(service: Class): EitherNetController fun newEitherNetController(service: KClass): EitherNetController { service.validateApi() // Get functions with retrofit annotations - val endpoints = service.functions - .filter { it.isApplicable } - .map { createEndpointKey(it.javaMethod!!) } // We know javaMethod is present per the filter - .associateWithTo(ConcurrentHashMap()) { ArrayDeque() } + val endpoints = + service.functions + .filter { it.isApplicable } + .map { createEndpointKey(it.javaMethod!!) } // We know javaMethod is present per the filter + .associateWithTo(ConcurrentHashMap()) { ArrayDeque() } val orchestrator = EitherNetTestOrchestrator(endpoints) val proxy = newProxy(service.java, orchestrator) return RealEitherNetController(orchestrator, proxy) @@ -63,7 +64,8 @@ private val KFunction<*>.isApplicable: Boolean !Modifier.isStatic(method.modifiers) && !method.isSynthetic && !method.isBridge - } ?: false + } + ?: false } @OptIn(ExperimentalStdlibApi::class, ExperimentalEitherNetApi::class) @@ -122,9 +124,9 @@ internal fun newProxy(service: Class, orchestrator: EitherNetTestOrchestr "Last arg is not a Continuation, did you forget to add the 'suspend' modifier?" } val argsArray = Array(finalArgs.size - 1) { i -> finalArgs[i] } - val body = orchestrator.endpoints.getValue(key) - .removeFirstOrNull() - ?: error("No result enqueued for ${key.name}.") + val body = + orchestrator.endpoints.getValue(key).removeFirstOrNull() + ?: error("No result enqueued for ${key.name}.") CoroutineTransformer.transform( argsArray, body, diff --git a/src/testFixtures/java/com/slack/eithernet/test/JavaEitherNetControllers.kt b/src/testFixtures/java/com/slack/eithernet/test/JavaEitherNetControllers.kt index bc5bdb0..481f327 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/JavaEitherNetControllers.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/JavaEitherNetControllers.kt @@ -28,6 +28,5 @@ fun EitherNetController.enqueueFromJava( ) { val method = clazz.declaredMethods.first { it.name == methodName } val key = createEndpointKey(method) - @OptIn(InternalEitherNetApi::class) - unsafeEnqueue(key) { resultBody } + @OptIn(InternalEitherNetApi::class) unsafeEnqueue(key) { resultBody } } diff --git a/src/testFixtures/java/com/slack/eithernet/test/Platform.kt b/src/testFixtures/java/com/slack/eithernet/test/Platform.kt index dad488b..773084c 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/Platform.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/Platform.kt @@ -37,10 +37,8 @@ internal open class Platform(private val hasJava8Types: Boolean) { try { // Because the service interface might not be public, we need to use a MethodHandle lookup // that ignores the visibility of the declaringClass. - lookupConstructor = Lookup::class.java.getDeclaredConstructor( - Class::class.java, - Int::class.javaPrimitiveType - ) + lookupConstructor = + Lookup::class.java.getDeclaredConstructor(Class::class.java, Int::class.javaPrimitiveType) lookupConstructor.setAccessible(true) } catch (ignored: NoClassDefFoundError) { // Android API 24 or 25 where Lookup doesn't exist. Calling default methods on non-public @@ -65,17 +63,13 @@ internal open class Platform(private val hasJava8Types: Boolean) { obj: Any, vararg args: Any ): Any? { - val lookup = if (lookupConstructor != null) { - lookupConstructor.newInstance( - declaringClass, - -1 /* trusted */ - ) - } else { - MethodHandles.lookup() - } - return lookup.unreflectSpecial(method, declaringClass) - .bindTo(obj) - .invokeWithArguments(*args) + val lookup = + if (lookupConstructor != null) { + lookupConstructor.newInstance(declaringClass, -1 /* trusted */) + } else { + MethodHandles.lookup() + } + return lookup.unreflectSpecial(method, declaringClass).bindTo(obj).invokeWithArguments(*args) } internal class Android : Platform(VERSION.SDK_INT >= 24) { @@ -95,9 +89,7 @@ internal open class Platform(private val hasJava8Types: Boolean) { } companion object { - val INSTANCE by lazy { - findPlatform() - } + val INSTANCE by lazy { findPlatform() } private fun findPlatform(): Platform { return if ("Dalvik" == System.getProperty("java.vm.name")) { diff --git a/src/testFixtures/java/com/slack/eithernet/test/RealEitherNetController.kt b/src/testFixtures/java/com/slack/eithernet/test/RealEitherNetController.kt index 1d96465..39c947d 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/RealEitherNetController.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/RealEitherNetController.kt @@ -36,11 +36,12 @@ internal class RealEitherNetController( val errors = mutableListOf() orchestrator.endpoints.forEach { (endpoint, resultsQueue) -> if (resultsQueue.isNotEmpty()) { - val directObject = if (resultsQueue.size == 1) { - "result" - } else { - "results" - } + val directObject = + if (resultsQueue.size == 1) { + "result" + } else { + "results" + } errors += "-- ${endpoint.name}() has ${resultsQueue.size} unprocessed $directObject" } } diff --git a/src/testFixtures/java/com/slack/eithernet/test/Util.kt b/src/testFixtures/java/com/slack/eithernet/test/Util.kt index 78a9f94..9fe3535 100644 --- a/src/testFixtures/java/com/slack/eithernet/test/Util.kt +++ b/src/testFixtures/java/com/slack/eithernet/test/Util.kt @@ -16,7 +16,6 @@ package com.slack.eithernet.test import com.slack.eithernet.ApiResult -import kotlinx.coroutines.Dispatchers import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED import kotlin.coroutines.intrinsics.intercepted import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn @@ -24,6 +23,7 @@ import kotlin.coroutines.resumeWithException import kotlin.reflect.KFunction import kotlin.reflect.KType import kotlin.reflect.typeOf +import kotlinx.coroutines.Dispatchers internal typealias SuspendedResult = suspend (args: Array) -> ApiResult<*, *> @@ -59,8 +59,7 @@ internal inline fun KFunction // Note that we don't use Moshi's nicer canonicalize APIs because it would lose the Kotlin type // information like nullability and intrinsic types. val type = returnType - val (success, error) = type.arguments - .map { it.type!! } + val (success, error) = type.arguments.map { it.type!! } check(success isEqualTo typeOf()) { "Type check failed! Expected success type of '$success' but found '${typeOf()}'. Ensure that your result type matches the target endpoint as the IDE won't correctly infer this!" @@ -79,5 +78,7 @@ internal inline fun KFunction */ @PublishedApi internal infix fun KType.isEqualTo(other: KType): Boolean { - return classifier == other.classifier && arguments == other.arguments && isMarkedNullable == other.isMarkedNullable + return classifier == other.classifier && + arguments == other.arguments && + isMarkedNullable == other.isMarkedNullable }