Skip to content

Commit

Permalink
refactor: [database/charge] dbflow to room (#2312)
Browse files Browse the repository at this point in the history
* MIFOSAC-392 [database/charge] dbflow to room

* backup

* Fix exception

* added singleton
  • Loading branch information
itsPronay authored Feb 19, 2025
1 parent b45e09b commit 03afe29
Show file tree
Hide file tree
Showing 15 changed files with 392 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.common.utils

class DatabaseFetchException(
message: String,
cause: Throwable? = null,
) : Exception(message, cause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.common.utils

import kotlinx.coroutines.flow.Flow
import retrofit2.CallAdapter
import retrofit2.Response
import retrofit2.Retrofit
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class FlowCallAdapterFactory private constructor() : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit,
): CallAdapter<*, *>? {
if (getRawType(returnType) != Flow::class.java) {
return null
}
check(returnType is ParameterizedType) { "Flow return type must be parameterized as Flow<Foo> or Flow<out Foo>" }
val responseType = getParameterUpperBound(0, returnType)
val rawFlowType = getRawType(responseType)
return if (rawFlowType == Response::class.java) {
check(responseType is ParameterizedType) { "Response must be parameterized as Response<Foo> or Response<out Foo>" }
ResponseCallAdapter<Any>(
getParameterUpperBound(
0,
responseType,
),
)
} else {
BodyCallAdapter<Any>(responseType)
}
}

companion object {
@JvmStatic
fun create() = FlowCallAdapterFactory()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.common.utils // FlowCallAdapter.kt
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.suspendCancellableCoroutine
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.Callback
import retrofit2.Response
import java.lang.reflect.Type
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

class ResponseCallAdapter<T>(
private val responseType: Type,
) : CallAdapter<T, Flow<Response<T>>> {
override fun adapt(call: Call<T>): Flow<Response<T>> {
return flow {
emit(
suspendCancellableCoroutine { continuation ->
call.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}

override fun onResponse(call: Call<T>, response: Response<T>) {
continuation.resume(response)
}
})
continuation.invokeOnCancellation { call.cancel() }
},
)
}
}

override fun responseType() = responseType
}

class BodyCallAdapter<T>(private val responseType: Type) : CallAdapter<T, Flow<T>> {
override fun adapt(call: Call<T>): Flow<T> {
return flow {
emit(
suspendCancellableCoroutine { continuation ->
call.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}

override fun onResponse(call: Call<T>, response: Response<T>) {
try {
continuation.resume(response.body()!!)
} catch (e: Exception) {
continuation.resumeWithException(e)
}
}
})
continuation.invokeOnCancellation { call.cancel() }
},
)
}
}

override fun responseType() = responseType
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,12 @@ package com.mifos.core.data.pagingSource

import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.mifos.core.entity.client.Charges
import com.mifos.core.common.utils.DatabaseFetchException
import com.mifos.core.model.objects.clients.Page
import com.mifos.core.network.datamanager.DataManagerCharge
import com.mifos.core.objects.clients.Page
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.Subscriber
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import com.mifos.room.entities.client.Charges
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first

class ClientChargesPagingSource(
private val clientId: Int,
Expand Down Expand Up @@ -48,29 +45,29 @@ class ClientChargesPagingSource(
)
} catch (exception: Exception) {
LoadResult.Error(exception)
} catch (exception: DatabaseFetchException) {
LoadResult.Error(exception)
}
}

private suspend fun getClientChargeList(
clientId: Int,
position: Int,
): Pair<List<Charges>, Int> {
return suspendCancellableCoroutine { continuation ->
dataManagerCharge.getClientCharges(clientId = clientId, offset = position, 10)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(object : Subscriber<Page<Charges>>() {
override fun onCompleted() {
}

override fun onError(exception: Throwable) {
continuation.resumeWithException(exception)
}
var page: Page<Charges>? = null

override fun onNext(page: Page<Charges>) {
continuation.resume(Pair(page.pageItems, page.totalFilteredRecords))
}
})
dataManagerCharge.getClientCharges(
clientId = clientId,
offset = position,
limit = 10,
).catch {
throw DatabaseFetchException("Failed to fetch client charges")
}.collect {
page = it
}

return page?.let {
Pair(it.pageItems, it.totalFilteredRecords)
} ?: throw DatabaseFetchException("Failed to fetch client charges")
}
}
29 changes: 29 additions & 0 deletions core/database/src/main/java/com/mifos/room/dao/ChargeDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.room.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.mifos.room.entities.client.Charges
import kotlinx.coroutines.flow.Flow

/**
* Created by Pronay Sarker on 14/02/2025 (3:32 PM)
*/
@Dao
interface ChargeDao {

@Query("SELECT * FROM Charges where clientId = :clientId")
fun getClientCharges(clientId: Int): Flow<List<Charges>>

@Insert
suspend fun insertAllCharges(vararg charges: List<Charges>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ package com.mifos.room.db
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.mifos.room.dao.ChargeDao
import com.mifos.room.dao.ColumnValueDao
import com.mifos.room.dao.GroupsDao
import com.mifos.room.dao.LoanDao
import com.mifos.room.dao.OfficeDao
import com.mifos.room.dao.StaffDao
import com.mifos.room.dao.SurveyDao
import com.mifos.room.entities.PaymentTypeOption
Expand Down Expand Up @@ -46,7 +48,6 @@ import com.mifos.room.utils.typeconverters.SurveyTypeConverters
// [TODO -> add other entities ]
entities = [
ColumnValue::class,
// loan
LoanWithAssociations::class,
LoanRepaymentRequest::class,
LoanRepaymentTemplate::class,
Expand All @@ -55,7 +56,6 @@ import com.mifos.room.utils.typeconverters.SurveyTypeConverters
Timeline::class,
Status::class,
Summary::class,
// survey
Survey::class,
QuestionDatas::class,
ResponseDatas::class,
Expand Down Expand Up @@ -85,7 +85,9 @@ abstract class MifosDatabase : RoomDatabase() {
abstract fun loanDao(): LoanDao
abstract fun surveyDao(): SurveyDao
abstract fun staffDao(): StaffDao
abstract fun officeDao(): OfficeDao
abstract fun groupsDao(): GroupsDao
abstract fun chargeDao(): ChargeDao

companion object {
const val VERSION = 1
Expand Down
14 changes: 14 additions & 0 deletions core/database/src/main/java/com/mifos/room/di/DaoModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package com.mifos.room.di

import com.mifos.room.dao.ChargeDao
import com.mifos.room.dao.ColumnValueDao
import com.mifos.room.dao.GroupsDao
import com.mifos.room.dao.LoanDao
Expand All @@ -20,37 +21,50 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DaoModule {
@Provides
@Singleton
fun providesColumnValueDao(database: MifosDatabase): ColumnValueDao {
return database.columnValueDao()
}

@Provides
@Singleton
fun providesLoanDao(database: MifosDatabase): LoanDao {
return database.loanDao()
}

@Provides
@Singleton
fun providesSurveyDao(database: MifosDatabase): SurveyDao {
return database.surveyDao()
}

@Provides
@Singleton
fun providesStaffDao(database: MifosDatabase): StaffDao {
return database.staffDao()
}

@Provides
@Singleton
fun providesGroupDao(database: MifosDatabase): GroupsDao {
return database.groupsDao()
}

@Provides
@Singleton
fun providesOfficeDao(database: MifosDatabase): OfficeDao {
return database.officeDao()
}

@Provides
@Singleton
fun providesClientDao(database: MifosDatabase): ChargeDao {
return database.chargeDao()
}
}
Loading

0 comments on commit 03afe29

Please sign in to comment.