I use OkHttp3 in Kotlin as a HTTP client and send Authorization Token Bearer header in request and return the result.
this is my code, but when run it the app closed :
RetrofitInstance.kt
class OAuthInterceptor(
private val tokenType: String,
private val acceessToken: String,
private val branchid : Int,
private val currency : Int,):Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response
{
var request = chain.request()
request = request.newBuilder()
.addHeader("Authorization", "$tokenType $acceessToken")
.addHeader("branchId", "$branchid")
.addHeader("currencyId", "$currency")
.build()
return chain.proceed(request)
}
}
class RetrofitInstance {
companion object {
private val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor(
"Bearer",
"YourToken",
123,
1)
)
.build()
val BASE_URL = "https://url"
fun getRetrofitInstance(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client).addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
}
}
}
AlbumService.Kt
interface AlbumService {
#GET("users")
suspend fun getAlbums() : Response<Albums>
}
Album.kt
class Albums : ArrayList<dataapi>()
AlbumItem.kt
data class dataapi(
#SerializedName("address")
val address: String,
)
Related
I want to know how can I access my token stored in Shared Preferences and pass it in the url when required.
RetrofitClient.kt file :
class OAuthInterceptor(private val tokenType: String, private val access_token: String):
Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", "$tokenType $access_token").build()
return chain.proceed(request)
}
}
val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor("Bearer", access_token))
.build()
val retrofit = Retrofit.Builder()
.baseUrl("___________________")
.client(client)
.build()
object RetrofitClient {
private const val BASE_URL = "_______"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.method(original.method, original.body)
val request = requestBuilder.build()
chain.proceed(request)
}.build()
val instance: MyApi by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(MyApi::class.java)
}
}
Below is my SharedPrefManager file :
class SharedPrefManager private constructor(private val mCtx: Context) {
val user: User
get() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
return User(
sharedPreferences.getString(user.access_token, ""),
sharedPreferences.getString(user.user_id.toString(), "" ),
sharedPreferences.getString(user.user_name, ""),
sharedPreferences.getString(user.status, ""),
sharedPreferences.getString(user.role_id,""),
sharedPreferences.getString(user.phone, "")
)
}
fun saveUser(user: User) {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString("access_token", user.access_token)
editor.putString("id", user.user_id)
editor.putString("username", user.user_name)
editor.putString("status", user.status)
editor.putString("role_id", user.role_id)
editor.putString("phone", user.phone)
editor.apply()
}
fun clear() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.clear()
editor.apply()
}
companion object {
private const val SHARED_PREF = "my_shared_pref"
private var mInstance: SharedPrefManager? = null
#Synchronized
fun getInstance(mCtx: Context): SharedPrefManager {
if (mInstance == null) {
mInstance = SharedPrefManager(mCtx)
}
return mInstance as SharedPrefManager
}
}
}
You should use the name of the save preference when retrieving them.
Try to change your SharedPrefManager get method like this:
get() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
return User(
sharedPreferences.getString("access_token", ""),
sharedPreferences.getString("id", "" ),
sharedPreferences.getString("username", ""),
sharedPreferences.getString("status", ""),
sharedPreferences.getString("role_id",""),
sharedPreferences.getString("phone", "")
)
}
OKHTTP Interceptor not working It gives me 401 Response(Unauthenticated/UnAuthorized)
Here is my Interceptor Class
InterceptorClass
class NetworkInterceptor(context: Context) : Interceptor {
private val applicationContext = context.applicationContext
private val sharedPreferenceData = SharedPreferenceData(applicationContext)
override fun intercept(chain: Interceptor.Chain): Response {
if (!isConnectionAvailable()) {
throw NoConnectionException("Error! Connecting to the network")
} else {
val requestBuilder = chain.request().newBuilder()
val token = sharedPreferenceData.getString("token", "")
requestBuilder.addHeader("Authorization", "Bearer $token")
Log.e("Token", "intercept: $token")
return chain.proceed(requestBuilder.build())
}
}
#Suppress("DEPRECATION")
fun isConnectionAvailable(): Boolean {
val cm =
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
cm.activeNetworkInfo.also {
return it != null && it.isConnected
}
}
}
Here is the network instance class
object NetworkInstance {
fun getApi(context: Context): DataApi {
val gsonBuilder = GsonBuilder()
gsonBuilder.setLenient()
val gson = gsonBuilder.create()
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient
.Builder()
.addInterceptor(logging)
.addInterceptor(NetworkInterceptor(context))
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.addInterceptor(logging)
.build()
val retrofit = Retrofit
.Builder()
.client(client)
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
return retrofit.create(DataApi::class.java)
}
}
The issue was because of my Stupidity. I have added BASE_URL inside build.gradle which generates a Static Final variable inside BuildCondig.java which can't be altered. While Adding header BASE_URL can't be changed.
I have the following controller for which I'm trying to test
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
class UserController(private val userService: UserService) {
#Get("/principal")
fun getPrincipal(principal: Principal): Principal = principal
}
My test looks as the following
class Credentials(val username: String, val password: String)
class LoginResponse(
#JsonProperty("access_token") val accessToken: String,
#JsonProperty("expires_in") val expiresIn: Int,
#JsonProperty("refresh_token") val refreshToken: String,
#JsonProperty("token_type") val tokenType: String,
#JsonProperty("username") val username: String)
#Client("/")
interface Client {
#Post("/login")
fun login(#Body credentials: Credentials): LoginResponse
#Get("/principal")
fun getPrincipal(#Header authorizationHeader: String): Principal
}
#MicronautTest
internal class UserControllerTest {
#Inject
lateinit var authenticationConfiguration: AuthenticationConfiguration
#Inject
lateinit var client: Client
#Test
fun getPrincipal() {
val credentials = Credentials(authenticationConfiguration.testUserEmail, authenticationConfiguration.testUserPassword)
val loginResponse = client.login(credentials)
val authorizationHeader = "Authorization:Bearer ${loginResponse.accessToken}"
val principal = client.getPrincipal(authorizationHeader)
}
}
The login works just fine. I get a bearer token and the authorizationHeader looks just fine. But the call to client.getPrincipal(authorizationHeader) fails with io.micronaut.http.client.exceptions.HttpClientResponseException: Unauthorized
Any clue what goes wrong?
It turns out that I can either declare my client as the following. By naming the argument to match the actual http header.
#Client("/")
interface Client {
...
#Get("/principal")
fun getPrincipal(#Header authorization: String): Principal
}
But it's also possible letting the #Header annotation take an argument specifying which http header to target
#Client("/")
interface Client {
...
#Get("/principal")
fun getPrincipal(#Header("Authorization") authorizationValue: String): Principal
}
I think somethings wrong about my code in TeamImplsTest, and i need advice :D
This is my code
API interface
interface API {
#GET("lookupteam.php")
fun getTeam(#Query("id") id: String): Call<TeamModel>
}
TeamPresenter
interface MatchPresenter {
fun loadTeamDetail(team_id: String)
}
TeamImpls
class TeamImpls(val teamView: TeamView) : TeamPresenter {
override fun loadTeamDetail(team_id: String) {
val call = RetrofitConfig().getApi().getTeam(team_id)
call.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful()) {
val res = response.body()
res?.let { teamView.onSuccess(it) }
}
}
override fun onFailure(call: Call, t: Throwable) {
Log.e("PrevMatchFragment", t.toString())
}
})
}
}
TeamModel
data class TeamModel(
val teams: ArrayList
)
data class TeamModeLResult(
val idTeam: String,
val strTeam: String,
val strAlternate: String,
val strSport: String,
val strStadium: String,
val strTeamBadge: String
)
And
This my TeamImplsTest
class TeamImplsTest {
#Mock
private lateinit var teamView: TeamView
#Mock
private lateinit var teamPresenter: TeamPresenter
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
teamPresenter = TeamImpls(teamView)
}
#Test
fun loadTeamDetail() {
val teams = TeamModel(arrayListOf())
val teamId = "133613"
teamPresenter.loadTeamDetail(teamId)
Mockito.verify(teamView).onSuccess(teams)
}
}
i got error
Wanted but not invoked:
teamView.onSuccess(TeamModel(teams=[]));
-> at com.fathurradhy.matchschedule.domain.presenter.TeamImplsTest.loadTeamDetail(TeamImplsTest.kt:34)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
teamView.onSuccess(TeamModel(teams=[]));
-> at com.fathurradhy.matchschedule.domain.presenter.TeamImplsTest.loadTeamDetail(TeamImplsTest.kt:34)
Actually, there were zero interactions with this mock.
You're not mocking the API call as loadTeamDetail creates its own API instance.
To enable you to test the API call behaviour you could provide the API instance through your constructor, e.g.
class TeamImpls(private val api: API, private val teamView: TeamView) : TeamPresenter {
override fun loadTeamDetail(team_id: String) {
val call = api.getTeam(team_id)
This would then allow you to mock the api behaviour and verify the presenter calls the correct method when the call fails/succeeds, e.g.
class TeamImplsTest {
#Mock
private lateinit var teamView: TeamView
#Mock
private lateinit var api: API
#Mock
private lateinit var teamPresenter: TeamPresenter
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
teamPresenter = TeamImpls(api, teamView)
}
#Test
fun loadTeamDetail() {
val teams = TeamModel(arrayListOf())
val teamId = "133613"
// Use retrofit-mock to create your mockResponse.
// See: https://github.com/square/retrofit/tree/master/retrofit-mock
Mockito.`when`(api.getTeam(teamId)).thenReturn(Calls.response(teams)
teamPresenter.loadTeamDetail(teamId)
Mockito.verify(teamView).onSuccess(teams)
}
}
I know there isn't a static function in Kotlin, so I write two code in OkHttpService.kt and my.kt
I don't know which is better, could you tell me? Thanks!
OkHttpService.kt
class OkHttpService {
companion object {
fun httpGet(username: String, callback: Callback) {
val fetchRepoUrl = "https://api.github.com/users/$username/repos?page=1&per_page=20"
val client = OkHttpClient()
val request = Request.Builder()
.url(fetchRepoUrl)
.build()
client.newCall(request).enqueue(callback)
}
}
}
my.kt
fun OkHttpService_httpGet(username: String, callback: Callback) {
val fetchRepoUrl = "https://api.github.com/users/$username/repos?page=1&per_page=20"
val client = OkHttpClient()
val request = Request.Builder()
.url(fetchRepoUrl)
.build()
client.newCall(request).enqueue(callback)
For scoping use a regular object, not companion:
object OkHttpService{
fun httpGet(username: String, callback: Callback) {
val fetchRepoUrl = "https://api.github.com/users/$username/repos?page=1&per_page=20"
val client = OkHttpClient()
val request = Request.Builder()
.url(fetchRepoUrl)
.build()
client.newCall(request).enqueue(callback)
}
}