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.
Related
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
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.
I use Silhouette and Play 2.4 and I'd like to restrict actions if a SecuredRequest body contains something wrong.
I know, I should use trait Authorization as described by official docs.
I'm trying to do the following:
case class WithCheck(checkCriteria: String) extends Authorization[User, CookieAuthenticator] {
def isAuthorized[B](user: User, authenticator: CookieAuthenticator)(implicit request: Request[B], messages: Messages) = {
Future.successful(user.criteria == checkCriteria)
}
}
and than
def myAction = SecuredAction(WithCheck("bar")) { implicit request =>
val foo = ...// deserialize object from request.body
val checkCriteria = foo.criteria
// do something else here
}
How can I use the checkCriteria value in the class WithCheck?
I found a solution.
Somehow, I was blind to see that isAuthorized has the same request as an implicit parameter. So, the check could be done entirely into the isAuthorized. For example,
case class WithCheck() extends Authorization[User, CookieAuthenticator] {
def isAuthorized[B](user: User, authenticator: CookieAuthenticator)(implicit request: Request[B], messages: Messages) = {
val foo = upickle.read[Foo](request.body.toString())
Future.successful(user.criteria == foo.criteria)
}
}
I wonder, how to get a body from the request if in the doc:
trait RequestHeader extends AnyRef
The HTTP request header. Note that it doesn’t contain the request body yet.
That seems from very 2.0 version ..
Trying to get example of async handing the request's body. In order to log it to file.
object AccessLoggingFilter extends EssentialFilter {
def apply(inputAction: EssentialAction) = new EssentialAction { request =>
val accessLogger = Logger("access")
def apply(requestHeader: RequestHeader): Iteratee[Array[Byte], Result] = { ...
Logger.info(s"""Request:
Body = ${requestHeader.???} """)
There are some philosophical answers on SO, here for example. But I would not call it answer..
Yes, play doesn't allow to access the body of the request in the filter stage.
If you only want to log the body, you can create a new action for that and compose it.
This is an example from play 2.6
def withLogging(action: Action[AnyContent]): Action[AnyContent] = {
Action.async(action.parser) { request =>
request.body match {
case AnyContentAsJson(json) => Logger.info("JSON body was: " + Json.stringify(json))
case _ => //implement more logging of different body types
}
action(request)
}
}
def foo = withLogging(Action.async(cc.parsers.anyContent) { implicit request =>
// do your stuff
}))
If you have only json endpoints, you can write a specific action for that.
Using the rather simple and elegant Scala Dispatch HTTP library. Since the Twitter Search API is now using OAuth 1.0A, I obviously need to start injecting Consumer and AccessToken information. I've got a simple request below:
val request = url("https://api.twitter.com/1.1/search/tweets.json?q=%23%sresult_type=mixed&count=4" format w.queryValue)
val response = Http(request OK as.String)
What's a way to add headers to this if I already know my Consumer and AccessToken information? The documentation is rather scarce. Thanks!
I'm not familiar with the OAuth API, but Dispatch allows you to add arbitrary HTTP Request headers with the <:< method/operator.
So mashing together your code example above and this "Authorizing a request" example from Twitter's Developer site, you might get something like this:
val authentication = """OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog", oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1318622958", oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", oauth_version="1.0""""
val request = url("https://api.twitter.com/1.1/search/tweets.json?q=%23%sresult_type=mixed&count=4" format w.queryValue)
val authHeader = Map("Authentication" -> authentication)
val requestWithAuthentication = request <:< authHeader
val response = Http(requestWithAuthentication OK as.String)
I haven't verified whether this actually works, but hopefully it should get you going in the right direction.
I am doing it like this with dispatch:
private def buildSearchReq(searchTerm: String, lat: Double, long: Double): Req = {
val consumer = new ConsumerKey(consumerKey, consumerSecret)
val requestToken = new RequestToken(token, tokenSecret)
val req = url(searchUrl)
.addQueryParameter("term", searchTerm)
.addQueryParameter("radius_filter", "40000")
.addQueryParameter("ll", s"$lat,$long")
new SigningVerbs(req).sign(consumer, requestToken)
}
You could also do something more like this if you wanted:
private def buildSearchReq(searchTerm: String, lat: Double, long: Double): Req = {
val req = url(searchUrl) <<? Map("term" -> searchTerm, "radius_filter" -> "40000", "ll" -> s"$lat,$long")
new SigningVerbs(req).sign(new ConsumerKey(consumerKey, consumerSecret), new RequestToken(token, tokenSecret))
}
There are probably even more terse ways of doing it, but you get the idea.