Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework ConnectError to ConnectException #120

Merged
merged 11 commits into from
Oct 3, 2023
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 @@ -247,11 +247,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.error).isInstanceOf(ConnectException::class.java)
val connectException = result.error 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 @@ -363,8 +363,8 @@ class Conformance(
val job = async {
try {
val result = streamResults(stream.resultChannel())
assertThat(result.error).isInstanceOf(ConnectError::class.java)
val connectErr = result.error as ConnectError
assertThat(result.error).isInstanceOf(ConnectException::class.java)
val connectErr = result.error as ConnectException
assertThat(connectErr.code).isEqualTo(Code.DEADLINE_EXCEEDED)
assertThat(result.code).isEqualTo(Code.DEADLINE_EXCEEDED)
} finally {
Expand Down Expand Up @@ -438,8 +438,8 @@ 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(result.error).isInstanceOf(ConnectException::class.java)
val connectErr = result.error as ConnectException
assertThat(connectErr.code).isEqualTo(Code.UNIMPLEMENTED)
} finally {
countDownLatch.countDown()
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()
val connectErr = result.connectException()
if (connectErr != null) {
throw connectErr
}
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()
val connectErr = result.connectException()
if (connectErr != null) {
throw connectErr
}
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
2 changes: 1 addition & 1 deletion library/src/main/kotlin/com/connectrpc/ResponseMessage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ sealed class ResponseMessage<Output>(

class Failure<Output>(
// The error.
val error: ConnectError,
val error: ConnectException,
// The status code of the response.
override val code: Code,
// Response headers specified by the server.
Expand Down
11 changes: 3 additions & 8 deletions library/src/main/kotlin/com/connectrpc/StreamResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,11 @@ 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>() {
/**
* 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() = error as? ConnectException

override fun toString(): String {
return "Complete{code=$code,error=$error,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 error: ConnectException? = null,
)
12 changes: 6 additions & 6 deletions library/src/main/kotlin/com/connectrpc/impl/ClientOnlyStream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package com.connectrpc.impl
import com.connectrpc.BidirectionalStreamInterface
import com.connectrpc.ClientOnlyStreamInterface
import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.Headers
import com.connectrpc.ResponseMessage

Expand Down Expand Up @@ -45,7 +45,7 @@ internal class ClientOnlyStream<Input, Output>(
var message: Output? = null
var trailers: Headers = emptyMap()
var code: Code? = null
var error: ConnectError? = null
var error: ConnectException? = null
for (result in resultChannel) {
result.maybeFold(
onHeaders = {
Expand All @@ -59,10 +59,10 @@ internal class ClientOnlyStream<Input, Output>(
trailers = it.trailers
val resultErr = it.error
if (resultErr != null) {
error = if (resultErr is ConnectError) {
error = if (resultErr is ConnectException) {
resultErr
} else {
ConnectError(code ?: Code.UNKNOWN, message = error?.message, exception = error, metadata = trailers)
ConnectException(code ?: Code.UNKNOWN, message = error?.message, exception = error, metadata = trailers)
}
}
},
Expand All @@ -72,14 +72,14 @@ internal class ClientOnlyStream<Input, Output>(
return ResponseMessage.Failure(error!!, code ?: Code.UNKNOWN, headers, trailers)
}
if (code == null) {
return ResponseMessage.Failure(ConnectError(Code.UNKNOWN, message = "unknown status code"), Code.UNKNOWN, headers, trailers)
return ResponseMessage.Failure(ConnectException(Code.UNKNOWN, message = "unknown status code"), Code.UNKNOWN, headers, trailers)
}
if (message != null) {
return ResponseMessage.Success(message!!, code!!, headers, trailers)
}
// We didn't receive an error at any point, however we didn't get a response message either.
return ResponseMessage.Failure(
ConnectError(Code.UNKNOWN, message = "missing response message"),
ConnectException(Code.UNKNOWN, message = "missing response message"),
code!!,
headers,
trailers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ class ProtocolClient(
is StreamResult.Complete -> {
isComplete = true
StreamResult.Complete(
streamResult.connectError()?.code ?: Code.OK,
streamResult.connectException()?.code ?: Code.OK,
error = streamResult.error,
trailers = streamResult.trailers,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package com.connectrpc.protocols

import com.connectrpc.Code
import com.connectrpc.Codec
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.ConnectErrorDetail
import com.connectrpc.Headers
import com.connectrpc.Idempotency
Expand Down Expand Up @@ -169,7 +169,7 @@ internal class ConnectInterceptor(
},
onCompletion = { result ->
val streamTrailers = result.trailers
val error = result.connectError()
val error = result.connectException()
StreamResult.Complete(error?.code ?: Code.OK, error = error, streamTrailers)
},
)
Expand Down Expand Up @@ -226,7 +226,7 @@ internal class ConnectInterceptor(
val code = Code.fromName(endStreamResponseJSON.error.code)
StreamResult.Complete(
code = code,
error = ConnectError(
error = ConnectException(
code = code,
errorDetailParser = serializationStrategy.errorDetailParser(),
message = endStreamResponseJSON.error.message,
Expand All @@ -237,24 +237,24 @@ internal class ConnectInterceptor(
}
}

private fun parseConnectUnaryError(code: Code, headers: Headers, source: Buffer?): ConnectError {
private fun parseConnectUnaryError(code: Code, headers: Headers, source: Buffer?): ConnectException {
if (source == null) {
return ConnectError(code, serializationStrategy.errorDetailParser(), "empty error message from source")
return ConnectException(code, serializationStrategy.errorDetailParser(), "empty error message from source")
}
return source.use { bufferedSource ->
val adapter = moshi.adapter(ErrorPayloadJSON::class.java)
val errorJSON = bufferedSource.readUtf8()
val errorPayloadJSON = try {
adapter.fromJson(errorJSON) ?: return ConnectError(
adapter.fromJson(errorJSON) ?: return ConnectException(
code,
serializationStrategy.errorDetailParser(),
errorJSON,
)
} catch (e: Throwable) {
return ConnectError(code, serializationStrategy.errorDetailParser(), errorJSON)
return ConnectException(code, serializationStrategy.errorDetailParser(), errorJSON)
}
val errorDetails = parseErrorDetails(errorPayloadJSON)
ConnectError(
ConnectException(
code = Code.fromName(errorPayloadJSON.code),
errorDetailParser = serializationStrategy.errorDetailParser(),
message = errorPayloadJSON.message,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.protocols

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.ConnectErrorDetail
import com.connectrpc.Headers
import com.connectrpc.SerializationStrategy
Expand All @@ -37,13 +37,13 @@ internal data class GRPCCompletion(
val metadata: Headers,
)

internal fun grpcCompletionToConnectError(completion: GRPCCompletion?, serializationStrategy: SerializationStrategy, error: Throwable?): ConnectError? {
if (error is ConnectError) {
internal fun grpcCompletionToConnectError(completion: GRPCCompletion?, serializationStrategy: SerializationStrategy, error: Throwable?): ConnectException? {
if (error is ConnectException) {
return error
}
val code = completion?.code ?: Code.UNKNOWN
if (error != null || code != Code.OK) {
return ConnectError(
return ConnectException(
code = code,
errorDetailParser = serializationStrategy.errorDetailParser(),
message = completion?.message?.utf8(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.protocols

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.Headers
import com.connectrpc.Interceptor
import com.connectrpc.ProtocolClientConfig
Expand Down Expand Up @@ -108,7 +108,7 @@ internal class GRPCInterceptor(
headers = headers,
message = result,
trailers = trailers,
error = ConnectError(
error = ConnectException(
code = code,
errorDetailParser = serializationStrategy.errorDetailParser(),
message = completion?.message?.utf8(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package com.connectrpc.protocols

import com.connectrpc.Code
import com.connectrpc.ConnectError
import com.connectrpc.ConnectException
import com.connectrpc.Headers
import com.connectrpc.Interceptor
import com.connectrpc.ProtocolClientConfig
Expand Down Expand Up @@ -106,7 +106,7 @@ internal class GRPCWebInterceptor(
headers = headers,
message = result,
trailers = trailers,
error = ConnectError(
error = ConnectException(
code = code,
errorDetailParser = serializationStrategy.errorDetailParser(),
message = completion?.message?.utf8(),
Expand Down Expand Up @@ -146,7 +146,7 @@ internal class GRPCWebInterceptor(
val result = Buffer()
val errorMessage = completionWithMessage.message
result.write(errorMessage)
ConnectError(
ConnectException(
code = finalCode,
errorDetailParser = serializationStrategy.errorDetailParser(),
message = errorMessage.utf8(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

class ConnectErrorTest {
class ConnectExceptionTest {
private val errorDetailParser: ErrorDetailParser = mock { }

@Test
Expand All @@ -30,15 +30,15 @@ class ConnectErrorTest {
"value".encodeUtf8(),
)
whenever(errorDetailParser.unpack(errorDetail.pb, String::class)).thenReturn("unpacked_value")
val connectError = ConnectError(
val connectException = ConnectException(
code = Code.UNKNOWN,
details = listOf(
errorDetail,
errorDetail,
),
errorDetailParser = errorDetailParser,
)
val parsedResult = connectError.unpackedDetails(String::class)
val parsedResult = connectException.unpackedDetails(String::class)
assertThat(parsedResult).contains("unpacked_value", "unpacked_value")
}

Expand All @@ -49,13 +49,13 @@ class ConnectErrorTest {
"value".encodeUtf8(),
)
whenever(errorDetailParser.unpack(errorDetail.pb, String::class)).thenReturn("unpacked_value")
val connectError = ConnectError(
val connectException = ConnectException(
code = Code.UNKNOWN,
details = listOf(
errorDetail,
),
)
val parsedResult = connectError.unpackedDetails(String::class)
val parsedResult = connectException.unpackedDetails(String::class)
assertThat(parsedResult).isEmpty()
}
}
Loading