support data field in error response

This commit is contained in:
a10zn8
2023-06-06 17:08:57 +04:00
committed by GitHub
parent 0db052024b
commit df8586258f
5 changed files with 32 additions and 35 deletions

View File

@@ -116,7 +116,7 @@ abstract class BaseHandler(
// If Proxy is configured to preserve original order it means that a client expect responses at exact same position // If Proxy is configured to preserve original order it means that a client expect responses at exact same position
// as requests even if a request completely failed for a some reason. It's very unlikely situation, but still possible // as requests even if a request completely failed for a some reason. It's very unlikely situation, but still possible
// At this case, if we found a gap in responses, we put a default response with an error // At this case, if we found a gap in responses, we put a default response with an error
?: NativeCall.CallResult(id, null, null, NativeCall.CallError(id, "No response", null), null, null, null) ?: NativeCall.CallResult(id, null, null, NativeCall.CallError(id, "No response", null, null), null, null, null)
} }
} }
.flatMapMany { .flatMapMany {

View File

@@ -203,7 +203,12 @@ open class NativeCall(
.setId(it.id) .setId(it.id)
if (it.isError()) { if (it.isError()) {
it.error?.let { error -> it.error?.let { error ->
result.setErrorMessage(error.message).setErrorCode(error.id) result.setErrorMessage(error.message)
.setErrorCode(error.id)
error.data?.let { data ->
result.setErrorData(data)
}
} }
} else { } else {
result.payload = ByteString.copyFrom(it.result) result.payload = ByteString.copyFrom(it.result)
@@ -301,7 +306,8 @@ open class NativeCall(
CallError( CallError(
requestItem.id, requestItem.id,
errorMessage, errorMessage,
JsonRpcError(RpcResponseError.CODE_METHOD_NOT_EXIST, errorMessage) JsonRpcError(RpcResponseError.CODE_METHOD_NOT_EXIST, errorMessage),
null
), ),
requestId, requestId,
requestCount requestCount
@@ -409,7 +415,7 @@ open class NativeCall(
counter.get().let { attempts -> counter.get().let { attempts ->
CallResult.fail( CallResult.fail(
ctx.id, ctx.nonce, ctx.id, ctx.nonce,
CallError(1, "No response or no available upstream for ${ctx.payload.method}", null), CallError(1, "No response or no available upstream for ${ctx.payload.method}", null, null),
ctx ctx
).also { ).also {
countFailure(attempts, ctx) countFailure(attempts, ctx)
@@ -592,20 +598,33 @@ open class NativeCall(
open class CallFailure(val id: Int, val reason: Throwable) : Exception("Failed to call $id: ${reason.message}") open class CallFailure(val id: Int, val reason: Throwable) : Exception("Failed to call $id: ${reason.message}")
open class CallError(val id: Int, val message: String, val upstreamError: JsonRpcError?) { open class CallError(val id: Int, val message: String, val upstreamError: JsonRpcError?, val data: String?) {
companion object { companion object {
private val log = LoggerFactory.getLogger(CallError::class.java)
private fun getDataAsSting(details: Any?): String? {
return when (details) {
is String -> details
null -> null
else -> {
log.debug("Unsupported error details: {}", details)
null
}
}
}
fun from(t: Throwable): CallError { fun from(t: Throwable): CallError {
return when (t) { return when (t) {
is JsonRpcException -> CallError(t.id.asNumber().toInt(), t.error.message, t.error) is JsonRpcException -> CallError(t.id.asNumber().toInt(), t.error.message, t.error, getDataAsSting(t.error.details))
is RpcException -> CallError(t.code, t.rpcMessage, null) is RpcException -> CallError(t.code, t.rpcMessage, null, getDataAsSting(t.details))
is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null) is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null, null)
else -> { else -> {
// May only happen if it's an unhandled exception. // May only happen if it's an unhandled exception.
// In this case try to find a meaningless details in the stack. Most important reason for doing that is to find an ID of the request // In this case try to find a meaningless details in the stack. Most important reason for doing that is to find an ID of the request
if (t.cause != null) { if (t.cause != null) {
from(t.cause!!) from(t.cause!!)
} else { } else {
CallError(1, t.message ?: "Upstream Error", null) CallError(1, t.message ?: "Upstream Error", null, null)
} }
} }
} }

View File

@@ -17,7 +17,7 @@ package io.emeraldpay.dshackle.upstream.rpcclient
import io.emeraldpay.etherjar.rpc.RpcException import io.emeraldpay.etherjar.rpc.RpcException
class JsonRpcError(val code: Int, val message: String, val details: Any?) { data class JsonRpcError(val code: Int, val message: String, val details: Any?) {
constructor(code: Int, message: String) : this(code, message, null) constructor(code: Int, message: String) : this(code, message, null)
@@ -33,26 +33,4 @@ class JsonRpcError(val code: Int, val message: String, val details: Any?) {
fun asException(id: JsonRpcResponse.Id?): JsonRpcException { fun asException(id: JsonRpcResponse.Id?): JsonRpcException {
return JsonRpcException(id ?: JsonRpcResponse.NumberId(-1), this, false) return JsonRpcException(id ?: JsonRpcResponse.NumberId(-1), this, false)
} }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is JsonRpcError) return false
if (code != other.code) return false
if (message != other.message) return false
if (details != other.details) return false
return true
}
override fun hashCode(): Int {
var result = code
result = 31 * result + message.hashCode()
result = 31 * result + (details?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return "JsonRpcError(code=$code, message='$message', details=$details)"
}
} }

View File

@@ -98,7 +98,7 @@ class WriteRpcJsonSpec extends Specification {
def call = new ProxyCall(ProxyCall.RpcType.SINGLE) def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
call.ids[1] = 1 call.ids[1] = 1
def data = [ def data = [
new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null), null, null, null) new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null, null), null, null, null)
] ]
when: when:
def act = writer.toJson(call, data[0]) def act = writer.toJson(call, data[0])
@@ -127,7 +127,7 @@ class WriteRpcJsonSpec extends Specification {
call.ids[3] = 15 call.ids[3] = 15
def data = [ def data = [
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null), new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null),
new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null), null, null, null), new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null, null), null, null, null),
new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null, null, null), new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null, null, null),
] ]
when: when: