4

I am trying to use scala dispatch to access the Rdio API like this:

import dispatch.url
import dispatch.Http
import dispatch.Defaults.executor
import dispatch._
import com.ning.http.client.oauth.ConsumerKey
import dispatch.oauth._

val consumer = new ConsumerKey("my key", "my secret")
val params = Map("method" -> "get", "keys" -> id, "extras" -> "-*,playCount")
val request = url("http://api.rdio.com/1/").POST <@ consumer << params <:< Map("Accept" -> "application/json")
val response = Http(request OK as.String)

I am getting an error 403.

What is wrong? I am sure my keys are correct.

Urist McDev
  • 498
  • 3
  • 14
Daniel Cukier
  • 11,502
  • 15
  • 68
  • 123
  • Not familiar with Scala, but I work at Rdio. Are you passing these parameters in the body of the POST request or in the query string? The body of the request is the correct thing to do. Also it's recommended to use pass the OAuth credentials via the Authorization header. – devin_s Mar 28 '14 at 23:11
  • Yes, the << params does exactly this: add params to the POST body. The Authentication headers were supposed to be passed by adding the <@ method with keys – Daniel Cukier Mar 29 '14 at 21:54
  • Which version of dispatch are you using? – alcarv Dec 11 '14 at 14:06
  • I use dispatch 0.11 version – Daniel Cukier Dec 12 '14 at 16:21

1 Answers1

1

I've analysed the difference between the python example on the rdio page, and what Scala does.

I think there are really two problems.

  1. The first is that you need to obtain an access token.

  2. The second issue is that clearly the sign method of dispatch library makes rdio unhappy. It removes trailing slashes, which makes the signature not to match.

The first problem, is quite easy to solve - you just have to use Exchange class, which will do most of the work for you.

The second problem was quite tricky, I've just copied the original sign method and altered the part, which was removing a trailing slash.

The code is below.

val ck = new ConsumerKey("YOUR_CONSUMER_KEY", "YOUR_CONSUMER_TOKEN")

// declare exchange to obtain an access token
val exchange = new Exchange with SomeHttp with SomeCallback with SomeConsumer with SomeEndpoints {

  override def http: HttpExecutor = Http

  override def callback: String = "oob"

  override def consumer: ConsumerKey = ck

  override def accessToken: String = "http://api.rdio.com/oauth/access_token"

  override def authorize: String = "http://api.rdio.com/oauth/authorize"

  override def requestToken: String = "http://api.rdio.com/oauth/request_token"
}

/// we change the default method of the dispatch
def sign(request: Req, consumer: ConsumerKey, token: RequestToken): Req = {
  val calc = new OAuthSignatureCalculator(consumer, token)
  request underlying { r =>
    val req = r.build
    //!!! here we make change so the trailing slash is not removed
    val baseurl = req.getURI.toString.takeWhile(_ != '?').mkString("")
    calc.calculateAndAddSignature(baseurl, req, r)
    r
  }
}

val response = exchange.fetchRequestToken.right.flatMap { rt =>

  // point your browser to this URL with the given oauth token, we'll get PIN back
  println(s"Go to https://www.rdio.com/oauth/authorize/?oauth_callback=oob&oauth_token=${rt.getKey}")
  print("Enter PIN:")
  val pin = readLine()

  exchange.fetchAccessToken(rt, pin)
}.right.flatMap { at =>
  // now we can call API using the consumer key and the access token
  val request = sign(url("http://api.rdio.com/1/").POST << Map("method" -> "currentUser"), ck, at)
  val response = Http(request > as.String)
  response.map(Right(_))
}

response.map { responseOrError =>
  responseOrError.fold(err => println(s"Error $err"), suc => println(s"Response: $suc"))
  Http.shutdown()
}
lpiepiora
  • 13,659
  • 1
  • 35
  • 47
  • I am not using user specific data, only open metadata from tracks. I suppose I don't need to get the token. I found a solution by using Rdio Simple Java Version (https://github.com/rdio/rdio-simple/tree/master/java), giving up from dispatch. – Daniel Cukier Dec 16 '14 at 18:05