Replace ${} parameters with values from a feeder file in an exec

The API’s I am testing have a complex authorization scheme that involves generating an MD5 value based on the payload, requestType and uri of the service. Values in that payload and uri will need to be parameterized so I have that defined in an external file and am feeding it in. Simple enough. The problem I am having is in the exec statement when I am generating the new session with the computed values, the ${} variables that are defined in the feeder document are not getting escaped. Unfortunately the SignatureCalculator that generates the Authorization header requires the payload, requestType and uri to be passed in so I need to have those ${} values resolved in that exec.

Take this code for example. This is a simple Get call that has no payload. The problem I am having is resolving the ${userid}, which is defined in get_user_data_request.csv:

object GetUserDataRequest extends BaseRequest {

val getUserDataRequestScenario = scenario(“Get User Data Request”)
.feed(csv(“get_user_data_request.csv”).random)
.exec(session => { val payload : String = “”
val requestType : String = “GET”
val uri : String = “/services/userids/${userid}”

val newSession : Session = session.set(“payload”, payload)
.set(“requestType”, requestType)
.set(“uri”, uri)
.set(“signatureCalculator”, new CustomSignatureCalculator(payload, requestType, uri, MainController.environment))
.set(“contentMd5”, Authorization.getMD5MessageDigest(payload))

newSession})
.exec(http(“get_user_data_request”)
.get("${uri}")
.headers(baseHeader)
.header(HttpHeaderNames.ContentMD5, “${contentMd5}”)
.signatureCalculator("${signatureCalculator}")
.check(status.is(200), bodyString.transform(string => string).saveAs(“responseBody”)))
.exec(session => { println(session(“responseBody”).as[String])
session})
}

I need to replace the value of ${userid} with a value coming in from an external CSV file from the feed statement. The way it is written now the request that is being made is not replacing the ${userid} parameter with it’s proper value and the request looks like this:

http://servername/services/userids/${userid}

when it should look like this:

http://servername/services/userids/szaluk

I need that replacement to happen in the exec where I am generating the new session as I need that uri and payload to have any ${} values replaced before I can generate the MD5 and Signature Calculator.

How can I get the ${userid} value replaced in the exec statement? Is this possible?

Sorry for the rambling. This is difficult to explain in a short email. :slight_smile:

Thanks,
Steve

EL in EL inception? No, that wouldn’t work.

Then, you’re trying to implement a signature calculator, so have you tried to search for “signature calculator” in our documentation (see textbox in banner)?

Hi Stephane,

I got the signature calculator working and the scenario above works fine if the URI or Payload has no dynamic data. All I would like to do it replace ${userid} in the following statement from the code above with the value from a feeder file:

val uri : String = “/services/userids/${userid}”

In the meantime I wrote my own EL processor that takes in a string and iterates over the session attributes and does the necessary replacements. I am sure there is a better way than that. I just can’t figure it out. :slight_smile:

Thanks Again,
Steve

Easy enough:

`
val uri : String = “/services/userids/” + session(“userid”).as[String]

`

It gets more complicated if you do not have the ability to change the code like that, like if the string is coming from an outside source. But if that is the case, say so and we can help you cross that bridge. :slight_smile:

@Steven I think you misunderstood the SignatureCalculator logic. The goal is that you shouldn’t be generating uri, payload and whatever upfront, but only on the spot.
Upfront generation has 2 drawbacks:

You have to access the resolved uri and body from the Request, and add the new header to the RequestBuilder.

Thanks for the great comments, John and Stephane!

I was able to simplify this somewhat and updated my SignatureCalculator. I have one more stumbling block I need to get over. Take this snippet of code for example:

val getUserDataRequestScenario = scenario(“Get User Data Request”)
.feed(csv(“get_user_data_request.csv”).random)
.exec(http(“get_user_data_request”)
.get("/services/userids/${userid}")
.headers(baseHeader)
.signatureCalculator(new CISignatureCalculator("", “GET”, “/services/userids/${userid}”, MainController.environment))
.check(status.is(200), bodyString.transform(string => string).saveAs(“responseBody”)))
.exec(session => { println(session(“responseBody”).as[String])
session})

How do I save the string that was generated in the get call after El strings have been replaced? I need to use that when I generate the SignatureCalculator.

Also, in the folllowing code:

val createAccountRequestScenario = scenario(“Create Account Request”)
.exec(http(“create_account_request”)
.post("/services/accounts")
.headers(baseHeader)
.body(ELFileBody(“create_account_request_template.json”)).asJSON
.signatureCalculator(new CISignatureCalculator("", “GET”, “/services/accounts”, MainController.environment))
.check(status.is(200), bodyString.transform(string => string).saveAs(“responseBody”)))
.exec(session => { println(session(“responseBody”).as[String])
session})

How do I save the string generated in the body call (create_account_request_template.json is template I am using with EL strings) so I can use that when generating the signature calculator?

Here is what create_account_request_template.json looks like:

{
“type”: “USER”,
“status”: “PERMANENT”,
“userId”: “${username}”,
“email”: “${username}@domain.com”,
“realm”: " ",
“realmAccountId”: “12345”,
“enabled”: true,
“phones”: {},
“label”: “${username}”
}

If I can do that easily then I can remove all that processing I was doing completely.

Thanks!

–Steve

  val getUserDataRequestScenario = scenario("Get User Data Request")

.feed(csv("get_user_data_request.csv").random)
                                   .exec(http("get_user_data_request")
                                   .get("/services/userids/${userid}")
                                   .headers(baseHeader)
                                   .signatureCalculator(new
CISignatureCalculator("", "GET", "/services/userids/${userid}",
MainController.environment))
                                   .check(status.is(200),
bodyString.transform(string => string).saveAs("responseBody")))
                                   .exec(session => {
println(session("responseBody").as[String])
                                                      session})

But why do you insist on passing "GET" and "/services/userids/${userid}" to
the CISignatureCalculator?!?!
You'll get the resolved values in the request passed to the
SignatureCalculator method.

CISignatureCalculator {
  def calculateAndAddSignature(request: Request, requestBuilder:
RequestBuilderBase): Unit = {
     val method = request.getMethod // GET
     val path = request.getUri.getPath //
/services/userids/userIdResolvedValue
     val body = request.getStringData // computed body
     val token = generateToken(method, path, body)
     requestBuilder.addHeader("X-Token", token)
  }
}

Hey Stephane,

That worked perfectly! I knew I had to be doing this wrong and making it more complicated than it needed to be. I can’t thank you enough for the help!

Thanks,
Steve

Great!
Glad I helped!