Jmeter - How to output all the values extracted from JSON extractor - jmeter

I have json with dynamic range which is groving all the time.
{
"payload": {
"totalCount": 302,
"items": [
{
"id": 1379,
"date": "2021-04-08T17:06:03+02:00",
"name": "Newcastle United Aston Villa",
"externalId": "2",
"mediaTag": null,
"deleted": false,
"originalId": null,
"cancelled": false,
"relatedEventsOrderIndex": []
},
{
"id": 1380,
"date": "2021-04-08T17:06:03+02:00",
"name": "Arsenal Tottenham Hotspur",
"externalId": "3",
"mediaTag": null,
"deleted": false,
"originalId": null,
"cancelled": false,
"relatedEventsOrderIndex": []
}
]
},
"errorCode": 0,
"errorTimeStamp": null,
"errorMessage": null,
"hasError": false
}
I can extract all the values like
And if i use as: ID1_1, ID1_2.......ID1_100 -> its working fine.
But, I want to output in the logger.
If I tried as:
def IDS = vars.getObject("ID1");
for (int i=0; i>IDS.length; i++) {
log.warn("ID as: " + IDS + (i + 1) );
}
I got: Cannot get property 'length' on null object
How can I output all the ID, from dynamic JSON?

I don't think you have ID1 variable, you have ID1_1, ID1_2 .... and ID1_matchNr which holds the number of matches, you can see it yourself using Debug Sampler
You need to change your code as:
for (int i = 1; i <= vars.get('ID1_matchNr') as int; i++) {
log.warn("ID as: " + vars.get("ID1_" + i));
}
in the above code vars stands for JMeterVariables class instance

It should be something like this (pseudo code)
for (int i=0; i>IDS.length; i++) {
log.warn("ID as: " + vars.get("ID1_" + i.toString() );
}
each of the ID1_1, ID1_2.......ID1_100 are variables. You can verify that using a Debug Sampler

Related

How to limit the number of ids selected in JSON extractor?

I have been able to get the id of all the fields using the JSON extractor in Jmeter with the following query:
$..MetaInputList.*.DocumentElementMetaInputId
But I want to get the limited number of random ids, let's say 4, 0r 5 ids in documentElementMetaInputId.
The ids are selected as:
documentElementMetaInputId_1=1398
documentElementMetaInputId_10=1407
documentElementMetaInputId_11=1408
documentElementMetaInputId_12=1409
documentElementMetaInputId_13=1410
documentElementMetaInputId_14=1411
documentElementMetaInputId_15=1412
.....so on.
How can I do that using JSON extractor? further, I want to use that list of ids in another request.
{
"documentElementMetaInputIdList": [
//list of ids
]
}
Updated JSON structure.
{
"Sections": [{
"Row": "R0",
"Disable": true,
"Cols": [{
"Column": "c0",
"Section": [{
"Name": "Eclampsia - EmerCheck",
"SectionElement": [{
"DocumentElementId": 47267
}]
}, {
"Name": "Eclampsia - EmerCheck",
"SectionElement": [{
"DocumentElementId": 47268
}]
}, {
"Name": "Eclampsia - EmerCheck",
"SectionElement": [{
"DocumentElementId": 47271,
"MetaInputList": [{
"DocumentElementMetaInputId": 3588
}, {
"DocumentElementMetaInputId": 3589
}, {
"DocumentElementMetaInputId": 3590
}, {
"DocumentElementMetaInputId": 3591
}, {
"DocumentElementMetaInputId": 3592
}, {
"DocumentElementMetaInputId": 3593
}, {
"DocumentElementMetaInputId": 3594
}, {
"DocumentElementMetaInputId": 3595
}, {
"DocumentElementMetaInputId": 3596
}, {
"DocumentElementMetaInputId": 3606
}]
}]
}, {
"Name": "Eclampsia - EmerCheck",
"SectionElement": [{
"DocumentElementId": 47272,
"MetaInputList": [{
"DocumentElementMetaInputId": 3607
}, {
"DocumentElementMetaInputId": 3608
}]
}]
}]
}]
}],
"metaInformation": []
}
Add JSR223 PostProcessor after your JSON Extractor
Put the following code into "Script" area:
def list = new HashSet<>()
1.upto(4, {
while (list.size() != 4) {
list.add(vars.get('documentElementMetaInputId_' + org.apache.commons.lang3.RandomUtils.nextInt(1, vars.get('documentElementMetaInputId_MatchNr') as int + 1)))
}
})
def payload = [documentElementMetaInputIdList: list.collect { entry -> entry as int }]
vars.put('payload', new groovy.json.JsonBuilder(payload).toPrettyString())
Use ${payload} JMeter Variable where you need the generated list of random IDs
More information:
Apache Groovy - Parsing and producing JSON
Apache Groovy - Why and How You Should Use It
Another solution which could go with the JSON Extractor.
Add following into a JSR223 Post Processor just below the JSON Extractor to limit the IDs.
def lstIds = []
int idsToIncludeCount = 50
int idCount = vars.get("DocumentElementMetaInputId_matchNr").toInteger()
log.info("Total IDs count : ${idCount}")
if (idCount >0) {
for (int i=1 ; i <= idCount; i++ ) {
String IdReference = "DocumentElementMetaInputId_" + i
String currentId = vars.get(IdReference)
lstIds.add(centreId)
log.info("Current centre is ${currentId}")
}
lstIds = lstIds[0.. idsToIncludeCount]
}
Groovy is used for slicing the elements (IDs) from a list.
Assign the IDs into a list
Slice the list to limit the IDs
Solution 1
You can use JSON JMESPath Extractor with slicing feature to limit the result.
Your JMESPath expressions
$..MetaInputList.*.DocumentElementMetaInputId | [0:5]
Another Solution with the For Each controller.
The solution will work with your JSON Extractor result, DocumentElementMetaInputId
Add a For Each controller to iterate over the required number of IDs. You can set the starting and end indexes.
You can use ${DocumentElementMetaInputIdOut} within the child controllers .

improve leftJoin nested mapObject when equivalence do and do not match

I am using the following dataweave function, and it does works.
%dw 2.0
import * from dw::core::Arrays
output application/json
var mysqlInvoices = [
{
"id": 1,
"owner": "Joseph"
},
{
"id": 2,
"owner": "Maria"
}
]
var sapInvoices = [
{
"number": 3,
"issuedBy": "XYZ"
},
{
"number": 4,
"issuedBy": "ABC"
}
]
---
leftJoin(mysqlInvoices, sapInvoices, (m) -> m.id, (s) -> s.number) map (item, index) ->
(item.l mapObject (sItem, sKey) ->
(if ((sKey) as String == "id") "identifier"
else if ((sKey) as String == "owner") "ownerName"
else (sKey)): sItem)
++
(if (item.r != null)
item.r mapObject (sItem, sKey) ->
(sKey): sItem
else
sapInvoices[0] mapObject
(sItem, sKey) -> (sKey): "")
However, I am thinking if I can improve this function at two points:
change the key conditions:
I dont think that is the best practice to check every key match an if condition to change it:
(if ((sKey) as String == "id") "identifier"
else if ((sKey) as String == "owner") "ownerName"
else (sKey)): sItem
Use the original object to map it as an empty string when the leftJoin do not match keys:
sapInvoices[0] mapObject (sItem, sKey) ->
(sKey): ""
I am uncomfortable with these two points, and I believe that there are ways to improve this code, I just dont know how.
If there is a very different way of doing the same task, I also appreciate that kind of suggestion.
Based on George's answer, you can remove pluck and match and directly combine left and right table. See below:
%dw 2.0
import * from dw::core::Arrays
output application/json
var mysqlInvoices = [
{
"id": 1,
"owner": "Joseph"
},
{
"id": 2,
"owner": "Maria"
}
]
var sapInvoices = [
{
"number": 3,
"issuedBy": "XYZ"
},
{
"number": 4,
"issuedBy": "ABC"
}
]
var fs2rn = {
id: "identifier",
owner: "ownerName"
}
var rightEmpty= {number:"",issuedBy:""}
---
leftJoin(
// Do the field renaming at the very begining
mysqlInvoices map ($ mapObject {(fs2rn[$$] default $$): $}),
sapInvoices,
(m) -> m.identifier,
(s) -> s.number
) map (item) -> item.l ++ (item.r default rightEmpty)
Give the following a try, if anything the code seems a bit simpler:
%dw 2.0
import * from dw::core::Arrays
output application/json
var mysqlInvoices = [
{
"id": 1,
"owner": "Joseph"
},
{
"id": 2,
"owner": "Maria"
}
]
var sapInvoices = [
{
"number": 3,
"issuedBy": "XYZ"
},
{
"number": 4,
"issuedBy": "ABC"
}
]
var fs2rn = {
id: "identifier",
owner: "ownerName"
}
var rightEmpty= {number:"",issuedBy:""}
---
leftJoin(
// Do the field renaming at the very begining
mysqlInvoices map ($ mapObject {(fs2rn[$$] default $$): $}),
sapInvoices,
(m) -> m.identifier,
(s) -> s.number
)
// Iterate over the results
// Get just the values, and colapse the objects into a single object
map (
{($ pluck $)}
)
// Iterate over the results and use pattern-matching to
//
map (
$ match {
// Check if you have an id but not a number fields
// In which case add the rightEmpty object
case o if (o.identifier? and not (o.number?)) -> o ++ rightEmpty
// Or give the object because you now have both an id and a number
else o -> o
}
)
The features and functions I used are:
Dynamic Elements, documentation
pluck, documentation
Pattern-matching using the match operator, documentation
If I was to give you an advice, it would be to better indent your code. Nonetheless, pretty good job!

Painless scripting initialize new array

I'm trying to add or update a nested object in Elasticsearch using a script. Below script works fine if integrations is already an array, but when it is null, below script throws a null pointer exception. How do I initialize ctx._source.integrations to be an empty array if its value is null? (Something like the equivalent of JavaScript's myObject.integrations = myObject.integrations ?? [])
POST /products/_update/VFfrnQrKlC5bwdfdeaQ7
{
"script": {
"source": """
ctx._source.integrations.removeIf(i -> i.id == params.integration.id);
ctx._source.integrations.add(params.integration);
ctx._source.integrationCount = ctx._source.integrations.length;
""",
"params": {
"integration": {
"id": "dVTV8GjHj8pXFnlYUUlI",
"from": true,
"to": false,
"vendor": "sfeZWDpZXlF5Qa8mUsiF",
"targetProduct": {
"id": "nyILhphvCrGYm53cfaOx",
"name": "Test Product",
"categoryIds": []
}
}
}
}
}
ok i think this does the trick:
if (ctx._source.integrations == null) {
ctx._source.integrations = new ArrayList();
}
is there a short hand to this like in the JS example?

Output not rendering using a method

Here is what I want to accomplish:
<span>{{ getGenres(movie.genre_ids) }}</span>
should output:
Action, Adventure, ..etc
I've got a JSON file which contains the following structure:
[
{
"id": 28,
"name": "Action"
},
{
"id": 12,
"name": "Adventure"
}
]
Here is my method:
getGenres(genre_ids) {
Movies.getGenres(genre_ids);
}
Movies.js contains the method is calling to, which is:
getGenres(genre_ids) {
let genres_array = [];
for (let i = 0; i < genre_ids.length; i++) {
let matching_genre = genres.filter(genre => genre.id === genre_ids[i]);
genres_array.push(matching_genre[0].name);
}
return genres_array;
}
The issue here is that it doesn't output the names, but if I console.log(genres_array); it does work.
Any help would be appreciated.
I cannot find return here
getGenres(genre_ids) {
Movies.getGenres(genre_ids);
}
I trust it should be like this
getGenres(genre_ids) {
return Movies.getGenres(genre_ids).join(', ');
}

loopback REST API filter by nested data

I would like to filter from REST API by nested data. For example this object:
[
{
"name": "Handmade Soft Fish",
"tags": "Rubber, Rubber, Salad",
"categories": [
{
"name": "women",
"id": 2,
"parent_id": 0,
"permalink": "/women"
},
{
"name": "kids",
"id": 3,
"parent_id": 0,
"permalink": "/kids"
}
]
},
{
"name": "Tasty Rubber Soap",
"tags": "Granite, Granite, Chair",
"categories": [
{
"name": "kids",
"id": 3,
"parent_id": 0,
"permalink": "/kids"
}
]
}
]
is comming by GET /api/products?filter[include]=categories
and i would like to get only products which has category name "women". How do this?
LoopBack does not support filters based on related models.
This is a limitation that we have never had bandwidth to solve, unfortunately :(
For more details, see the discussion and linked issues here:
Filter on level 2 properties: https://github.com/strongloop/loopback/issues/517
Filter by properties of related models (use SQL JOIN in queries): https://github.com/strongloop/loopback/issues/683
Maybe you want to get this data by the Category REST API. For example:
GET /api/categories?filter[include]=products&filter[where][name]=woman
The result will be a category object with all products related. To this, will be necessary declare this relation on the models.
Try like this.It has worked for me.
const filter = {
where: {
'categories.name': {
inq: ['women']**strong text**
}
}
};
Pass this filter to request as path parameters and the request would be like bellow
GET /api/categoriesfilter=%7B%22where%22:%7B%categories.name%22:%7B%22inq%22:%5B%women%22%5D%7D%7D%7D
Can you share how it looks like without filter[include]=categorie, please ?
[edit]
after a few questions in comment, I'd build a remote method : in common/models/myModel.js (inside the function) :
function getItems(filter, categorieIds = []) {
return new Promise((resolve, reject) => {
let newInclude;
if (filter.hasOwnProperty(include)){
if (Array.isArray(filter.include)) {
newInclude = [].concat(filter.include, "categories")
}else{
if (filter.include.length > 0) {
newInclude = [].concat(filter.include, "categories");
}else{
newInclude = "categories";
}
}
}else{
newInclude = "categories";
}
myModel.find(Object.assign({}, filter, {include: newInclude}))
.then(data => {
if (data.length <= 0) return resolve(data);
if (categoriesIds.length <= 0) return resolve(data);
// there goes your specific filter on categories
const tmp = data.filter(
item => item.categories.findIndex(
categorie => categorieIds.indexOf(categorie.id) > -1
) > -1
);
return resolve(tmp);
})
}
}
myModel.remoteMethod('getItems', {
accepts: [{
arg: "filter",
type: "object",
required: true
}, {
arg: "categorieIds",
type: "array",
required: true
}],
returns: {arg: 'getItems', type: 'array'}
});
I hope it answers your question...

Resources