How to add entries to my map without overwrite existing with a fallback in case of empty - arrays

I'm trying to create a map from a list of files that changed in our SCM,
So far I've managed to get the list of the changed files(in the code below I put hard-coded example )
and then map them to the relevant services which I want to build.
I also added the fallback in case the path is not mapped in the "path2component" function.
My problem is that the map that I'm creating is always overriding itself,
and not inserting the entries one after the other as expected.
ChangedFilesList = [main/exporter/pom.xml, main/app/download-server/pom.xml]
ChangedFilesList.each {
println "Changes in file: ${it}"
def file = "${it}"
triggermap = path2component.findAll{ // find relevant map entries
file.find(it.key)
}.collectEntries{ // build your map
["trigger-${it.value}", true]
} ?: ["trigger-none": true] // provide a fallback
println "triggermap:" + triggermap
}
My output is this:
Changes in file: main/app/download-server/pom.xml
triggermap:[trigger-download-server:true]
Changes in file: main/exporter/pom.xml
triggermap:[trigger-rest-exporter:true]
Expected output:
Changes in file: main/app/download-server/pom.xml
triggermap:[trigger-download-server:true]
Changes in file: main/exporter/pom.xml
triggermap:[trigger-download-server:true,trigger-rest-exporter:true]

The following code:
def changedFiles = ['main/exporter/pom.xml', 'main/app/download-server/pom.xml']
def path2component = [
'main/exporter': 'rest-exporter',
'main/app/download-server': 'download-server'
]
def triggerMap = path2component.findAll { path, service ->
changedFiles.any { it.find(path) }
}.collectEntries { path, service ->
["trigger-${service}".toString(), true]
}.withDefault { false }
println triggerMap
println("rest-exporter: ${triggerMap['trigger-rest-exporter']}")
println("unchanged-service: ${triggerMap['trigger-unchanged-service']}")
uses the groovy map withDefault construct to specify a value which should be returned if a key is not found in the map. This code tries to mock your scenario with a path2component map. Result of running the above:
─➤ groovy solution.groovy
[trigger-rest-exporter:true, trigger-download-server:true]
rest-exporter: true
unchanged-service: false
However if they only thing you want to do is figure out if a service needs rebuilding, there is really no need to have the 'trigger-' prefix in there:
def simplerMap = path2component.findAll { path, service ->
changedFiles.any { it.find(path) }
}.collectEntries { path, service ->
[service, true]
}.withDefault { false }
println simplerMap
println("rest-exporter: ${simplerMap['rest-exporter']}")
println("unchanged-service: ${simplerMap['unchanged-service']}")
which prints:
─➤ groovy solution.groovy
[rest-exporter:true, download-server:true]
rest-exporter: true
unchanged-service: false
and if you don't have millions of items in this trigger map (i.e. performance is not an issue), you could also just do:
def changedServices = path2component.findAll { path, service ->
changedFiles.any { it.find(path) }
}.values()
println changedServices
println("rest-exporter: ${changedServices.contains('rest-exporter')}")
println("unchanged-service: ${changedServices.contains('unchanged-service')}")
which prints:
─➤ groovy solution.groovy
[rest-exporter, download-server]
rest-exporter: true
unchanged-service: false

Groovy provides variant of get, that lets you provide a fallback, which also get's written into the map. E.g.:
def m = [a: true]
assert m.get("b", false)==false
assert m.containsKey("b")
Yet, the absence of a key with a regular get will give you null, which is basically false

Related

How to extract data from an API and create an array to send to the another API in Jmeter?

Example:
API A:
{
"customer":[
{
"name":"Jane",
"phone":"9999999",
"email":"jane#test.com"
},
{
"name":"John",
"phone":"8888888",
"email":"john#test.com"
},
{
"name":"Joe",
"phone":"7777777",
"email":"Joe#test.com"
}
]
}
Using the JSON extractor, I want to get the names of all the customers
so: Jane, John, Joe
How do I get these values and turn them into an array
[{"name":"Jane", "name":"John", "name":"Joe"}]
And pass it onto the next API?
Note: That it has to be dynamic so API A could show different 2 names or 1 name or more and needs to be adjusted into the array
First of all your [{"name":"Jane", "name":"John", "name":"Joe"}] is not a valid JSON, you can check it yourself:
so I strongly doubt that this is the string you need to generate.
So if you really need to construct this value you can do something like:
Add JSR223 PostProcessor as a child of the request which returns this "customers" data
Put the following code into "Script" area:
def response = new groovy.json.JsonSlurper().parse(prev.getResponseData())
def payload = new StringBuilder()
payload.append('[{')
0.upto(response.customer.size - 1, { index ->
payload.append('"name": "').append(response.customer[index].name).append('"')
if (index != response.customer.size - 1) {
payload.append(',')
}
})
payload.append('}]')
vars.put('payload', payload as String)
Refer the generated value as ${payload} where required
Demo:
More information:
JsonSlurper
Apache Groovy - Parsing and producing JSON
Apache Groovy - Why and How You Should Use It

Filtering/Selecting array values from index in Json in groovy

Trying to filter selective index. Fieldid values changes with every build, What do not change is fieldName": "TX.Sessionval.cost". Need to filter out the whole stringval and save into variable
fieldName": "TX.Sessionval.cost"
[
{
"Fieldid": "Fieldid/112",
"fieldName": "TX.Sessionval.cost",
"stringval": "jklah-dw-4c8d-8320-das313s3ASsda|000725N8WuUrfwAS7alP|banker_name"
}
]
tried def slurper = new JsonSlurper() def result = slurper.parseText(response.getResponseBodyContent()) def newf = result.findAll { it.contains("TX.Sessionval.cost") } getting groovy.lang.MissingMethodException: No signature of method: java.util.LinkedHashMap$Entry.contains()
in Postman locator works fine
postman.setEnvironmentVariable("Stdid", jsonData.newFields[112].stringValue)
Your JSON in your question is wrong. Extrapolating from your working
code, this should work:
result.newFields.find{ it.fieldName == "TX.Sessionval.cost" }?.stringValue
This works with me result.newFields.findAll { Map map -> map.get("fieldName").contains("TX.Sessionval.cost") }?.stringValue

Return Stringbuilder when using variable name to create string builder

trying to return string builder in a loop. is this workable.. I am collecting a list with each(), then appending 'it' to "scriptBldr_" to create a different object name each time to hold the string. then I collect the object names in a list. And trying to return using a for loop for each object name. But it's failing.
List.each {
String builderstring = "scriptBldr_" + it.replaceAll (/"/, '')
StringBuilder $builderstring = StringBuilder.newInstance()
if (ValidUDA == "Region") {
$builderstring <<"""
XYZCode
"""
StringBuilders.add(builderstring)
}
}
for(item in StringBuilders)
{
return item
}
I guess the following code does what you intended to code:
def myList = ['Hello "World"', 'asb"r"sd']
def ValidUDA = "Region"
def builders = [:]
myList.each {
String builderstring = "scriptBldr_" + it.replaceAll (/"/, '')
builders[builderstring] = StringBuilder.newInstance()
if (ValidUDA == "Region") {
builders[builderstring] <<"""
XYZCode
"""
}
}
return builders
A return statement will immediatly return from the method and hence will exit the loop and only called once. So, I guess, what you wanted to achieve is to return a list of StrinngBuilders.
some hints:
it is unusual in Groovy to start a variable with $ and you can run into problems with such a naming
when asking a question on SO, try to come up with a working example. As you can see, you example was missing some definitions
Update: as you've stated in you comment that you tryed to create dynamic variable names, I've updated the code to use maps. The returned map now contains the StringBuilders together with their names.

Insert multiple records into database with Vapor3

I want to be able to bulk add records to a nosql database in Vapor 3.
This is my Struct.
struct Country: Content {
let countryName: String
let timezone: String
let defaultPickupLocation: String
}
So I'm trying to pass an array of JSON objects but I'm not sure how to structure the route nor how to access the array to decode each one.
I have tried this route:
let countryGroup = router.grouped("api/country")
countryGroup.post([Country.self], at:"bulk", use: bulkAddCountries)
with this function:
func bulkAddCountries(req: Request, countries:[Country]) throws -> Future<String> {
for country in countries{
return try req.content.decode(Country.self).map(to: String.self) { countries in
//creates a JSON encoder to encode the JSON data
let encoder = JSONEncoder()
let countryData:Data
do{
countryData = try encoder.encode(country) // encode the data
} catch {
return "Error. Data in the wrong format."
}
// code to save data
}
}
}
So how do I structure both the Route and the function to get access to each country?
I'm not sure which NoSQL database you plan on using, but the current beta versions of MongoKitten 5 and Meow 2.0 make this pretty easy.
Please note how we didn't write documentation for these two libraries yet as we pushed to a stable API first. The following code is roughly what you need with MongoKitten 5:
// Register MongoKitten to Vapor's Services
services.register(Future<MongoKitten.Database>.self) { container in
return try MongoKitten.Database.connect(settings: ConnectionSettings("mongodb://localhost/my-db"), on: container.eventLoop)
}
// Globally, add this so that the above code can register MongoKitten to Vapor's Services
extension Future: Service where T == MongoKitten.Database {}
// An adaptation of your function
func bulkAddCountries(req: Request, countries:[Country]) throws -> Future<Response> {
// Get a handle to MongoDB
let database = req.make(Future<MongoKitten.Database>.self)
// Make a `Document` for each Country
let documents = try countries.map { country in
return try BSONEncoder().encode(country)
}
// Insert the countries to the "countries" MongoDB collection
return database["countries"].insert(documents: documents).map { success in
return // Return a successful response
}
}
I had a similar need and want to share my solution for bulk processing in Vapor 3. I’d love to have another experienced developer help refine my solution.
I’m going to try my best to explain what I did. And I’m probably wrong.
First, nothing special in the router. Here, I’m handling a POST to items/batch for a JSON array of Items.
router.post("items", "batch", use: itemsController.handleBatch)
Then the controller’s handler.
func createBatch(_ req: Request) throws -> Future<HTTPStatus> {
// Decode request to [Item]
return try req.content.decode([Item].self)
// flatMap will collapse Future<Future<HTTPStatus>> to [Future<HTTPStatus>]
.flatMap(to: HTTPStatus.self) { items in
// Handle each item as 'itm'. Transforming itm to Future<HTTPStatus>
return items.map { itm -> Future<HTTPStatus> in
// Process itm. Here, I save, producing a Future<Item> called savedItem
let savedItem = itm.save(on: req)
// transform the Future<Item> to Future<HTTPStatus>
return savedItem.transform(to: HTTPStatus.ok)
}
// flatten() : “Flattens an array of futures into a future with an array of results”
// e.g. [Future<HTTPStatus>] -> Future<[HTTPStatus]>
.flatten(on: req)
// transform() : Maps the current future to contain the new type. Errors are carried over, successful (expected) results are transformed into the given instance.
// e.g. Future<[.ok]> -> Future<.ok>
.transform(to: HTTPStatus.ok)
}
}

Groovy read most recent file in directory

I just have a question about writing a function that will search a directory for the most recent log in a directory. I currently came up with one, but I'm wondering if there is a better (perhaps more proper) way of doing this.
I'm currently using hdsentinel to create logs on computer and placing the log in a directory. The logs are saved like so:
/directory/hdsentinel-computername-date
ie. C:/hdsentinel-owner-2010-11-11.txt
So I wrote a quick script that loops through certain variables to check for the most recent (within the past week) but after looking at it, I'm question how efficient and proper it is to do things this way.
Here is the script:
String directoryPath = "D:"
def computerName = InetAddress.getLocalHost().hostName
def dateToday = new Date()
def dateToString = String.format('%tm-%<td-%<tY', dateToday)
def fileExtension = ".txt"
def theFile
for(int i = 0; i < 7; i++) {
dateToString = String.format('%tY-%<tm-%<td', dateToday.minus(i))
fileName = "$directoryPath\\hdsentinel-$computerName-$dateToString$fileExtension"
theFile = new File(fileName)
if(theFile.exists()) {
println fileName
break;
} else {
println "Couldn't find the file: " + fileName
}
}
theFile.eachLine { print it }
The script works fine, perhaps it has some flaws. I felt I should go ahead and ask what the typical route is for this type of thing before I continue with it.
All input is appreciated.
Though a bit messy, you could implement a multi-column sort via the 'groupBy' method (Expounding on Aaron's code)..
def today = new Date()
def recent = {file -> today - new Date(file.lastModified()) < 7}
new File('/yourDirectory/').listFiles().toList()
.findAll(recent)
.groupBy{it.name.split('-')[1]}
.collect{owner, logs -> logs.sort{a,b -> a.lastModified() <=> b.lastModified()} }
.flatten()
.each{ println "${new Date(it.lastModified())} ${it.name}" }
This finds all logs created within the last week, groups them by owner name, and then sorts according to date modified.
If you have files other than logs in the directory, you may first need to grep for files containing 'hdsentinel.'
I hope this helps.
EDIT:
From the example you provided, I cannot determine if the least significant digit in the format:
C:/hdsentinel-owner-2010-11-11.txt
represents the month or the day. If the latter, sorting by file name would automatically prioritize by owner, and then by date created (without all of the chicanery of the above code).
For Instance:
new File('/directory').listFiles().toList().findAll(recent).sort{it.name}
Hopefully this helps some..This sorts a given path by date modified in a groovier way. The lists them out.
you can limit the list, and add other conditions in the closure to get the desired results
new File('/').listFiles().sort() {
a,b -> a.lastModified().compareTo b.lastModified()
}.each {
println it.lastModified() + " " + it.name
}
As I was trying to solve a similar problem, learnt a much cleaner approach.
Define a closure for sorting
def fileSortCondition = { it.lastModified() }
And File.listFiles() has other variation which accepts FileFilter and FilenameFilter in Java, and these interfaces has a single method called accept, Implement the interface as a closure.
def fileNameFilter = { dir, filename ->
if(filename.matches(regrx))
return true
else
return false
} as FilenameFilter
And lastly
new File("C:\\Log_Dir").listFiles(fileNameFilter).sort(fileSortCondition).reverse()
Implement FileFilter interface if filtering is to be done by File attributes.
def fileFilter = { file ->
if(file.isDirectory())
return false
else
return true } as FileFilter

Resources