Jenkins Groovy extend properties array - arrays

Inside my jenkinsfile I want to set multiple properties based on some dependencies.
So in the top of my jenkinsfile I am setting my first parameter:
properties([
parameters([
booleanParam(
defaultValue: false,
description: '...',
name: 'parameters1'
),
])
])
Some lines below I want to set another parameter if a condition is met
if(awesomeCondition) {
properties([
parameters([
booleanParam(
defaultValue: false,
description: '...',
name: 'parameters2'
),
])
])
}
The problem I am now running into is that the second parameter is overriding the first parameter. How to handle this correctly?

The properties step overrides the existing job properties so, as you noted, the second call overrides the previous one. This is expected behaviour.
What you need to do is to keep a list of new parameters and then use a single call to properties step:
def newParameters = []
newParameters.add([
$class: 'hudson.model.BooleanParameterDefinition',
name: "p1",
default: false,
description:"Some help text"
])
...
if(awesomeCondition) {
newParameters.add([
$class: 'hudson.model.BooleanParameterDefinition',
name: "p2",
default: false,
description:"Some help text"
])
}
...
properties([parameters(newParameters)])
The $class: 'hudson.model.BooleanParameterDefinition' is needed since we are creating the objects outside of the properties step. For other types of parameters see sub-classes to this class.

Related

React - setState doesn't refresh component as expected

I might be experiencing either a bug, or I misunderstand something about general javascript syntax.
Using ServiceNow UI Builder, I'm trying to refresh the datasource of a specific data visualization component. Which requires me to use setState and send in an entire JSON blob.
The following works as expected:
api.setState('intAssignedDonut', {
"header": "Interactions assigned to one of my groups",
"datasource": [{
"isDatabaseView": false,
"allowRealTime": true,
"sourceType": "table",
"label": {
"message": "Interaction"
},
"tableOrViewName": "interaction",
"filterQuery": "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744",
"id": "intAssigned",
"elm": {}
}],
"metric": [{
"dataSource": "intAssigned",
"id": "intAssignedMetric",
"aggregateFunction": "COUNT",
"numberFormat": {
"customFormat": false
},
"axisId": "primary"
}],
"groupBy": [{
"maxNumberOfGroups": "ALL",
"numberOfGroupsBasedOn": "NO_OF_GROUP_BASED_ON_PER_METRIC",
"showOthers": false,
"groupBy": [{
"dataSource": "intAssigned",
"groupByField": "state",
"isRange": false,
"isPaBucket": false
}]
}]
});
However, I only need to alter a few properties, not the whole thing.
So I thought I'd just clone the thing into a temp object, change what I need, then pass the cloned object back.
let clientState_intAssignedDonut = api.state.intAssignedDonut;
clientState_intAssignedDonut.header = 'Interactions assigned to one of my groups';
clientState_intAssignedDonut.datasource[0].filterQuery = 'active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744';
api.setState("intAssignedDonut", clientState_intAssignedDonut);
This seems to update the header properly, but the component doesn't refresh the datasource.
Even if I console.log api.state.intAssignedDonut it looks identical to the whole JSON blob.
EDIT: I also tried using spread operators, but I can't figure out how to target the datasource[0]
api.setState("intAssignedDonut", {
...api.state.intAssignedDonut,
header: "Interactions assigned to one of my groups",
datasource[0].filterQuery: "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744"
});
Javascript objects are passed by reference values, and react state is immutable:
let clientState_intAssignedDonut = api.state.intAssignedDonut;
api.setState("intAssignedDonut", clientState_intAssignedDonut);
This is mutating the state directly, and React will ignore your update if the next state is equal to the previous state, which is determined by an Object.is comparison to check if both objects are of the same value, see docs
Your second attempt is heading to the right direction using spread operator:
Update method one: first copy the nested object using: JSON.parse(JSON.stringify(object)), or you can find other method in this question: What is the most efficient way to deep clone an object in JavaScript?
let copied = JSON.parse(JSON.stringify(api.state.intAssignedDonut)); // copy a nested object
copied.header = "Interactions assigned to one of my groups";
copied.datasource[0].filterQuery = "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744";
setState("intAssignedDonut",copied);
Update method two:
setState("intAssignedDonut",{
...api.state.intAssignedDonut,
header: "Interactions assigned to one of my groups",
datasource: [{ ...state.datasource[0], filterQuery: "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744" }]
});
Check out sandbox

Avoid serialization of properties

When I require to add a private property to an object (for view or logic control) that will be submitted to a rest api latter, is valid prefix the property with $$? This is tricky in cases when I have an object with a list of children and each child requires a private property that should not be sent.
{
name: 'my object',
items: [
{
name: 'my child',
$$editing: true
},
{
name: 'my other child',
$$editing: true
}
]
}
Yes, angularjs $http service uses the angular.toJson method by default.
All properties with $$ are filtered out, because angular uses such properties internally. (e.g. you may have seen the $$hashKey property, which is added by angular)
You can try:
console.log(angular.toJson({a:1, $$b:2, c: {x:2,$$_y:3}}))
results in "{"a":1,"c":{"x":2}}"

Ng-admin: How to show different fields according to user's selection?

I am using ng-admin to write a admin management. I met below issue, can somebody help me?
In my creationView, I would like to show different fields(text/video/pictures) according to the selection of "type" field. How I can make it?
var articles = nga.entity('articles');
articles.creationView().fields([
nga.field('type','choice')
.validation({ required: true })
.choices([
// 1: txt, 2: pic, 3: vid
{ value: 1, label: 'Text'},
{ value: 2, label: 'Picture'},
{ value: 3, label: 'Video'},
]),
nga.field('txt','file')
.attributes({ placeholder: 'Write something... !' }),
nga.field('pic','file')
.label('Picture(.jpg|.png)')
.uploadInformation({ 'url': '/api/adminapi/uploadPicture', 'apifilename': 'pictures', 'accept': 'image/jpg,image/png'}),
nga.field('vid','file')
.label('Video(.mp4)')
.uploadInformation({ 'url': '/api/adminapi/uploadVideo', 'apifilename': 'video', 'accept': 'video/mp4'}),
])
The Field Configuration doc page explains how to do this using the "template" field config:
template(String|Function, templateIncludesLabel=false) All field types
support the template() method, which makes it easy to customize the
look and feel of a particular field, without sacrificing the native
features.
...
To force the template to replace the
entire line (including the label), pass true as second argument to the
template() call. This can be very useful to conditionally hide a field
according to a property of the entry:
post.editionView()
.fields([
nga.field('category', 'choice')
.choices([
{ label: 'Tech', value: 'tech' },
{ label: 'Lifestyle', value: 'lifestyle' }
]),
nga.field('subcategory', 'choice')
.choices(function(entry) {
return subCategories.filter(function (c) {
return c.category === entry.values.category;
});
})
// display subcategory only if there is a category
.template('<ma-field ng-if="entry.values.category" field="::field" value="entry.values[field.name()]" entry="entry"
entity="::entity" form="formController.form"
datastore="::formController.dataStore"></ma-field>', true),
]);
I just have a work round method. That is using .attributes(onclick, "return updateButton();") for nga.field("type"). In updateButton() method, it will help to check current 'type' field value and using DOM methods to change the button enable and disabled.
However, I still realy appreciate that if this requirement can be support in future. It will be helpful for user to make UI controls easily.

Backbone fetching a model: Parsing format in array of objects

My model in the server-side has an attribute which is an array of objects. In the client-side I'm trying to fetch that model in a backbone's model.
After fetching the model from my server I get this object:
{
round: 17,
username: bob,
football_bets: [
0: { one: true, x: false, two: false },
1: { one: false, x: false, two: true },
2: { one: true, x: true, two: false },
]
}
However, I was expecting something like this:
{
round: 17,
username: bob,
football_bets: [
{ one: true, x: false, two: false },
{ one: false, x: false, two: true },
{ one: true, x: true, two: false },
]
}
I understand that this is something related with how backbone parses the result of a fetch action, but I don't know how I should deal with.
Should I override the parse method of the model to get the result as I expect?
Should I use collections instead of trying to model everything inside of a single model?
Should I use some third-party library to deal with nested objects?
I really appreciate any kind of suggestions!
The format google-chrome console adopts for arrays is like the first array I've posted in the question. I was thinking that the numbers appears before the object itself was something that backbone it was adding when you perform a fetch action ... I was thinking this because when I debugged my code I had an error that said me it was not possible to access to an attribute.
After more debugging I realize that there was an indexation problem trying to access to an undefined element ... So the conclusion of all this mess is how the google-chrome console formats arrays! Hope this helps somebody ...

Setting array value in backbone.js initialize function

I'm trying to set an array value in a backbone.js model initialize function. In the line that starts with 'this.set...' I get a 'unexpected string' error. Is it not possible to set array values this way?
Thanks!
var bGenericItem = Backbone.Model.extend({
defaults: {
attrArray: new Array({'item_id': '', 'type': '', 'name':''})
},
initialize: function(){
// Set the id to cid for now
this.set({ attrArray["item_id"]: this.cid });
}
});
What you're trying to do doesn't make any sense. Your defaults is an array which holds a single object:
defaults: {
attrArray: [
{ item_id: '', type: '', name: '' }
]
},
You'd use an array if you wanted to hold a list of attribute objects. But, if you had a list of attribute objects, which one's item_id would you expect attrArray['item_id'] to refer to? Are you assuming that attrArray will always be initialized to the default value and that no one would ever send an attrArray in as part of your model's initial data? If so, you'd want something more like this:
// Use a function so that each instance gets its own array,
// otherwise the default array will be attached to the prototype
// and shared by all instances.
defaults: function() {
return {
attrArray: [
{ item_id: '', type: '', name: '' }
]
};
},
initialize: function() {
// get will return a reference to the array (not a copy!) so
// we can modify it in-place.
this.get('attrArray')[0]['item_id'] = this.cid;
}
Note that you'll run into some issues with array attributes that require special handling:
get('attrArray') will return a reference to the array that is inside the model so modifying that return value will change the model.
Things like a = m.get('attrArray'); a.push({ ... }); m.set('attrArray', a) won't work the way you expect them to, the set won't notice that the array has changed (because it hasn't, a == a is true after all) so you won't get "change" events unless you clone the attrArray somewhere between get and set.
There are several problems with your code
1: The defaults setting is an object literal which means the value that you assign to it is set as soon as it's defined. You need to set your defaults to a function, instead of a literal value. This will ensure each model instance gets it's own copy of the default values, instead of sharing a copy across every model instance.
2: You should also not use new Array, just use an array literal syntax []. But you're not really using an array in this code, so I removed the array wrapper for now.
3: You can't access attrArray directly. You must get it from the model's attributes and then update it
var bGenericItem = Backbone.Model.extend({
defaults: function(){
return {
attrArray: {'item_id': '', 'type': '', 'name':''}
};
},
initialize: function(){
// Set the id to cid for now
var arr = this.get("attrArray");
arr["item_id"] = this.cid;
}
});

Resources