Checking JSONP

Any thoughts on checking JSONP return values?

I started to build out an extractor that first matched on the function call and then transformed out the json payload:

regex("""^(\w+.)\w+(.)$""").find.transform(ostr => ostr match {
case Some(str) => {
val i1 = str.indexOf("(") + 1
val i2 = str.lastIndexOf(")")
Some(str.substring(i1, (i2-i1)))
}
case None => Some("") // don’t expect to get here because regex will fail
}).saveAs(“jsonData”)

But now I don’t know how to check jsonData, which is what I really want to do.

Thanks,

–Spencer

Hi Spencer,

Have you considered our built in JsonPath support?

Stéphane

Digging into the gatling code a little more, it looks like I’d want something like a varCheckFactory that’s alongside the status/url/checksum/header/body/time Check Factories.

So, then we could add a jsonPath(“expr”, “varName”)

Would this be the right track?

Sorry, could you detail what you want to achieve with an example, please? :slight_smile:

I’m getting a JSONP return from a service:

foo.bar.func({“some”:“value”,“another”:“value”,“an”:{“obj”:1}})

The regex I provided will validate the foo.bar.func() and extract {“some”:“value”,“another”:“value”,“an”:{“obj”:1}} to a variable jsonData

I then need to evaluate some jsonPath calls against jsonData, e.g. jsonPath("$/an/obj", jsonData)

Does that make more sense?

OK, get it.

You’ll have to go with a snapshot of the next version in order to be able to use the new API.

import io.gatling.core.check.Validator
import io.gatling.core.validation.Validation
import io.gatling.core.check.extractor.jsonpath.SingleJsonPathExtractor

val myValidator = new Validator[String] {

val name = “MyJsonPathValidator”

def apply(session: Session, actual: Option[String]): Validation[Option[String]] =
new SingleJsonPathExtractor[String](“path”, 0).apply(session, actual)

}

regex("""^(\w+.)\w+(.)$""").find.transform(ostr => ostr match {
case Some(str) => {
val i1 = str.indexOf("(") + 1
val i2 = str.lastIndexOf(")")
val jsonString = str.substring(i1, (i2 - i1))
Some(jsonString)
}
case None => Some("") // don’t expect to get here because regex will fail
}).validate(myValidator).saveAs(“jsonData”)

In order to use a snapshot of the next version - is there a different maven repo with snapshots available? Or will I need to clone the gatling repo and mvn install it?

There an ETA on the release of a next version?

In order to use a snapshot of the next version - is there a different
maven repo with snapshots available? Or will I need to clone the gatling
repo and mvn install it?

There an ETA on the release of a next version?

Awesome - thanks!

Alright - got back to this today.

So, this is what I’m doing:

def checkJsonpAssetStructure(req: HttpRequestBuilder, count: Int) = {

def jsonValidator(path: String) =
new Validator[String] {
def name: String = “JsonP Validator”

def apply(session: Session, actual: Option[String]): Validation[Option[String]] = {
val foo = new CountJsonPathExtractor(path).apply(session, actual)
foo match {
case Success(x) => Some(path + " passed").success
case Failure(x) => Some("%s failed: %s".format(path, x)).mapError(s => s)
}
}
}

req.check(
regex("""(?:\w+.)\w+(.)""").find.transform(ostr => ostr match {

case Some(str) => {
val i1 = str.indexOf("(") + 1
val i2 = str.lastIndexOf(")")
Some(str.substring(i1, (i2-i1)))
}
case None => Some("")
}).validate(jsonValidator("$[*]"))
)
}

That’s not really working out the way I was wanting, and I can’t tell what I’m doing wrong.

What I’d really like to be doing is something like:

def checkJsonpAssetStructure(req: HttpRequestBuilder, count: Int) = {
req.check(

regex("""(?:\w+.)\w+(.)""").find.transform(ostr => ostr match {

case Some(str) => {
val i1 = str.indexOf("(") + 1
val i2 = str.lastIndexOf(")")
Some(str.substring(i1, (i2-i1)))
}
case None => Some("")
}).saveAs(“jsonData”)
)
.check(jsonPath("$[]", “jsonData”).count.is(count)) //check that var jsonData matches jsonPath
.check(jsonPath("$[
].id", “jsonData”).count.is(count)) //check that var jsonData matches jsonPath
.check(jsonPath("$[*].importance", “jsonData”).count.is(count)) //check that var jsonData matches jsonPath
}

I understand you’re directing me to use the validator on the transformed data to do the checks - I’m just not exactly certain how to do that.

And, just to be sure I’m not missing something, now that I’m working against the snapshot, there haven’t been changes in the jsonPath feature to test/match jsonp?

Thanks for the help!

–Spencer

Sorry for the delay.
What’s sure is that jsonPath checks work against the response body, not session attributes.

You’ll have to write your own Check. I’ll try to help you with this tomorrow,

Cheers,

Stéphane

I finally realized that I wasn’t going in the proper direction: what you have to develop is not a custom Validator but a custom Preparer.

Just for HttpBodyJsonPathCheckBuilder so that you first use your regex for extracting json with a regex and then deserialize it.
https://github.com/excilys/gatling/blob/master/gatling-http/src/main/scala/io/gatling/http/check/body/HttpBodyJsonPathCheckBuilder.scala#L32

Get it?

Stéphane

I believe so.

So, what I ended up with was:

object HttpBodyJsonPPathCheckBuilder extends Logging {

val preparer: Preparer[Response, Any] = (response: Response) =>
try {
val resp = response.getResponseBody(configuration.core.encoding)
val i1 = resp.indexOf("(") + 1
val i2 = resp.lastIndexOf(")")

val json = resp.substring(i1, i2)

JsonPathExtractor.parse(json).success
} catch {
case e: Exception =>
val message = s"Could not parse response into a JSON object: ${e.getMessage}"
logger.info(message, e)
message.failure
}

def jsonPath[X](path: Expression[String])(implicit groupExtractor: JsonFilter[X]) =
new HttpMultipleCheckBuilder[Any, X](HttpCheckBuilders.bodyCheckFactory, preparer) {
def findExtractor(occurrence: Int) = new SingleJsonPathExtractor(path, occurrence)
def findAllExtractor = new MultipleJsonPathExtractor(path)
def countExtractor = new CountJsonPathExtractor(path)
}
}

Elsewhere, I defined:

def jsonPPath(path: String) = HttpBodyJsonPPathCheckBuilder.jsonPathString

And so now my checks for JSONP look like:

def checkJsonpAssetStructure(count: Int): Iterable[HttpCheck] = {

List(
regex("""(?:\w+.)\w+(.)""").count.is(1),
jsonPPath("$").count.is(1),
jsonPPath("$.status").find.is(“200”),
jsonPPath("$.body[]").count.is(count),
jsonPPath("$.body[
].id").count.is(count),

jsonPPath("$.body[*].title").count.is(count),

jsonPPath("$.body[].description").count.is(count)//,
jsonPPath("$.body[
].publishdate").count.is(count),

)

}

This seems to be working like a charm.

Might be worth it for other people to have this integrated more directly into gatling. If that were to happen, you’d probably want to incorporate the regex check into the HttpBodyJsonPPathCheckBuilder. Not real sure what that would look like though.

Anyhow - thanks for the help - I’ll be making heavy use of this.

–Spencer

Actually, I’ve just pushed jsonpJsonPath(regex, path) on master :slight_smile:

See https://github.com/excilys/gatling/issues/1517

lol

well, thanks!

You’re welcome.
Please feed me back if you get the chance to try it out.

Might be worth to providing a default regex per the standard proposed in http://json-p.org/

Per that standard, I think:
^\w+(?:["\w+"]|.\w+)(.)$

That gives you the wrapping function call. The Json tester can test the contents between the ( and )

Time for a contrib? :wink:

No problem :slight_smile: