I have defined a SchedulableState as follows:
class MySchedulableState() : SchedulableState {
override val participants = listOf<Party>()
val nextActivityTime = Instant.ofEpochMilli(Instant.now().toEpochMilli() + 100)
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
return ScheduledActivity(flowLogicRefFactory.create("com.template.ScheduledFlow", thisStateRef), nextActivityTime)
}
}
However, when I create this state in a flow, the scheduled activity is never run. Wh
The issue is that your node uses the state's constructor to recreate the state every time you extract it from the vault. As part of constructing the state, Instant.now() is called again and assigned to nextActivityTime, pushing the scheduled event into the future.
Instead, you should define your SchedulableState as follows:
class MySchedulableState(val now: Instant) : SchedulableState {
override val participants = listOf<Party>()
val nextActivityTime = Instant.ofEpochMilli(now.toEpochMilli() + 100)
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
return ScheduledActivity(flowLogicRefFactory.create("com.template.ScheduledFlow", thisStateRef), nextActivityTime)
}
}
Note how we pass the current time in the constructor. This value will not change every time the state is reconstructed (note that it must be a val to ensure it is serialised).
Related
Sorry if that title is not clear enough but I didn't know how to sum it up in one sentence.
I have a webservice that returns an ArrayList of objects named Father.
The Father object is structured like this:
class Father {
ArrayList<Child> children;
}
I have another webservice that returns me the detail of the object Child.
How can I concat the first call that returns me the arraylist of Father and the multiple calls for the multiple objects Child ?
So far I can make the calls separately, like this:
Call for ArrayList of Father
myRepository.getFathers().subscribeOn(Schedulers.io())
.observeOn(Schedulers.io()).subscribeWith(new DisposableSingleObserver<List<Father>>() {
})
multiple call for ArrayList of Child
childListObservable
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap((Function<List<Child>, ObservableSource<Child>>) Observable::fromIterable)
.flatMap((Function<Child, ObservableSource<Child>>) this::getChildDetailObservable)
.subscribeWith(new DisposableObserver<Child>() {
// do whatever action after the result of each Child
}))
Prerequisite
Gradle
implementation("io.reactivex.rxjava2:rxjava:2.2.10")
testImplementation("io.mockk:mockk:1.10.0")
testImplementation("org.assertj:assertj-core:3.11.1")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.3.1")
Classes / Interfaces
interface Api {
fun getFather(): Single<List<Father>>
fun childDetailInfo(child: Child): Single<ChildDetailInfo>
}
interface Store {
fun store(father: Father): Completable
fun store(child: ChildDetailInfo): Completable
}
class ApiImpl : Api {
override fun getFather(): Single<List<Father>> {
val child = Child("1")
val child1 = Child("2")
return Single.just(listOf(Father(listOf(child, child1)), Father(listOf(child))))
}
override fun childDetailInfo(child: Child): Single<ChildDetailInfo> {
return Single.just(ChildDetailInfo(child.name))
}
}
data class Father(val childes: List<Child>)
data class Child(val name: String)
data class ChildDetailInfo(val name: String)
Solution
val fathersStore = api.getFather()
.flatMapObservable {
Observable.fromIterable(it)
}.flatMapCompletable {
val detailInfos = it.childes.map { child ->
api.childDetailInfo(child)
.flatMapCompletable { detail -> store.store(detail) }
}
store.store(it)
.andThen(Completable.concat(detailInfos))
}
On each emit of a List of fathers, the list is flatten. The next opreator (flatMapCompletable) will take an Father. The completable will get the details of each Child with Api#childDetailInfo. The result is build by calling the API one by one. There is no concurrency happening wegen "concat". When the father is stored sucessfully, the childs will be stored as-well, when retrieved successfully. If one of the API-calls fails (e.g. network) everything fails, because the onError will be propgated to the subscriber.
Test
#Test
fun so62299778() {
val api = ApiImpl()
val store = mockk<Store>()
every { store.store(any<Father>()) } returns Completable.complete()
every { store.store(any<ChildDetailInfo>()) } returns Completable.complete()
val fathersStore = api.getFather()
.flatMapObservable {
Observable.fromIterable(it)
}.flatMapCompletable {
val detailInfos = it.childes.map { child ->
api.childDetailInfo(child)
.flatMapCompletable { detail -> store.store(detail) }
}
store.store(it)
.andThen(Completable.concat(detailInfos))
}
fathersStore.test()
.assertComplete()
verify { store.store(eq(Father(listOf(Child("1"), Child("2"))))) }
verify { store.store(eq(Father(listOf(Child("1"))))) }
verify(atLeast = 2) { store.store(eq(ChildDetailInfo("1"))) }
verify(atLeast = 1) { store.store(eq(ChildDetailInfo("2"))) }
}
Please provide next time some classes/ interfaces. When your question contains all vital information, you will get an answer quicker.
I have got three classes:
class A {
//Duration in seconds
private val durationProperty = SimpleLongProperty(300)
var duration by durationProperty
}
class B(list: List<A>) {
private val aClassesProperty = SimpleListProperty(list.observable())
val aClasses by aClassesProperty
}
class C(classB: B, repetitions: Int){
private val bClassProperty = SimpleObjectProperty(classB)
private val repetitionsProperty = SimpleIntProperty(repetitions)
val repetitions by repetitionsProperty
}
Now I would like, to create and bind durationProperty inside class B as sum of class A durationProperty (which will be sensitive to adding new class A instance into list and changing any duration of already present instance) and similarly in class C - durationProperty as duration of classB multiplied by repetitions.
I don't know kotlin, but the answer should be easy to translate.
You need to use an 'extractor' to handle this, using:
FXCollections#observableArrayList(Callback<E, Observable[]> extractor)
Like:
ObservableList<A> list = FXCollections.observableArrayList(item -> new Observable[] {item.durationProperty});
list.addListener((InvalidationListener) observable -> {
//Update you sum here
});
The extractor causes any changes to the given observable array of each item in the list to trigger both the InvalidationListener and ListChangeListener of the list to be fired.
SephB's solution is correct, but finally I used one from there. I found it a little bit more elastic even though there is a lot of boilerplate.
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)
}
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.")
}
}
So its a basic question.
What I am trying to achieve is refreshing views from another views.
Lets say I have a view EmployeeTableView which shows a tabular representation of employees by doing a REST API call.
In another view, I have a the filter EmployeeFilterView wherein I have gender, salary range, employee type, etc.
I also have a userContext object in which I store the user preferences. So by default lets say I have stored the value of gender filter to be Male, salary range to be ALL, etc. This object is send as a parameter to the EmployeeTableView.
When the EmployeeTableView is loaded I do a restAPI call with the userContext values to get the employee details. So that works fine. Now I change the gender filter to Female and assign this value in my userContext.
Now if I could just reload the EmployeeTableView with the userContext object, the restapi call would get the updated values.
But how can I do that ?
Also suggest a better approach if you have.
The EventBus is one valid solution to this. Another would be to use a ViewModel or Controller as the UserContext object and let that include the actual observable list of employees and then bind that list to the TableView in EmployeeTableView. Whenever the list in the context is updated, the TableView will update as well.
The filter view would call a function in the UserContext to perform the actual REST call and update the list of employees based on that.
You could create a separate EmployeeQuery object that can be injected into both the EmployeeFilterView and the UserContext so it can extract the selected filter values to perform the query. This query object contains a list of all the search parameters you want to pass to the server.
You could also consider creating a separate scope to keep these components separated if that makes sense to your architecture.
Exactly how you define these components are mostly a matter of taste, here is one suggestion. I used the RangeSlider from ControlsFX for the mock search UI.
To make it easier to imagine how this ties together, here is a screenshot:
(All names and salaries are fiction :)
/**
* The employee domain model, implementing JsonModel so it can be fetched
* via the REST API
*/
class Employee : JsonModel {
val nameProperty = SimpleStringProperty()
var name by nameProperty
val salaryProperty = SimpleIntegerProperty()
var salary by salaryProperty
val genderProperty = SimpleObjectProperty<Gender>()
var gender by genderProperty
override fun updateModel(json: JsonObject) {
with (json) {
name = getString("name")
salary = getInt("salary")
gender = Gender.valueOf(getString("gender"))
}
}
}
enum class Gender { Male, Female }
/**
* Container for the list of employees as well as a search function called by the filter
* view whenever it should update the employee list.
*/
class EmployeeContext : Controller() {
val api: Rest by inject()
val query: EmployeeQuery by inject()
val employees = SimpleListProperty<Employee>()
fun search() {
runAsync {
FXCollections.observableArrayList(Employee().apply {
name = "Edvin Syse"
gender = Gender.Male
salary = 200_000
})
//api.post("employees/query", query).list().toModel<Employee>()
} ui {
employees.value = it
}
}
}
/**
* Query object used to define the query sent to the server
*/
class EmployeeQuery : ViewModel(), JsonModel {
val genderProperty = SimpleObjectProperty<Gender>(Gender.Female)
var gender by genderProperty
val salaryMinProperty = SimpleIntegerProperty(50_000)
var salaryMin by salaryMinProperty
val salaryMaxProperty = SimpleIntegerProperty(250_000)
var salaryMax by salaryMaxProperty
val salaryDescription = stringBinding(salaryMinProperty, salaryMaxProperty) {
"$$salaryMin - $$salaryMax"
}
override fun toJSON(json: JsonBuilder) {
with(json) {
add("gender", gender.toString())
add("salaryMin", salaryMin)
add("salaryMax", salaryMax)
}
}
}
/**
* The search/filter UI
*/
class EmployeeFilterView : View() {
val query: EmployeeQuery by inject()
val context: EmployeeContext by inject()
override val root = form {
fieldset("Employee Filter") {
field("Gender") {
combobox(query.genderProperty, Gender.values().toList())
}
field("Salary Range") {
vbox {
alignment = Pos.CENTER
add(RangeSlider().apply {
max = 500_000.0
lowValueProperty().bindBidirectional(query.salaryMinProperty)
highValueProperty().bindBidirectional(query.salaryMaxProperty)
})
label(query.salaryDescription)
}
}
button("Search").action {
context.search()
}
}
}
}
/**
* The UI that shows the search results
*/
class EmployeeTableView : View() {
val context: EmployeeContext by inject()
override val root = borderpane {
center {
tableview(context.employees) {
column("Name", Employee::nameProperty)
column("Gender", Employee::genderProperty)
column("Salary", Employee::salaryProperty)
}
}
}
}
/**
* A sample view that ties the filter UI and result UI together
*/
class MainView : View("Employee App") {
override val root = hbox {
add(EmployeeFilterView::class)
add(EmployeeTableView::class)
}
}
I ended up using Tornadofx -> EventBus
Basically, when I change any of the filters, I fire an even which rebuilds the Node with the updated values.
Not sure whether the approach is right, that's why still keeping it open for discussion.