Karate filtering json response - karate

In my Karate tests (0.9.4), I have a json response like the following:
[
{
"id": "id_number_1",
"name": "name"
},
{
"id": "id_number_2",
"name": "name 2",
"nestedThing" {
"id": "another_id",
"name": object2_name"
}
},
{
"id": "id_number_3",
"name": "name 3"
}
]
Some of the objects in the response will have a nestedThing and others will not. First, I want to get rid of all the items in the list that do not have nestedThing. Second, once that's done, I want to def a list that only contains the first-level id fields. So, it would look like:
["id_number_1", "id_number_3"]

This can be done in one line:
* def ids = response.filter(x => !x.nestedThing).map(x => x.id)
Refer: https://github.com/karatelabs/karate#json-transforms
EDIT: the below works in versions 0.9.X
* def temp = karate.filter(response, function(x){ return !x.nestedThing })
* def ids = karate.map(temp, function(x){ return x.id })
* match ids == ['id_number_1', 'id_number_3']

Related

Karate - How change key name in JSON

I have the following JSON. I want to change the keyName 'freeDelivery' to 'isFreeDelivery' but I can't figure out how to do it.
{
"result": [
{
"deliverySlots": [
{
"id": "2DNN",
"date": "2022-04-05",
"freeDelivery": false,
"label": "All day delivery 08:30am to 5pm",
"price": "£5.00",
"fullSlotId": "2DNN"
},
{
"id": "2DPM",
"date": "2022-04-05",
"freeDelivery": false,
"label": "Afternoon 12pm to 5pm",
"price": "£10.00",
"fullSlotId": "2DPM"
}
]
},
{
"deliverySlots": [
{
"id": "2DNN",
"date": "2022-04-06",
"freeDelivery": false,
"label": "All day delivery 08:30am to 5pm",
"price": "£5.00",
"fullSlotId": "2DNN"
},
{
"id": "2DPM",
"date": "2022-04-06",
"freeDelivery": false,
"label": "Afternoon 12pm to 5pm",
"price": "£10.00",
"fullSlotId": "2DPM"
}
]
}
]
}
I've looked at the following pages but still can't figure out how to do it. Do I have to do a transorm or is there an easier way?
https://github.com/karatelabs/karate/blob/master/karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature
https://github.com/karatelabs/karate#json-transforms
Here you go:
* def payload = { before: 'foo' }
* remove payload.before
* payload.after = 'bar'
* match payload == { after: 'bar' }
Instead of remove this will also work (using pure JS):
* eval delete payload.before
EDIT: after seeing the comments, I would treat this as a JSON transform.
* def payload = { before: 'foo' }
* def fun = function(x){ var res = {}; res.after = x.before; return res }
* def result = fun(payload)
* match result == { after: 'foo' }
I'm sure now you'll want to "retain" all the existing data. Fine, here you go:
* def payload = { before: 'foo' }
* def fun = function(x){ var res = x; res.after = x.before; delete res.before; return res }
* def result = fun(payload)
* match result == { after: 'foo' }
And you already know that you can run a transform on all array elements like this:
* def result = karate.map(someArray, fun)
Please note that you can create 2 or 3 transforms - and "nest" them.

Karate - filter a specific json key from response based on another static array

I have the following JSON response (reference name: "list") and
[
{
"key": "101",
"val": {
"portCall": {
"id": 12664978
},
"status": "in-port"
}
},
{
"key": "102",
"val": {
"portCall": {
"id": 12415798
},
"status": "in-port"
}
},
{
"key": "103",
"val": {
"status": "on-voyage",
"voyage": {
"id": "7kynv-7lq85"
}
}
},
{
"key": "104",
"val": {
"status": "on-voyage",
"voyage": {
"id": "7kynv-2385"
}
}
}
]
also, I have an array list of few key values, evImos = [101,102,104]
In that, I have to identify the first key in the "list" response that has status as "on-voyage". So, the result should be "104".
I have tried the following and I need some help to make it work. Any help would be appreciated.
* def function getFirst = function(evImos) { for (let num of evImos) { let results = list.filter(d => d["key"] === num && d["val"]["status"] === "on-voyage"); if(results.length === 1) { karate.log(num); return num; } } }
* list.forEach(getFirst(evImos))
I'll just give you one hint. This one line will convert the whole thing in to a form that is much easier for you to validate:
* def temp = {}
* list.forEach(x => temp[x.key] = x.val.status)
Which gives you:
{
"101": "in-port",
"102": "in-port",
"103": "on-voyage",
"104": "on-voyage"
}
Now you can do:
* def isOnVoyage = function(key){ return temp[key] == 'on-voyage' }
Also read this: https://stackoverflow.com/a/59162760/143475
Thanks, to #Peter.
Based on his hint, I just tweaked it a little bit to match my requirement and it worked for me.
Here is the working copy for anyone to refer in the future.
* def temp = {}
* list.forEach(x => temp[x.key] = x.val.status)
* def isOnVoyage = function(keys){ for (let key of keys) { if(temp[key] == 'on-voyage'){ karate.log(key); karate.set('num', key); break; }}}
* isOnVoyage(evImos)

Karate- How can I pass a param as a key?

I have the following case and need to use firstKeyStr as a key, my code now is seeing "#firstKeyStr" as the key not the one stored in it
Code
Given path '/api/v1/sites'
When method get
Then status 200
And match response.success == true
And match response.data == "#object"
* def keys = karate.keysOf(response.data)
* def firstKeyStr = keys[0]
And match response.data."#firstKeyStr" == "#object"
Json Response Body
{
"success": true,
"data": {
"5ef34d0ca5a3c56ae14d2a23": {
"devices": [
"5f03192010a47f3e5b714193"
],
"groups": [
"5f0d9f30ef89e22778a2d230"
],
"users": [],
"triggers": [],
"alerts": [
"5f0d92b967bac60b84d3989b"
],
"name": "test",
"country": "US",
]
}
}
I'm looking for a way to pass this dynamic key (5ef34d0ca5a3c56ae14d2a23) in this line (And match response.data."#firstKeyStr" == "#object")
Since you are new to stack overflow, please read the following to best get help:
https://stackoverflow.com/help/how-to-ask
https://stackoverflow.com/help/someone-answers
As Peter said, your JSON is not well formed and the order of keys is not guaranteed (unless you have just a single key). The following code should get you going.
Sample Code:
Feature: firstKey
Scenario: firstKey
* def resp =
"""
{
"success": true,
"data": {
"5ef34d0ca5a3c56ae14d2a23": {
"devices": [
"5f03192010a47f3e5b714193"
],
"groups": [
"5f0d9f30ef89e22778a2d230"
],
"users": [],
"triggers": [],
"alerts": [
"5f0d92b967bac60b84d3989b"
],
"name": "test",
"country": "US"
}
}
}
"""
And match resp.data == "#object"
* def keys = karate.keysOf(resp.data)
* def firstKeyStr = keys[0]
* print firstKeyStr
* print (resp.data[firstKeyStr])
And match (resp.data[firstKeyStr]) == "#object"
Use round brackets so that JS is used instead of JsonPath:
* def response = { data: { a: 1, b: 2 } }
* def keys = karate.keysOf(response.data)
* def first = keys[0]
* match (response.data[first]) == 1

Karate: Traverse thru a complex JSON to find a match

I am hitting an API end point and getting something like below.
{
"emp": {
"id": "123",
"firstNm": "test",
"lastNm": "last",
"dob": "200-01-01",
"gender": {
"code": "F",
"name": "Female",
"description": "Female"
},
"test1": [
{
"tes2": "F50045A3B994FB2BDF4E3D3FC906F592",
"t2": "a23",
"test3": {
"code": "432",
},
"ind": [
"ABC",
"BCD",
]
}
]
}
}
I want to match the elements in the array
"ind": [
"ABC",
"BCD",
]
I have tried the below:
Feature: test
Background:
* url BaseUrl
* configure headers = read('classpath:headers.js')
Scenario Outline: test
Given path '/path'
And param id = <id>
When method get
Then status 200
* def json = response
* def has = {ind:['#string'] }
* def indicator = { ind: '#[] has' }
* match json.member == '#[] indicator'
Examples:
| id |
| '1234' |
But observed the below exception:
expected: '#[] ind', reason: not an array or list
Can someone please help?
I am not really following your logic since indicators is not in the json response or defined variable but to get to the ind array the path is emp.test1[0].ind
if you want to match that the array has ABC and BCD you would do the following
* match response.emp.test1[0].ind == ['ABC', 'BCD']

Extract data from json array in Karate

In the below JSON response, I need to extract the 'cid' for the record that has the 'nationalityDecription' as 'USA'. By using this query as a reference, I used the below loc in the karate feature file, but 1st line itself fails with syntax error(tried different combinations). For now, I'm using the custom javascript as a workaround which is working fine. I need help to check if i'm missing anything in syntax. Thanks
Response:
{
"header": {
"Id": "12345678",
"timeStamp": "2018-09-17T10:09:812.000"
},
"dataRecords": [
{
"cid": "31H678",
"cidMeta": "00",
"nationalityDecription": "CHINA"
},
{
"cid": "31S421",
"cidMeta": "01",
"nationalityDecription": "USA"
}
]
}
Feature file:
* def record= $response.dataRecords[?(#.nationalityDecription=='USA')]
* def cid = record.cid
* def response = { "header": { "Id": "12345678", "timeStamp": "2018-09-17T10:09:812.000" }, "dataRecords": [ { "cid": "31H678", "cidMeta": "00", "nationalityDecription": "CHINA" }, { "cid": "31S421", "cidMeta": "01", "nationalityDecription": "USA" } ] }
* def cid = get[0] response.dataRecords[?(#.nationalityDecription=='USA')].cid
* match cid == '31S421'