Scala testing Web Service Client for POST request - scala

So the Play documentation you can find here gives a very simple example for a GET call without anything like auth header or parameters.
Could somebody help me figure out how to use a similar strategy for something more complex like a POST request with JSON data as body and auth header required? I can't get it to work right now.
I want to know how to test a client using ws to do it's external http requests.
Thanks

Here is a snippet of code from one of my projects which sends sms via twilio api:
class SmsServiceImpl #Inject()(config: Configuration, ws: WSClient) extends SmsService {
val twilloAccountSid = config.getString("twillo.accountSid").get
val twilloAuthToken = config.getString("twillo.authToken").get
val smsApiUrl = config.getString("twillo.apiBaseUrl").get + "/" +twilloAccountSid+"/Messages.json"
override def send(phone: String, message: String): Future[Unit] = {
val data = Map(
"To" -> Seq(phone),
"From" -> Seq(config.getString("twillo.fromPhone").get),
"Body" -> Seq(message)
)
val response: Future[WSResponse] = ws.url(smsApiUrl).withAuth(twilloAccountSid, twilloAuthToken, WSAuthScheme.BASIC).post(data)
response.map { response =>
response.status match {
case 201 => Unit
case _ => throw new SmsSendingException(s"Failed to send sms. Got https status: ${response.status}")
}
}
}
It is POST request with authentication.

Related

ScalaPlay > 2.6 how to access POST requests while faking a trivial server in tests

I'm trying to setup a fake server with Play2.7 and the environment suggested by https://developer.lightbend.com/guides/play-rest-api/ just echoing json from a POST request. While I was able to make GET and POST requests returning hardwired values I can't access directly the request to return or process it. NOTE: this was doable with versions < 2.6 but now Action has become deprecated, so I'm wondering which is the correct way to deal with this in Play >= 2.6
I have read the following how to mock external WS API calls in Scala Play framework and How to unit test servers in Play 2.6 now that Action singleton is deprecated which are actually doing almost all I am trying to do, but it seems I need something different to access the Request. In previous version of Play I could do something like the following:
case POST(p"/route") => Action { request => Ok(request.body.asJson.getOrElse(JsObject.empty)) }
But it seems calling the action this way is not more possible since I received the 'infamous'
object Action in package mvc is deprecated: Inject an ActionBuilder (e.g. DefaultActionBuilder) or extend BaseController/AbstractController/InjectedController
error.
my actual working code is
object FakeServer {
def withServerForStep1[T](codeBlock: WSClient => T): T =
Server.withRouterFromComponents() { cs =>
{
case POST(p"/route") =>
cs.defaultActionBuilder {
Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
}
}
} { implicit port =>
WsTestClient.withClient(codeBlock)
}
}
and the unit Spec is something like
"The step 1" should {
"Just call the fakeservice" in {
setupContext()
FakeServer.withServerForStep1 ( {
ws =>
val request = ws.url("/route")
val data = Json.obj(
"key1" -> "value1",
"key2" -> "value2"
)
val response = request.post(data).futureValue
response.status mustBe 200
response.body mustBe Json.toJson(data)
})
}
}
I would like to write the FakeServer in such a way that the Spec will succeed in checking that returned body is equal to original sent json. Currently it is obviously failing with
"[{"full_name":"octocat/Hello-World"}]" was not equal to {"key1":"value1","key2":"value2"}
I eventually found how to do it, and the correct way as often happens in Scala is... trivial.
The "trick" was just to add request => in the body of cs.defaultActionBuilder as in the next example
object FakeServer {
def withServerForStep1[T](codeBlock: WSClient => T): T =
Server.withRouterFromComponents() { cs =>
{
case POST(p"/route") =>
cs.defaultActionBuilder { request =>
val bodyAsJson = request.body.asJson.getOrElse(JsObject.empty)
Results.Ok(bodyAsJson)
}
}
} { implicit port =>
WsTestClient.withClient(codeBlock)
}
}
Then the test just needed to deal with possible extra wrapping quotes and reads as
val response = request.post(data).futureValue
response.status mustBe 200
response.body mustBe Json.toJson(data).toString()

What are ways to unit test Rest API clients - i.e. testing that the right Rest requests are sent

At work we're developing a service that sits between two other services. We expose a Rest API which is called by some service -- the request is processed by some logic and then, according to the logic, HTTP requests will be sent out another service.
There're plenty of resources on the internet on how to best test API endpoints you provide. I, on the other hand want to test what API requests are sent out, without sending them to the actual service.
I guess I could always set up an entire server skeleton on localhost:8080 that just records what it receives but this feels kinda dirty. Testing the Rest API we provide for external services (we use akka-http to do that) is pretty comfortable to test with akka-http-testkit which is excellent. I was just wondering if there is any comparably comfortable to use tooling to test what Http reqeusts go out.
Functional Programming "Tooling"
The easiest way I've found to test these scenarios is to use plain-old functional programming principles in your design. You can embed your Route creation within a higher order function. This higher order function will take in the function which queries the downstream service:
type ComputedData = ???
val computeData : HttpRequest => ComputedData = ???
def intermediateRoute(downstreamService : ComputedData => Future[HttpResponse]) : Route =
extractRequest { request =>
val computedData : ComputedData = computeData(request)
complete(downstreamService(computedData))
}
This higher order function can now be used in Production:
val queryDownStreamService : ComputedData => Future[HttpResponse] = ???
val productionRoute : Route = intermediateRoute(queryDownStreamService)
Or, it can be used in unit-testing to ensure the logic is correct:
val testComputedData : ComputedData => Boolean = ???
val testResponse : HttpResponse = ???
val testService : ComputedData => Future[HttpResponse] = (computedData) => {
assert(testComputedData(computedData))
Success(testResponse)
}
val testRoute = intermediateRoute(testService)
Get("/test") ~> testRoute ~> check {
response should be testResponse
}
We do it the way you call dirty, though I don't think it's dirty.
we have a base trait that starts/shuts down the server (we use http4s and scalatest)
trait EmbeddedServer extends BeforeAndAfterAll with Http4sDsl[IO] {
self: Suite =>
private var server: Server[IO] = _
protected var lastRequest: Request[IO] = _
private def captureRequest: HttpService[IO] = Kleisli { req: Request[IO] =>
lastRequest = req
service(req)
}
override protected def beforeAll(): Unit = {
server = BlazeBuilder[IO]
.bindAny()
.mountService(captureRequest, "/")
.start
.unsafeRunSync()
super.beforeAll()
}
override protected def afterAll(): Unit = {
super.afterAll()
server.shutdownNow()
}
def address: InetSocketAddress = server.address
def rootURI: String = s"http:/$address"
def service: HttpService[IO]
}
then we mix it in our client spec
something along these lines
class SomeRequesterSpec extends WordSpec with EmbeddedServer {
override def service: HttpService[IO] = HttpService[IO] {
case GET -> Root / "failure" => ServiceUnavailable()
case GET -> Root / "success" => Ok(SuccessBody)
case GET -> Root / "partial-success" => Ok(PartialSuccessBody)
case GET -> Root / "malformed" => Ok(MalformedBody)
case GET -> Root / "empty" => Ok(EmptyResponse)
}
//... you specs go here
}
and in you specs you call your mocked server with your client
using s"$rootURI/success" or s"$rootURI/failure" endpoints and check that it handles responses correctly.
Also lastRequest var always has last request issues, so you can run assertions against it, like
lastRequest.headers should contain(`Accept-Encoding`(ContentCoding.gzip))
This approach works very well for us and we can test that our clients handle all sorts of outputs from servers as well as all manipulations with request they do

Handling Future[WSResponse] to find success or error state

In Scala I have a call to service in controller which is returning me Future[WSResponse]. I want to make sure service is returning valid result so send Ok(..) otherwise send BadRequest(...). I don't think I can use map. Any other suggestion?
def someWork = Action.async(parse.xml) { request =>
val result:Future[WSResponse] = someService.processData(request.body.toString())
//Need to send back Ok or BadRequest Message
}
EDIT
Solution from #alextsc is working fine. Now moving to test my existing test is failing. It is getting 400 instead of 200.
test("should post something") {
val requestBody = <value>{UUID.randomUUID}</value>
val mockResponse = mock[WSResponse]
val expectedResponse: Future[WSResponse] = Future.successful(mockResponse)
val request = FakeRequest(Helpers.POST, "/posthere").withXmlBody(requestBody)
when(mockResponse.body).thenReturn("SOME_RESPONSE")
when(someService.processData(any[String])).thenReturn(expectedResponse)
val response: Future[Result] = call(controller.someWork , request)
whenReady(response) { response =>
assert(response.header.status == 200)
}
}
You're on the right track and yes, you can use map.
Since you're using Action.async already and your service returns a future as it stands all you need to do is map that future to a Future[Result] so Play can handle it:
def someWork = Action.async(parse.xml) { request =>
someService.processData(request.body.toString()).map {
// Assuming status 200 (OK) is a valid result for you.
case resp : WSResponse if resp.getStatus == 200 => Ok(...)
case _ => BadRequest(...)
}
}
(I note that your service returns WSResponse (from the play ws java library) and not play.api.libs.ws.Response (the scala version of it), hence getStatus and not just status)

Endpoint requires authentication Rest API Spray

I would like to consume some resources from This Rest API
But it seems that I need some Authorization. And actually as a beginner I don't know how to access to this endpoint using Spray.
A simple example of Spray Client:
val pipeline = sendReceive ~> unmarshal[GuildwarsApiResult]
val responseFuture = pipeline {
Get("https://api.guildwars2.com/v2/commerce/listings/46741")
}
Thanks !
I see a lot of questions lately from people who want to use API of GuilWars. Was it recently created ?
As per your question you need to specify HTTP header with API token as stated in documentation for POST requests. For GET requests you should pass token in URL.
This shows how to create and use custom http header.
object ApiTokenHeader extends ModeledCustomHeaderCompanion[ApiTokenHeader] {
def renderInRequests = false
def renderInResponses = false
override val name = "Authorization"
override def parse(value: String) = Try(new ApiTokenHeader(value))
}
final class ApiTokenHeader(token: String) extends ModeledCustomHeader[ApiTokenHeader] {
def renderInRequests = false
def renderInResponses = false
override val companion = ApiTokenHeader
override def value: String = "Bearer " + token
}
and use it as
val responseFuture = pipeline {
Get("https://api.guildwars2.com/v2/commerce/listings/46741").withHeaders(ApiTokenHeader("55eec993e046c63bc8f486ee"))
}
I didn't compile it but it should work.

Return exact response/header?

From the client-side of a webapp, I hit a server-side route which is just a wrapper for a third-party API. Using dispatch, I am trying to make that server-side request return the exact header and response of the third-party API to the client-side AJAX call.
When I do this:
val req = host("third-pary.api.com, 80)
val post = req.as("user", "pass") / "route" << Map("key" -> "akey", "val" -> "aval")
Http(post > as.String)
I always see a 200 response returned to the AJAX call (kind of expectedly). I have seen an Either syntax used, but I'm really more of an Any, as it's just the exact response and header. How would this be written?
I should mention I'm using Scalatra on the server-side, so the local route is:
post("/route") {
}
EDIT:
Here is the suggested Either matching example, which I'm playing with, but the match syntax doesn't make sense - I don't care if there is an error, I just want to return it. Also, I can't seem to get the BODY returned with this method.
val asHeaders = as.Response { response =>
println("BODY: " + response.getResponseBody())
scala.collection.JavaConverters.mapAsScalaMapConverter(
response.getHeaders).asScala.toMap.mapValues(_.asScala.toList)
}
val response: Either[Throwable, Map[String, List[String]]] =
Http(post > asHeaders).either()
response match {
case Left(wrong) =>
println("Left: " + wrong.getMessage())
// return Action with header + body
case Right(good) =>
println("Right: " + good)
// return Action with header + body
}
Ideally, the solutions returns the Scalatra ActionResult(responseStatus(status, reason), body, headers).
It's actually very easy to get response headers while using Dispatch. For example with 0.9.4:
import dispatch._
import scala.collection.JavaConverters._
val headers: java.util.Map[String, java.util.List[String]] = Http(
url("http://www.google.com")
)().getHeaders
And now, for example:
scala> headers.asScala.mapValues(_.asScala).foreach {
| case (k, v) => println(k + ": " + v)
| }
X-Frame-Options: Buffer(SAMEORIGIN)
Transfer-Encoding: Buffer(chunked)
Date: Buffer(Fri, 30 Nov 2012 20:42:45 GMT)
...
If you do this often it's better to encapsulate it, like this, for example:
val asHeaders = as.Response { response =>
scala.collection.JavaConverters.mapAsScalaMapConverter(
response.getHeaders
).asScala.toMap.mapValues(_.asScala.toList)
}
Now you can write the following:
val response: Either[Throwable, Map[String, List[String]]] =
Http(url("http://www.google.com") OK asHeaders).either()
And you've got error checking, nice immutable collections, etc.
We needed the response body of failed requests to an API, so we came up with this solution:
Define your own ApiHttpError class with code and body (for the body text):
case class ApiHttpError(code: Int, body: String)
extends Exception("Unexpected response status: %d".format(code))
Define OkWithBodyHandler similar to what is used in the source of displatch:
class OkWithBodyHandler[T](f: Response => T) extends AsyncCompletionHandler[T] {
def onCompleted(response: Response) = {
if (response.getStatusCode / 100 == 2) {
f(response)
} else {
throw ApiHttpError(response.getStatusCode, response.getResponseBody)
}
}
}
Now, near your call to the code that might throw and exception (calling API), add implicit override to the ToupleBuilder (again similar to the source code) and call OkWithBody on request:
class MyApiService {
implicit class MyRequestHandlerTupleBuilder(req: Req) {
def OKWithBody[T](f: Response => T) =
(req.toRequest, new OkWithBodyHandler(f))
}
def callApi(request: Req) = {
Http(request OKWithBody as.String).either
}
}
From now on, fetching either will give you the [Throwable, String] (using as.String), and the Throwable is our ApiHttpError with code and body.
Hope it helped.