Skip to content

Commit

Permalink
Rework ConnectError to ConnectException (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
jzbrooks authored Oct 3, 2023
1 parent 2b36495 commit 7b7c746
Show file tree
Hide file tree
Showing 20 changed files with 175 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.conformance

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.Headers
import com.connectrpc.ProtocolClientConfig
import com.connectrpc.RequestCompression
Expand Down Expand Up @@ -44,6 +44,7 @@ import com.google.protobuf.empty
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
Expand Down Expand Up @@ -177,7 +178,7 @@ class Conformance(
},
).getOrThrow()
val results = streamResults(stream.resultChannel())
assertThat(results.error).isNull()
assertThat(results.cause).isNull()
assertThat(results.code).isEqualTo(Code.OK)
assertThat(results.messages.map { it.payload.type }.toSet()).isEqualTo(setOf(PayloadType.COMPRESSABLE))
assertThat(results.messages.map { it.payload.body.size() }).isEqualTo(sizes)
Expand Down Expand Up @@ -211,7 +212,7 @@ class Conformance(
val results = streamResults(stream.resultChannel())
// We've already read all the messages
assertThat(results.messages).isEmpty()
assertThat(results.error).isNull()
assertThat(results.cause).isNull()
assertThat(results.code).isEqualTo(Code.OK)
stream.receiveClose()
}
Expand Down Expand Up @@ -247,11 +248,11 @@ class Conformance(
val result = streamResults(stream.resultChannel())
assertThat(result.messages.map { it.payload.body.size() }).isEqualTo(sizes)
assertThat(result.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
assertThat(result.error).isInstanceOf(ConnectError::class.java)
val connectError = result.error as ConnectError
assertThat(connectError.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
assertThat(connectError.message).isEqualTo("soirée 🎉")
assertThat(connectError.unpackedDetails(ErrorDetail::class)).containsExactly(
assertThat(result.cause).isInstanceOf(ConnectException::class.java)
val connectException = result.cause as ConnectException
assertThat(connectException.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
assertThat(connectException.message).isEqualTo("soirée 🎉")
assertThat(connectException.unpackedDetails(ErrorDetail::class)).containsExactly(
expectedErrorDetail,
)
} finally {
Expand Down Expand Up @@ -329,9 +330,9 @@ class Conformance(
testServiceConnectClient.unaryCall(message) { response ->
assertThat(response.code).isEqualTo(Code.UNKNOWN)
response.failure { errorResponse ->
assertThat(errorResponse.error).isNotNull()
assertThat(errorResponse.cause).isNotNull()
assertThat(errorResponse.code).isEqualTo(Code.UNKNOWN)
assertThat(errorResponse.error.message).isEqualTo("test status message")
assertThat(errorResponse.cause.message).isEqualTo("test status message")
countDownLatch.countDown()
}
response.success {
Expand Down Expand Up @@ -360,13 +361,13 @@ class Conformance(
}
val stream = client.streamingOutputCall()
withContext(Dispatchers.IO) {
val job = async {
val job = launch {
try {
val result = streamResults(stream.resultChannel())
assertThat(result.error).isInstanceOf(ConnectError::class.java)
val connectErr = result.error as ConnectError
assertThat(connectErr.code).isEqualTo(Code.DEADLINE_EXCEEDED)
assertThat(result.code).isEqualTo(Code.DEADLINE_EXCEEDED)
assertThat(result.cause).isInstanceOf(ConnectException::class.java)
assertThat(result.code)
.withFailMessage { "Expected Code.DEADLINE_EXCEEDED but got ${result.code}" }
.isEqualTo(Code.DEADLINE_EXCEEDED)
} finally {
countDownLatch.countDown()
}
Expand All @@ -392,7 +393,7 @@ class Conformance(
},
) { response ->
response.failure { errorResponse ->
val error = errorResponse.error
val error = errorResponse.cause
assertThat(error.code).isEqualTo(Code.UNKNOWN)
assertThat(response.code).isEqualTo(Code.UNKNOWN)
assertThat(error.message).isEqualTo(statusMessage)
Expand Down Expand Up @@ -438,9 +439,9 @@ class Conformance(
try {
val result = streamResults(stream.resultChannel())
assertThat(result.code).isEqualTo(Code.UNIMPLEMENTED)
assertThat(result.error).isInstanceOf(ConnectError::class.java)
val connectErr = result.error as ConnectError
assertThat(connectErr.code).isEqualTo(Code.UNIMPLEMENTED)
assertThat(result.cause).isInstanceOf(ConnectException::class.java)
val exception = result.cause as ConnectException
assertThat(exception.code).isEqualTo(Code.UNIMPLEMENTED)
} finally {
countDownLatch.countDown()
}
Expand All @@ -461,7 +462,7 @@ class Conformance(
testServiceConnectClient.failUnaryCall(simpleRequest {}) { response ->
assertThat(response.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
response.failure { errorResponse ->
val error = errorResponse.error
val error = errorResponse.cause
assertThat(error.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
assertThat(error.message).isEqualTo("soirée 🎉")
val connectErrorDetails = error.unpackedDetails(ErrorDetail::class)
Expand Down Expand Up @@ -544,9 +545,9 @@ class Conformance(
val response = testServiceConnectClient.unaryCallBlocking(message).execute()
assertThat(response.code).isEqualTo(Code.UNKNOWN)
response.failure { errorResponse ->
assertThat(errorResponse.error).isNotNull()
assertThat(errorResponse.cause).isNotNull()
assertThat(errorResponse.code).isEqualTo(Code.UNKNOWN)
assertThat(errorResponse.error.message).isEqualTo("test status message")
assertThat(errorResponse.cause.message).isEqualTo("test status message")
}
response.success {
fail<Unit>("unexpected success")
Expand All @@ -566,7 +567,7 @@ class Conformance(
},
).execute()
response.failure { errorResponse ->
val error = errorResponse.error
val error = errorResponse.cause
assertThat(error.code).isEqualTo(Code.UNKNOWN)
assertThat(response.code).isEqualTo(Code.UNKNOWN)
assertThat(error.message).isEqualTo(statusMessage)
Expand Down Expand Up @@ -597,7 +598,7 @@ class Conformance(
val response = testServiceConnectClient.failUnaryCallBlocking(simpleRequest {}).execute()
assertThat(response.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
response.failure { errorResponse ->
val error = errorResponse.error
val error = errorResponse.cause
assertThat(error.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
assertThat(error.message).isEqualTo("soirée 🎉")
val connectErrorDetails = error.unpackedDetails(ErrorDetail::class)
Expand Down Expand Up @@ -692,9 +693,9 @@ class Conformance(
testServiceConnectClient.unaryCall(message) { response ->
assertThat(response.code).isEqualTo(Code.UNKNOWN)
response.failure { errorResponse ->
assertThat(errorResponse.error).isNotNull()
assertThat(errorResponse.cause).isNotNull()
assertThat(errorResponse.code).isEqualTo(Code.UNKNOWN)
assertThat(errorResponse.error.message).isEqualTo("test status message")
assertThat(errorResponse.cause.message).isEqualTo("test status message")
countDownLatch.countDown()
}
response.success {
Expand All @@ -720,7 +721,7 @@ class Conformance(
},
) { response ->
response.failure { errorResponse ->
val error = errorResponse.error
val error = errorResponse.cause
assertThat(error.code).isEqualTo(Code.UNKNOWN)
assertThat(response.code).isEqualTo(Code.UNKNOWN)
assertThat(error.message).isEqualTo(statusMessage)
Expand Down Expand Up @@ -766,7 +767,7 @@ class Conformance(
testServiceConnectClient.failUnaryCall(simpleRequest {}) { response ->
assertThat(response.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
response.failure { errorResponse ->
val error = errorResponse.error
val error = errorResponse.cause
assertThat(error.code).isEqualTo(Code.RESOURCE_EXHAUSTED)
assertThat(error.message).isEqualTo("soirée 🎉")
val connectErrorDetails = error.unpackedDetails(ErrorDetail::class)
Expand Down Expand Up @@ -817,7 +818,7 @@ class Conformance(
val messages: List<Output>,
val code: Code,
val trailers: Trailers,
val error: Throwable?,
val cause: Throwable?,
)

/*
Expand Down Expand Up @@ -852,7 +853,7 @@ class Conformance(
}
code = it.code
trailers = it.trailers
error = it.error
error = it.cause
},
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.examples.kotlin

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.ProtocolClientConfig
import com.connectrpc.eliza.v1.ElizaServiceClient
import com.connectrpc.eliza.v1.converseRequest
Expand Down Expand Up @@ -72,11 +72,11 @@ class Main {
},
onCompletion = { result ->
if (result.code != Code.OK) {
val connectErr = result.connectError()
if (connectErr != null) {
throw connectErr
val exception = result.connectException()
if (exception != null) {
throw exception
}
throw ConnectError(code = result.code, metadata = result.trailers)
throw ConnectException(code = result.code, metadata = result.trailers)
}
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.examples.kotlin

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.ProtocolClientConfig
import com.connectrpc.eliza.v1.ElizaServiceClient
import com.connectrpc.eliza.v1.converseRequest
Expand Down Expand Up @@ -72,11 +72,11 @@ class Main {
},
onCompletion = { result ->
if (result.code != Code.OK) {
val connectErr = result.connectError()
if (connectErr != null) {
throw connectErr
val exception = result.connectException()
if (exception != null) {
throw exception
}
throw ConnectError(code = result.code, metadata = result.trailers)
throw ConnectException(code = result.code, metadata = result.trailers)
}
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import kotlin.reflect.KClass
* Typed error provided by Connect RPCs that may optionally wrap additional typed custom errors
* using [details].
*/
data class ConnectError(
data class ConnectException(
// The resulting status code.
val code: Code,
private val errorDetailParser: ErrorDetailParser? = null,
Expand All @@ -32,7 +32,7 @@ data class ConnectError(
val details: List<ConnectErrorDetail> = emptyList(),
// Additional key-values that were provided by the server.
val metadata: Headers = emptyMap(),
) : Throwable(message, exception) {
) : Exception(message, exception) {

/**
* Unpacks values from [details] and returns the first matching error, if any.
Expand All @@ -51,10 +51,10 @@ data class ConnectError(
}

/**
* Creates a new [ConnectError] with the specified [ErrorDetailParser].
* Creates a new [ConnectException] with the specified [ErrorDetailParser].
*/
fun setErrorParser(errorParser: ErrorDetailParser): ConnectError {
return ConnectError(
fun setErrorParser(errorParser: ErrorDetailParser): ConnectException {
return ConnectException(
code,
errorParser,
message,
Expand Down
14 changes: 7 additions & 7 deletions library/src/main/kotlin/com/connectrpc/ResponseMessage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ sealed class ResponseMessage<Output>(
}

class Failure<Output>(
// The error.
val error: ConnectError,
// The problem.
val cause: ConnectException,
// The status code of the response.
override val code: Code,
// Response headers specified by the server.
Expand All @@ -51,7 +51,7 @@ sealed class ResponseMessage<Output>(
override val trailers: Trailers,
) : ResponseMessage<Output>(code, headers, trailers) {
override fun toString(): String {
return "Failure{error=$error,code=$code,headers=$headers,trailers=$trailers}"
return "Failure{cause=$cause,code=$code,headers=$headers,trailers=$trailers}"
}
}

Expand All @@ -77,7 +77,7 @@ sealed class ResponseMessage<Output>(
fun ResponseMessage<*>.exceptionOrNull(): Throwable? {
return when (this) {
is ResponseMessage.Success -> null
is ResponseMessage.Failure -> this.error
is ResponseMessage.Failure -> this.cause
}
}

Expand All @@ -93,7 +93,7 @@ inline fun <R, T> ResponseMessage<T>.fold(
): R {
return when (this) {
is ResponseMessage.Success -> onSuccess(this.message)
is ResponseMessage.Failure -> onFailure(this.error)
is ResponseMessage.Failure -> onFailure(this.cause)
}
}

Expand All @@ -117,7 +117,7 @@ fun <T> ResponseMessage<T>.getOrDefault(defaultValue: T): T {
inline fun <R, T : R> ResponseMessage<T>.getOrElse(onFailure: (exception: Throwable) -> R): R {
return when (this) {
is ResponseMessage.Success -> this.message
is ResponseMessage.Failure -> onFailure(this.error)
is ResponseMessage.Failure -> onFailure(this.cause)
}
}

Expand All @@ -139,6 +139,6 @@ fun <T> ResponseMessage<T>.getOrNull(): T? {
fun <T> ResponseMessage<T>.getOrThrow(): T {
return when (this) {
is ResponseMessage.Success -> this.message
is ResponseMessage.Failure -> throw this.error
is ResponseMessage.Failure -> throw this.cause
}
}
15 changes: 5 additions & 10 deletions library/src/main/kotlin/com/connectrpc/StreamResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,16 @@ sealed class StreamResult<Output> {
}

// Stream is complete. Provides the end status code and optionally an error and trailers.
class Complete<Output>(val code: Code, val error: Throwable? = null, val trailers: Trailers = emptyMap()) : StreamResult<Output>() {
class Complete<Output>(val code: Code, val cause: Throwable? = null, val trailers: Trailers = emptyMap()) : StreamResult<Output>() {
/**
* Get the ConnectError from the result.
* Get the ConnectException from the result.
*
* @return The [ConnectError] if present, null otherwise.
* @return The [ConnectException] if present, null otherwise.
*/
fun connectError(): ConnectError? {
if (error is ConnectError) {
return error
}
return null
}
fun connectException() = cause as? ConnectException

override fun toString(): String {
return "Complete{code=$code,error=$error,trailers=$trailers}"
return "Complete{code=$code,cause=$cause,trailers=$trailers}"
}
}

Expand Down
4 changes: 2 additions & 2 deletions library/src/main/kotlin/com/connectrpc/http/HTTPResponse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.http

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.Headers
import com.connectrpc.Trailers
import okio.BufferedSource
Expand All @@ -38,5 +38,5 @@ class HTTPResponse(
// null in cases where no response was received from the server.
val tracingInfo: TracingInfo?,
// The accompanying error, if the request failed.
val error: ConnectError? = null,
val cause: ConnectException? = null,
)
Loading

0 comments on commit 7b7c746

Please sign in to comment.