Joel
Joel

Reputation: 23140

Making asynchronous HTTP calls from flows

In Corda, how can I make an asynchronous HTTP request from within a flow? Is there an API to suspend a flow while awaiting the response to the HTTP call, or a way to provide a callback?

Upvotes: 1

Views: 905

Answers (2)

Corda uses quasar fibers to make sync-like (async) calls. Fortunately quasar has support for java's Future and guava's ListenableFuture.

Based on that, you could create a Flow like this

@InitiatingFlow
class ListenableFutureFlow<V>(val futureFn: () -> ListenableFuture<V>,
                              override val progressTracker: ProgressTracker) : FlowLogic<V>() {

    constructor(futureFn: () -> ListenableFuture<V>) : this(futureFn, tracker())

    companion object {
        object EXECUTING_FUTURE: ProgressTracker.Step("Executing future call inside fiber")
        fun tracker() = ProgressTracker(EXECUTING_FUTURE)
    }

    @Suspendable
    override fun call(): V {
        progressTracker.currentStep = EXECUTING_FUTURE
        return AsyncListenableFuture.get(futureFn())
    }
}

And you can use it like this:

val asyncResponse = subFlow(ListenableFutureFlow { myAsyncCall(param1, param2) })

It is not the best solution but at least works with corda's infrastructure :)

Let me know if it works for you!

Upvotes: 1

Joel
Joel

Reputation: 23140

Corda doesn't currently provide a mechanism for making an asynchronous HTTP request and then either suspending the flow while awaiting a response, or providing a callback that will be called when the response is received.

Instead, it is recommended that you make the HTTP request before initiating the flow, then pass in the response as an argument when instantiating the flow.

Sometimes, this isn't possible. For example, the HTTP request may be required by a response flow that is initiated automatically, or it may depend on the contents of a message received from a counterparty.

In this case, you can still support this kind of workflow as follows. Suppose you are writing a CorDapp for loan applications, which are represented as LoanApplicationStates. Without an HTTP call, the responder doesn't know whether to accept the loan application or not.

Instead of creating a LoanApplicationState directly, the flow would create an UnacceptedLoanApplicationState, which the responder would store on their ledger. Once the flow ends, the responder can make an HTTP call outside of the flow framework. Based on the result of the HTTP call, the responder will either kick off an approve flow that creates a transaction to transform the UnacceptedLoanApplicationState into an LoanApplicationState, or kick off a reject flow to consume the UnacceptedLoanApplicationState state without issuing an accepted LoanApplicationState.

Alternatively, you could add a status field to the LoanApplicationState, specifying whether the loan is approved or not. Initially, the loan state would have the field set to unapproved. Then, based on the result of the HTTP request, the responder would initiate one of two flows to update the LoanApplicationState, updating it with either an approved or a rejected status.

Upvotes: 2

Related Questions