I am trying to call an API using Retrofit in an Android application using Kotlin. The API requires a a header and body similar to the following sample input:
Sample Input
Header:
loginName: 65848614-6697-4cf7-a64a-e0b9374c4aee
Body:
clientId: DKOKTrykIQ987yQcLNehT8SWJRMyQLdP secret: 6Jt1ENlDn9gxPu5f
The content type has to be passed as application/x-www-form-urlencoded.
Currently, I am using the following classes:
YodleeService.kt
interface YodleeService {
@Headers(
"loginName: de5559cc-5375-4aca-8224-990343774c08_ADMIN",
"Api-Version: 1.1",
"Content-Type: application/x-www-form-urlencoded"
)
@POST("auth/token")
fun generateLoginToken(
@Body postBody: PostBody
) : Call<LoginToken>
}
AccountRetriever.kt
class AccountRetriever {
private val service: YodleeService
companion object {
const val BASE_URL = "https://sandbox.api.yodlee.com:443/ysl/"
}
init {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
service = retrofit.create(YodleeService::class.java)
}
fun login(callback: Callback<LoginToken>) {
val postBody = PostBody("TG86ljAk6lt28GYZlRTQWssaLmGpS6jV", "A2P9ZPEqB4uos1nv")
val call = service.generateLoginToken(postBody)
call.enqueue(callback)
}
}
PostBody
data class PostBody(
val clientId: String?,
val secret: String?
)
MainActivity
class MainActivity : AppCompatActivity() {
private val accountRetriever = AccountRetriever()
private val loginCallback = object : Callback<LoginToken> {
override fun onFailure(call: Call<LoginToken>, t: Throwable) {
Log.e("MainActivity", "Problem calling Yodlee API {${t.message}")
}
override fun onResponse(call: Call<LoginToken>?, response: Response<LoginToken>?) {
response?.isSuccessful.let {
Log.i("MainActivity", "errorBody - Content = ${response?.raw()}")
val loginToken = LoginToken(
response?.body()?.accessToken ?: "",
response?.body()?.expiresIn ?: "",
response?.body()?.issuedAt ?: "")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
accountsList.layoutManager = LinearLayoutManager(this)
if (isNetworkConnected()) {
accountRetriever.login(loginCallback)
} else {
AlertDialog.Builder(this).setTitle("No Internet Connection")
.setMessage("Please check your internet connection and try again")
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setIcon(android.R.drawable.ic_dialog_alert).show()
}
}
private fun isNetworkConnected(): Boolean {
val connectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
return networkCapabilities != null &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}
When I debug my application, I am getting a response of [size=213 text=\n {\n "errorCode": "Y303",\n …]
. The documentation for the API says that this error code means the clientId or secret is missing.
When I dig through the debugger, I am seeing that the raw call reads as
Request {
method = POST, url = https: //sandbox.api.yodlee.com/ysl/auth/token,
tags = {
class retrofit2.Invocation =
com.example.budgettracker.api.YodleeService.generateLoginToken()[PostBody(
clientId = TG86ljAk6lt28GYZlRTQWssaLmGpS6jV, secret = A2P9ZPEqB4uos1nv)]
}
}
I cannot determine why the API is not seeing the POST body contents. Any help would be greatly appreciated.