crashed with 'j.u.NoSuchElementException: No attribute named 'notStartedRefIds' is defined' - scala

I am working with Gatling and have received this error which occurs claiming 'notStartedRefIds' is defined.
I am new to Gatling and am confident that the value has been declared properly already, however I cannot see why it is not being recognised.
The code which contains reference to the value is below:
case object StudentAssignmentScenario {
private val notStartedRefIdsSession = "notStartedRefIds"
private val studentAssignmentRefIdSession = "studentAssignmentRefId"
private val endpoint: String = environmentAssignmentsUrls + "/v2/studentAssignments"
private val startAssignment = http("Start StudentAssignment")
.put(endpoint + "/${studentAssignmentRefId}/start")
.headers(getHeaderMap)
.check(status.is(200))
private val submitAssignment = http("Submit StudentAssignment")
.put(endpoint + "/${studentAssignmentRefId}/submit")
.headers(getHeaderMap)
.check(status.is(200))
private val startAndSubmitAssignments = exec { session =>
val refId = session(notStartedRefIdsSession).as[Seq[String]].apply(0)
session.set(studentAssignmentRefIdSession, refId)
}.exec(startAssignment).exec(submitAssignment)
private val startAssignments = exec { session =>
val refId = session(notStartedRefIdsSession).as[Seq[String]].apply(1)
session.set(studentAssignmentRefIdSession, refId)
}.exec(startAssignment)
def readStudentAssignments = exec(http("Get Not Started StudentAssignments")
.get(endpoint)
.headers(getHeaderMap)
.queryParamMap(Map[String, String]("inclExpired" -> "true",
"context" -> "basel",
"sort" -> "dueDate,title",
"limit" -> "25", "offset" -> "0",
"status" -> "NOT_STARTED"))
.check(status.is(200),
// save refIds
jsonPath("$.items[?(#.status == 'NOT_STARTED')].refId").findAll.optional.saveAs("notStartedRefIds")))
private val studentAssignmentWorkflow = exec(startAndSubmitAssignments)
.exec(startAssignments)
val studentAssignmentWorkflowSetup: ChainBuilder = exitBlockOnFail(
group("Student Assignment Workflow") {
studentAssignmentWorkflow
})
}

The only place you seem to be populating notStartedRefIds is in readStudentAssignments where you extract it from the response thanks to a jsonPath.
However, you've configured this check as optional meaning you expect it to sometimes not be here. If that's the case, it's not surprise it can be undefined when calling session(notStartedRefIdsSession).

Related

Use a functional construct to crate a List of classes from REST response

I wrote the below code to:
Query a REST endpoint
Create an instance of the case class Pair from the name and value fields in the response
The Pair instance added to the list lb.
This code is very imperative:
case class Pair(name: String, value: Double, update: String)
var lb = new ListBuffer[Currency]()
for (elem <- Data.updates) {
try {
val request = "http://...../values/" + elem
val response = scala.io.Source.fromURL(request).mkString
val jsonObject = Json.parse(response)
val value = (jsonObject \ "value").get.toDouble
val update = (jsonObject \ "update").get
lb += Pair(elem , value, update)
} catch {
case e: IOException => println("Exception occured accessing data for " +elem)
}
}
The above code provides the context of the problem. I've refactored and written a block that runs on Scastie with changes but hopefully conveys the same problem:
import play.api.libs.json.{Json, __}
val updates = List("test1" , "test2")
case class Pair(name: String, value: Double, update: String)
var lb = new scala.collection.mutable.ListBuffer[Pair]()
for (elem <- updates) {
val request = "http://www.google.com" + elem
val response : String = "{\"value\" : 1 , \"update\" : \"test\"}"
val jsonObject = Json.parse(response)
val value = (jsonObject \ "value").get.toString().toDouble
val update = (jsonObject \ "update").get.toString()
lb += Pair(elem , value, update)
}
println(lb.toList)
Scastie: https://scastie.scala-lang.org/RmBUUem5SxGT2dYLIqLryQ
I need to replace the ListBuffer with List and somehow populate the List using a functional construct instead of a loop?
Just use .map:
val lb = updates.map { elem =>
...
Pair(elem, value, update)
}
I'd first define a method:
def getPairFromElem(elem: String): Pair = {
val request = "http://www.google.com" + elem
val response: String = "{\"value\" : 1 , \"update\" : \"test\"}"
val jsonObject = Json.parse(response)
val value = (jsonObject \ "value").get.toString().toDouble
val update = (jsonObject \ "update").get.toString()
Pair(elem , value, update)
}
Then use it:
val updates = List("test1" , "test2")
val lb = updates.map(getPairFromElem)
Code run at Scastie.

Deserialization of json using jackson in scala

I am trying to de-serialize the below json string to scala object using jackson json api
{ "Domain1": { "data-file": "dataFile1", "filter": {
"affected-object": "AffectedObject1", "affected-nd":
"AffectedNd1" } }, "Domain2": { "data-file": "dataFile2",
"filter": { "affected-ci": "AffectedCI2", "affected-net":
"AffectedNet2" } } }
I tried to use case class and tried first using "ClassOf" in ValueType of "readValue" Method but the output is Map of Map Object. Data is not converted into Case Class Object.
case class CrossDomainFilterObj(#JsonProperty("data-file")dataFile: String,
#JsonProperty("filter")filter: Map[String,String])
val jsonString = "{\"Domain1\": {\"data-file\": \"dataFile1\", \"filter\": { \"affected-object\":
\"AffectedObject1\", \"affected-nd\" : \"AffectedNd1\"}},\"Domain2\": {\"data-file\":
\"dataFile2\", \"filter\": { \"affected-ci\":\"AffectedCI2\", \"affected-net\" :
\"AffectedNet2\"}}}"
val mapper = new ObjectMapper
mapper.registerModule(DefaultScalaModule)
val data = mapper.readValue(jsonString, classOf[Map[String, CrossDomainFilterObj]])
println(data)
I am getting output like below
Map(Domain1 -> Map(data-file -> dataFile1, filter -> Map(affected-object ->
AffectedObject1, affected-nd -> AffectedNd1)), Domain2 -> Map(data-file ->
dataFile2, filter -> Map(affected-ci -> AffectedCI2,
affected-net -> AffectedNet2)))
But I am expecting an output like below
Map(Domain1 -> CrossDomainFilterObj(dataFile1, Map(affected-object ->
AffectedObject1, affected-nd -> AffectedNd1)), Domain2 ->
CrossDomainFilterObj(dataFile2, Map(affected-ci ->
AffectedCI2, affected-net -> AffectedNet2)))
Then i tried using TypeReference as ValueType as shown below,
case class CrossDomainFilterObj(#JsonProperty("data-file")dataFile: String,
#JsonProperty("filter")filter: Map[String,String])
val jsonString = "{\"Domain1\": {\"data-file\": \"dataFile1\", \"filter\": { \"affected-object\":
\"AffectedObject1\", \"affected-nd\" : \"AffectedNd1\"}},\"Domain2\": {\"data-file\":
\"dataFile2\", \"filter\": { \"affected-ci\":\"AffectedCI2\", \"affected-net\" :
\"AffectedNet2\"}}}"
val mapper = new ObjectMapper
mapper.registerModule(DefaultScalaModule)
val reference = new TypeReference[Map[String, CrossDomainFilterObj]] {}
val data = mapper.readValue(jsonString, reference)
println(data)
I am getting error like below
dead code following this construct
"val data = mapper.readValue(jsonString, reference)"
Could someone help to identify what I am doing wrong here.
Just make sure you use ScalaObjectMapper:
val mapper = new ObjectMapper() with ScalaObjectMapper
Then this should work:
val data = mapper.readValue[Map[String, CrossDomainFilterObj]](jsonString)

How filter a JSLookup/JSObject with the Scala Play library

Say I have some params coming from our API clients like this:
val params = (request \ "params")
I want to filter them and remove certain key/values. Like if I get:
{
"foo": "bar",
"hello": "world"
}
I want to filter it to
{
"foo": "bar"
}
Here's my WIP code but, as more advanced Scala people will probably tell right away, it doesn't work.
val params = (request \ "params").get.as[List[JsObject]]
val blacklistedParams = Seq("foo")
val approvedParams = params.filter((param: JsObject) => {
!blacklistedParams.contains(param)
})
That first line always fails. I've tried doing .get.as in all sorts of types but always get errors. I'm still new to Scala and types in general.
I think I figured out a way:
val params = (request \ "params").get.as[Map[String, JsValue]]
val blacklistedParams = Seq("foo")
val approvedParams = params.filter((param) => {
!blacklistedParams.contains(param._1)
})
My only annoyance with this method is that ._1. To me its not very clear for the next person that this is the key of the key/val pair of the params.
You can use -
val filtered = (request \ "params").as[JsObject] - "hello"
Full example:
def index = Action{
val json = Json.obj(
"params" -> Json.obj(
"foo" -> "bar",
"hello" -> "world"))
val filtered = (json \ "params").as[JsObject] - "hello"
Ok(filtered)
}
output:
{
foo: "bar"
}

playframework 2.4 - Unspecified value parameter headers error

I am upgrading playframework 2.4 from 2.3, I changed versions then if I compile same code, I see following error. Since I am novice at Scala, I am trying to learn Scala to solve this issue but still don't know what is the problem. What I want to do is adding a request header value from original request headers. Any help will be appreciated.
[error] /mnt/garner/project/app-service/app/com/company/playframework/filters/LoggingFilter.scala:26: not enough arguments for constructor Headers: (headers: Seq[(String, String)])play.api.mvc.Headers.
[error] Unspecified value parameter headers.
[error] val newHeaders = new Headers { val data = (requestHeader.headers.toMap
The LoggingFilter class
class LoggingFilter extends Filter {
val logger = AccessLogger.getInstance();
def apply(next: (RequestHeader) => Future[Result])(requestHeader: RequestHeader): Future[Result] = {
val startTime = System.currentTimeMillis
val requestId = logger.createLog();
val newHeaders = new Headers { val data = (requestHeader.headers.toMap
+ (AccessLogger.X_HEADER__REQUEST_ID -> Seq(requestId))).toList }
val newRequestHeader = requestHeader.copy(headers = newHeaders)
next(newRequestHeader).map { result =>
val endTime = System.currentTimeMillis
val requestTime = endTime - startTime
val bytesToString: Enumeratee[ Array[Byte], String ] = Enumeratee.map[Array[Byte]]{ bytes => new String(bytes) }
val consume: Iteratee[String,String] = Iteratee.consume[String]()
val resultBody : Future[String] = result.body |>>> bytesToString &>> consume
resultBody.map {
body =>
logger.finish(requestId, result.header.status, requestTime, body)
}
result;
}
}
}
Edit
I updated codes as following and it compiled well
following codes changed
val newHeaders = new Headers { val data = (requestHeader.headers.toMap
+ (AccessLogger.X_HEADER__REQUEST_ID -> Seq(requestId))).toList }
to
val newHeaders = new Headers((requestHeader.headers.toSimpleMap
+ (AccessLogger.X_HEADER__REQUEST_ID -> requestId)).toList)
It simply states that if you want to construct Headers you need to supply a field named headers which is of type Seq[(String, String)]. If you omit the inital new you will be using the apply function of the corresponding object for Headers which will just take a parameter of a vararg of (String, String) and your code should work. If you look at documentation https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.mvc.Headers and flip between the docs for object and class it should become clear.

Why is immutable map size always zero?

Below Scala class parses a file using JDOM and populates the values from the file into a Scala immutable Map. Using the + operator on the Map does not seem to have any effect as the Map is always zero.
import java.io.File
import org.jsoup.nodes.Document
import org.jsoup.Jsoup
import org.jsoup.select.Elements
import org.jsoup.nodes.Element
import scala.collection.immutable.TreeMap
class JdkElementDetail() {
var fileLocation: String = _
def this(fileLocation: String) = {
this()
this.fileLocation = fileLocation;
}
def parseFile : Map[String , String] = {
val jdkElementsMap: Map[String, String] = new TreeMap[String , String];
val input: File = new File(fileLocation);
val doc: Document = Jsoup.parse(input, "UTF-8", "http://example.com/");
val e: Elements = doc.getElementsByAttribute("href");
val href: java.util.Iterator[Element] = e.iterator();
while (href.hasNext()) {
var objectName = href.next();
var hrefValue = objectName.attr("href");
var name = objectName.text();
jdkElementsMap + name -> hrefValue
println("size is "+jdkElementsMap.size)
}
jdkElementsMap
}
}
println("size is "+jdkElementsMap.size) always prints "size is 0"
Why is the size always zero, am I not adding to the Map correctly?
Is the only fix for this to convert jdkElementsMap to a var and then use the following?
jdkElementsMap += name -> hrefValue
Removing the while loop here is my updated object:
package com.parse
import java.io.File
import org.jsoup.nodes.Document
import org.jsoup.Jsoup
import org.jsoup.select.Elements
import org.jsoup.nodes.Element
import scala.collection.immutable.TreeMap
import scala.collection.JavaConverters._
class JdkElementDetail() {
var fileLocation: String = _
def this(fileLocation: String) = {
this()
this.fileLocation = fileLocation;
}
def parseFile : Map[String , String] = {
var jdkElementsMap: Map[String, String] = new TreeMap[String , String];
val input: File = new File(fileLocation);
val doc: Document = Jsoup.parse(input, "UTF-8", "http://example.com/");
val elements: Elements = doc.getElementsByAttribute("href");
val elementsScalaIterator = elements.iterator().asScala
elementsScalaIterator.foreach {
keyVal => {
var hrefValue = keyVal.attr("href");
var name = keyVal.text();
println("size is "+jdkElementsMap.size)
jdkElementsMap += name -> hrefValue
}
}
jdkElementsMap
}
}
Immutable data structures -- be they lists or maps -- are just that: immutable. You don't ever change them, you create new data structures based on changes to the old ones.
If you do val x = jdkElementsMap + (name -> hrefValue), then you'll get the new map on x, while jdkElementsMap continues to be the same.
If you change jdkElementsMap into a var, then you could do jdkEleemntsMap = jdkElementsMap + (name -> hrefValue), or just jdkElementsMap += (name -> hrefValue). The latter will also work for mutable maps.
Is that the only way? No, but you have to let go of while loops to achieve the same thing. You could replace these lines:
val href: java.util.Iterator[Element] = e.iterator();
while (href.hasNext()) {
var objectName = href.next();
var hrefValue = objectName.attr("href");
var name = objectName.text();
jdkElementsMap + name -> hrefValue
println("size is "+jdkElementsMap.size)
}
jdkElementsMap
With a fold, such as in:
import scala.collection.JavaConverters.asScalaIteratorConverter
e.iterator().asScala.foldLeft(jdkElementsMap) {
case (accumulator, href) => // href here is not an iterator
val objectName = href
val hrefValue = objectName.attr("href")
val name = objectName.text()
val newAccumulator = accumulator + (name -> hrefValue)
println("size is "+newAccumulator.size)
newAccumulator
}
Or with recursion:
def createMap(hrefIterator: java.util.Iterator[Element],
jdkElementsMap: Map[String, String]): Map[String, String] = {
if (hrefIterator.hasNext()) {
val objectName = hrefIterator.next()
val hrefValue = objectName.attr("href")
val name = objectName.text()
val newMap = jdkElementsMap + name -> hrefValue
println("size is "+newMap.size)
createMap(hrefIterator, newMap)
} else {
jdkElementsMap
}
}
createMap(e.iterator(), new TreeMap[String, String])
Performance-wise, the fold will be rather slower, and the recursion should be very slightly faster.
Mind you, Scala does provide mutable maps, and not just to be able to say it has them: if they fit better you problem, then go ahead and use them! If you want to learn how to use the immutable ones, then the two approaches above are the ones you should learn.
The map is immutable, so any modifications will return the modified map. jdkElementsMap + (name -> hrefValue) returns a new map containing the new pair, but you are discarding the modified map after it is created.
EDIT: It looks like you can convert Java iterables to Scala iterables, so you can then fold over the resulting sequence and accumulate a map:
import scala.collection.JavaConverters._
val e: Elements = doc.getElementsByAttribute("href");
val jdkElementsMap = e.asScala
.foldLeft(new TreeMap[String , String])((map, href) => map + (href.text() -> href.attr("href"))
if you don't care what kind of map you create you can use toMap:
val jdkElementsMap = e.asScala
.map(href => (href.text(), href.attr("href")))
.toMap