use interceptor, remove arguments from api calls

This commit is contained in:
Matthieu 2021-04-18 22:32:50 +02:00
parent 2ef017aad3
commit dfb452113d
17 changed files with 91 additions and 107 deletions

View File

@ -185,16 +185,17 @@ class MainActivity : BaseActivity() {
}
private fun getUpdatedAccount() {
if (hasInternet(applicationContext)) {
val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.orEmpty()
val refreshToken = user?.refreshToken
val clientId = user?.clientId.orEmpty()
val clientSecret = user?.clientSecret.orEmpty()
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
lifecycleScope.launchWhenCreated {
try {
val account = api.verifyCredentials("Bearer $accessToken")
val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.orEmpty()
val refreshToken = user?.refreshToken
val clientId = user?.clientId.orEmpty()
val clientSecret = user?.clientSecret.orEmpty()
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val account = api.verifyCredentials()
addUser(db, account, domain, accessToken = accessToken, refreshToken = refreshToken, clientId = clientId, clientSecret = clientSecret)
fillDrawerAccountInfo(account.id!!)
} catch (exception: IOException) {

View File

@ -29,7 +29,6 @@ import com.h.pixeldroid.postCreation.carousel.CarouselItem
import com.h.pixeldroid.postCreation.carousel.ImageCarousel
import com.h.pixeldroid.postCreation.photoEdit.PhotoEditActivity
import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Attachment
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
@ -338,7 +337,7 @@ class PostCreationActivity : BaseActivity() {
val description = data.imageDescription?.let { MultipartBody.Part.createFormData("description", it) }
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val inter = api.mediaUpload("Bearer $accessToken", description, requestBody.parts[0])
val inter = api.mediaUpload(description, requestBody.parts[0])
postSub = inter
.subscribeOn(Schedulers.io())
@ -383,7 +382,6 @@ class PostCreationActivity : BaseActivity() {
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
api.postStatus(
authorization = "Bearer $accessToken",
statusText = description,
media_ids = photoData.mapNotNull { it.uploadId }.toList()
)

View File

@ -124,7 +124,7 @@ class PostActivity : BaseActivity() {
lifecycleScope.launchWhenCreated {
status.id.let {
try {
val statuses = api.statusComments(it, credential).descendants
val statuses = api.statusComments(it).descendants
binding.commentContainer.removeAllViews()
@ -155,7 +155,7 @@ class PostActivity : BaseActivity() {
val nonNullText = textIn.toString()
status.id.let {
try {
val response = api.postStatus(credential, nonNullText, it)
val response = api.postStatus(nonNullText, it)
binding.commentIn.visibility = View.GONE
//Add the comment to the comment section

View File

@ -42,7 +42,11 @@ class ReportActivity : BaseActivity() {
lifecycleScope.launchWhenCreated {
try {
api.report("Bearer $accessToken", status?.account?.id!!, listOf(status), binding.textInputLayout.editText?.text.toString())
api.report(
status?.account?.id!!,
listOf(status),
binding.textInputLayout.editText?.text.toString()
)
reportStatus(true)
} catch (exception: IOException) {

View File

@ -275,7 +275,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
status?.id?.let {
try {
val resp = api.reblogStatus(credential, it)
val resp = api.reblogStatus(it)
//Update shown share count
binding.nshares.text = resp.getNShares(binding.root.context)
@ -297,7 +297,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
//Call the api function
status?.id?.let {
try {
val resp = api.undoReblogStatus(credential, it)
val resp = api.undoReblogStatus(credential)
//Update shown share count
binding.nshares.text = resp.getNShares(binding.root.context)
@ -395,7 +395,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
db.homePostDao().delete(id, user.user_id, user.instance_uri)
db.publicPostDao().delete(id, user.user_id, user.instance_uri)
try {
api.deleteStatus("Bearer ${user.accessToken}", id)
api.deleteStatus(id)
binding.root.visibility = View.GONE
} catch (exception: HttpException) {
Toast.makeText(
@ -503,7 +503,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
status?.id?.let {
try {
val resp = api.likePost(credential, it)
val resp = api.likePost(it)
//Update shown like count and internal like toggle
binding.nlikes.text = resp.getNLikes(binding.root.context)
@ -526,7 +526,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
status?.id?.let {
try {
val resp = api.unlikePost(credential, it)
val resp = api.unlikePost(it)
//Update shown like count and internal like toggle
binding.nlikes.text = resp.getNLikes(binding.root.context)

View File

@ -61,10 +61,10 @@ class NotificationsRemoteMediator @Inject constructor(
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val accessToken = user.accessToken
val apiResponse = api.notifications("Bearer $accessToken",
max_id = max_id,
min_id = min_id,
limit = state.config.pageSize.toString(),
val apiResponse = api.notifications(
max_id = max_id,
min_id = min_id,
limit = state.config.pageSize.toString(),
)
apiResponse.forEach{it.user_id = user.user_id; it.instance_uri = user.instance_uri}

View File

@ -46,9 +46,10 @@ class HomeFeedRemoteMediator @Inject constructor(
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val accessToken = user.accessToken
val apiResponse = api.timelineHome( "Bearer $accessToken",
max_id= max_id, min_id = min_id,
limit = state.config.pageSize.toString())
val apiResponse = api.timelineHome(
max_id= max_id,
min_id = min_id, limit = state.config.pageSize.toString()
)
val dbObjects = apiResponse.map{
HomeStatusDatabaseEntity(user.user_id, user.instance_uri, it)

View File

@ -6,7 +6,6 @@ import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Account
import retrofit2.HttpException
import java.io.IOException
import java.math.BigInteger
class FollowersPagingSource(
private val api: PixelfedAPI,
@ -22,17 +21,19 @@ class FollowersPagingSource(
// Laravel's paging mechanism, while Mastodon uses the Link header for pagination.
// No need to know which is which, they should ignore the non-relevant argument
if(following) {
api.followers(account_id = accountId,
authorization = "Bearer $accessToken",
api.followers(
account_id = accountId,
max_id = position,
limit = params.loadSize,
page = position,
max_id = position)
page = position
)
} else {
api.following(account_id = accountId,
authorization = "Bearer $accessToken",
api.following(
account_id = accountId,
max_id = position,
limit = params.loadSize,
page = position,
max_id = position)
page = position
)
}
val accounts = if(response.isSuccessful){

View File

@ -15,10 +15,10 @@ class ProfilePagingSource(
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
val position = params.key
return try {
val posts = api.accountPosts("Bearer $accessToken",
account_id = accountId,
max_id = position,
limit = params.loadSize
val posts = api.accountPosts(
account_id = accountId,
max_id = position,
limit = params.loadSize
)
LoadResult.Page(

View File

@ -20,11 +20,12 @@ class SearchPagingSource<T: FeedContent>(
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
val position = params.key
return try {
val response = api.search(authorization = "Bearer $accessToken",
offset = position?.toString(),
q = query,
val response = api.search(
type = type,
limit = params.loadSize.toString())
q = query,
limit = params.loadSize.toString(),
offset = position?.toString()
)
@Suppress("UNCHECKED_CAST")

View File

@ -125,7 +125,7 @@ class ProfileActivity : BaseActivity() {
} else {
lifecycleScope.launchWhenResumed {
val myAccount: Account = try {
pixelfedAPI.verifyCredentials("Bearer $accessToken")
pixelfedAPI.verifyCredentials()
} catch (exception: IOException) {
Log.e("ProfileActivity:", exception.toString())
return@launchWhenResumed showError()
@ -236,7 +236,7 @@ class ProfileActivity : BaseActivity() {
lifecycleScope.launch {
try {
val relationship = pixelfedAPI.checkRelationships(
"Bearer $accessToken", listOf(account.id.orEmpty())
listOf(account.id.orEmpty())
).firstOrNull()
if(relationship != null){
@ -268,7 +268,7 @@ class ProfileActivity : BaseActivity() {
setOnClickListener {
lifecycleScope.launchWhenResumed {
try {
val rel = pixelfedAPI.follow(account.id.orEmpty(), "Bearer $accessToken")
val rel = pixelfedAPI.follow(account.id.orEmpty())
if(rel.following == true) setOnClickUnfollow(account, rel.requested == true)
else setOnClickFollow(account)
} catch (exception: IOException) {
@ -298,7 +298,7 @@ class ProfileActivity : BaseActivity() {
fun unfollow() {
lifecycleScope.launchWhenResumed {
try {
val rel = pixelfedAPI.unfollow(account.id.orEmpty(), "Bearer $accessToken")
val rel = pixelfedAPI.unfollow(account.id.orEmpty())
if(rel.following == false && rel.requested == false) setOnClickFollow(account)
else setOnClickUnfollow(account, rel.requested == true)
} catch (exception: IOException) {

View File

@ -20,12 +20,6 @@ import com.h.pixeldroid.posts.PostActivity
import com.h.pixeldroid.utils.BaseFragment
import com.h.pixeldroid.utils.ImageConverter
import com.h.pixeldroid.utils.bindingLifecycleAware
import com.mikepenz.iconics.IconicsColor
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.color
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import retrofit2.HttpException
import java.io.IOException
@ -93,7 +87,7 @@ class SearchDiscoverFragment : BaseFragment() {
private fun getDiscover() {
lifecycleScope.launchWhenCreated {
try {
val discoverPosts = api.discover("Bearer $accessToken")
val discoverPosts = api.discover()
adapter.addPosts(discoverPosts.posts)
showError(show = false)
} catch (exception: IOException) {

View File

@ -74,31 +74,23 @@ interface PixelfedAPI {
@FormUrlEncoded
@POST("/api/v1/accounts/{id}/follow")
suspend fun follow(
//The authorization header needs to be of the form "Bearer <token>"
@Path("id") statusId: String,
@Header("Authorization") authorization: String,
@Field("reblogs") reblogs : Boolean = true
) : Relationship
@POST("/api/v1/accounts/{id}/unfollow")
suspend fun unfollow(
//The authorization header needs to be of the form "Bearer <token>"
@Path("id") statusId: String,
@Header("Authorization") authorization: String
) : Relationship
@POST("api/v1/statuses/{id}/favourite")
suspend fun likePost(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Path("id") statusId: String
) : Status
@POST("/api/v1/statuses/{id}/unfavourite")
suspend fun unlikePost(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Path("id") statusId: String
) : Status
@ -106,8 +98,6 @@ interface PixelfedAPI {
@FormUrlEncoded
@POST("/api/v1/statuses")
suspend fun postStatus(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Field("status") statusText : String,
@Field("in_reply_to_id") in_reply_to_id : String? = null,
@Field("media_ids[]") media_ids : List<String> = emptyList(),
@ -124,14 +114,12 @@ interface PixelfedAPI {
@DELETE("/api/v1/statuses/{id}")
suspend fun deleteStatus(
@Header("Authorization") authorization: String,
@Path("id") statusId: String
)
@FormUrlEncoded
@POST("/api/v1/statuses/{id}/reblog")
suspend fun reblogStatus(
@Header("Authorization") authorization: String,
@Path("id") statusId: String,
@Field("visibility") visibility: String? = null
) : Status
@ -139,14 +127,12 @@ interface PixelfedAPI {
@POST("/api/v1/statuses/{id}/unreblog")
suspend fun undoReblogStatus(
@Path("id") statusId: String,
@Header("Authorization") authorization: String
) : Status
//Used in our case to retrieve comments for a given status
@GET("/api/v1/statuses/{id}/context")
suspend fun statusComments(
@Path("id") statusId: String,
@Header("Authorization") authorization: String? = null
) : Context
@GET("/api/v1/timelines/public")
@ -160,8 +146,6 @@ interface PixelfedAPI {
@GET("/api/v1/timelines/home")
suspend fun timelineHome(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Query("max_id") max_id: String? = null,
@Query("since_id") since_id: String? = null,
@Query("min_id") min_id: String? = null,
@ -171,8 +155,6 @@ interface PixelfedAPI {
@GET("/api/v2/search")
suspend fun search(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Query("account_id") account_id: String? = null,
@Query("max_id") max_id: String? = null,
@Query("min_id") min_id: String? = null,
@ -187,8 +169,6 @@ interface PixelfedAPI {
@GET("/api/v1/notifications")
suspend fun notifications(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Query("max_id") max_id: String? = null,
@Query("since_id") since_id: String? = null,
@Query("min_id") min_id: String? = null,
@ -200,13 +180,12 @@ interface PixelfedAPI {
@GET("/api/v1/accounts/verify_credentials")
suspend fun verifyCredentials(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String
@Header("Authorization") authorization: String? = null
): Account
@GET("/api/v1/accounts/{id}/statuses")
suspend fun accountPosts(
@Header("Authorization") authorization: String,
@Path("id") account_id: String,
@Query("min_id") min_id: String? = null,
@Query("max_id") max_id: String?,
@ -215,14 +194,12 @@ interface PixelfedAPI {
@GET("/api/v1/accounts/relationships")
suspend fun checkRelationships(
@Header("Authorization") authorization : String,
@Query("id[]") account_ids : List<String>
) : List<Relationship>
@GET("/api/v1/accounts/{id}/followers")
suspend fun followers(
@Path("id") account_id: String,
@Header("Authorization") authorization: String,
@Query("max_id") max_id: String? = null,
@Query("since_id") since_id: String? = null,
@Query("limit") limit: Number? = null,
@ -232,7 +209,6 @@ interface PixelfedAPI {
@GET("/api/v1/accounts/{id}/following")
suspend fun following(
@Path("id") account_id: String,
@Header("Authorization") authorization: String,
@Query("max_id") max_id: String? = null,
@Query("since_id") since_id: String? = null,
@Query("limit") limit: Number? = 40,
@ -241,36 +217,29 @@ interface PixelfedAPI {
@GET("/api/v1/accounts/{id}")
suspend fun getAccount(
@Header("Authorization") authorization: String,
@Path("id") accountId : String
): Account
@GET("/api/v1/statuses/{id}")
suspend fun getStatus(
@Header("Authorization") authorization: String,
@Path("id") accountId : String
): Status
@Multipart
@POST("/api/v1/media")
fun mediaUpload(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Part description: MultipartBody.Part? = null,
@Part file: MultipartBody.Part
): Observable<Attachment>
// get discover
@GET("/api/v2/discover/posts")
suspend fun discover(
@Header("Authorization") authorization: String
) : DiscoverPosts
suspend fun discover() : DiscoverPosts
@FormUrlEncoded
@POST("/api/v1/reports")
@JvmSuppressWildcards
suspend fun report(
@Header("Authorization") authorization: String,
@Field("account_id") account_id: String,
@Field("status_ids") status_ids: List<Status>,
@Field("comment") comment: String,

View File

@ -6,13 +6,7 @@ import android.util.Log
import androidx.core.content.ContextCompat.startActivity
import com.h.pixeldroid.profile.ProfileActivity
import com.h.pixeldroid.utils.api.PixelfedAPI
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException
import java.io.Serializable
@ -59,7 +53,7 @@ data class Account(
*/
suspend fun openAccountFromId(id: String, api : PixelfedAPI, context: Context, credential: String) {
val account = try {
api.getAccount(credential, id)
api.getAccount(id)
} catch (exception: IOException) {
Log.e("GET ACCOUNT ERROR", exception.toString())
return

View File

@ -8,8 +8,8 @@ interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUser(user: UserDatabaseEntity)
@Query("UPDATE users SET accessToken = :accessToken WHERE user_id = :id and instance_uri = :instance_uri")
fun updateAccessToken(accessToken: String, id: String, instance_uri: String)
@Query("UPDATE users SET accessToken = :accessToken, refreshToken = :refreshToken WHERE user_id = :id and instance_uri = :instance_uri")
fun updateAccessToken(accessToken: String, refreshToken: String, id: String, instance_uri: String)
@Query("SELECT * FROM users")
fun getAll(): List<UserDatabaseEntity>

View File

@ -1,6 +1,7 @@
package com.h.pixeldroid.utils.di
import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Token
import com.h.pixeldroid.utils.db.AppDatabase
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
import dagger.Module
@ -22,17 +23,30 @@ class APIModule{
}
}
class TokenAuthenticator(val user: UserDatabaseEntity, val db: AppDatabase) : Authenticator {
class TokenAuthenticator(val user: UserDatabaseEntity, val db: AppDatabase, val apiHolder: PixelfedAPIHolder) : Authenticator {
private val pixelfedAPI = PixelfedAPI.createFromUrl(user.instance_uri)
// Returns the number of tries for this response by walking through the priorResponses
private fun Response.responseCount(): Int {
var result = 1
var response: Response? = priorResponse
while (response != null) {
result++
response = response.priorResponse
}
return result
}
override fun authenticate(route: Route?, response: Response): Request? {
if (response.request.header("Authorization") != null) {
return null // Give up, we've already failed to authenticate.
if (response.responseCount() > 3) {
return null // Give up, we've already failed to authenticate a couple times
}
// Refresh the access_token using a synchronous api request
val newAccessToken: String? = try {
val newAccessToken: Token = try {
runBlocking {
pixelfedAPI.obtainToken(
scope = "",
@ -40,19 +54,25 @@ class TokenAuthenticator(val user: UserDatabaseEntity, val db: AppDatabase) : Au
refresh_token = user.refreshToken,
client_id = user.clientId,
client_secret = user.clientSecret
).access_token
)
}
}catch (e: Exception){
null
return null
}
if (newAccessToken != null) {
db.userDao().updateAccessToken(newAccessToken, user.user_id, user.instance_uri)
// Save the new access token and refresh token
if (newAccessToken.access_token != null && newAccessToken.refresh_token != null) {
db.userDao().updateAccessToken(
newAccessToken.access_token,
newAccessToken.refresh_token,
user.user_id, user.instance_uri
)
apiHolder.setDomainToCurrentUser(db)
}
// Add new header to rejected request and retry it
return response.request.newBuilder()
.header("Authorization", "Bearer ${newAccessToken.orEmpty()}")
.header("Authorization", "Bearer ${newAccessToken.access_token.orEmpty()}")
.build()
}
}
@ -61,6 +81,7 @@ class PixelfedAPIHolder(db: AppDatabase?){
private val intermediate: Retrofit.Builder = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
var api: PixelfedAPI? =
db?.userDao()?.getActiveUser()?.let {
setDomainToCurrentUser(db, it)
@ -73,10 +94,11 @@ class PixelfedAPIHolder(db: AppDatabase?){
val newAPI = intermediate
.baseUrl(user.instance_uri)
.client(
OkHttpClient().newBuilder().authenticator(TokenAuthenticator(user, db))
OkHttpClient().newBuilder().authenticator(TokenAuthenticator(user, db, this))
.addInterceptor {
it.request().newBuilder().run {
header("Accept", "application/json")
header("Authorization", "Bearer ${user.accessToken}")
it.proceed(build())
}
}.build()

View File

@ -4,7 +4,6 @@ import com.github.tomakehurst.wiremock.client.WireMock.*
import com.github.tomakehurst.wiremock.junit.WireMockRule
import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.*
import io.reactivex.Single
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Rule
@ -91,7 +90,7 @@ class APIUnitTest {
statuses = PixelfedAPI.createFromUrl("http://localhost:8089")
.timelinePublic(null, null, null, null, null)
statusesHome = PixelfedAPI.createFromUrl("http://localhost:8089")
.timelineHome("abc", null, null, null,null, null)
.timelineHome(null, null, null, null, null)
}