Parse key-value map string in groovy - groovy

I have some output from a call to a backend server (DSDB) using the unix shell .execute command in groovy. What it gives me is a list of key value pairs separated by a line and each pair separated by a colon. I need to have each Key Value pair placed into a map. This is the output I receive:
Group Name: groupName
GID: 12345
Type: 1
Comments:
Visibility: visibile1
Owner Name: name1
Owner Number: 123
Manager Name: manager1
Manager Number: 234
Environment: dev
State: 0
I need to get the value of Owner Name within a function and pass it back as a variable and I also need the value of environment in another function. These will be two separate functions.

Couldn't find a regex that would do all of that, but a bit of groovy fixes that:
final data = """
Group Name: groupName
GID: 12345
Type: 1
Comments:
Visibility: visibile1
Owner Name: name1
Owner Number: 123
Manager Name: manager1
Manager Number: 234
Environment: dev
State: 0
"""
final a = (data =~ /\s*([^:]+):(.*)/)
.collect { [it[1], it[2].trim()] }
.collectEntries()
assert a["Owner Name"] == "name1"
assert a["Environment"] == "dev"
Java patterns are in "single line mode" by default. This regex is matching keys and values separated by colon and finds a match for each line.
The collect then maps the matches into tuples. I'm also abusing this step to strip the leading space from the values (except from "Comments" field, which doesn't have a value).
Finally, collectEntries can be used to map the list of tuples into a single map.

Related

find if all elements of a list exists in a file and create new list

Team,
issue is that it is still appending those lines to needed_list that match the pattern defined in skip_list. Not sure if there is space or why it is not skipping them, ex: // or }
I have two files and I need to see if a string from each line in fileA is referred in file2.
For this, I am looping over all strings just before : in fileA and check against all lines in file2 and printing them and skipping those lines from being printed that are not used in file2.
But I am observing that some strings from skip_list are still being printed.
Please ignore what sort of files these are because these are manually created sample files to test code.
file1
package deploy
application: sonarqube: helm: values: {
sonarqube: {
namespace: "sonarqube"
deploymentType: "StatefulSet"
role: "server"
// There should not be more than 1 sonarqube instance connected to the same database. Please set this value to 1 or 0 (in case you need to scale down programmatically).
file2
metadata:
name: {{ .scanjob }}
namespace: {{ .Values.namespace }}
command: ls
deploy: deploymentType
type: role: server
code
class SearchIn():
skip_list = ['{','}','/','[]','[',']','//','#']
needed_list = []
def charts_files(self, args):
chart_name = args
self.local_path = Path(environ.get("HOME"))/chart_name/"templates"
self.files_list=listdir(local_path)
return self.files_list
def values(self, args):
self.charts_files(chart_name)
values = "values.cue"
with open(self.local_path/values, 'r') as v:
for aline in v:
trimmed_line=aline.strip().split(":",1)[0]
print(trimmed_line)
for aptrn in self.skip_list:
if aptrn in trimmed_line or aptrn is trimmed_line:
continue
else:
print("no pattern found")
print(trimmed_line)
if trimmed_line not in self.needed_list:
self.needed_list.append(trimmed_line)
print(self.needed_list)
co = SearchIn()
chart_name = input(r'Enter chart name:')
co.values(chart_name)
Output: some snip
}
no pattern found
}
['package deploy', 'application', 'sonarqube', 'namespace', 'deploymentType', 'role', '// There should not.....
..
..
expected output
needed_list should not contain any element from skip_list
}
['package deploy', 'application', 'sonarqube', 'namespace', 'deploymentType','role'....]

regexp to search and replace any string that starts from a keyword

Hi want a create a regexp that takes below string and masks all ID at all position that starts from "dream".
s = """Cli house create --house-id dream1.house.hc1.area.aaaaaaaah454432fg7zf5h2ufqxvfhzqokkaufgf5znua36p4puwnog7akcq --zip-code 123456 --sub-zip-id dream1.zip.zc1.area..aaaaaaaawjdrauwdwzn4kfsma7bomq26l3zjeqmx6qsgihhr4kadmbgngxlq --type victorian"""
and converts it to
s = """Cli house create --house-id dream1.house.hc1.area.<ID> --zip-code 123456 --sub-zip-id dream1.zip.zc1.area..<ID> --type victorian"""
s = re.sub(r"(dream\w*.+)(?= *.)(.+)", "\1", s)
but it only works if ID is at end i.e
dreamCli house create --house-id dream1.house.hc1.area.aaaaaaaah454432fg7zf5h2ufqxvfhzqokkaufgf5znua36p4puwnog7akcq
an id will always in in this format
dream< digit >.< string >.< string >.< string > ....
You could match
(\bdream\S*\.)\S*
( Capture group 1
\bdream\S*\. A word boundary, match dream followed till the last dot whithout matching whitespace chars,
) Close group
\S* Match 0+ non whitespace char (Match what follows without matching whitespace chars)
And replace with the capturing group followed by <ID>
\1<ID>
Regex demo
Example code
import re
regex = r"(\bdream\S*\.)\S*"
test_str = "Cli house create --house-id dream1.house.hc1.area.aaaaaaaah454432fg7zf5h2ufqxvfhzqokkaufgf5znua36p4puwnog7akcq --zip-code 123456 --sub-zip-id dream1.zip.zc1.area..aaaaaaaawjdrauwdwzn4kfsma7bomq26l3zjeqmx6qsgihhr4kadmbgngxlq --type victorian"
result = re.sub(regex, r"\1<ID>", test_str)
print(result)
Output
Cli house create --house-id dream1.house.hc1.area.<ID> --zip-code 123456 --sub-zip-id dream1.zip.zc1.area..<ID> --type victorian

how to skip 1 st line of payload - groovy

I have final payload in csv format with some amount of employee records. I have 2 main values to work with:
-Sequence field (added for each record)
-personal_id (unique for every employee)
Because of the fact that each employee can have multiple records, the need is to have according sequence number for each ID. In other words If employee with ID "123" have 5 records, the sequence value should be 1,2,3,4,5 instead of 1,1,1,1,1. All records are grouped by ID.
Also the payload below shows only 2 fields needed for the description, normally it has much more fields and is an example of static payload. Normally it will be dynamically.
here are the input payload:
Sequence;ID
123456
232323
232323
232323
111111
111111
222222
222222
222222
222222
222222
222222
222222
and here is expected payload
Sequence;ID
1;123456
1;232323
2;232323
3;232323
1;111111
2;111111
1;222222
2;222222
3;222222
4;222222
5;222222
6;222222
7;222222
Here is the actual payload after using groovy script:
1;Sequence;ID
1;123456
1;232323
2;232323
3;232323
1;111111
2;111111
1;222222
2;222222
3;222222
4;222222
5;222222
6;222222
7;222222
I am using the following groovy script but the problem is that the first line of payload is also numbered by 1. Can you show me how to skip the first line of payload?
The script is adding a number in every record to have it counted. The clue is to have all records of the same id counted from 1 incrementally. The script does that perfectly but the issue is as I said while i want to skip the first line od payload "Sequence;..." to not have it counted.
import com.sap.gateway.ip.core.customdev.util.Message
def Message processData(Message message) {
def payload = message.getBody(java.lang.String)
def prevId = ''
def sequence = 1
def sb = new StringBuilder()
def line = new StringReader()
payload.eachLine { line ->
def values = line.split(';')
if (values[1] != prevId) {
// New personal ID
sequence = 1
prevId = values[1]
} else {
// Another line of the same personal ID
sequence += 1
}
line = readLine()
values[0] = sequence
sb.append(values.join(';')).append(System.lineSeparator())
}
message.setBody(sb.toString())
return message
}
Assuming message is just a String, .eachLine can be given two arguments: the line and the index. You could use that to do whatever logic you want in your closure: skip index 0 entirely, output it as-is, etc.
Basic example:
payload.eachLine { line, idx ->
if (idx == 0) {
// do something with the first line
} else {
// everything you currently have
}
}

Update a Map in groovy spock framework

I have the below spock specification and want to update the map from data table. Can some body help achieve this
def "groovy map update"() {
setup: "step1"
Map json = [
user :[
name : 'ABC'
]]
when: "step2"
println ('Before modification:')
println (json)
then: "step3"
json.with {
//user.name = value // this one works
(field) = value // this one does not work
}
println ('After modification:')
println (json)
where:
field | value
'user.name' | 'XYZ'
}
The then section is intended for asserts and not for updates etc. So you have to update the map in the when section and then test the result in the then section. For example like this:
def "groovy map update"() {
setup: 'create json'
Map json = [user: [name: 'ABC']]
when: 'update it'
def target = json
for (node in path - path.last()) {
target = target[node]
}
target[path.last()] = value
then: 'check the assignment'
json.user.name == value
where:
path | value
['user', 'name'] | 'XYZ'
}
One way how to update nested Map value can be by using list of path nodes instead of field notation and then iterate over them to obtain the last Map instance and set the value there:
def target = json
for (node in path - path.last()) {
target = target[node]
}
target[path.last()] = value
The accepted solution is correct, I just want to show an alternative doing the same in a slightly different way, assuming you want to stick with the dotted notation for field in your where: block. I just added two more test cases in order to make sure it works as expected.
#Unroll
def "set #field to #value"() {
setup: 'create json'
Map json = [user: [name: 'ABC', address: [street: '21 Main St', zip: '12345', city: 'Hometown']]]
when: 'update it'
def subMap = json
field.split("[.]").each {
if (subMap[it] instanceof Map)
subMap = subMap[it]
else
subMap[it] = value
}
println json
then: 'check the assignment'
json.newField == value ||
json.user.name == value ||
json.user.address.zip == value
where:
field | value
'newField' | 'dummy'
'user.name' | 'XYZ'
'user.address.zip' | '98765'
}
Update: If you want to save a few lines of code you can also use a fold (or reduce or accumulate) operation via inject(..) as described here
#Unroll
def "set #field to #value"() {
setup: 'create json'
Map json = [user: [name: 'ABC', address: [street: '21 Main St', zip: '12345', city: 'Hometown']]]
when: 'update it'
field.split("[.]").inject(json) { subMap, key ->
subMap[key] instanceof Map ? subMap[key] : subMap.put(key, value)
}
println json
then: 'check the assignment'
json.newField == value ||
json.user.name == value ||
json.user.address.zip == value
where:
field | value
'newField' | 'dummy'
'user.name' | 'XYZ'
'user.address.zip' | '98765'
}
Whether you find that readable or not may depend on your familiarity with topics like functional programming in general or map/reduce in particular. The charm here in addition to brevity is that we no longer need a local variable outside of our closure but we just inject (hence the method name) the result of iteration n to iteration n+1.
BTW, as a nice side effect inject(..) as I am using it here returns the previous value of the value you set or overwrite. Just add println in front of field.split("[.]").inject(json) ... in order to see it.
Update 2: Please note that both variants only work if there is no existing field value of type Map in the target field because of the instanceof Map check heuristics in my code. I.e. these two cases would not work:
'user.address' | [street: '23 Test Blvd', zip: '33333', city: 'Somewhere']
'user.address' | '23 Test Blvd, 33333 Somewhere'
This one would work, though, because there is no preexisting value:
'user.alternativeAddress' | [street: '23 Test Blvd', zip: '33333', city: 'Somewhere']

Not able to get a value from yml file in groovy

I have stored an entire yaml file into a Map yamlConfig which i am able to print and check.
The Output when I run the code: yamlConfig.each{ k, v -> println "${k}:${v}" } is:
Host:localhost
Port:10000
application:[name:ABC, preferences:[UUID:d3f3278e, server:localhost:2222]]
services:[[name:XYZ, instances:1, start:true]]
dataSets:[[name:ABC], [name:XYZ]]
Now, I am trying to fetch a value from Map using following code:
println yamlConfig.get("services").getAt("name")
However, I am getting the value: [XYZ]. Instead I need the result as XYZ, without square brackets.
Yml file I am using:
Host: localhost
Port: 10000
application:
name: ABC
preferences:
UUID: d3f3278e
server: localhost:2222
services:
- name: XYZ
instances: 1
start: true
data:
- name: ABC
- name: XYZ
This is because of the - character placed before your name property. It makes the yaml parser treat what's inside the services section as an array.
When you ask for the name property doing yamlConfig['services']['name'] groovy gives you all the macthing properties of array items in the services array, and it can only return them in another array.
So either remove the - or use yamlConfig['services'][0]['name'].
yamlConfig.get("services")
return a list but not a service, therefore when you apply getAt to the returned list of services it returns a list of names.
yamlConfig.get("services").getAt('name')
actually does
yaml['services'].collect { it['name'] }
so in order to get a name of a certain service you need to do something like this:
println yaml['services'][0]['name']

Resources