How to get result from WS call in play framework/scala? - web-services

I have a big trouble with getting value from WS.url call.
val x =WS.url("https://www.google.com/recaptcha/api/siteverify?
secret=XX&response="+captcha).get().map {
response =>response.body}
When i try
Console.println("X: "+x)
I don't have expected value but:
X: scala.concurrent.impl.Promise$DefaultPromise#e17c7c
BUT, when i try to print value println(response.body) inside map function it works fine.
I also tried playframework tutorial but the same results.
So, how can I assign result of GET call into some variable?

Please don't assemble your own query string, use the withQueryString method.
There are two solutions to your problem: blocking and non-blocking. Blocking will mean that your request's thread will idle until the HTTP call completes. Non-blocking is preferred and you can provide Play with a Future to complete the request with. All you have to do is instead of Action use Action.async in your controller.
val captchaResponse: Future[String] =
WS.url("https://www.google.com/recaptcha/api/siteverify")
.withQueryString("secret" -> "XX", "response" -> "captcha")
.get()
.map(_.body)
// Non-blocking solution:
captchaResponse.map {
body =>
Console.println("X: " + body)
Ok(views.html.page(body.toBoolean))
}
// Blocking solution:
import scala.concurrent.duration._
val x = Await.result(captchaResponse, 3.seconds)
Console.println(x)

Related

actix web test doesn't seem to be routing requests as expected

I recently updated to actix web 4, I had some tests that used the actix-web test module that stopped working as expected in the process. I'm sure it's something simple but I'm having trouble figuring out what changed. Here is a minimal example of the issue:
use actix_web::{test, web, App, HttpResponse, HttpRequest};
#[actix_rt::test]
async fn says_hello() {
let req = test::TestRequest::get().uri("/index.html").to_request();
let mut server =
test::init_service(App::new().service(web::scope("/").route("index.html", web::get().to(|_req: HttpRequest| async {
println!("Hello?");
HttpResponse::Ok()
})))).await;
let _resp = test::call_and_read_body(&mut server, req).await;
}
running this test I would expect to see "Hello?" output to my console, however, the request handler function I have defined at "/index.html" doesn't seem to be called and I receive no output.
To be clear, the tests are more complicated and have assertions etc, this is just a working example of the main issue I am trying to resolve
actix-web = { version = "4.1.0", default-features = false }
note:
if I change all paths to the root path it will call the handler, I.E.
let req = test::TestRequest::get().uri("/").to_request();
let mut server =
test::init_service(App::new().service(web::scope("/").route("/", web::get().to(|_req: HttpRequest| async {
println!("Hello?");
HttpResponse::Ok()
})))).await;
let _resp = test::call_and_read_body(&mut server, req).await;
// prints "Hello?" to the console
However no other route combination I have tried calls the request handler.
Rust tests capture the output and only output them for failed tests.
If you want to show output on all tests you have to tell them to do so with either testbinary --nocapture or cargo test -- --nocapture.
I was able to make things work by changing the path in the scope to an empty string
let req = test::TestRequest::get().uri("/index.html").to_request();
let mut server =
test::init_service(App::new().service(web::scope("").route("index.html", web::get().to(|_req: HttpRequest| async {
println!("Hello?");
HttpResponse::Ok()
})))).await;
let _resp = test::call_and_read_body(&mut server, req).await;
// prints "Hello?"

Attempting to put a delay in a loop in Postman

I'm trying to put a 1 second delay using setTimeout(()=>{},1000) in the Pre-request Script for a Postman POST call.
var moment = require('moment');
var tap1TimeStr = pm.environment.get("now");
var tap1TimeMoment = moment(tap1TimeStr,"YYYY-MM-DDTHH:mm:ss");
var expTap2Time = tap1TimeMoment.add(2, 'minutes').format("YYYY-MM-DDTHH:mm:ss");
console.log("Tap 2 timestamp should be: " + expTap2Time);
var timestamp;
var timecheck = false;
while(!timecheck)
{
setTimeout(() => {},1000);
timecheck = moment.utc().isSame(expTap2Time);
console.log("timecheck: " + timecheck);
timestamp = moment.utc().format("YYYY-MM-DDTHH:mm:ss");
}
console.log("Timestamp is now: " + timestamp);
pm.environment.set("now", timestamp);
But it doesn't seem to work and I can see that the console.log line is being printed far more frequently than 1sec. And the exercise here is to send the "Tap 2" POST exactly 2mins after the first POST (tracked by the 'now' variable). Also, it seems like Postman takes a fair bit of time before it even starts executing this particular script.
Edit: The main requirement here is to send the "Tap 2" POST request exactly 2mins AFTER the "Tap 1" POST request. HOW best to implement that? Espcially if setTimeout() is non-blocking and thus probably can't be used in a loop.
Anyone has any ideas?
setTimeout() takes a callback function which is executed after the specified delay so only what happens in the callback function will happen after that delay.
setTimeout(() => {
console.log("This will be executed after 1 second");
}, 1000);
console.log("This will immediately be executed");
setTimeout() is asynchronous and non-blocking so JavaScript will call set timeout but not wait for 1 second for it to return and instead immediately move on to the next instruction. Only after that 1 second has passed the callback passed to setTimeout() will be scheduled and executed. Have a look at this YouTube video for a good explanation of what's going on.

Using Thread.sleep() inside an foreach in scala

I've a list of URLs inside a List.
I want to get the data by calling WS.url(currurl).get(). However, I want add a delay between each request. Can I add Thread.sleep() ? or is there another way of doing this?
one.foreach {
currurl => {
import play.api.libs.ws.WS
println("using " + currurl)
val p = WS.url(currurl).get()
p.onComplete {
case Success(s) => {
//do something
}
case Failure(f) => {
println("failed")
}
}
}
}
Sure, you can call Thread.sleep inside your foreach function, and it will do what you expect.
That will tie up a thread, though. If this is just some utility that you need to run sometimes, then who cares, but if it's part of some server you are trying to write and you might tie up many threads, then you probably want to do better. One way you could do better is to use Akka (it looks like you are using Play, so you are already using Akka) to implement the delay -- write an actor that uses scheduler.schedule to arrange to receive a message periodically, and then handle one request each time the message is read. Note that Akka's scheduler itself ties up a thread, but it can then send periodic messages to an arbitrary number of actors.
You can do it with scalaz-stream
import org.joda.time.format.DateTimeFormat
import scala.concurrent.duration._
import scalaz.stream._
import scalaz.stream.io._
import scalaz.concurrent.Task
type URL = String
type Fetched = String
val format = DateTimeFormat.mediumTime()
val urls: Seq[URL] =
"http://google.com" :: "http://amazon.com" :: "http://yahoo.com" :: Nil
val fetchUrl = channel[URL, Fetched] {
url => Task.delay(s"Fetched " +
s"url:$url " +
s"at: ${format.print(System.currentTimeMillis())}")
}
val P = Process
val process =
(P.awakeEvery(1.second) zipWith P.emitAll(urls))((b, url) => url).
through(fetchUrl)
val fetched = process.runLog.run
fetched.foreach(println)
Output:
Fetched url:http://google.com at: 1:04:25 PM
Fetched url:http://amazon.com at: 1:04:26 PM
Fetched url:http://yahoo.com at: 1:04:27 PM

Play 2.1: unit testing EssentialActions

I want to unit test a controller method that returns an EssentialAction. I pass a FakeRequest to it, and get back a Iteratee[Array[Byte], Result].
It looks like the test helpers contentAsString, contentType and status do not accept this result type.
Is there an implicit conversion I am missing? Is there an example somewhere of controllers being unit tested without bringing up an entire FakeApplication?
An essential action is a RequestHeader => Iteratee[Indata, Result], you can apply it to FakeRequest since it implements RequestHeader. To actually execute the iteratee you either stuff it with data or just tell it right away that there is no more indata. For both those cases you get a Future[Result] back which you need to wait for in the tests.
So, for a simple GET with no request body (using the play test helper await method) you could do it like this:
val iteratee = controllers.SomeController.action()(FakeRequest())
val result: Result = await(iteratee.run)
If you want to do requests with request bodies you will have to do some more stuff to be able to feed the request body to the iteratee and also take care of encoding data your indata correctly.
In Play 2.3, PlaySpecification includes a couple of helper methods. In order to handle EssentialActions, you'd use call. The resulting future is handled by other more specific helpers.
class MySpec extends PlaySpecification {
...
val result1: Result = call(controllers.SomeController.action(), FakeRequest(...))
status(of = result1) must equalTo (OK)
...
val result2 = call(controllers.SomeController.action(), RequestHeader(...), "Body")
status(of = result2) must equalTo (BAD_REQUEST)
}

Call multiple webservices from play 2

I am a play2.0-Scala-beginner and have to call several Webservices to generate a HTML page.
After reading the The Play WS API page and a very interesting article from Sadek Drobi I am still unsure what's the best way to accomplish this.
The article shows some code snippets which I don't fully understand as a Play beginner.
Figure 2 on page 4:
val response: Either[Response,Response] =
WS.url("http://someservice.com/post/123/comments").focusOnOk
val responseOrUndesired: Either[Result,Response] = response.left.map {
case Status(4,0,4) => NotFound
case Status(4,0,3) => NotAuthorized
case _ => InternalServerError
}
val comments: Either[Result,List[Comment]] =
responseOrUndesired.right.map(r => r.json.as[List[Comment]])
// in the controller
comment.fold(identity, cs => Ok(html.showComments(cs)))
What does the last line with the fold do? Should comment be comments? Haven't I group the last statement in an Async block?
Figure 4 shows how to combine several IO calls with a single for-expression:
for {
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield Json.obj(
"profile" -> profile,
"events" -> events,
"articles" -> articles )
}
// in the controller
def showInfo(...) = Action { rq =>
Async {
actorInfo(...).map(info => Ok(info))
}
}
How can I use this snippet? (I am a bit confused by the extra-} after the for-expression.)
Should I write something like this?
var actorInfo = for { // Model
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield Json.obj(
"profile" -> profile,
"events" -> events,
"articles" -> articles )
def showInfo = Action { rq => // Controller
Async {
actorInfo.map(info => Ok(info))
}
}
What's the best way to combine the snippets from figure 2 and 4 (error handling + composition of IO non-blocking calls)? (f.ex. I want to produce a Error 404 status code if any of the called webservice produce an Error 404).
Maybe someone knows a complete example of calling webservices in the play framework (cannot find an example in the play Sample applications or anywhere else).
I have to say that the article is wrong in the example you show in Figure 2. The method focusOnOk does not exist in Play 2.0. I assume the author of the article used a pre-release version of Play 2 then.
Regarding comment, yes it should be comments. The fold in the statement is operating on an Either. It takes 2 functions as parameters. The first is a function to apply if it is a left value. The second is a function to apply if it is a right value. A more detailed explanation can be found here: http://daily-scala.blogspot.com/2009/11/either.html
So what the line does is. If I have a left value (which meant I got an undesired response), apply the built-in identity function which just gives you back the value. If it has a right value (which means I got an OK response), make a new result that shows the comments somehow.
Regarding Async, it's not actually asynchronous. focusOnOk is a blocking function (a remnant from the old Java days of Play 1.x). But remember, that's not valid Play 2 code.
As for Figure 4, the trailing } is actually because it's a partial alternative of what's in Figure 3. Instead of the numerous promise flatMaps. You can do a for comprehension instead. Also, I think it should be userInfo(...).map instead of actorInfo(...).map.
The Play documentation you linked to actually already shows you a full example.
def feedTitle(feedUrl: String) = Action {
Async {
WS.url(feedUrl).get().map { response =>
Ok("Feed title: " + (response.json \ "title").as[String])
}
}
}
will get whatever is at feedUrl, and you map it to do something with the response which has a status field you can check to see if it was a 404 or something else.
To that end, the Figure 3 and 4 of your linked article should give you a starting point. So you'd have something like,
def getInfo(...) : Promise[String] = {
val profilePromise = WS.url(...).get()
val attachedEventsPromise = WS.url(...).get()
val topArticlesPromise = WS.url(...).get()
for {
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield {
// or return whatever you want
// remember to change String to something else in the return type
profile.name
}
}
def showInfo(...) = Action { rq =>
Async {
getInfo(...).map { info =>
// convert your info to a Result
Ok(info)
}
}
}