I have two flows, say their names are:
flow_out (requires 1 input state)
flow_in (the above input
state/transaction is stored by this)
My flow(flow_out) have 1 input state and 1 output state. The input state is retrieved from vault in the flow(flow_out) and the same is verified in contract by all the parties(Currrently 3 parties in test MockNetwork).
Now the test case is failing as my flow(flow_out) is unable to get that state, as that transaction never occurred(it's part of a different flow i.e flow_in).
To get around it, I initiated the other flow(flow_in) also in #Before of Junit, to store the transaction required for input state and everything passed.
What are some other ways available in Corda's flow testing APIs to
store input transaction/states directly without running the flows only
to store those input transacations?
Thanks for any help.
Since you have access to the nodes' ServiceHubs, you can build, sign and store transactions directly in the test method, rather than using a flow:
class FlowTests {
lateinit var network: MockNetwork
lateinit var a: StartedMockNode
lateinit var b: StartedMockNode
#Before
fun setup() {
network = MockNetwork(listOf("com.example.contract"))
a = network.createPartyNode()
b = network.createPartyNode()
listOf(a, b).forEach { it.registerInitiatedFlow(ExampleFlow.Acceptor::class.java) }
network.runNetwork()
}
#After
fun tearDown() {
network.stopNodes()
}
#Test
fun `a flow test`() {
val lender = a.info.legalIdentities.first()
val borrower = b.info.legalIdentities.first()
val transactionBuilder = TransactionBuilder(network.defaultNotaryIdentity)
.addOutputState(IOUState(99, lender, borrower), IOUContract.IOU_CONTRACT_ID)
.addCommand(IOUContract.Commands.Create(), listOf(lender.owningKey, borrower.owningKey))
a.transaction { transactionBuilder.verify(a.services) }
val partSignedTransaction = a.services.signInitialTransaction(transactionBuilder)
val signedTransaction = b.services.addSignature(partSignedTransaction)
a.services.recordTransactions(signedTransaction)
TODO("Test next flow.")
}
}
Related
I am new with Axon and maybe I missed something, but need help to understand.
I have a simple food cart aggregate.
Here is example:
#Aggregate
class FoodCard {
#AggregateIdentifier
private lateinit var foodCardId: UUID
private lateinit var selectedProduct: MutableMap<UUID, Int>
constructor()
#CommandHandler
constructor(command: CreateFoodCartCommand) {
AggregateLifecycle.apply(FoodCartCreateEvent(
UUID.randomUUID()
))
}
#CommandHandler
fun handle(command: SelectProductCommand) {
AggregateLifecycle
.apply(ProductSelectedEvent(foodCardId, command.productId, command.quantity))
}
#CommandHandler
fun handle(command: DeleteFoodCartCommand) {
AggregateLifecycle
.apply(FoodCartDeleteEvent(foodCardId))
}
#CommandHandler
fun handle(command: DeselectProductCommand) {
val productId = command.productId
if (!selectedProduct.containsKey(productId)) {
throw ProductDeselectionException("ProductDeselectionException")
}
AggregateLifecycle
.apply(ProductDeselectEvent(foodCardId, productId, command.quantity))
}
#EventSourcingHandler
fun on(event: FoodCartCreateEvent) {
foodCardId = event.foodCardId
selectedProduct = mutableMapOf()
}
#EventSourcingHandler
fun on(event: ProductSelectedEvent) {
selectedProduct.merge(
event.productId,
event.quantity
) {a, b -> a + b}
}
}
As ES I am using Axon Server.
For FoodCard projector I am using JPA repository that connects to DB.
I want to get all foodcards that contain special product (concrete UUID) and change quantity to -1 for all of them.
I understood there are two types of actions -> read and write
So the question how to correctly implement this flow with Axon?
Thanks
from your explanation and code I feel that you will probably need to complete your implementation of DeselectProductCommand introducing an EventSourcingHandler for ProductDeselectEvent. If I understood correctly your "quantity" information is stored into the selectProduct Map. In this case, based on your code, I see that the information of the quantity that should be subtracted to your product is in the command.
You will also need a Query, such as FindAllFoodCardByProductId, that will retrieve the foodCardId aggregate identifier that contains a certain productId: this operation will be performed on your Projection through the jpa repository.
As a reference you can have a look at the ref guide here https://docs.axoniq.io/reference-guide/implementing-domain-logic/query-handling on how to use QueryGateway into your controller and implement a QueryHandler into your Projection.
Corrado.
I have a unit test using this notation:
ledgerServices.ledger {
transaction {
...
this.verifies()
}
}
I'd like to actually have the LedgerTransaction object so that I can test a helper method that takes in the tx as an argument. Is it possible to do this using the LedgerDSL or do I need to manually use the TransactionBuilder to create the LedgerTransaction instance in my unit test?
I don't think so.
I would suggest you compute a transaction and test your helper method as you test a flow:
#Test
fun flowReturnsCorrectlyFormedPartiallySignedTransaction() {
val lender = a.info.chooseIdentityAndCert().party
val borrower = b.info.chooseIdentityAndCert().party
val stx = issueIou(IOUState(10.POUNDS, lender, borrower))
val inputIou = stx.tx.outputs.single().data as IOUState
val flow = IOUTransferFlow(inputIou.linearId, c.info.chooseIdentityAndCert().party)
val future = a.startFlow(flow)
mockNetwork.runNetwork()
val ptx = future.getOrThrow()
// Check the transaction is well formed...
// One output IOUState, one input state reference and a Transfer command with the right properties.
assert(ptx.tx.inputs.size == 1)
assert(ptx.tx.outputs.size == 1)
assert(ptx.tx.inputs.single() == StateRef(stx.id, 0))
println("Input state ref: ${ptx.tx.inputs.single()} == ${StateRef(stx.id, 0)}")
val outputIou = ptx.tx.outputs.single().data as IOUState
println("Output state: $outputIou")
val command = ptx.tx.commands.single()
assert(command.value == IOUContract.Commands.Transfer())
ptx.verifySignaturesExcept(b.info.chooseIdentityAndCert().party.owningKey, c.info.chooseIdentityAndCert().party.owningKey,
mockNetwork.defaultNotaryNode.info.legalIdentitiesAndCerts.first().owningKey)
}
This is my code to get 5 items from realtime database:
val database = FirebaseDatabase.getInstance()
val brandReference = database.getReference("brandGame").limitToFirst(5)
brandReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
dataSnapshot.children.forEach {
...
}
}
}
And this is how my real-time database looks like:
What's the best way to get 5 items randomly? I know there isn't a random function in real time database yet.
If you know the number of elements in the brandGame/-reference, you could pick 5 random numbers between 1 and numberOfElements and retrieve those. This would result in multiple calls to the database.
Alternatively, you could download everything from the brandGame/-reference and just pick 5 random elements using pure Kotlin. But then you must download everything in the reference, which could be a lot.
The best option is to set up a cloud function that does the "pick 5 random options"-logic server side. https://firebase.google.com/docs/functions/ But this requires that you write some js :)
As you say, there is no built-in way to get random elements from a reference.
To get a random brand, please use the following code user side:
val rootRef = FirebaseDatabase.getInstance().reference
val brandGameRef = rootRef.child("brandGame")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val brandCountList = ArrayList<String>()
for (ds in dataSnapshot.children) {
val brand = ds.child("brand").getValue(String::class.java)
brandCountList.add(brand!!)
}
val brandCount = brandCountList.size
val randomNumber = Random().nextInt(brandCount)
val randomBrand = ArrayList<String>()
randomBrand.add(brandCountList.get(randomNumber)) //Add the brand product to list
val arrayAdapter = ArrayAdapter(applicationContext, android.R.layout.simple_list_item_1, randomBrand)
list_view.adapter = arrayAdapter
}
override fun onCancelled(databaseError: DatabaseError) {
//Handle exceptions
}
}
brandGameRef.addListenerForSingleValueEvent(valueEventListener)
In Corda, suppose I am running a flow that creates a transaction. I have signed the transaction, but now the flow is suspended waiting for the counterparty to sign.
Is there any way for me to see a list of transactions that are pending in this way?
As of Corda 3, you cannot see the contents of these transactions.
However, you can use flow progress-tracker steps to find out where each flow is in its lifecyle. For example, you could count the number of flows that are paused at some user-defined Transaction is pending. progress-tracker step as follows:
class Client {
val proxy: CordaRPCOps
init {
val nodeAddress = NetworkHostAndPort.parse("localhost:10006")
val client = CordaRPCClient(nodeAddress)
proxy = client.start("user1", "test").proxy
}
fun currentNumberOfPendingTxs(): Int {
val stateMachineInfos = proxy.stateMachinesSnapshot()
val stateMachinesPendingTxs = stateMachineInfos.filter { info ->
val progressTracker = info.progressTrackerStepAndUpdates
if (progressTracker == null) {
false
} else {
progressTracker.snapshot == "Transaction is pending."
}
}
return stateMachinesPendingTxs.size
}
}
I need to know when the new state got committed into the node's vault for getting the timestamp at that moment. So, I think if I can handle the committed state then trigger my timestamp record class would be nice.
By the way, please let me know if you have any suggestions about capturing timestamp on state evolving over time.
Yes, you can do this using the CordaRPCOps.vaultTrackBy method. This method returns an observable of updates to the vault. You can subscribe to this observable to be notified whenever a new state is recorded in the vault.
RPC example
fun main(args: Array<String>) {
require(args.size == 1) { "Usage: ExampleClientRPC <node address>" }
val nodeAddress = NetworkHostAndPort.parse(args[0])
val client = CordaRPCClient(nodeAddress)
val rpcOps = client.start("user1", "test").proxy
val updates = rpcOps.vaultTrackBy<ContractState>().updates
// Log the 'placed' IOU states and listen for new ones.
updates.toBlocking().subscribe { update ->
update.produced.forEach { stateAndRef ->
val timeStamp = Instant.now()
// TODO("Use the timestamp as you wish.")
}
}
}
Flow test example
class FlowTests {
lateinit var network: MockNetwork
lateinit var a: StartedMockNode
lateinit var b: StartedMockNode
#Before
fun setup() {
network = MockNetwork(
listOf("com.example.contract"),
// We need each node to operate on a separate thread so that we can
// subscribe to the vault updates on a separate thread later.
threadPerNode = true)
a = network.createPartyNode()
b = network.createPartyNode()
listOf(a, b).forEach { it.registerInitiatedFlow(ExampleFlow.Acceptor::class.java) }
}
#After
fun tearDown() {
network.stopNodes()
}
#Test
fun `flowTest`() {
// Trying to access the node database in a separate thread would result in a
// `java.lang.IllegalStateException: Was expecting to find CordaPersistence set on current thread` exception
val updates = a.services.vaultService.trackBy<ContractState>().updates
Thread {
updates.toBlocking().subscribe { update ->
update.produced.forEach { stateAndRef ->
val timeStamp = Instant.now()
// TODO("Use the timestamp as you wish.")
}
}
}.start()
repeat(3) {
val flow = ExampleFlow.Initiator(1, b.info.singleIdentity())
a.startFlow(flow).getOrThrow()
}
// We give the other thread time to observe the updates.
Thread.sleep(10000)
}
}