Gatling/Scala - how to substring value from html response and use it in the next request? - gatling

I need to get CSRF token from this example page element
data-address-editor-options="{"changeBilling":true,"csrfToken":"07566cd8c8ef33a86b45b03e5f0a.2zBP36-ojP7OWTw5nvRINKjzldwKLzAQbkyA5grriJQ.iEQHsOzJ9La7Gl177IV6cveZ3o5wQHRcLwPMrnCR66CYRyeK5uX-iog9eQ"}"
and then in the next request in json body this token need to be used
def createBody(csrfToken: String, changeBilling: Boolean, changeShipping: Boolean): String = {
"""
|{
|"id": false,
|"_csrf_token": """ + csrfToken +
""",
|"changeableAddresses":
|{
|"changeBilling": """ + changeBilling +
""",
|"changeShipping": """ + changeShipping +
"""
|}
|}
""".stripMargin
}
So the question is how to substring csrf token from 'string' value html response web element
data-address-editor-options
and then used it as a parameter to the function which create json body of the next POST request?
Gatling version 3.4.2
Please let me know if more information is required to answer.

First, Gatling 3.4.2 is really outdated.
As of now, the latest version is 3.7.4 and it also supports Java and Kotlin for writing your tests.
Considering the Scala errors in your code (no offense meant), you're not a Scala developer, so one of those other options would definitely be a better choice for you.
I need to get CSRF token from this example page element
Not sure how to understand it. Have you already gone through a first extraction to get this, or is it your full payload?
Anyway, the best solution for you is probably to use a regex check.
And then, for crafting the body of your next request (assuming Java 17):
private static String createBody(String csrfToken, boolean changeBilling, boolean changeShipping) {
return "{\"id\": false, \"_csrf_token\": \"" + csrfToken + "\", \"changeableAddresses\": { \"changeBilling\": " + changeBilling + "\", \"changeShipping\": " + changeShipping + "}}";
}
StringBody(session -> createBody(session.getString("token"), true, true))

You should do something like this:
/** getting token */
val getToken: HttpRequestBuilder = http("getToken")
.post(myHost + "/auth/main/token")
.header("Content-Type", "application/x-www-form-urlencoded")
.formParam("username", "${username}")
.formParam("password", "${password}")
.formParam("client", "partners")
.check(status.is(200))
.check(regex("\"access_token\":\"(.+?)\"").saveAs("token"))
/** sending token */
val getApplications: HttpRequestBuilder = http("getApplications")
.get(myHost + "/api/partners/applications")
.header("Authorization", "Bearer ${token}")
.check(status.is(200))

Related

javascript evaluation failed- karate

I am trying to call an API in second feature file , passing arguments from first feature file . These has to be passed as a param for second API
* def activeDetails =
"""
function(times){
for(i=0;i<=times;i++){
karate.log('Run test round: '+(i+1));
karate.call('getActiveRouteDetails.feature', { token: token, currentPage: i });
}
java.lang.Thread.sleep(1*1000);
}
"""
* call activeDetails totalPages
In my second feature , I am able to print the values passed , Getting error while passing it as a param 'no step-definition method match found for: param pageNumber'
And print currentPage
And print token
And param pageNumber = '#currentPage'
And param token = token
Watch the white space around the = sign. Normally if you use IDE support you can avoid these issues. You seem to have an extra space after pageNumber.
So make this change:
And param pageNumber = currentPage
I request you to please read the docs and examples. You are still un-necessarily making 2 calls. And over complicating your test.

Can't get basic HTTP Post to work in Salesforce Apex Callout

This is pretty basic. Based on what I've read, this should work. I have a more complex version of this as well.
The To parameter listed below is a List of strings. From and Body are strings.
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.testdomain.com/batchemail');
request.setHeader('Authorization', 'Bearer ' + token);
request.setMethod('POST');
request.setHeader('Accept', '*/*');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
// Set the body as a JSON object
request.setBody('{"To": ["to#email.com"]}');
request.setBody('{"From": "from#email.com"}');
request.setBody('{"Body": "Test message"}');
HttpResponse response = http.send(request);
Here is an example of the JSON that the API accepts.
{
"to": ["to#email.com"],
"from": "from#email.com",
"body": "Test message"
}
The endpoint has been added in Remote Site Settings.
Any idea why this isn't working? Any help is much appreciated. Thanks!
UPDATE 1 (this works)
request.setBody('{"To": ["to#email.com"], "From": "from#email.com", "Body": "Test message." }');
UPDATE 2 (this works too)
JSONGenerator gen = JSON.createGenerator(true);
// Write data to the JSON string.
gen.writeStartObject();
gen.writeObjectField('to', emailList);
gen.writeStringField('from', 'from#email.com');
gen.writeStringField('body', message);
gen.writeEndObject();
// Get the JSON string.
String pretty = gen.getAsString();
request.setBody(pretty);
Update 3 (this also works)
Gareth Jordan's Solution works as well.
Why not just make a simple class (or inner class) for your body and use JSON.serialize(payload). It makes it cleaner and clearer.
class Payload{
String[] to;
String sender;
String body;
public Payload(String[] recipients, String sender, String message){
this.to = recipients;
this.sender = sender;
this.body = message;
}
}
Then your code would be
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.testdomain.com/batchemail');
request.setHeader('Authorization', 'Bearer ' + token);
request.setMethod('POST');
request.setHeader('Accept', '*/*');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
Payload body = new Payload(emailList, 'from#email.com','Test Message');
request.setBody(JSON.serialize(body).replace('"sender"','"from"'));
HttpResponse response = http.send(request);
For the first part, call setBody once, your 3 calls to setBody are just overwriting each other.
for the json generator part, you are double encoding, the variable pretty contains the valid generated json, no need to call json.serialize it.

Error in JSON.parse() (when called from API Gateway)

I'm working on AWS lambda + API Gateway, and I need to pass an array of numbers in the url (GET method) for a REST call. It seems a good way is to pass the numbers as string (comma separated) and then use JSON.parse for the conversion to an array of numbers.
Following is the AWS lambda code I'm using;
exports.handler = (event, context, callback) => {
var arr = JSON.parse('[' + event.numbers + ']');
console.log("array: " + arr);
// TODO implement
callback(null, 'Hello from Lambda');
};
I'm testing this function in AWS Lambda using this Input test event;
{
"numbers": "1,5"
}
And everything works as expected; no errors.
However, when I test it via API Gateway, and passing the numbers as string in the query, I get following error (observed via CloudWatch);
*19:19:02
START RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f Version: $LATEST
19:19:02
2017-08-29T19:19:02.688Z eabab882-8cee-11e7-8e2f-79d3086e061f SyntaxError: Unexpected token u in JSON at position 1 at Object.parse (native) at exports.handler (/var/task/index.js:4:20)
19:19:02
END RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f
19:19:02
REPORT RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f Duration: 215.25 ms Billed Duration: 300 ms Memory Size: 128 MB Max Memory Used: 18 MB
19:19:02
RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f Process exited before completing request*
This is the request passed to lambda as shown in the log;
"body-json" : {},
"params" : {
"path" : {
}
,"querystring" : {
"numbers" : "1,6"
}
,"header" : {
}
},
"stage-variables" : {
},
I can't figure out what the problem is, since I'm passing same string in both cases.
I would appreciate any help.
Thanks
Gus
With this input json informed, you need to get it like this:
var arr = JSON.parse('[' + event.params.querystring.numbers + ']');
rather than:
var arr = JSON.parse('[' + event.numbers + ']');
Or make a body mapping template to stay the way you want:
{ "number": "$input.params('number')" }
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
I hope I have helped!

Collect errors with Gatling?

In my long, but simple awesome Gatling simulation, I have few responses that ended with error 500. Is it possible to tell gatling to collect these error responses messages in a file during the simulation?
No in production mode. You only have them when debug logging is enabled.
It is possible to collect what ever you want and save it into simulation.log file. Use extraInfoExtractor method when you define protocol:
val httpProtocol = http
.baseURL(url)
.check(status.is(successStatus))
.extraInfoExtractor { extraInfo => List(getExtraInfo(extraInfo)) }
Then define in your getExtraInfo(extraInfo: ExtraInfo) method whatever criteria you want. Example bellow outputs request and response in case of debug is enables via Java System Property OR response code is not 200 OR status of request is KO (it can be KO if you have setup some max time and this max time gets increased)
private val successStatus: Int = 200
private val isDebug = System.getProperty("debug").toBoolean
private def getExtraInfo(extraInfo: ExtraInfo): String = {
if (isDebug
|| extraInfo.response.statusCode.get != successStatus
|| extraInfo.status.eq(Status.valueOf("KO"))) {
",URL:" + extraInfo.request.getUrl +
" Request: " + extraInfo.request.getStringData +
" Response: " + extraInfo.response.body.string
} else {
""
}
}

Spring MVC + RequestParam as Map + get URL array parameters not working

I'm currently working on an API controller. This controller should be able to catch any - unknown - parameter and put it in a Map object. Now I'm using this code to catch all parameters and put them in a Map
public String processGetRequest(final #RequestParam Map params)
Now the url I call is as follows:
server/api.json?action=doSaveDeck&Card_IDS[]=1&Card_IDS[]=2&Card_IDS[]=3
Then I print out the parameters, for debugging purposes, with this piece of code:
if (logger.isDebugEnabled()) {
for (Object objKey : params.keySet()) {
logger.debug(objKey.toString() + ": " + params.get(objKey));
}
}
The result of that is:
10:43:01,224 DEBUG ApiRequests:79 - action: doSaveDeck
10:43:01,226 DEBUG ApiRequests:79 - Card_IDS[]: 1
But the expected result should be something like:
10:43:XX DEBUG ApiRequests:79 - action: doSaveDeck
10:43:XX DEBUG ApiRequests:79 - Card_IDS[]: 1
10:43:XX DEBUG ApiRequests:79 - Card_IDS[]: 2
10:43:XX DEBUG ApiRequests:79 - Card_IDS[]: 3
Or atleast tell me that the Card_IDS is an String[] / List<String> and therefore should be casted. I also tried casting the parameter to a List<String> manually but that does not work either:
for (Object objKey : params.keySet()) {
if(objKey.equals(NameConfiguration.PARAM_NAME_CARDIDS)){ //Never reaches this part
List<String> ids = (List<String>)params.get(objKey);
for(String id : ids){
logger.debug("Card: " + id);
}
} else {
logger.debug(objKey + ": " + params.get(objKey));
}
}
Could someone tell me how to get the array Card_IDS - It must be dynamically - from the Map params by using the NameConfiguration.PARAM_NAME_CARDIDS?
When you request a Map annotated with #RequestParam Spring creates a map containing all request parameter name/value pairs. If there are two pairs with the same name, then only one can be in the map. So it's essentially a Map<String, String>
You can access all parameters through a MultiValueMap:
public String processGetRequest(#RequestParam MultiValueMap parameters) {
This map is essentially a Map<String, List<String>>. So parameters with the same name would be in the same list.

Resources