If I want to read a single parameter in a get request in scalatra I can do it as follows:
get("mypath/:id") {
val id = params("id")
...
}
According to the scalatra documentation I can also use multiParams to get a sequence of parameters:
val ids = multiParams("ids")
But it does not say how the URL should be formed should I wish to pass more than one parameter. So if I wanted to pass multiple ids what is the format for the URL?
I have tried it with ampersands, commas and semi-colons but to no avail: e.g.
../mypath/id1&id2
Check the docs: http://scalatra.org/guides/2.4/http/routes.html
As an example, let’s hit a URL with a GET like this:
/articles/52?foo=uno&bar=dos&baz=three&foo=anotherfoo
Look closely: there are two “foo” keys in there.
Assuming there’s a matching route at /articles/:id, we get the
following results inside the action:
get("/articles/:id") {
params("id") // => "52"
params("foo") // => "uno" (discarding the second "foo" parameter value)
params("unknown") // => generates a NoSuchElementException
params.get("unknown") // => None - this is what Scala does with unknown keys in a Map
multiParams("id") // => Seq("52")
multiParams("foo") // => Seq("uno", "anotherfoo")
multiParams("unknown") // => an empty Seq
}
So you would need to name each param. e.g. /mypath/?ids=id1&ids=id2&ids=id3
You can embed multiple same name parameters in the path and get them through multiParams:
// http://localhost:8080/articles/id1/id2
get("/articles/:id/:id"){
println(multiParams("id")) // => Seq("id1", "id2")
}
Related
I'm working on a command line tool written in Scala which is executed as:
sbt "run --customerAccount 1234567"
Now, I wish to make this flexible to accept "--CUSTOMERACCOUNT" or --cUsToMerAccount or --customerACCOUNT ...you get the drift
Here's what the code looks like:
lazy val OptionsParser: OptionParser[Args] = new scopt.OptionParser[Args]("scopt") {
head(
"XML Generator",
"Creates XML for testing"
)
help("help").text(s"Prints this usage message. $envUsage")
opt[String]('c', "customerAccount")
.text("Required: Please provide customer account number as -c 12334 or --customerAccount 12334")
.required()
.action { (cust, args) =>
assert(cust.nonEmpty, "cust is REQUIRED!!")
args.copy(cust = cust)
}
}
I assume the opt[String]('c', "customerAccount") does the pattern matching from the command line and will match with "customerAccount" - how do I get this to match with "--CUSTOMERACCOUNT" or --cUsToMerAccount or --customerACCOUNT? What exactly does the args.copy (cust = cust) do?
I apologize if the questions seem too basic. I'm incredibly new to Scala, have worked in Java and Python earlier so sometimes I find the syntax a little hard to understand as well.
You'd normally be parsing the args with code like:
OptionsParser.parse(args, Args())
So if you want case-insensitivity, probably the easiest way is to canonicalize the case of args with something like
val canonicalized = args.map(_.toLowerCase)
OptionsParser.parse(canonicalized, Args())
Or, if you for instance wanted to only canonicalize args starting with -- and before a bare --:
val canonicalized =
args.foldLeft(false -> List.empty[String]) { (state, arg) =>
val (afterDashes, result) = state
if (afterDashes) true -> (arg :: result) // pass through unchanged
else {
if (arg == "==") true -> (arg :: result) // move to afterDash state & pass through
else {
if (arg.startsWith("--")) false -> (arg.toLowerCase :: result)
else false -> (arg :: result) // pass through unchanged
}
}
}
._2 // Extract the result
.reverse // Reverse it back into the original order (if building up a sequence, your first choice should be to build a list in reversed order and reverse at the end)
OptionsParser.parse(canonicalized, Args())
Re the second question, since Args is (almost certainly) a case class, it has a copy method which constructs a new object with (most likely, depending on usage) different values for its fields. So
args.copy(cust = cust)
creates a new Args object, where:
the value of the cust field in that object is the value of the cust variable in that block (this is basically a somewhat clever hack that works with named method arguments)
every other field's value is taken from args
I am new to scala and gatling . I am trying to fetch values from feeder and post zip file to service . but ${extensionId} is not been updated with fetched value instead it remain as ${extensionId} . Could some one please help me know If I miss some thing here .
def installExtension() =
exec(http("template - Install Extension")
.post(url + "/v1/extensions")
.basicAuth("jack", "password")
.headers(namespaceHeader)
// using testUtils to get InputStream conte
.body(InputStreamBody(TestUtils.toStream(hashMap.get("${extensionId}").getOrElse(null))))
.check(status.is( 201)))
class extmgrSimulations extends Simulation {
val extensionIds = csv(s"${Configuration.dataDirectory}/extensionId.csv").circular
val extMgrScenerio = scenario("extensionMgr - Scenario")
.during(Configuration.duration) {
exitBlockOnFail(
group("load-test") {
exec(
pace(Configuration.paceFrom, Configuration.paceTo),
feed(extensionIds),feed(extensionIds)
randomSwitch(
50.00 -> group("Install and delete") {
exec(
extmgrChain.installExtension(),
extmgrChain.deleteExtension(),
)
},
50.00 -> extmgrChain.listExtension()
)
)
}
)
}
That can't work. Gatling EL (the ${} syntax in strings) doesn't magically work anywhere. This is explained in the documentation.
Warning
This Expression Language only works on String values being passed to Gatling DSL methods. Such Strings are parsed only once, when the Gatling simulation is being instantiated.
For example queryParam("latitude", session => "${latitude}") wouldn’t work because the parameter is not a String, but a function that returns a String.
Also, queryParam("latitude", "${latitude}".toInt) wouldn’t because the toInt would happen before passing the parameter to the queryParam method.
The solution here would be to pass a function:
session => session("latitude").validate[Int].
I have a tiny problem when trying to put an item in my second mutable map.
My objective: gather some elements located in many different xml files, and organise them in the hierarchy they belong (the files are an unstructured mess, with categories being given in seemingly no logical order).
Those elements are: the level in hierarchy of the category (1 - x, 1 is top level) as iLevel, the category code as catCode, its name, and if need be, the name of its parents (all names located in namesCategories).
val categoryMap = mutable.Map.empty[Int, mutable.Map[String, IndexedSeq[String]]]
...
//Before: search in a first file links to other files
// for each category file found, will treat it and store it for further treatement.
matches.foreach{f =>
....
//Will search for a specific regex, and for each matches store what we are interested in
matchesCat.foreach{t =>
sCat = t.replaceFirst((system_env + """\S{4}"""), "")
//iLevel given by the number of '/' remaining in the string
iLevel = sCat.count(_ == '/')
//reset catCode and namesCategories
catCode = ""
namesCategories.clear()
//Search and extract the datas from sCat using premade regex patterns
sCat match {
case patternCatCode(codeCat) => catCode = s"$codeCat"
}
//remove the category code to prepare to extract names
sCat.replace(patternCatCode.toString(), "")
//extract names
do {
sCat match {
case patternCatNames(name) => namesCategories += s"$name"
}
sCat.replace(patternCatNames.toString(), "")
}while(sCat!="")
// create the level entry if it doesn't exist
if(!(categoryMap.contains(iLevel))) {
categoryMap.put(iLevel, mutable.Map.empty[String, IndexedSeq[String]])
}
//Try to add my cat code and the names, which must be in order for further treatment, to my map
categoryMap(iLevel).put(catCode, namesCategories.clone())
}
}
}
Problem:
Type mismatch, expected: IndexedSeq[String], actual: mutable.Builder[String, IndexedSeq[String]]
As Travis Brown kindly noted, I have an issue with a type mismatch, but I don't know how to fix that and get the general idea working.
I tried to keep the code to only what is relevant here, if more is needed I'll edit again.
Any tips?
Thanks for your help
There is a web service returning array of something
{"apps": [{"name": "one"}, {"name": "two"}]}
In my code I want to iterate every name
val request = WS.url(s"http://localhost:9000/getData")
val json = request.get.map { response =>
(response.json \ "apps" \\ "name")
}
json.foreach(println)
However all my attempts return single record
// Expect
one
two
// Actual
ListBuffer("one", "two")
First of all, the neat solution here would be:
val request = WS.url(s"http://localhost:9000/getData")
request.get.map { response =>
val names = (response.json \ "apps" \\ "name")
names.foreach(println)
}
Secondly, if you don't want to get confused about the types, you should change your naming standards. For a Future object, you may start with the prefix future, for an Option, it could start with maybe, etc. If you do so, the problem in your example will be more obvious:
val request = WS.url(s"http://localhost:9000/getData")
val futureJson = request.get.map { response =>
(response.json \ "apps" \\ "name")
}
futureJson.foreach(println) // you call foreach for a Future, not for a List
Thirdly, why would Future trait have a method called foreach? I think that's confusing for beginners and even mid-level developers. We know from other languages that, foreach means iterate over a list of objects. In Scala, it is considered part of "Monadic operations" which is still a gray area for me :), but the comment for Future.foreach in Scala source is this:
/** Asynchronously processes the value in the future once the value becomes available.
*
* Will not be called if the future fails.
*/
def foreach[U]
Your value of json is actually a Future[Seq[JsValue]], so when you foreach over the future you get the entire list back. You would need an additional foreach to iterate over the list of values.
I have standard list of objects which is used for the some analysis. The analysis generates a list of Strings and i need to look through the standard list of objects and retrieve objects with same name.
case class TestObj(name:String,positions:List[Int],present:Boolean)
val stdLis:List[TestObj]
//analysis generates a list of strings
var generatedLis:List[String]
//list to save objects found in standard list
val lisBuf = new ListBuffer[TestObj]()
//my current way
generatedLis.foreach{i=>
val temp = stdLis.filter(p=>p.name.equalsIgnoreCase(i))
if(temp.size==1){
lisBuf.append(temp(0))
}
}
Is there any other way to achieve this. Like having an custom indexof method that over rides and looks for the name instead of the whole object or something. I have not tried that approach as i am not sure about it.
stdLis.filter(testObj => generatedLis.exists(_.equalsIgnoreCase(testObj.name)))
use filter to filter elements from 'stdLis' per predicate
use exists to check if 'generatedLis' has a value of ....
Don't use mutable containers to filter sequences.
Naive solution:
val lisBuf =
for {
str <- generatedLis
temp = stdLis.filter(_.name.equalsIgnoreCase(str))
if temp.size == 1
} yield temp(0)
if we discard condition temp.size == 1 (i'm not sure it is legal or not):
val lisBuf = stdLis.filter(s => generatedLis.exists(_.equalsIgnoreCase(s.name)))