Create Bulk XML from template for POST request using gatling/scala - scala

I want to send bulk xml to my soap request body. I want to know is there any way to generate them dynamically through Gatling/Scala
This is how I am doing
private val request=exec( http("Post request to create")
.post("/endPoint")
.headers(Utils.soapHeaders)
.body(ElFileBody("requestbody/ids.xml"))
.check(status.is(Utils.successStatus)) )
private val feedId = Iterator.continually( Map(
"id" -> Random.alphanumeric.take(13).mkString,
"transactionId" -> Random.alphanumeric.take(14).mkString
) )
val scnPostVehicleAsn=Utils.createScenario("soapService", feedId,
request)
and requestbody/ids.xml has the template with dynamic values ${transactionId} & ${id}.
So Is there any way to dynamically generate xml based on the template, I am not looking for repeat functionality.
Just generate xml once before execution and pass it and later I will make rest calls to validate them

I figured it out myself. I have created a list of random number and passed it to xml directly
val randomNumbers =
List.fill(number)(prefix.concat(Random.alphanumeric.take(13).mkString))
{ randomNumbers.map( i => i }
Then,
val file = new File(System.getProperty("user.dir")
+"/performance-tests/src/test/resources/requestBody/ids.xml") val bw = new BufferedWriter(new FileWriter(file))
bw.write(VehicleAsns.toString()) bw.close()

Related

How to generate multivalue map with random values and inject into request body using a feeder in Gatling

I'm writing a load test for an api and want to create a feeder which generates random values to inject into the body of the POST request. I initially tried copying the random email example from the documentation and adding additional fields to the generated map, but when that didn't work I went down to a single field, basically copying the documentation; however, even this doesn't work for some reason. There are a bunch of solutions on here that use this syntax as well, but something about the way I'm doing is causing the fields I try to inject into the body to be null when the request is made.
Current Code:
val userFeeder: Iterator[Map[String, Unit]] =
Iterator.continually(Map("userName" -> ("user_" + Random.alphanumeric.take(15).mkString)))
var scn: ScenarioBuilder = scenario("GENERATE USER")
.feed(userFeeder)
.exec(
http("CREATE USER")
.post(userBaseUrl)
.headers(userHeaders)
.body(StringBody("userName: ${userName}")))
setUp(
scn.inject(atOnceUsers(1))
)
Ideally I'd like to be able to expand the feeder to include multiple values, i.e.
val userFeeder: Iterator[Map[String, Unit]] =
Iterator.continually(Map("userName" -> ("user_" + Random.alphanumeric.take(15).mkString),
"userEmail" -> (Random.alphanumeric.take(15).mkString) + "#random.edu"),
"address" -> Random.alphanumeric.take(15).mkString)))
and so on, but I'm a little stumped as to why my current code doesn't even work, as it seems to follow the documentation example pretty faithfully. The values are always null in my requests despite trying a few different strategies.
Log output
body:StringChunksRequestBody{contentType='application/json', charset=UTF-8, content=userName: ()}
Figured it out. Turns out that even though Feeder is a wrapper for Iterator, the proper way to do what I want is to declare it like this:
val userFeeder: Feeder[Any] =
Iterator.continually(Map("userName" -> ("user_" + Random.alphanumeric.take(15).mkString),
"userEmail" -> (Random.alphanumeric.take(15).mkString) + "#random.edu"),
"address" -> Random.alphanumeric.take(15).mkString)))

Modify contentHeaders of Swagger Codegen methods in kotlin

I'm using swagger codegen for my REST API calls. For authentication purposes i need to send a session-token within the headers of every request. This is currently done, via APIClients' defaultHeaders
open class ApiClient(val baseUrl: String) {
companion object {
...
#JvmStatic
var defaultHeaders: Map<String, String> by ApplicationDelegates.setOnce(mapOf(ContentType to JsonMediaType, Accept to JsonMediaType))
...
}
}
The way swagger generates the code, these headers can only be modified once.
ApiClient.defaultHeaders += mapOf("Authorization" to userSession!!.idToken.jwtToken)
The problem with this is, that i cannot change the token (e.g. because another user logged in within the application lifetime). Looking deeper into the generated code, before each request is sent, a merge of both defaultHeaders and requestConfig.headers (=contentHeaders) is being made.
inline protected fun <reified T: Any?> request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse<T?> {
...
val headers = defaultHeaders + requestConfig.headers
...
}
The given RequestConfig object comes from every api call. However it is not possible to change these contentHeaders. Also they are empty by default.
fun someAPIRestCall(someParam: kotlin.String) : Unit {
val localVariableBody: kotlin.Any? = type
val localVariableQuery: MultiValueMap = mapOf()
val contentHeaders: kotlin.collections.Map<kotlin.String,kotlin.String> = mapOf() // THESE WILL BE MERGED WITH defaultHeaders
val acceptsHeaders: kotlin.collections.Map<kotlin.String,kotlin.String> = mapOf("Accept" to "application/json")
val localVariableHeaders: kotlin.collections.MutableMap<kotlin.String,kotlin.String> = mutableMapOf()
localVariableHeaders.putAll(contentHeaders)
localVariableHeaders.putAll(acceptsHeaders)
val localVariableConfig = RequestConfig(
RequestMethod.POST,
"someEndpointURL"),
query = localVariableQuery,
headers = localVariableHeaders // THESE WILL BE MERGED WITH defaultHeaders
)
val response = request<Unit>(
localVariableConfig,
localVariableBody
)
...
}
Is it possible to tell swagger-codegen to include some kind of parameter to the generated method signature to add values to those contentHeaders?
EDIT:
This is the current code-gen call within my gradle build chain
task generateSwagger(type: JavaExec) {
main = "-jar"
args "swagger-codegen-cli-2.4.7.jar", "generate", "-i", "./swagger_core.yml", "-l", "kotlin", "-o", "./tmp/RestApi", "--type-mappings", "number=kotlin.Long"
}
By now, i found a solution, that is more of a hack, but it works.
As i am using gradle to build the app, i introduced a task, that changes the generated swagger code, before it actually compiles.
task editAPISources {
def token = "Map<String, String> by ApplicationDelegates.setOnce(mapOf(ContentType to JsonMediaType, Accept to JsonMediaType))"
def value = "MutableMap<String, String> = mutableMapOf(ContentType to JsonMediaType, Accept to JsonMediaType)"
def file = new File("./app/tmp/RestApi/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt")
def newConfig = file.text.replace(token, value)
file.write newConfig
}
The result is a now changeable header :=
#JvmStatic
var defaultHeaders: MutableMap<String, String> = mutableMapOf(ContentType to JsonMediaType, Accept to JsonMediaType)

REST API from Spark Streaming

I am working on usecase where i have to read (id , token and type values delimited with pipe )from kafka topic using spark streaming job.
Using the above values, i have to make rest api call to pull data belongs to id.
I am using below code which i am able to get the data in json format.
val httpget = new HttpGet("///////");
val httpParams = httpClient.getParams
HttpConnectionParams.setConnectionTimeout(httpParams,timeoutConnection)
val httpResponse = httpClient.execute(httpget)
println(httpResponse.getStatusLine().getStatusCode)
val entity = httpResponse.getEntity()
var content = ""
if (entity != null) {
val inputStream = entity.getContent()
content = scala.io.Source.fromInputStream(inputStream).getLines.mkString
inputStream.close
}
My problem here is using this logic in dStream.foreachRDD() block and parsing the json content effectively.
Any help will be appreciated.

PlayFramework Testing: Uploading File in Fake Request Errors

I want to test my method, which requires uploading a file. It is initialized like this:
val tempFile = TemporaryFile(new java.io.File("/home/ophelia/Desktop/students"))
val part = FilePart[TemporaryFile](
key = "students",
filename = "students",
contentType = Some("text/plain"),
ref = tempFile)
val files = Seq[FilePart[TemporaryFile]](part)
val formData = MultipartFormData(
dataParts = Map(),
files = Seq(part),
badParts = Seq(),
missingFileParts = Seq())
I pass it into the FakeRequest:
val result = route(
FakeRequest(POST, "/api/courses/"+"4f3c4ec9-46bf-4a05-a0b2-886c2040f2f6"+"/import" )
.withHeaders("Authorization" -> ("Session " + testSessionA.id.string))
.withMultipartFormDataBody(formData)
)
But when I run the test I get the following error:
Cannot write an instance of play.api.mvc.AnyContentAsMultipartFormData to HTTP response. Try to define a Writeable[play.api.mvc.AnyContentAsMultipartFormData]
What am I doing wrong and how to fix it? I looked on the internet, I didnt find any useful way to understand and resolve this problem.
It's important to remember that http requests are entirely text. route() takes an implicit Writeable to convert the body type of the provided request into text. Without the right Writeable, there is no way to know how to turn MultipartFormData into a request body.
There doesn't seem to be a Writeable for MultipartFormData, but you can provide your own. jroper has a great Writeable you could use for reference. (EDIT: That code is buggy, here's a working Writeable for AnyContentAsMultipartFormData)
Once you have your Writeable, you will need to make it accessible to your call to route(). Bear in mind, you currently have a FakeRequest[AnyContentAsMultipartFormData], not a FakeRequest[MultipartFormData]. You can either convert your request first:
val request = FakeRequest(POST,
"/api/courses/"+"4f3c4ec9-46bf-4a05-a0b2-886c2040f2f6"+"/import" )
.withHeaders("Authorization" -> ("Session "))
.withMultipartFormDataBody(formData)
route(request.map(_.mdf).asInstanceOf[FakeRequest[MultipartFormData[TemporaryFile]]])
or make your Writeable a Writeable[AnyContentAsMultipartFormData].
route for a given Request[T] requires an implicit parameter of type Writeable[T] that knows how to serialize the request body, because it will actually call the controller action just like an actual web request would, by pushing bytes onto it.
The problem is that there is no Writeable[MultipartFormData] predefined (you can see which are in play.api.test.Writeables).
This means you basically have two options:
write your own Writeable that serializes a MultipartFormData into bytes
Skip the routing part and call the action directly instead, like in the accepted answer in Play Framework Testing using MultipartFormData in a FakeRequest. This way of testing actions takes a shortcut and does not actually serialize and deserialize the request.
IMHO the first option is way too much pain for the gain, but if you go down that road, maybe contribute it to play when you succeed.
One of the possible solutions is to use wsUrl. For example
"File uploading action" should {
"upload sent file and result in ID" in {
val file = Paths.get(getClass.getResource("/1.txt").toURI)
val action = wsUrl("/upload").post(Source.single(FilePart("file", "hello.txt", Option("text/plain"), FileIO.fromPath(file))))
val res = Await.result(action, timeout)
res.status mustBe OK
res.body contains "123"
}
}

Gatling 2 dynamic queryParam on each request

I am trying to run a load test using Gatling 2. I need to generate one of the query parameters dynamically on each request.
My scenario is defined like this:
val scn = scenario("Load Test Scenario")
.exec(
http("Test API")
.post(url)
.body(StringBody("Some XML"))
.queryParam("x", DigestUtils.md5Hex(generateX().getBytes("UTF-8")))
)
def generateX() : String = {
// generate random string and return
}
This only calls generateX once and uses the result in each request. Is there anyway to have the generateX call on every request?
You have to pass a function, not a value. See Gatling documentation about Expression.
Here, you can just discard the session input parameter as you don't use it, so you can simply write:
.queryParam("x", _ => DigestUtils.md5Hex(generateX().getBytes("UTF-8")))