Karate : Matching data between nested array - json

Is there a way to match the response data from API which contain a nested array for a key where key-value pair are in different order inside the nested array in karate?
Scenario: Verify original data contains expected data
def original = [{ a:1, b: [{c:2},{d:3}]}]
def expected = [{ b: [{d:3},{c:2}], a:1 }]
Using contains deep method will solve the issue but I am expecting original data from a API response so in some point of time if one more field gets added to the API response, then my scenario will still get passed

Don't try to do everything in one-line. Split your matches, and there is more explanation in the docs:
* def inner = [{ c: 2 }, { d: 3 }]
* def response = [{ a: 1, b: [{ d: 3 }, { c: 2 }]}]
* match each response contains { b: '#(^^inner)' }
* match each response == { a: 1, b: '#(^^inner)' }
* match response[0] == { a: 1, b: '#(^^inner)' }
* match response == [{ a: 1, b: '#(^^inner)' }]
You don't need to use all of these, I'm showing the possible options.

Related

Vega: transform array of values into accepted format

I'm using Vega for data visualization. Vega requires data which is shaped as an array of objects:
const accepted = [
{ num: 1, char: 'A' },
{ num: 2, char: 'B' },
{ num: 3, char: 'C' },
]
However, for sending data (e.g. via JSON), it is not efficient as the keys num and char are repeated multiple times. A more efficient way is sending the data as follows:
const efficient = {
num: [1, 2, 3 ],
char: ['A', 'B','C']
}
Right now I'm manually transforming the efficient structure above into the accepted structure before passing it into Vega. Is there any Vega transforms that I can use so taht I can pass efficient without having to transform it into accepted?
Use the flatten transform which should do what you need.
https://vega.github.io/vega/docs/transforms/flatten/
https://vega.github.io/vega-lite/docs/flatten.html

How to retrieve a key value based on another key value form an array object of the response body in karate [duplicate]

I think Karate documentation is great and I have tried to read as much as possible but this one little thing is stumping me right now. I think what I am trying to do is fairly straightforward but I am failing at it miserably. I have a JSON object here based on the example in the docs but slightly modified:
* def cat =
"""
[
{
name: 'Billie',
kittens: [
{ id: 23, nickName: 'Bob' },
{ id: 42, nickName: 'Wild' }
]
},
{
name: 'Billie2',
kittens: [
{ id: 233, nickName: 'Bob2' },
{ id: 422, nickName: 'Wild2' }
]
}
]
"""
All I want to find is the value of nickName when id is 233. (In this example the answer is Bob2)
I tried this:
get[0] cat.kittens[?(#.id==233)]
But I think I am missing something.
What is tripping me is when there are multiple name and kittens, as opposed to a single set in the example given in the documentation website.
I apologize as the answer for this is pretty straightforward, but any hint in the right direction will get greatly appreciated. Thank you!
You are close. When there is an array involved, typically [*] is needed.
* def temp1 = get[0] cats[*].kittens[?(#.id==233)]
* match temp1.nickName == 'Bob2'
Note that this will also work:
* def temp2 = get[0] cats..kittens[?(#.id==233)]
* match temp2.nickName == 'Bob2'

How do I concatenate a dynamic string value using Groovy to JSON response after it's parsed to get a specific node value in JSON

slurperresponse = new JsonSlurper().parseText(responseContent)
log.info (slurperresponse.WorkItems[0].WorkItemExternalId)
The above code helps me get the node value "WorkItems[0].WorkItemExternalId" using Groovy. Below is the response.
{
"TotalRecordCount": 1,
"TotalPageCount": 1,
"CurrentPage": 1,
"BatchSize": 10,
"WorkItems": [ {
"WorkItemUId": "4336c111-7cd6-4938-835c-3ddc89961232",
"WorkItemId": "20740900",
"StackRank": "0",
"WorkItemTypeUId": "00020040-0200-0010-0040-000000000000",
"WorkItemExternalId": "79853"
}
I need to append the string "WorkItems[0].WorkItemExternalId" (being read from a excel file) and multiple other such nodes dynamically to "slurperresponse" to get the value of nodes rather than directly hard coding as slurperresponse.WorkItems[0].WorkItemExternalId..
Tried append and "+" operator but i get a compilation error. What other way can I do this?
slurperrsesponse is an object its not a string that's why the concatenation does not work
Json Slurper creates an object out of the input string. This object is dynamic by nature, you can access it, you can add fields to it or alter the existing fields. Contatenation won't work here.
Here is an example:
import groovy.json.*
​def text = '{"total" : 2, "students" : [{"name": "John", "age" : 20}, {"name": "Alice", "age" : 21}] }'
def json = new JsonSlurper().parseText(text)​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
json.total = 3 // alter the value of the existing field
json.city = 'LA' // add a totally new field
json.students[0].age++ // change the field in a list
println json​
This yields the output:
[total:3, students:[[name:John, age:21], [name:Alice, age:21]], city:LA]
Now if I've got you right you want to add a new student dynamically and the input is a text that you've read from Excel. So here is the example:
json.students << new JsonSlurper().parseText('{"name" : "Tom", "age" : 25}')
// now there are 3 students in the list
Update
Its also possible to get the values without 'hardcoding' the property name:
// option 1
println json.city // prints 'LA'
// option 2
println json.get('city') // prints 'LA' but here 'city' can be a variable
// option 3
println json['city'] // the same as option 2

How to check a value matches with another correpsonding value in a json response

What is the most dynamic way of checking each instance of a json response value matches another json response value within a script assertion?
What I mean is lets say I have the following response below:
{
"xxx": [{
"roomInformation": [{
"xxx": xxx
}],
"totalPrice": xxx
},
{
"roomInformation": [{
xxx: xxx
}],
"totalPrice": xxx
}
]
}
I want to check that the first room price to match with the first totalPrice and the second roomPrice to match with the second totalPrice. It has to be dynamic as I may get many different instances of this so I can't simply just look through the json with [0] and [1]. Virtually check each roomPrice matches with its corresponding totalPrice.
Thanks
So given the Json as a variable:
def jsonTxt = '''{
"hotels": [{
"roomInformation": [{
"roomPrice": 618.4
}],
"totalPrice": 618.4
},
{
"roomInformation": [{
"roomPrice": 679.79
}],
"totalPrice": 679.79
}
]
}'''
We can then use the following script:
import groovy.json.*
new JsonSlurper().parseText(jsonTxt).hotels.each { hotel ->
assert hotel.roomInformation.roomPrice.sum() == hotel.totalPrice
}
As you can see, I'm using sum to add all the roomInformation.roomPrice values together. In your example, you only have one price, so this will be fine.. And it will also cover the case where you have multiple rooms adding up to make the total
Here is the script assertion to check each roomPrice is matching or not with totalPrice.
EDIT: based on OP's full response provided here
Script Assertion:
//Check if the response is not empty
assert context.response, "Response is empty or null"
def json = new groovy.json.JsonSlurper().parseText(context.response)
def sb = new StringBuffer()
json.regions.each { region ->
region.hotels.each { hotel ->
(hotel?.totalPrice == hotel?.roomInformation[0]?.roomPrice) ?: sb.append("Room price ${hotel?.roomInformation[0]?.roomPrice} is not matching with total price ${hotel.totalPrice}")
}
}
if (sb.toString()) {
throw new Error(sb.toString())
} else { log.info 'Prices match' }

How to get data in specific format using scala?

I have a raw json in following format-
"luns": [
{
"numReadBlocks": 15444876,
"numWriteBlocks": 13530714,
"blockSizeInBytes": 512,
"writeIops": 495344,
"readIops": 312702,
"serialNumber": "aaaaaaa",
"uuid": "id",
"shareState": "none",
"usedBytes": 6721716224,
"totalSizeBytes": 16106127360,
"path": "/vol/lun_23052014_025830_vol/lun_23052014_025830"
},
{
"numReadBlocks": 15444876,
"numWriteBlocks": 13530714,
"blockSizeInBytes": 512,
"writeIops": 495344,
"readIops": 312702,
"serialNumber": "aaaaaaa",
"uuid": "id",
"shareState": "none",
"usedBytes": 6721716224,
"totalSizeBytes": 16106127360,
"path": "/vol/lun_23052014_025830_vol/lun_23052014_025830"
}]
The luns may contains list.
I want to process above json and form output as following-
"topStorageLuns": [
{
"name": "Free (in GB)",
"data": [7.79,7.79]
},
{
"name": "Used (in GB)",
"data": [7.21,7.21]
}]
I tried following in order to get output-
val storageLuns = myRawJson
val topStorageLuns = storageLuns.map { storageLun =>
val totalLunsSizeOnStorageDevice = storageLun.luns.foldLeft(0.0) {
case (totalBytesOnDevice, lun) =>
totalBytesOnDevice + lun.usedBytes.getOrElse(0.0).toString.toLong
}
val totalAvailableLunsOnStorageDevice = storageLun.luns.foldLeft(0.0) {
case (totalBytesOnDevice, lun) =>
totalBytesOnDevice + lun.usedBytes.getOrElse(0.0).toString.toLong
}
Json.obj("name" -> storageLun.hostId, "data" -> "%.2f".format(totalLunsSizeOnStorageDevice / (1024 * 1024 * 1024)).toDouble)
}
Can anybody help me to get desired output please???
The key lesson I want to impart is that your algorithm should reflect the shape of the output you want. Work backward from the result you want to build the algorithm.
It looks to me like you want to create an array of length 2, where each entry has a corresponding algorithm (spaced used, space free). Within each of these elements, you want a nested array with an element for each item in your input array, calculated using the algorithm from the outer array. Here's how I would approach the problem:
1) Define your algorithms
val dfAlgorithm: (Seq[(String, JsValue)] => Double) = _.foldLeft(0.0) { (acc, item) =>
/* whatever logic you need to do */
}
val duAlgorithm: (Seq[(String, JsValue)] => Double) = _.foldLeft(0.0) { (acc, item) =>
/* whatever logic you need to do */
}
2) Create a data structure to map over to build your final output
val stats = Seq("Free (in GB)" -> dfAlgorithm, "Used (in GB)" -> duAlgorithm)
3) Map over your input data within your mapping over your algorithms (the logic here reflects the shape of the result you want)
stats.map { case (name, algorithm) =>
Json.obj("name" -> name, "data" -> storageLuns.map { storageLun => algorithm(storageLun) }
}
This isn't going to be a turnkey solution, since I don't know how your free/used algorithms are supposed to work, but this overall scheme should get you there.