ConfigSlurper bad result with nested values with same key - groovy

I'm trying to use the Groovy's ConfigSlurper in a projetct but it happens to have a weird behaviour when the same key appears two times in the configuration. Here is an example :
def struct = """
node2 {
subnode21="Test1"
}
root1 {
node2 {
subnode1="Test2"
subnode2=node2.subnode21
}
}
"""
def config = new ConfigSlurper().parse(struct)
This will produce the following result :
[
node2:[
subnode21:Test1
],
root1:[
node2:[
subnode1:Test2,
subnode21:[:],
subnode2:[:]
]
]
]
ConfigSlurper seems to consider that subnode2=node2.subnode21 in the root1 closure refers to root1.node2 and not the node2 closure declared above.
Is this a known limitation or am I missing something ?

Related

Groovy method as variable

I have the following Groovy script:
def Deploy() {
if (App == "TEST"){
def book = load "book.groovy"
book.buildList.each {
a lot of actions
}
else {
book.each {
the same a lot of actions
}
}
So the difference only in execution methods (properties): book.buildList.each or book.each. How to avoid to repeat those a lot of actions and keep code cleaner. Probably there is a way to put book.buildList.each or book.each into the variable?
File Book.groovy contain few map:
buildList = [
'key1':'value1',
'key2':'value2',
'key3':'value3',
]
anotherList = [
'key11':'value11',
'key22':'value22',
'key33':'value33',
]
return this
But if App not "TEST" I have book map:
[
'key1':'value1',
'key2':'value2',
'key3':'value3',
]
If both each invocations do the exact same thing, you can extract the body of the closure you pass to each method to a variable and share it between both invocations. Consider the following (simplified) example:
def closure = { k, v ->
println "k = $k, v = $v"
}
def a = [
'key1': 'value1',
'key2': 'value2',
'key3': 'value3',
]
def b = [
'key11': 'value11',
'key22': 'value22',
'key33': 'value33',
]
println "Calling common closure on the map a:"
a.each closure
println "Calling common closure on the map b:"
b.each closure
In this example, I common closure to the variable, and then I was able to re-use it in both iterations. Also, keep in mind that in your example you are using maps, not lists. Calling each on map is absolutely correct, and in this case, you can use a closure with k and v parameters to map key and value accurately.

Remove hyphens from keys in deeply nested map

I posted this question in the Groovy mailing lists, but I've not yet gotten an answer. I was wondering if someone can help here. I am re-posting relevant text from my original question.
I have an input json that’s nested, that is read via a JsonSlurper, and some of the keys have hyphens in them. I need to replace those keys that have hyphens with underscores and convert it back to json for downstream processing. I looked at the JsonGenerator.Options documentation and I could not find any documentation for this specific requirement.
I also looked through options to iterate through the Map that is produced from JsonSlurper, but unfortunately I’m not able to find an effective solution that iterates through a nested Map, changes the keys and produces another Map which could be converted to a Json string.
Example Code
import groovy.json.*
// This json can be nested many levels deep
def inputJson = """{
"database-servers": {
"dc-1": [
"server1",
"server2"
]
},
"discovery-servers": {
"dc-3": [
"discovery-server1",
"discovery-server2"
]
}
}
"""
I need to convert the above to json that looks like the example below. I can iterate through and convert using the collectEntries method which only works on the first level, but I need to do it recursively, since the input json can be an nested many levels deep.
{
"database_servers": {
"dc_1": [
"server1",
"server2"
]
},
"discovery_servers": {
"dc_3": [
"discovery-server1",
"discovery-server2"
]
}
}
Seems like you just need a recursive method to process the slurped Map and its sub-Maps.
import groovy.json.JsonSlurper
JsonSlurper slurper = new JsonSlurper()
def jsonmap = slurper.parseText( inputJson )
Map recurseMap( def inputMap ) {
return inputMap.collectEntries { key, val ->
String newkey = key.replace( "-", "_" )
if ( val instanceof Map ) {
return [ newkey, recurseMap( val ) ]
}
return [ newkey, val ]
}
}
def retmap = recurseMap( jsonmap )
println retmap // at this point you can use output this however you like

Inject String to Map - Groovy

I am new to Groovy and working on a device handler for my Smartthing Hub which is written in Groovy. I am having trouble parsing a string.
def parseDescriptionAsMap(description) {
println "description: '${description}"
def test = description.split(",")
println "test: '${test}"
test.inject([:]) { map, param ->
def nameAndValue = param.split(":")
println "nameAndValue: ${nameAndValue}"
if(map)
{
println "map is NOT NULL"
map.put(nameAndValue[0].trim(),nameAndValue[1].trim())
}
else
{
println "map is NULL!"
}
}
}
Output:
description: 'index:17, mac:AAA, ip:BBB, port:0058, requestId:ce6598b2-fe8b-463d-bdf3-01ec35055f7a, tempImageKey:ba416127-14e3-4c7b-8f1f-5b4d633102e5
test: '[index:17, mac:AAA, ip:BBB, port:0058, requestId:ce6598b2-fe8b-463d-bdf3-01ec35055f7a, tempImageKey:ba416127-14e3-4c7b-8f1f-5b4d633102e5]
nameAndValue: [index, 17]
nameAndValue: [ mac, AAA]
map is NULL!
nameAndValue: [ ip, BBB]
map is NULL!
map is NULL!
nameAndValue: [ port, 0058]
nameAndValue: [ requestId, ce6598b2-fe8b-463d-bdf3-01ec35055f7a]
Two questions:
1. Why is the variable, map, null?
2. Why is the function not printing the nameAndValue->'tempImageKey' info?
map cannot be null, if(map) checks if it's null or empty...and it will not be null in this situation (so long as you follow #2)
You need to return map from the inject closure, so that is can be aggregated.
test.inject([:]) { map, param ->
def nameAndValue = param.split(":")
println "nameAndValue: ${nameAndValue}"
map.put(nameAndValue[0].trim(),nameAndValue[1].trim())
map
}
A simpler version of what you're trying would be:
description.split(',')*.trim()*.tokenize(':').collectEntries()

How can I retrieve the build parameters from a queued job?

I would like to write a system groovy script which inspects the queued jobs in Jenkins, and extracts the build parameters (and build cause as a bonus) supplied as the job was scheduled. Ideas?
Specifically:
def q = Jenkins.instance.queue
q.items.each { println it.task.name }
retrieves the queued items. I can't for the life of me figure out where the build parameters live.
The closest I am getting is this:
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
it.task.properties.each { key, val ->
println(" ${key}=${val}")
}
}
This gets me this:
4.1.next-build-launcher:
com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty$ScannerJobPropertyDescriptor#b299407=com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty#5e04bfd7
com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty$DescriptorImpl#40d04eaa=com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty#16b308db
hudson.model.ParametersDefinitionProperty$DescriptorImpl#b744c43=hudson.mod el.ParametersDefinitionProperty#440a6d81
...
The params property of the queue element itself contains a string with the parameters in a property file format -- key=value with multiple parameters separated by newlines.
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
println("Parameters: ${it.params}")
}
yields:
dbacher params:
Parameters:
MyParameter=Hello world
BoolParameter=true
I'm no Groovy expert, but when exploring the Jenkins scripting interface, I've found the following functions to be very helpful:
def showProps(inst, prefix="Properties:") {
println prefix
for (prop in inst.properties) {
def pc = ""
if (prop.value != null) {
pc = prop.value.class
}
println(" $prop.key : $prop.value ($pc)")
}
}
def showMethods(inst, prefix="Methods:") {
println prefix
inst.metaClass.methods.name.unique().each {
println " $it"
}
}
The showProps function reveals that the queue element has another property named causes that you'll need to do some more decoding on:
causes : [hudson.model.Cause$UserIdCause#56af8f1c] (class java.util.Collections$UnmodifiableRandomAccessList)

Groovy Spock unit tests with closures

How can I do this in Spock/groovy?
package org.jenkinsci.plugins
import hudson.matrix.*
import spock.lang.*
import org.junit.Rule
import org.jvnet.hudson.test.JenkinsRule
class xxxx extends Specification {
#Rule JenkinsRule rule = new JenkinsRule()
def 'matrix'() {
given:
def matrixProject = rule.createMatrixProject()
AxisList axl = new AxisList();
def axis = new TextAxis('TEST', "1", "2", "3")
axl.add(axis)
matrixProject.setAxes(axl)
expect: matrixProject.scheduleBuild2(0).get().logFile.text.contains("Some String!")
matrixProject.scheduleBuild2(0).get().getRuns().each(){
expect: it.logFile.text.contains("Another String")
}
}
}
specifically, how can I run a closure with a nested test? The "Another String" test doesn't work
Does this work?
def 'matrix'() {
given:
def matrixProject = rule.createMatrixProject()
def axis = new TextAxis('TEST', "1", "2", "3")
matrixProject.axes.add(axis)
expect:
with( matrixProject.scheduleBuild2(0).get() ) {
logFile.text.contains("Some String!")
runs.every { it.logFile.text.contains("Another String") }
}
}
}
Either use every instead of each, or use a nested assert.
I'm not sure if I understand your question well. However if by nested test you mean evaluating statement inside of each closure, why not just use assert
expect:
matrixProject.scheduleBuild2(0).get().logFile.text.contains("Some String!")
matrixProject.scheduleBuild2(0).get().getRuns().each() {
assert it.logFile.text.contains("Another String")
}
#tim_yates's approach also seems fine and it's more like Spock's way. I haven't tested it though.
EDIT
If you want be sure that all logFiles contain test string then use 'every' method as Peter suggested.
expect:
...
matrixProject.scheduleBuild2(0).get().getRuns().every {
it.text.contains('Another String')
}
Other approach, if you prefer to know how many logFiles don't contain test string on test fail count them and compare result size to zero:
expect:
...
matrixProject.scheduleBuild2(0).get().getRuns().count {
!it.text.contains('Another String')
} == 0
Yet another, if you like to know which files caused test to fail, get names of those which don't contain test string and compare that to an empty list:
expect:
...
matrixProject.scheduleBuild2(0).get().getRuns().findAll {
!it.text.contains('Another String')
}*.name == []

Resources