mirror of
https://github.com/corda/corda.git
synced 2025-02-20 17:33:15 +00:00
Including FlowException in the RPC exception whitelist (CORDA-1264) (#3037)
These exceptions are designed to be propagated in P2P and so makes sense to keep them visible if the recipient is an RPC user.
This commit is contained in:
parent
42edf58b92
commit
adef57f127
@ -18,7 +18,6 @@ import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
import net.corda.nodeapi.exceptions.InternalNodeException
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
@ -154,15 +153,6 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
println("Result: ${flowHandle.returnValue.getOrThrow()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sub-type of FlowException thrown by flow`() {
|
||||
login(rpcUser.username, rpcUser.password)
|
||||
val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, identity)
|
||||
assertThatExceptionOfType(InternalNodeException::class.java).isThrownBy {
|
||||
handle.returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check basic flow has no progress`() {
|
||||
login(rpcUser.username, rpcUser.password)
|
||||
|
@ -2,10 +2,12 @@ package net.corda.nodeapi.exceptions
|
||||
|
||||
import net.corda.core.CordaRuntimeException
|
||||
import net.corda.core.contracts.TransactionVerificationException
|
||||
import net.corda.core.flows.FlowException
|
||||
import java.io.InvalidClassException
|
||||
|
||||
// could change to use package name matching but trying to avoid reflection for now
|
||||
private val whitelisted = setOf(
|
||||
FlowException::class,
|
||||
InvalidClassException::class,
|
||||
RpcSerializableError::class,
|
||||
TransactionVerificationException::class
|
||||
@ -23,7 +25,6 @@ class InternalNodeException(message: String) : CordaRuntimeException(message) {
|
||||
fun defaultMessage(): String = DEFAULT_MESSAGE
|
||||
|
||||
fun obfuscateIfInternal(wrapped: Throwable): Throwable {
|
||||
|
||||
(wrapped as? CordaRuntimeException)?.setCause(null)
|
||||
return when {
|
||||
whitelisted.any { it.isInstance(wrapped) } -> wrapped
|
||||
|
@ -29,61 +29,70 @@ class RpcExceptionHandlingTest {
|
||||
|
||||
@Test
|
||||
fun `rpc client handles exceptions thrown on node side`() {
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = true)) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
|
||||
val node = startNode(NodeParameters(rpcUsers = users)).getOrThrow()
|
||||
|
||||
assertThatCode { node.rpc.startFlow(::Flow).returnValue.getOrThrow() }.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
|
||||
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
|
||||
}
|
||||
assertThatCode { node.rpc.startFlow(::Flow).returnValue.getOrThrow() }
|
||||
.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rpc client handles client-relevant exceptions thrown on node side`() {
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = true)) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
|
||||
val node = startNode(NodeParameters(rpcUsers = users)).getOrThrow()
|
||||
val clientRelevantMessage = "This is for the players!"
|
||||
|
||||
assertThatCode { node.rpc.startFlow(::ClientRelevantErrorFlow, clientRelevantMessage).returnValue.getOrThrow() }.isInstanceOfSatisfying(ClientRelevantException::class.java) { exception ->
|
||||
assertThatCode { node.rpc.startFlow(::ClientRelevantErrorFlow, clientRelevantMessage).returnValue.getOrThrow() }
|
||||
.isInstanceOfSatisfying(ClientRelevantException::class.java) { exception ->
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(clientRelevantMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(clientRelevantMessage)
|
||||
}
|
||||
@Test
|
||||
fun `FlowException is received by the RPC client`() {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
val node = startNode(NodeParameters(rpcUsers = users)).getOrThrow()
|
||||
val exceptionMessage = "Flow error!"
|
||||
assertThatCode { node.rpc.startFlow(::FlowExceptionFlow, exceptionMessage).returnValue.getOrThrow() }
|
||||
.isInstanceOfSatisfying(FlowException::class.java) { exception ->
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(exceptionMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `rpc client handles exceptions thrown on counter-party side`() {
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = true)) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
|
||||
val nodeA = startNode(NodeParameters(providedName = ALICE_NAME, rpcUsers = users)).getOrThrow()
|
||||
val nodeB = startNode(NodeParameters(providedName = BOB_NAME, rpcUsers = users)).getOrThrow()
|
||||
|
||||
assertThatCode { nodeA.rpc.startFlow(::InitFlow, nodeB.nodeInfo.singleIdentity()).returnValue.getOrThrow() }.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
|
||||
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
|
||||
}
|
||||
assertThatCode { nodeA.rpc.startFlow(::InitFlow, nodeB.nodeInfo.singleIdentity()).returnValue.getOrThrow() }
|
||||
.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
|
||||
assertThat(exception).hasNoCause()
|
||||
assertThat(exception.stackTrace).isEmpty()
|
||||
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
class Flow : FlowLogic<String>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call(): String {
|
||||
|
||||
throw GenericJDBCException("Something went wrong!", SQLException("Oops!"))
|
||||
}
|
||||
}
|
||||
@ -91,10 +100,8 @@ class Flow : FlowLogic<String>() {
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class InitFlow(private val party: Party) : FlowLogic<String>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call(): String {
|
||||
|
||||
val session = initiateFlow(party)
|
||||
return session.sendAndReceive<String>("hey").unwrap { it }
|
||||
}
|
||||
@ -102,10 +109,8 @@ class InitFlow(private val party: Party) : FlowLogic<String>() {
|
||||
|
||||
@InitiatedBy(InitFlow::class)
|
||||
class InitiatedFlow(private val initiatingSession: FlowSession) : FlowLogic<Unit>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
|
||||
initiatingSession.receive<String>().unwrap { it }
|
||||
throw GenericJDBCException("Something went wrong!", SQLException("Oops!"))
|
||||
}
|
||||
@ -113,10 +118,12 @@ class InitiatedFlow(private val initiatingSession: FlowSession) : FlowLogic<Unit
|
||||
|
||||
@StartableByRPC
|
||||
class ClientRelevantErrorFlow(private val message: String) : FlowLogic<String>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call(): String {
|
||||
override fun call(): String = throw ClientRelevantException(message, SQLException("Oops!"))
|
||||
}
|
||||
|
||||
throw ClientRelevantException(message, SQLException("Oops!"))
|
||||
}
|
||||
}
|
||||
@StartableByRPC
|
||||
class FlowExceptionFlow(private val message: String) : FlowLogic<String>() {
|
||||
@Suspendable
|
||||
override fun call(): String = throw FlowException(message)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user