Enable RPC start of Flows that return nothing (eg. Void). (#1374)

* Enable RPC start of Flows that return nothing (eg. Void).

* Fix blocking test (caused by not running mockNetwork).
Improve execution times by moving redundant setup() initialisation to only tests that use it.
This commit is contained in:
josecoll 2017-09-01 15:44:53 +01:00 committed by GitHub
parent 954ed69102
commit a286f7553b
4 changed files with 49 additions and 26 deletions

View File

@ -194,14 +194,14 @@ interface CordaRPCOps : RPCOps {
* Start the given flow with the given arguments. [logicType] must be annotated with [net.corda.core.flows.StartableByRPC]. * Start the given flow with the given arguments. [logicType] must be annotated with [net.corda.core.flows.StartableByRPC].
*/ */
@RPCReturnsObservables @RPCReturnsObservables
fun <T : Any> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T> fun <T> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T>
/** /**
* Start the given flow with the given arguments, returning an [Observable] with a single observation of the * Start the given flow with the given arguments, returning an [Observable] with a single observation of the
* result of running the flow. [logicType] must be annotated with [net.corda.core.flows.StartableByRPC]. * result of running the flow. [logicType] must be annotated with [net.corda.core.flows.StartableByRPC].
*/ */
@RPCReturnsObservables @RPCReturnsObservables
fun <T : Any> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T>
/** /**
* Returns Node's identity, assuming this will not change while the node is running. * Returns Node's identity, assuming this will not change while the node is running.
@ -327,25 +327,25 @@ inline fun <reified T : ContractState> CordaRPCOps.vaultTrackBy(criteria: QueryC
* Note that the passed in constructor function is only used for unification of other type parameters and reification of * Note that the passed in constructor function is only used for unification of other type parameters and reification of
* the Class instance of the flow. This could be changed to use the constructor function directly. * the Class instance of the flow. This could be changed to use the constructor function directly.
*/ */
inline fun <T : Any, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: () -> R flowConstructor: () -> R
): FlowHandle<T> = startFlowDynamic(R::class.java) ): FlowHandle<T> = startFlowDynamic(R::class.java)
inline fun <T : Any, A, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, A, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: (A) -> R, flowConstructor: (A) -> R,
arg0: A arg0: A
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0) ): FlowHandle<T> = startFlowDynamic(R::class.java, arg0)
inline fun <T : Any, A, B, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, A, B, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: (A, B) -> R, flowConstructor: (A, B) -> R,
arg0: A, arg0: A,
arg1: B arg1: B
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1) ): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1)
inline fun <T : Any, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: (A, B, C) -> R, flowConstructor: (A, B, C) -> R,
arg0: A, arg0: A,
@ -353,7 +353,7 @@ inline fun <T : Any, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
arg2: C arg2: C
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2) ): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2)
inline fun <T : Any, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: (A, B, C, D) -> R, flowConstructor: (A, B, C, D) -> R,
arg0: A, arg0: A,
@ -362,7 +362,7 @@ inline fun <T : Any, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startFlow
arg3: D arg3: D
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3) ): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3)
inline fun <T : Any, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: (A, B, C, D, E) -> R, flowConstructor: (A, B, C, D, E) -> R,
arg0: A, arg0: A,
@ -372,7 +372,7 @@ inline fun <T : Any, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startF
arg4: E arg4: E
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4) ): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4)
inline fun <T : Any, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startFlow( inline fun <T, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
flowConstructor: (A, B, C, D, E, F) -> R, flowConstructor: (A, B, C, D, E, F) -> R,
arg0: A, arg0: A,
@ -387,20 +387,20 @@ inline fun <T : Any, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.sta
* Same again, except this time with progress-tracking enabled. * Same again, except this time with progress-tracking enabled.
*/ */
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: () -> R flowConstructor: () -> R
): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java) ): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java)
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, A, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, A, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: (A) -> R, flowConstructor: (A) -> R,
arg0: A arg0: A
): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0) ): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0)
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, A, B, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, A, B, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: (A, B) -> R, flowConstructor: (A, B) -> R,
arg0: A, arg0: A,
@ -408,7 +408,7 @@ inline fun <T : Any, A, B, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlo
): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1) ): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1)
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: (A, B, C) -> R, flowConstructor: (A, B, C) -> R,
arg0: A, arg0: A,
@ -417,7 +417,7 @@ inline fun <T : Any, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startTracked
): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1, arg2) ): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1, arg2)
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: (A, B, C, D) -> R, flowConstructor: (A, B, C, D) -> R,
arg0: A, arg0: A,
@ -427,7 +427,7 @@ inline fun <T : Any, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startTrac
): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1, arg2, arg3) ): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1, arg2, arg3)
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: (A, B, C, D, E) -> R, flowConstructor: (A, B, C, D, E) -> R,
arg0: A, arg0: A,
@ -438,7 +438,7 @@ inline fun <T : Any, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startT
): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4) ): FlowProgressHandle<T> = startTrackedFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4)
@Suppress("unused") @Suppress("unused")
inline fun <T : Any, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow( inline fun <T, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startTrackedFlow(
@Suppress("unused_parameter") @Suppress("unused_parameter")
flowConstructor: (A, B, C, D, E, F) -> R, flowConstructor: (A, B, C, D, E, F) -> R,
arg0: A, arg0: A,

View File

@ -127,7 +127,7 @@ class CordaRPCOpsImpl(
} }
} }
override fun <T : Any> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> { override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> {
val stateMachine = startFlow(logicType, args) val stateMachine = startFlow(logicType, args)
return FlowProgressHandleImpl( return FlowProgressHandleImpl(
id = stateMachine.id, id = stateMachine.id,
@ -136,12 +136,12 @@ class CordaRPCOpsImpl(
) )
} }
override fun <T : Any> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T> { override fun <T> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T> {
val stateMachine = startFlow(logicType, args) val stateMachine = startFlow(logicType, args)
return FlowHandleImpl(id = stateMachine.id, returnValue = stateMachine.resultFuture) return FlowHandleImpl(id = stateMachine.id, returnValue = stateMachine.resultFuture)
} }
private fun <T : Any> startFlow(logicType: Class<out FlowLogic<T>>, args: Array<out Any?>): FlowStateMachineImpl<T> { private fun <T> startFlow(logicType: Class<out FlowLogic<T>>, args: Array<out Any?>): FlowStateMachineImpl<T> {
require(logicType.isAnnotationPresent(StartableByRPC::class.java)) { "${logicType.name} was not designed for RPC" } require(logicType.isAnnotationPresent(StartableByRPC::class.java)) { "${logicType.name} was not designed for RPC" }
val rpcContext = getRpcContext() val rpcContext = getRpcContext()
rpcContext.requirePermission(startFlowPermission(logicType)) rpcContext.requirePermission(startFlowPermission(logicType))

View File

@ -126,7 +126,7 @@ interface ServiceHubInternal : PluginServiceHub {
* @throws net.corda.core.flows.IllegalFlowLogicException or IllegalArgumentException if there are problems with the * @throws net.corda.core.flows.IllegalFlowLogicException or IllegalArgumentException if there are problems with the
* [logicType] or [args]. * [logicType] or [args].
*/ */
fun <T : Any> invokeFlowAsync( fun <T> invokeFlowAsync(
logicType: Class<out FlowLogic<T>>, logicType: Class<out FlowLogic<T>>,
flowInitiator: FlowInitiator, flowInitiator: FlowInitiator,
vararg args: Any?): FlowStateMachineImpl<T> { vararg args: Any?): FlowStateMachineImpl<T> {

View File

@ -7,6 +7,7 @@ import net.corda.core.contracts.Issued
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
@ -44,6 +45,7 @@ import rx.Observable
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
class CordaRPCOpsImplTest { class CordaRPCOpsImplTest {
@ -71,12 +73,6 @@ class CordaRPCOpsImplTest {
startFlowPermission<CashIssueFlow>(), startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>() startFlowPermission<CashPaymentFlow>()
)))) ))))
aliceNode.database.transaction {
stateMachineUpdates = rpc.stateMachinesFeed().updates
transactions = rpc.verifiedTransactionsFeed().updates
vaultTrackCash = rpc.vaultTrackBy<Cash.State>().updates
}
} }
@After @After
@ -86,6 +82,11 @@ class CordaRPCOpsImplTest {
@Test @Test
fun `cash issue accepted`() { fun `cash issue accepted`() {
aliceNode.database.transaction {
stateMachineUpdates = rpc.stateMachinesFeed().updates
vaultTrackCash = rpc.vaultTrackBy<Cash.State>().updates
}
val quantity = 1000L val quantity = 1000L
val ref = OpaqueBytes(ByteArray(1) { 1 }) val ref = OpaqueBytes(ByteArray(1) { 1 })
@ -131,6 +132,12 @@ class CordaRPCOpsImplTest {
@Test @Test
fun `issue and move`() { fun `issue and move`() {
aliceNode.database.transaction {
stateMachineUpdates = rpc.stateMachinesFeed().updates
transactions = rpc.verifiedTransactionsFeed().updates
vaultTrackCash = rpc.vaultTrackBy<Cash.State>().updates
}
val anonymous = false val anonymous = false
val result = rpc.startFlow(::CashIssueFlow, val result = rpc.startFlow(::CashIssueFlow,
100.DOLLARS, 100.DOLLARS,
@ -248,4 +255,20 @@ class CordaRPCOpsImplTest {
@Suspendable @Suspendable
override fun call() = Unit override fun call() = Unit
} }
@Test
fun `attempt to start RPC flow with void return`() {
CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = setOf(
startFlowPermission<VoidRPCFlow>()
))))
val result = rpc.startFlow(::VoidRPCFlow)
mockNet.runNetwork()
assertNull(result.returnValue.getOrThrow())
}
@StartableByRPC
class VoidRPCFlow : FlowLogic<Void?>() {
@Suspendable
override fun call() : Void? = null
}
} }