Jmeter ForEach Controller failing to write variables to file in order retrieved - foreach

Jmeter ForEach Controller failing to write variables in original order correctly
I am executing a http request retrieving a json payload with an array of employees. For each record (employee) I need to parse the record for specific fields e.g. firstName, lastName, PersonId and write to a single csv file, incrementing a new row per record.
Unfortunately, the file created has two issues. The PersonId never gets written and secondly the sequence of the values is not consistent with the returned original values. Sometimes I get the same record for lastName with the wrong firstName and vice versa. Not sure if the two issues are related, I suspect my regular expression extract is wrong for a number.
Jmeter setup. (5.2.1)
jmeter setUp
Thread group
+ HTTP Request
++ JSON JMESPath Extractor
+ ForEach Controller
++ Regular Expression Extractor: PersonId
++ Regular Expression Extractor: firstName
++ Regular Expression Extractor: lastName
++ BeanShell PostProcessor
getWorker returns the following payload
jsonPayload
JSON JMESPath Extractor to handle the payload.
{
"items" : [
{
"PersonId" : 398378,
"firstName" : "Sam",
"lastName" : "Shed"
},
{
"PersonId" : 398379,
"firstName" : "Bob",
"lastName" : "House"
}
],
"count" : 2,
"hasMore" : true,
"limit" : 2,
"offset" : 0,
"links" : [
{
"rel" : "self",
"href" : "https://a.site.on.the.internet.com/employees",
"name" : "employees",
"kind" : "collection"
}
]
}
JSON JMESPath Extractor Configuration
Name of created variables: items
JMESPath expressions: items
Match No. -1
Default Values: Not Found
ForEach Controller
ForEach Controller Configuration
Input variable prefix: items
Start Index: Empty
End Index: Empty
Output variable name: items
Add "_"? Checked
Each of the Regular Expression Extracts follow the same pattern as below.
Extract PersonId with Regular Expression
Apply to: Main Sample Only
Field to check: Body
Name of created variable: PersonId
Regular Expression: "PersonId":"(.+?)"
Template: $1$
Match No. Empty
Default Value: PersonId
The final step in the thread is where I write out the parsed results.
BeanShell PostProcessor
PersonNumber = vars.get("PersonNumber");
DisplayName = vars.get("DisplayName");
f = new FileOutputStream("/Applications/apache-jmeter-5.2.1/bin/scripts/getWorker/responses/myText.csv", true);
p = new PrintStream(f);
this.interpreter.setOut(p);
print(PersonId+", "+ PersonNumber+ ", " + DisplayName);
f.close();
I am new to this and looking either for someone to tell me where I screwed up or direct me to a place I can read up on the appropriate topics. (Both are fine). Thank you.

For Each Controller doesn't know the structure of items variable since it is in JSON format. It is capable of just understanding an array and traverses through them. I would suggest to move away from For Each Controller in your case and use the JSON extractor itself for all the values like below
Person ID
First Name
Last Name
Beanshell Sampler Code
import java.io.FileWriter; // Import the FileWriter class
int matchNr = Integer.parseInt(vars.get("personId_C_matchNr"));
log.info("Match number is "+matchNr);
f = new FileOutputStream("myText.csv", true);
p = new PrintStream(f);
for (int i=1; i<=matchNr; i++){
PersonId = vars.get("personId_C_"+i);
FirstName = vars.get("firstName_C_"+i);
LastName = vars.get("lastName_C_"+i);
log.info("Iteration is "+i);
log.info("Person ID is "+PersonId);
log.info("First Name is "+FirstName);
log.info("Last Name is "+LastName);
p.println(PersonId+", "+FirstName+", "+LastName);
}
p.close();
f.close();
Output File
HOW THE ABOVE ACTUALLY WORKS
When you extract values using the matchNr, it goes in a sequential order in which the response has arrived. For example, in your case, Sam & Shed appear as first occurrences and Bob & House appear as subsequent occurrences. Hence JMeter captures them with the corresponding match and stores them as 1st First Name = Sam, 2nd First Name = Bob and so on.
GENERIC STUFF
The regex expression for capturing Person ID which you have used seems to be inaccurate. The appropriate one would be
"PersonId" :(.+?),
and not
"PersonId":"(.+?)"
Move to JSR223 processors instead of Beanshell as they are more performant. Source: Which one is efficient : Java Request, JSR223 or BeanShell Sampler for my script. The migration is pretty simple. Just copy the code that you have in Beanshell and paste it in JSR223.
Close any stream or writer that is open appropriately else it might cause issues when other users are trying to write to the file during load test
In case you are planning to use this file as a subsequent input within JMeter, please note that there is a space between comma and the next element. For example, it is "Sam, Shed" and not "Sam,Shed".JMeter by default does not trim any spaces and will use the value just like that. Hence you might want to take a judicious call regarding that space
Hope this helps!

Since JMeter 3.1 you shouldn't be using Beanshell, go for JSR223 Test Elements and Groovy language for scripting.
Given Groovy has built-in JSON support you shouldn't need any extractors, you can write the data into a file in a single shot like:
new groovy.json.JsonSlurper().parse(prev.getResponseData()).items.each { item ->
new File('myText.csv') << item.get('PersonId') << ',' << item.get('firstName') << ',' << item.get('lastName') << System.getProperty('line.separator')
}
More information: Apache Groovy - Why and How You Should Use It

Related

How do you get strip RTF formatting and get actual string value using DXL in DOORS?

I am trying to get the values in "ID" column of DOORS and I am currently doing this
string ostr=richtext_identifier(o)
When I try to print ostr, in some modules I get just the ID(which is what I want). But in other modules I will get values like "{\rtf1\ansi\ansicpg1256\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Times New Roman;}{\f1\froman\fcharset0 Times New Roman;}} {*\generator Riched20 10.0.17134}\viewkind4\uc1 \pard\f0\fs20\lang1033 SS_\f1\fs24 100\par } " This is the RTF value and I am wondering what the best way is to strip this formatting and get just the value.
Perhaps there is another way to go about this that I am not thinking of as well. Any help would be appreciated.
So the ID column of DOORS is actually a composite- DOORS builds it out of the Module level attribute 'Prefix' and the Object level attribute 'Absolute Number'.
If you wish to grab this value in the future, I would do the following (using your variables)
string ostr = ( module ( o ) )."Prefix" o."Absolute Number" ""
This is opposed to the following, which (despite seeming to be a valid attribute in the insert column dialog) WILL NOT WORK.
string ostr = o."Object Identifier" ""
Hope this helps!
Comment response: You should not need the module name for the code to work. I tested the following successfully on DOORS 9.6.1.10:
Object o = current
string ostr = ( module ( o ) )."Prefix" o."Absolute Number" ""
print ostr
Another solution is to use the identifier function, which takes an Object as input parameter, and returns the identifier as a plain (not RTF) string:
Declaration
string identifier(Object o)
Operation
Returns the identifier, which is a combination of absolute number and module prefix, of object o as a string.
The optimal solution somewhat depends on your underlying requirement for retrieving the object ID.

URL Mapping - Replacing characters in a parameter pulled from a database

I am currently trying to figure out, how to modify the parameter being integrated into the URL Mapping I am using.
static mappings =
{
"/$controller/$action?/$id?/(.$format)?"
{
constraints {
// apply constraints here
}
}
name test1: "/.../$title/..."{
controller = "study"
action = "st_show"
}
name test2: "/.../$title/..."{
controller = "search"
action = "se_show"
}
The parameter $title is pretty much a dataset, which is pulled from a database and which will get transmitted in the following format [ this is a title ]. So there are square brackets in front and behind the string and words are seperated through blanks.
If I am creating a link through g:link now with the params nested in, it gets put into the url as it is pulled from the database. What I am attempting is to create SEO-URLs, which will present a certain title of a publication devided by hyphens instead of url-encoded "%20".
Until now, I was able to generate dynamic urls looking like this:
http://localhost:8080/projectname/show/%5BAllgemeine%20Bevölkerungs[...]/782/...PARAMS...
Furthermore I already implemented it through JQuery, though it should be static and users should be able to copy the link to open up the page themselves - that wouldn't be possible when changing the url client-side while loading up the page.
Is there a way to define a function with something like replaceAll.(' ', '-'), which can be invoked onto the parameter in the mapping to replace blanks with hyphens and f.e. square brackets with an empty character?
That's pretty much, what I wasn't able to come by through the documentation.
Thank you already in advance for your help!
I managed to solve my problem by creating a service with a function containing a regex and executing this function onto the parameter title in my g:link, which I firstly converted to a string, which gets passed to the function.
<g:link controller="study" action="st_show" params="[data: data, ... title: ConversionService.convert(fieldValue(bean: path).toString(), ... data: data)]"></g:link>
And the function in the ConversionService
public static String convert(String title){
title = title.replaceAll("\\s", "-").replaceAll("[^0-9a-zA-Z\\-]", "");
return title;
}

Neo4j+PopotoJS: filter graph based-on predefined constraints

I have a question about the query based on the predefined constraints in PopotoJs. In this example, the graph can be filtered based on the constraints defined in the search boxes. The sample file in this example visualizations folder, constraint is only defined for "Person" node. It is specified in the sample html file like the following:
"Person": {
"returnAttributes": ["name", "born"],
"constraintAttribute": "name",
// Return a predefined constraint that can be edited in the page.
"getPredefinedConstraints": function (node) {
return personPredefinedConstraints;
},
....
In my graph I would like to apply that query function for more than one node. For example I have 2 nodes: Contact (has "name" attribute) and Delivery (has "address" attribute)
I succeeded it by defining two functions for each nodes. However, I also had to put two search box forms with different input id (like constraint1 and constraint2). And I had to make the queries in the associated search boxes.
Is there a way to make queries which are defined for multiple nodes in one search box? For example searching Contact-name and/or Delivery-adress in the same search box?
Thanks
First I’d like to specify that the predefined constraints feature is still experimental (but fully functional) and doesn’t have any documentation yet.
It is intended to be used in configuration to filter data displayed in nodes and in the example the use of search boxes is just to show dynamically how it works.
A common use of this feature would be to add the list of predefined constraint you want in the configuration for every node types.
Let's take an example:
With the following configuration example the graph will be filtered to show only Person nodes having "born" attribute and only Movie nodes with title in the provided list:
"Person": {
"getPredefinedConstraints": function (node) {
return ["has($identifier.born)"];
},
...
}
"Movie": {
"getPredefinedConstraints": function (node) {
return ["$identifier.title IN [\"The Matrix\", \"The Matrix Reloaded\", \"The Matrix Revolutions\"]"];
},
...
}
The $identifier variable is then replaced during query generation with the corresponding node identifier. In this case the generated query would look like this:
MATCH (person:`Person`) WHERE has(person.born) RETURN person
In your case if I understood your question correctly you are trying to use this feature to implement a search box to filter the data. I'm still working on that feature but it won't be available soon :(
This is a workaround but maybe it could work in your use case, you could keep the search box value in a variable:
var value = d3.select("#constraint")[0][0].value;
inputValue = value;
Then use it in the predefined constraint of all the nodes type you want.
In this example Person will be filtered based on the name attribute and Movie on title:
"Person": {
"getPredefinedConstraints": function (node) {
if (inputValue) {
return ["$identifier.name =~ '(?i).*" + inputValue + ".*'"];
} else {
return [];
}
},
...
}
"Movie": {
"getPredefinedConstraints": function (node) {
if (inputValue) {
return ["$identifier.title =~ '(?i).*" + inputValue + ".*'"];
} else {
return [];
}
},
...
}
Everything is in the HTML page of this example so you can view the full source directly on the page.
#Popoto, thanks for the descriptive reply. I tried your suggestion and it worked pretty much well. With the actual codes, when I make a query it was showing only the queried node and make the other node amount zero. I wanted to make a query which queries only the related node while the number of other nodes are still same.
I tried a temporary solution for my problem. What I did is:
Export the all the node data to JSON file, search my query constraint in the exported JSONs, if the file is existing in JSON, then run the query in the related node; and if not, do nothing.
With that way, of course I needed to define many functions with different variable names (as much as the node amount). Anyhow, it is not a propoer way, bu it worked for now.

grails gorm mongodb `like` functionality in criteria

Is like or rlike supported for searching a string in a collection's property value?
Does the collection need to define text type index for this to work? Unfortunately I can not create a text index for the property. There are 100 million documents and text index killed the performance (MongoDB is on single node). If this is not do-able without text index, its fine with me. I will look for alternatives.
Given below collection:
Message {
'payload' : 'XML or JSON string'
//few other properties
}
In grails, I created a Criteria to return me a list of documents which contain a specific string in the payload
Message.list {
projections {
like('payload' : searchString)
}
}
I tried using rlike('payload' : ".*${searchString}.*") as well. It did not result in any doc to me.
Note: I was able to get the document when I fired the native query on Mongo shell.
db.Message.find({payload : { $regex : ".*My search string.*" }}).pretty()
I got it working in a round about way. I believe there is a much better grails solution. Criteria approach did not work. So used the low level API converted the DBObjects to Domain objects.
def query = ['payload' : [ '$regex' : /${searchString}/ ] ]
def dbObjects = Message.collection.find(query).skip(offset).limit(defaultPageSize).toArray()
dbObjects?.collect { new Message(new JsonSlurper().parseText(it.toString()))}

Grails excel-import plugin Numbers populated as real with exponent

I am trying to import a ms-excel 2007 sheet using excel-import plugin. It was simple to integrate with my project and I found it working as expected until I noticed that the number values in the cells are populated as real numbers with exponent.
For example if the cell contains value 9062831150099 -(populated as)->9.062831150099E12 i.e.
A |
_____________________
Registration Number |
____________________
9062831150099
Is populated as: [RegNum:9.062831150099E12]
Anyone could suggest me how I can change this representation back to its original format keeping its type as number?
Missed it at the first attempt but I figured out how to to achieve it:
When invoking the key methods (the ones that process cellMap and columnMaps) for example List bookParamsList = excelImportService.columns(workbook, CONFIG_BOOK_COLUMN_MAP) or Map bookParams = excelImportService.cells(workbook, CONFIG_BOOK_CELL_MAP )
There is also ability to pass in configuration information, i.e. to specify what to do if a cell is empty (i.e. to provide a default value), to make sure a cell is of expected type, etc.
So in my case I created a configuration parameter Map of properties of the object and passed it to the above methods. i.e.
Class UploadExcelDataController {
def excelImportService
static Map CONFIG_BOOK_COLUMN_MAP = [
sheet:'Sheet1',
startRow: 2,
columnMap: [
'A':'registrationNumber',
'B':'title',
'C':'author',
'D':'numSold',
]
]
static Map configBookPropertyMap = [
registrationNumber: ([expectedType: ExpectedPropertyType.IntType, defaultValue:0])
]
def uploadFile(){
...
List bookParamsList = excelImportService.columns(workbook, CONFIG_BOOK_COLUMN_MAP,configBookPropertyMap )
...
}
}

Resources