diff --git a/db/src/androidTest/java/com/readrops/db/dao/NewFeedDaoTest.kt b/db/src/androidTest/java/com/readrops/db/dao/NewFeedDaoTest.kt new file mode 100644 index 00000000..c80e1c93 --- /dev/null +++ b/db/src/androidTest/java/com/readrops/db/dao/NewFeedDaoTest.kt @@ -0,0 +1,107 @@ +package com.readrops.db.dao + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.readrops.db.Database +import com.readrops.db.entities.Feed +import com.readrops.db.entities.Folder +import com.readrops.db.entities.account.Account +import com.readrops.db.entities.account.AccountType +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class NewFeedDaoTest { + + private lateinit var database: Database + private lateinit var account: Account + + @Before + fun before() = runTest { + val context = ApplicationProvider.getApplicationContext() + database = Room.inMemoryDatabaseBuilder(context, Database::class.java).build() + + account = Account(accountType = AccountType.LOCAL).apply { + id = database.newAccountDao().insert(this).toInt() + } + + repeat(2) { time -> + database.newFolderDao().insert( + Folder( + name = "Folder $time", + remoteId = "folder_$time", + accountId = account.id + ) + ) + } + + repeat(3) { time -> + database.newFeedDao().insert( + Feed( + name = "Feed $time", + remoteId = "feed_$time", + remoteFolderId = "folder_${if (time % 2 == 0) 0 else 1}", + accountId = account.id + ) + ) + } + } + + @After + fun after() { + database.close() + } + + @Test + fun upsertFeedsTest() = runTest { + val newFeeds = listOf( + // updated feed (name + folder to null) + Feed( + name = "New Feed 0", + remoteId = "feed_0", + remoteFolderId = null, + accountId = account.id + ), + + // deleted feed + /*Feed( + name = "Feed 1", + remoteId = "feed_1", + folderId = 1, + accountId = account.id + ),*/ + + // updated feed (folder change) + Feed( + name = "Feed 2", + remoteId = "feed_2", + remoteFolderId = "folder_1", + accountId = account.id + ), + + // inserted feed + Feed( + name = "Feed 3", + remoteId = "feed_3", + folderId = 1, + accountId = account.id + ), + ) + + database.newFeedDao().upsertFeeds(newFeeds, account) + val allFeeds = database.newFeedDao().selectFeeds(account.id) + + assertTrue(allFeeds.any { it.name == "New Feed 0" && it.folderId == null }) + assertTrue(allFeeds.any { it.remoteId == "feed_2" && it.folderId == 2 }) + + assertFalse(allFeeds.any { it.remoteId == "feed_1" }) + assertTrue(allFeeds.any { it.remoteId == "feed_3" }) + } +} \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/dao/newdao/NewFeedDao.kt b/db/src/main/java/com/readrops/db/dao/newdao/NewFeedDao.kt index 5dcd3a9d..1bd73f74 100644 --- a/db/src/main/java/com/readrops/db/dao/newdao/NewFeedDao.kt +++ b/db/src/main/java/com/readrops/db/dao/newdao/NewFeedDao.kt @@ -3,9 +3,11 @@ package com.readrops.db.dao.newdao import androidx.room.Dao import androidx.room.Query import androidx.room.RawQuery +import androidx.room.Transaction import androidx.sqlite.db.SupportSQLiteQuery import com.readrops.db.entities.Feed import com.readrops.db.entities.Item +import com.readrops.db.entities.account.Account import com.readrops.db.pojo.FeedWithCount import kotlinx.coroutines.flow.Flow @@ -35,4 +37,49 @@ abstract class NewFeedDao : NewBaseDao { @Query("Select count(*) from Feed Where account_id = :accountId") abstract suspend fun selectFeedCount(accountId: Int): Int + + @Query("Select remoteId From Feed Where account_id = :accountId") + abstract suspend fun selectFeedRemoteIds(accountId: Int): MutableList + + @Query("Select id From Folder Where remoteId = :remoteId And account_id = :accountId") + abstract suspend fun selectRemoteFolderLocalId(remoteId: String, accountId: Int): Int + + @Query("Update Feed set name = :name, folder_id = :folderId Where remoteId = :remoteFeedId And account_id = :accountId") + abstract fun updateFeedNameAndFolder(remoteFeedId: String, accountId: Int, name: String, folderId: Int?) + + @Query("Delete from Feed Where remoteId in (:ids) And account_id = :accountId") + abstract fun deleteByIds(ids: List, accountId: Int) + + /** + * Insert, update and delete feeds by account + * + * @param feeds feeds to insert or update + * @param account owner of the feeds + * @return the list of the inserted feeds ids + */ + @Transaction + suspend fun upsertFeeds(feeds: List, account: Account): List { + val localFeedIds = selectFeedRemoteIds(account.id) + + val feedsToInsert = feeds.filter { feed -> localFeedIds.none { localFeedId -> feed.remoteId == localFeedId } } + val feedsToDelete = localFeedIds.filter { localFeedId -> feeds.none { feed -> localFeedId == feed.remoteId } } + + // feeds to update + feeds.filter { feed -> localFeedIds.any { localFeedId -> feed.remoteId == localFeedId } } + .forEach { feed -> + val folderId: Int? = if (feed.remoteFolderId == null) { + null + } else { + selectRemoteFolderLocalId(feed.remoteFolderId!!, account.id) + } + + updateFeedNameAndFolder(feed.remoteId!!, account.id, feed.name!!, folderId) + } + + if (feedsToDelete.isNotEmpty()) { + deleteByIds(feedsToDelete, account.id) + } + + return insert(feedsToInsert) + } } \ No newline at end of file