I'm going to improve my parameterized pipelines. I want to save previous selected items in active choice reactive parameters for each value from active choice parameter on which reactive choice parameter depends. I tried to use a this variable in Groovy script of active choice reactive parameter:
// A - active choice parameter on which reactive choice parameter depends
if (!this.hasProperty('B')) {
this.B = [:]
}
if (this.B.containsKey(A)) {
return this.B[A]
} else {
// save and return some default values
if (A == 1) {
this.B[A] = ['val1.1', 'val1.2']
} else {
this.B[A] = ['val2.1', 'val2.2']
}
return this.B[A]
}
Hovewer, this of active choice reactive parameter references to new object after changing of active choice parameter.
Does scope of variables of declarative pipelines have any general context for store some values in it during selecting parameters for new build?
Related
I m trying to grasp the purpose of the following 3 concepts ( classes ) that are core functionalities in Idempiere/Adempiere.
Based on code description
I do understand that GridTab have the state of the model representing the ad_tab which is the ViewModel Part of any ad_table. simple said we will found the data bound to the ad_table.
First, for the GridField I believe is the model of the view, if I can abuse it is like the the DOM state: what do we have as fields, values of fields and events, I believe that is template view centric.
Dicovering this two ( if I m not mistaken in my analyses ) made me wonder. What do really the Ctx stands for? what state is it representing ?
The code is not commenting on this , can any body answer me?
Thanks .
In iDempiere the context is a Properties object that is global to the whole application.
You can think about the context as a global set of variables that you can access from any point of the system.
The context variables can be viewed clicking on the iDempiere icon, then navigating to the Errors tab, and then clicking on the View button, you'll find there the variables after the line:
=== Context ===
Within the context you can find a lot of information:
Login variables: some of those starting with #, like #AD_Role_ID
Defaults: records that are marked as default, also starting with #, like #C_BP_Group_ID
Accounting related variables: those starting with $, like $C_Currency_ID
Global Preferences: starting with P like P|AutoCommit
Window Preferences: starting with P and a number, example P132|GL_Category_ID
And then, the context variables that you're interested in, the value of each field on the windows that are open:
Window fields: those starting with a number, like 1|DiscountSchema - this means the field DiscountSchema in the first window opened
Tab fields: those starting with two numbers, like 1|2|DatePromised - this means the field DatePromised in the third tab (the number 2, tabs are numbered from zero) of the first window opened (the number 1)
You can access those context variables using Env.getContext... methods, and you can also add and delete your own variables with methods Env.setContext...
The use and intent of Context in ADempiere is the same as described by Carlos except for the access. In the web you can access the context from the top right of the window as shown below.
Another example of how the context provides global state is in testing. Here is a snippet from a test setup class that initializes the context with the time and login information. The context can then be accessed by test classes performing integration tests with a database as if they were in actual use. The context here is limited to login information but it could be extended to include any other element of the context required for the tests.
#BeforeAll
public static void setUpBeforeClass() {
today = TimeUtil.getDay(System.currentTimeMillis());
ctx = Env.getCtx();
ctx.setProperty("#AD_Org_ID", Integer.toString(AD_ORG_ID));
ctx.setProperty("#AD_User_ID", Integer.toString(AD_USER_ID));
ctx.setProperty("#AD_Client_ID", Integer.toString(AD_CLIENT_ID));
ctx.setProperty("#Date", TimeUtil.getDay(System.currentTimeMillis()).toString());
ctx.setProperty("#AD_Language", "en");
Ini.setClient (IS_CLIENT);
Ini.loadProperties(false);
org.compiere.Adempiere.startup(IS_CLIENT);
trxName = Trx.createTrxName("TestRun_" + randomString(4));
trx = Trx.get(trxName, false);
try {
mainSavepoint = trx.setSavepoint("AllTests_" + randomString(4));
} catch (SQLException e) {
fail(e.getMessage());
}
}
#AfterAll
public static void tearDownAfterClass() {
try {
tryToRollback(mainSavepoint);
trx.close();
}
catch(SQLException e) {
fail("Unable to rollback. " + e.getMessage());
}
finally {
trx.close();
trx = null;
ctx = null;
}
}
Similar to channels and broadcast channels, can flows also be instantiated and reused at multiple places?
General usage of creating flows is wrapping the logic to emit the data inside the flow's body and is returned.
Snippet :
fun listenToDataChanges() : Flow<T>
{
return flow {
dataSource.querySomeInfo()?.consumeEach {
data->
if (someCondition) {
emit(data)
}
}
}
}
Everytime listenToDataChanges() is called, a new flow instance is created and multiple subscriptions would be made. Instead is it possible to create and reuse the instance to avoid multiple subscriptions?
Yes, you just need to store it in a variable instead of recreating the flow each time
By the way it seems like you could simplify this way:
val customFlow = dataSource.querySomeInfo()?.filter { someCondition }
I have below code:
Mono<Property> property = propertyService.findById(id);
String title;
Flux<Photo> photos = property.flatMapMany(prop ->
{
title = prop.title + '-' + prop.type;
return photoService.findByPropertyId(prop.getId());
}
);
model.addAttribute("prop", property);
model.addAttribute("title", title);
model.addAttribute("photos", photos);
// ajx is query param coming from request
if(ajx != null && !ajx.isEmpty() && ajx.equals("1"))
return Mono.just("fragments/propertyfrag");
else
return Mono.just("property");
The code shows what I want to achieve but it does not even compile. It gives error saying title and type on prop are not visible.
Note that the last statement is reference to thymeleaf template named property. Withn thyeleaf template I have access to variable prop as if it was not reactive but plain prop object that enables me to directly access parameters on prop object. Does that mean within thymeleaf template property.block() has been performed?
In actual code there is some business logic that I need to do after getting title variable in above code and therefore I cannot avail the use of prop passed as model attribute to thymleaf template to directly get title within thymeleaf.
How to solve this problem?
Keep in mind your Flux<Photo> is an asynchronous process, so it cannot update the title variable outside of it in this imperative style. Note that your Flux<Photo> is also never subscribed to or composed upon, so it will effectively never be invoked...
To answer your other question, yes in Spring Framework 5 having a Mono in the Map passed to Thymeleaf will resolve that Mono lazily and inject the resulting value in the Thymeleaf model.
Pending more information on the use of photos flux, for the title generation you probably need to compose more:
propertyService.findById(id)
.doOnNext(prop -> model.addAttribute("prop", prop)) //reactively add the prop to the model
.flatMapMany(prop -> {
String title = prop.title + '-' + prop.type;
if(validate(title)) //do some validation
return photoService.findByPropertyId(prop.getId());
else
return Mono.error(new IllegalArgumentException("validation failed"));
}) //not sure what you do with the `Photo`s there :/
//for now let's ignore the flux photos and at the end simply emit a String to change the view:
.thenReturn("property"); //then(Mono.just("property")) in older versions of reactor 3.1.x
I'm playing with angular-meteor and still trying to understand how to use 3-way binding. How do I run a method that checks multiple 'reactive' variables?
For example, the setVisibleLocation() method below would need to be re-run if any of the following values changed, event.setting.isPublic, event.participantIds, event.location, event.address, event.neighborhood, Meteor.userId(). The last few might not need to update reactively - a page reload might be acceptable.
showExactLocation = (event, userId) ->
return true if event.setting.isPublic
return true if userId == event.ownerId
return true if event.participantIds && ~event.participantIds.indexOf(userId)
return false
setVisibleLocation = (event, userId) ->
userId ?= Meteor.userId()
if showExactLocation(event, userId)
event.visible = {
address: event.address
marker: event.location
}
else
event.visible = {
address: event.neighborhood
marker: utils.maskLatLon(event.location, event.title)
}
return event
What are the more efficient patterns for handling this? I can think of a few options.
this.autorun ()=>
this.getReactively('event', true)
setVisibleLocation(this.event)
// plus any number of other methods that might need to update
// based on changes to this.event properties
or
this.autorun ()=>
this.getReactively('event.setting.isPublic')
this.getReactively('event.location')
this.getCollectionReactively('event.participantIds')
this.getReactively('event.address')
this.getReactively('event.neighborhood')
setVisibleLocation(this.event)
this.autorun ()=>
// plus any number of other combination of reactive properties
// and methods that need to be rerun
Is a deep watch advisable if event has a lot of properties to check?
I am running our automated tests using TestNG. The reason we picked TestNG is because we can send variables inputs into the test methods example public void testXX( String userId ) and the userId can change for each test.
The code below shows three different userIds I can use to execute my tests. So my exact same test will run three times for each of the three different users. This feature is awesome and really enables me to have multiple tests under different scenarios because each of our users carry different profiles.
// All valid Pricing Leads
#DataProvider(name = "userIds")
public Object[][] createPricingLeadUsersParameters() {
return new Object[][] {
{ "TestUser001" },
{ "TestUser002" },
{ "TestUser003" }
};
}
#Test( dataProvider = "userIds" )
public void createGroup( String userIds) {
............
}
The problem I am having right now is during certain conditions I can only have one userId used or else all of my tests will fail. I would like to keep my exact same test but only pass in on userId not the three shown above. It there a way to configure TestNG to make this variable on the command line so at times I would use the three defined, but under another condition it would only be one of the three or a new userId?
Sure, there are plenty of ways to do this. How about passing a system property when you run TestNG?
java -Dfoo=bar org.testng.TestNG...
and then your data provider can test the value of foo with System.getProperty() and adjust what it returns accordingly.