From f7d8ab86ad7be060aa4f5b91bf74498ee28e91f4 Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Mon, 20 Mar 2023 17:47:26 +0300 Subject: [PATCH] Ref #18: add paging source for matches --- .../home/swap/repository/IPersonRepository.kt | 1 - .../home/swap/repository/PersonRepository.kt | 55 ++++++++------- .../pagination/DemandsPagingSource.kt | 10 +-- .../pagination/MatchesPagingSource.kt | 69 +++++++++++++++++++ .../pagination/OffersPagingSource.kt | 13 ++-- .../java/ru/home/swap/core/network/IApi.kt | 8 +++ .../wallet/repository/WalletRepository.kt | 2 +- 7 files changed, 117 insertions(+), 41 deletions(-) create mode 100644 mobile client/app/src/main/java/ru/home/swap/repository/pagination/MatchesPagingSource.kt diff --git a/mobile client/app/src/main/java/ru/home/swap/repository/IPersonRepository.kt b/mobile client/app/src/main/java/ru/home/swap/repository/IPersonRepository.kt index 99e84ec7..88ea353e 100644 --- a/mobile client/app/src/main/java/ru/home/swap/repository/IPersonRepository.kt +++ b/mobile client/app/src/main/java/ru/home/swap/repository/IPersonRepository.kt @@ -17,5 +17,4 @@ interface IPersonRepository { fun removeDemand(contact: String, secret: String, id: Long): Flow> fun getContacts(contact: String, secret: String, serviceId: Long): Flow> fun cleanCachedAccount(): Flow - } \ No newline at end of file diff --git a/mobile client/app/src/main/java/ru/home/swap/repository/PersonRepository.kt b/mobile client/app/src/main/java/ru/home/swap/repository/PersonRepository.kt index a40e94a7..1ce41045 100644 --- a/mobile client/app/src/main/java/ru/home/swap/repository/PersonRepository.kt +++ b/mobile client/app/src/main/java/ru/home/swap/repository/PersonRepository.kt @@ -9,6 +9,7 @@ import ru.home.swap.App import ru.home.swap.R import ru.home.swap.core.converters.ApiResponseConverter import ru.home.swap.core.extensions.attachIdlingResource +import ru.home.swap.core.logger.Logger import ru.home.swap.core.model.PersonProfile import ru.home.swap.core.model.Service import ru.home.swap.core.network.IApi @@ -17,26 +18,28 @@ import java.net.HttpURLConnection class PersonRepository(val api: IApi, val cache: Cache, val context: Context): IPersonRepository { + private var logger: Logger = Logger.getInstance() + override fun createAccountAsFlow(person: PersonProfile): Flow> { return flow { - Log.d(App.TAG, "[a] createProfile() call") + logger.d( "[a] createProfile() call") val response = api.createProfile( credentials = AppCredentials.basic(person.contact, person.secret), person = person ) - Log.d(App.TAG, "[b] get profile response") + logger.d( "[b] get profile response") if (response.isSuccessful) { - Log.d(App.TAG, "[c] response is ok") + logger.d( "[c] response is ok") var payload = response.body()!! emit(Response.Data(payload.payload)) } else { - Log.d(App.TAG, "[d] response is not ok") + logger.d( "[d] response is not ok") val errorPayload = ApiResponseConverter().toDomain(response.errorBody()?.string()!!); emit(Response.Error.Message("${response.message()}:\n\n${errorPayload.payload}")) } } .catch { ex -> - Log.e(App.TAG, "Failed to create account and process result on network layer", ex) + logger.e( "Failed to create account and process result on network layer", ex) emit(Response.Error.Exception(ex)) } } @@ -44,23 +47,23 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I override suspend fun createAccount(person: PersonProfile): Response { lateinit var result: Response try { - Log.d(App.TAG, "[a] createProfile() call") + logger.d( "[a] createProfile() call") val response = api.createProfile( credentials = AppCredentials.basic(person.contact, person.secret), person = person ) - Log.d(App.TAG, "[b] get profile response") + logger.d( "[b] get profile response") if (response.isSuccessful) { - Log.d(App.TAG, "[c] response is ok") + logger.d( "[c] response is ok") var payload = response.body()!! result = Response.Data(payload.payload) } else { - Log.d(App.TAG, "[d] response is not ok") + logger.d( "[d] response is not ok") val errorPayload = ApiResponseConverter().toDomain(response.errorBody()?.string()!!); result = Response.Error.Message("${response.message()}:\n\n${errorPayload.payload}") } } catch (ex: Exception) { - Log.e(App.TAG, "Failed to create account and process result on network layer", ex) + logger.e("Failed to create account and process result on network layer", ex) result = Response.Error.Exception(ex) } return result @@ -84,12 +87,12 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I override fun getAccount(person: PersonProfile): Flow> { return flow { - Log.d(App.TAG, "[a] getProfile() call") + logger.d( "[a] getProfile() call") val response = api.getProfile( credentials = AppCredentials.basic(person.contact, person.secret) ) if (response.isSuccessful) { - Log.d(App.TAG, "[b1] get body and emit") + logger.d( "[b1] get body and emit") if (response.code() == HttpURLConnection.HTTP_NO_CONTENT) { emit(Response.Error.Message(context.getString(R.string.error_no_data_for_your_params))) } else { @@ -97,20 +100,20 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I emit(Response.Data(payload.payload)) } } else { - Log.d(App.TAG, "[b2] get error and emit") + logger.d( "[b2] get error and emit") emit(Response.Error.Message(response.message())) } } .catch { ex -> - Log.d(App.TAG, "[c] get an exception") - Log.e(App.TAG, "Exception on getAccount() call", ex) + logger.d("[c] get an exception") + logger.e( "Exception on getAccount() call", ex) emit(Response.Error.Exception(ex)) } } override fun addOffer(contact: String, secret: String, newService: Service): Flow> { return flow { - Log.d(App.TAG, "[add offer] start") + logger.d( "[add offer] start") val response = api.addOffer( credentials = AppCredentials.basic(contact, secret), service = newService @@ -120,22 +123,22 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I * with server resources atomic at the same time * */ if (response.isSuccessful) { - Log.d(App.TAG, "[add offer] success case") + logger.d( "[add offer] success case") val profileResponse = api.getProfile( credentials = AppCredentials.basic(contact, secret) ) val payload = profileResponse.body()!! emit(Response.Data(payload.payload)) } else { - Log.d(App.TAG, "[add offer] error case") + logger.d( "[add offer] error case") val errorPayload = ApiResponseConverter().toDomain(response.errorBody()?.string()!!); emit(Response.Error.Message("${response.message()}:\n\n${errorPayload.payload}")) } - Log.d(App.TAG, "[add offer] end") + logger.d( "[add offer] end") } .attachIdlingResource() .catch { ex -> - Log.e(App.TAG, "Exception on addOffer() call", ex) + logger.e( "Exception on addOffer() call", ex) emit(Response.Error.Exception(ex)) } } @@ -147,7 +150,7 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I service = newService ) if (response.isSuccessful) { - Log.d(App.TAG, "[add demand] success case") + logger.d( "[add demand] success case") val profileResponse = api.getProfile( credentials = AppCredentials.basic(contact, secret) ) @@ -159,7 +162,7 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I } } .catch { ex -> - Log.e(App.TAG, "Exception on addDemand() call", ex) + logger.e( "Exception on addDemand() call", ex) emit(Response.Error.Exception(ex)) } } @@ -171,7 +174,7 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I serviceId = id ) if (response.isSuccessful) { - Log.d(App.TAG, "[delete offer] success case") + logger.d( "[delete offer] success case") val profileResponse = api.getProfile( credentials = AppCredentials.basic(contact, secret) ) @@ -182,7 +185,7 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I } } .catch { ex -> - Log.e(App.TAG, "Exception on removeOffer() call", ex) + logger.e( "Exception on removeOffer() call", ex) emit(Response.Error.Exception(ex)) } } @@ -204,7 +207,7 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I } } .catch { ex -> - Log.e(App.TAG, "Exception on removeOffer() call", ex) + logger.e( "Exception on removeOffer() call", ex) emit(Response.Error.Exception(ex)) } } @@ -223,7 +226,7 @@ class PersonRepository(val api: IApi, val cache: Cache, val context: Context): I } } .catch { ex -> - Log.e(App.TAG, "Exception on removeOffer() call", ex) + logger.e( "Exception on removeOffer() call", ex) emit(Response.Error.Exception(ex)) } } diff --git a/mobile client/app/src/main/java/ru/home/swap/repository/pagination/DemandsPagingSource.kt b/mobile client/app/src/main/java/ru/home/swap/repository/pagination/DemandsPagingSource.kt index 8fd8123c..fca22a8a 100644 --- a/mobile client/app/src/main/java/ru/home/swap/repository/pagination/DemandsPagingSource.kt +++ b/mobile client/app/src/main/java/ru/home/swap/repository/pagination/DemandsPagingSource.kt @@ -16,6 +16,10 @@ import kotlin.collections.ArrayList class DemandsPagingSource(private val api: IApi, private val pageSize: Int) : PagingSource() { + companion object { + const val DEFAULT_START_PAGE = 1 + } + private var contact: String? = null private var secret: String? = null @@ -40,7 +44,7 @@ class DemandsPagingSource(private val api: IApi, private val pageSize: Int) ) if (response.isSuccessful) { val body = response.body() - val data = body?.payload?.asList() ?: Collections.emptyList() + val data = body?.payload?.toList() ?: Collections.emptyList() Log.d(App.PAGING, "Paging call") return LoadResult.Page( data = data, @@ -62,8 +66,4 @@ class DemandsPagingSource(private val api: IApi, private val pageSize: Int) this.secret = secret } - private fun Collection.asList(): List { - return ArrayList(this) - } - } \ No newline at end of file diff --git a/mobile client/app/src/main/java/ru/home/swap/repository/pagination/MatchesPagingSource.kt b/mobile client/app/src/main/java/ru/home/swap/repository/pagination/MatchesPagingSource.kt new file mode 100644 index 00000000..d8f8d53f --- /dev/null +++ b/mobile client/app/src/main/java/ru/home/swap/repository/pagination/MatchesPagingSource.kt @@ -0,0 +1,69 @@ +package ru.home.swap.repository.pagination + +import android.util.Log +import androidx.paging.PagingSource +import androidx.paging.PagingState +import retrofit2.HttpException +import ru.home.swap.App +import ru.home.swap.core.model.Service +import ru.home.swap.core.network.IApi +import ru.home.swap.utils.AppCredentials +import java.io.IOException +import java.util.* +import kotlin.IllegalStateException +import kotlin.collections.ArrayList + +class MatchesPagingSource(private val api: IApi, private val pageSize: Int) + : PagingSource() { + + companion object { + const val DEFAULT_START_PAGE = 1 + } + + private var contact: String? = null + private var secret: String? = null + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) + ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) + } + } + + override suspend fun load(params: LoadParams): LoadResult { + if (contact == null || secret == null) + throw IllegalStateException("Credentials are empty. Did you pass credentials for API request?") + + try { + val page = params.key ?: DEFAULT_START_PAGE + + val response = api.getMatchesForUserDemands( + credentials = AppCredentials.basic(contact, secret), + page = page, + size = pageSize + ) + if (response.isSuccessful) { + val body = response.body() + val data = body?.payload?.toList() ?: Collections.emptyList() + Log.d(App.PAGING, "Paging call") + return LoadResult.Page( + data = data, + prevKey = if (page == DEFAULT_START_PAGE) null else page.minus(1), + nextKey = if (data.isEmpty()) null else page.plus(params.loadSize / pageSize) + ) + } else { + return LoadResult.Error(IllegalStateException("Server returned response, but it wasn't successful.")) + } + } catch (ex: IOException) { + return LoadResult.Error(ex) + } catch (ex: HttpException) { + return LoadResult.Error(ex) + } + } + + fun setCredentials(contact: String, secret: String) { + this.contact = contact + this.secret = secret + } + +} \ No newline at end of file diff --git a/mobile client/app/src/main/java/ru/home/swap/repository/pagination/OffersPagingSource.kt b/mobile client/app/src/main/java/ru/home/swap/repository/pagination/OffersPagingSource.kt index 7b01ee53..5ba7ca02 100644 --- a/mobile client/app/src/main/java/ru/home/swap/repository/pagination/OffersPagingSource.kt +++ b/mobile client/app/src/main/java/ru/home/swap/repository/pagination/OffersPagingSource.kt @@ -11,13 +11,14 @@ import ru.home.swap.utils.AppCredentials import java.io.IOException import java.util.* import kotlin.IllegalStateException -import kotlin.collections.ArrayList - -const val DEFAULT_START_PAGE = 1 class OffersPagingSource(private val api: IApi, private val pageSize: Int) : PagingSource() { + companion object { + const val DEFAULT_START_PAGE = 1 + } + private var contact: String? = null private var secret: String? = null @@ -42,7 +43,7 @@ class OffersPagingSource(private val api: IApi, private val pageSize: Int) ) if (response.isSuccessful) { val body = response.body() - val data = body?.payload?.asList() ?: Collections.emptyList() + val data = body?.payload?.toList() ?: Collections.emptyList() Log.d(App.PAGING, "Paging call") return LoadResult.Page( data = data, @@ -64,8 +65,4 @@ class OffersPagingSource(private val api: IApi, private val pageSize: Int) this.secret = secret } - private fun Collection.asList(): List { - return ArrayList(this) - } - } \ No newline at end of file diff --git a/mobile client/core/src/main/java/ru/home/swap/core/network/IApi.kt b/mobile client/core/src/main/java/ru/home/swap/core/network/IApi.kt index fa4a4aaf..04d504e9 100644 --- a/mobile client/core/src/main/java/ru/home/swap/core/network/IApi.kt +++ b/mobile client/core/src/main/java/ru/home/swap/core/network/IApi.kt @@ -65,4 +65,12 @@ interface IApi { suspend fun getContacts( @Header("Authorization") credentials: String, @Query("serviceId") serviceId: Long): Response> + + @Headers("Content-Type: application/json; charset=utf-8") + @GET("/api/v1/account/matches") + suspend fun getMatchesForUserDemands( + @Header("Authorization") credentials: String, + @Query("page") page: Int, + @Query("size") size: Int + ): Response>> } \ No newline at end of file diff --git a/mobile client/wallet/src/main/java/ru/home/swap/wallet/repository/WalletRepository.kt b/mobile client/wallet/src/main/java/ru/home/swap/wallet/repository/WalletRepository.kt index 29ca1811..6de2fdba 100644 --- a/mobile client/wallet/src/main/java/ru/home/swap/wallet/repository/WalletRepository.kt +++ b/mobile client/wallet/src/main/java/ru/home/swap/wallet/repository/WalletRepository.kt @@ -47,7 +47,7 @@ class WalletRepository( web3 = Web3j.build(httpService) runBlocking { launch(Dispatchers.IO) { - loadContract(R.string.test_account_3_private_key) + loadContract(R.string.test_account_private_key) } } }