Skip to content

Commit

Permalink
Merge pull request #3 from Grigoriym/db_encapsulation
Browse files Browse the repository at this point in the history
create interfaces for db
  • Loading branch information
Grigoriym authored Jan 3, 2024
2 parents 6ac2b46 + 97974f2 commit d5975c1
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/android_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- run: echo "The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "This job is running on a ${{ runner.os }} server hosted by GitHub!"
- uses: actions/checkout@v4
- name: Set up JSK 11
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package com.grappim.hateitorrateit.data.cleanerimpl

import com.grappim.hateitorrateit.commons.IoDispatcher
import com.grappim.hateitorrateit.data.cleanerapi.DataCleaner
import com.grappim.hateitorrateit.data.db.HateItOrRateItDatabase
import com.grappim.hateitorrateit.data.db.dao.DatabaseDao
import com.grappim.hateitorrateit.data.db.utils.TransactionController
import com.grappim.hateitorrateit.data.db.wrapper.DatabaseWrapper
import com.grappim.hateitorrateit.data.repoapi.ProductsRepository
import com.grappim.hateitorrateit.domain.DraftProduct
import com.grappim.hateitorrateit.domain.ProductImageData
import com.grappim.hateitorrateit.utils.FileUtils
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
Expand All @@ -20,7 +21,9 @@ import javax.inject.Inject
class DataCleanerImpl @Inject constructor(
private val fileUtils: FileUtils,
private val productsRepository: ProductsRepository,
private val hateItOrRateItDatabase: HateItOrRateItDatabase,
private val transactionController: TransactionController,
private val databaseDao: DatabaseDao,
private val databaseWrapper: DatabaseWrapper,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : DataCleaner {

Expand Down Expand Up @@ -72,15 +75,13 @@ class DataCleanerImpl @Inject constructor(
}

private suspend fun clearDatabaseData() {
hateItOrRateItDatabase.runInTransaction {
runBlocking {
hateItOrRateItDatabase.clearAllTables()
hateItOrRateItDatabase.databaseDao().clearPrimaryKeyIndex()
}
transactionController.runInTransaction {
databaseWrapper.clearAllTables()
databaseDao.clearPrimaryKeyIndex()
}
}

private fun clearFileSystemData() {
fileUtils.getMainFolder("").deleteRecursively()
fileUtils.clearMainFolder()
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
package com.grappim.hateitorrateit.data.cleanerimpl

import com.grappim.hateitorrateit.data.cleanerapi.DataCleaner
import com.grappim.hateitorrateit.data.db.HateItOrRateItDatabase
import com.grappim.hateitorrateit.data.db.dao.DatabaseDao
import com.grappim.hateitorrateit.data.db.utils.TransactionController
import com.grappim.hateitorrateit.data.db.wrapper.DatabaseWrapper
import com.grappim.hateitorrateit.data.repoapi.ProductsRepository
import com.grappim.hateitorrateit.domain.DraftProduct
import com.grappim.hateitorrateit.domain.HateRateType
import com.grappim.hateitorrateit.domain.ProductImageData
import com.grappim.hateitorrateit.utils.FileUtils
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.time.OffsetDateTime
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class DataCleanerImplTest {

private val fileUtils: FileUtils = mockk()
private val productsRepository: ProductsRepository = mockk()
private val hateItOrRateItDatabase: HateItOrRateItDatabase = mockk()
private val transactionController: TransactionController = mockk()
private val databaseDao: DatabaseDao = mockk()
private val databaseWrapper: DatabaseWrapper = mockk()

private val dataCleaner: DataCleaner = DataCleanerImpl(
fileUtils = fileUtils,
productsRepository = productsRepository,
hateItOrRateItDatabase = hateItOrRateItDatabase,
transactionController = transactionController,
databaseDao = databaseDao,
databaseWrapper = databaseWrapper,
ioDispatcher = UnconfinedTestDispatcher()
)

Expand All @@ -36,12 +46,13 @@ class DataCleanerImplTest {
every { fileUtils.deleteFile(uriString = any()) } returns true
coEvery { productsRepository.deleteProductImage(any(), any()) } returns Unit

dataCleaner.clearProductImage(
val actual = dataCleaner.clearProductImage(
id = 1L,
imageName = "image",
uriString = "uri"
)

assertTrue(actual)
verify { fileUtils.deleteFile("uri") }
coVerify { productsRepository.deleteProductImage(1L, "image") }
}
Expand All @@ -52,12 +63,13 @@ class DataCleanerImplTest {
every { fileUtils.deleteFile(uriString = any()) } returns false
coEvery { productsRepository.deleteProductImage(any(), any()) } returns Unit

dataCleaner.clearProductImage(
val actual = dataCleaner.clearProductImage(
id = 1L,
imageName = "image",
uriString = "uri"
)

assertFalse(actual)
verify { fileUtils.deleteFile("uri") }
coVerify(exactly = 0) { productsRepository.deleteProductImage(1L, "image") }
}
Expand Down Expand Up @@ -135,4 +147,20 @@ class DataCleanerImplTest {
verify { fileUtils.deleteFolder("folder") }
coVerify { productsRepository.removeProductById(1L) }
}

@Test
fun `on clearAllData, should call the needed functions`() = runTest {
coEvery { databaseWrapper.clearAllTables() } just Runs
coEvery { databaseDao.clearPrimaryKeyIndex() } just Runs
coEvery { transactionController.runInTransaction(any()) } coAnswers {
firstArg<suspend () -> Unit>().invoke()
}
every { fileUtils.clearMainFolder() } just Runs

dataCleaner.clearAllData()

coVerify { databaseWrapper.clearAllTables() }
coVerify { databaseDao.clearPrimaryKeyIndex() }
verify { fileUtils.clearMainFolder() }
}
}
1 change: 1 addition & 0 deletions data/db/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ android {
dependencies {
implementation(project(":domain"))
implementation(project(":utils"))
implementation(project(":commons"))

implementation(libs.androidx.room.runtime)
ksp(libs.androidx.room.compiler)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
package com.grappim.hateitorrateit.data.db.di

import android.content.Context
import androidx.room.Room
import com.grappim.hateitorrateit.data.db.BuildConfig
import com.grappim.hateitorrateit.data.db.converters.DateTimeConverter
import com.grappim.hateitorrateit.data.db.HateItOrRateItDatabase
import com.grappim.hateitorrateit.data.db.dao.DatabaseDao
import com.grappim.hateitorrateit.data.db.dao.ProductsDao
import com.grappim.hateitorrateit.data.db.utils.TransactionController
import com.grappim.hateitorrateit.data.db.wrapper.DatabaseWrapper
import com.grappim.hateitorrateit.data.db.wrapper.DatabaseWrapperImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@[Module InstallIn(SingletonComponent::class)]
class DatabaseModule {

@[Provides Singleton]
fun provideRoomDatabase(
@ApplicationContext context: Context,
dateTimeConverter: DateTimeConverter,
): HateItOrRateItDatabase =
Room.databaseBuilder(
context,
HateItOrRateItDatabase::class.java,
"hateitorrateit_${BuildConfig.BUILD_TYPE}.db"
)
.fallbackToDestructiveMigration()
.addTypeConverter(dateTimeConverter)
.build()
fun provideProductsDao(
databaseWrapper: DatabaseWrapper
): ProductsDao = databaseWrapper.productsDao

@[Provides Singleton]
fun provideProductsDao(
hateItOrRateItDatabase: HateItOrRateItDatabase
): ProductsDao = hateItOrRateItDatabase.productsDao()
}
fun provideDatabaseDao(
databaseWrapper: DatabaseWrapper
): DatabaseDao = databaseWrapper.databaseDao

@[Provides Singleton]
fun provideTransactionController(
databaseWrapper: DatabaseWrapper
): TransactionController = databaseWrapper.transactionController
}

@[Module InstallIn(SingletonComponent::class)]
interface DatabaseBindsModule {
@Binds
fun bindDatabaseWrapper(databaseWrapper: DatabaseWrapperImpl): DatabaseWrapper
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const val productsTable = "products_table"
* @param shop - shop where product was bought
* @param type - type of product
* @param isCreated - flag that shows if product was created or not. It is needed to show only created products in list
*
* When we use a primary key of type Int or Long and pass 0 as its value, Room will auto-generate a new value for the primary key colum
*/
@Entity(
tableName = productsTable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.grappim.hateitorrateit.data.db.utils

interface TransactionController {
suspend fun runInTransaction(block: suspend () -> Unit)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.grappim.hateitorrateit.data.db.utils

import androidx.room.RoomDatabase
import androidx.room.withTransaction

class TransactionControllerImpl(
private val roomDatabase: RoomDatabase,
) : TransactionController {

override suspend fun runInTransaction(
block: suspend () -> Unit,
) {
roomDatabase.withTransaction {
block.invoke()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.grappim.hateitorrateit.data.db.wrapper

import com.grappim.hateitorrateit.data.db.dao.DatabaseDao
import com.grappim.hateitorrateit.data.db.dao.ProductsDao
import com.grappim.hateitorrateit.data.db.utils.TransactionController

interface DatabaseWrapper {
val productsDao: ProductsDao

val databaseDao: DatabaseDao

val transactionController: TransactionController

suspend fun clearAllTables()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.grappim.hateitorrateit.data.db.wrapper

import android.content.Context
import androidx.room.Room
import com.grappim.hateitorrateit.commons.IoDispatcher
import com.grappim.hateitorrateit.data.db.BuildConfig
import com.grappim.hateitorrateit.data.db.HateItOrRateItDatabase
import com.grappim.hateitorrateit.data.db.converters.DateTimeConverter
import com.grappim.hateitorrateit.data.db.dao.DatabaseDao
import com.grappim.hateitorrateit.data.db.dao.ProductsDao
import com.grappim.hateitorrateit.data.db.utils.TransactionController
import com.grappim.hateitorrateit.data.db.utils.TransactionControllerImpl
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class DatabaseWrapperImpl @Inject constructor(
@ApplicationContext private val context: Context,
dateTimeConverter: DateTimeConverter,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : DatabaseWrapper {

private val db: HateItOrRateItDatabase

init {
db = Room.databaseBuilder(
context,
HateItOrRateItDatabase::class.java,
"hateitorrateit_${BuildConfig.BUILD_TYPE}.db"
)
.addTypeConverter(dateTimeConverter)
.build()
}

override val productsDao: ProductsDao get() = db.productsDao()

override val databaseDao: DatabaseDao get() = db.databaseDao()

override val transactionController: TransactionController get() = TransactionControllerImpl(db)

override suspend fun clearAllTables() = withContext(ioDispatcher) {
db.clearAllTables()
}
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ startup = "1.1.1"
timber = "5.0.1"
datastore-prefs = "1.0.0"
activity-compose = "1.8.0"
room = "2.6.0"
room = "2.6.1"
sqlite = "2.4.0"
archCore = "2.2.0"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class FileUtilsTest {
}

@Test
fun `toProductImageData_call_should_return_correct_ProductImageData`() {
fun toProductImageData_call_should_return_correct_ProductImageData() {
val expected = ProductImageData(
name = NAME,
mimeType = "png",
Expand All @@ -88,7 +88,7 @@ class FileUtilsTest {
}

@Test
fun `getMainFolder_should_return_correct_folder`() {
fun getMainFolder_should_return_correct_folder() {
every {
context.filesDir
} returns File(FILES_DIR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ class FileUtils @Inject constructor(
)
}

fun clearMainFolder() {
getMainFolder("").deleteRecursively()
}

fun getMainFolder(
child: String
): File {
Expand Down

0 comments on commit d5975c1

Please sign in to comment.