Add interfaces for DAOs

pull/1323/head
lihenggui 2 years ago
parent 30e28e677f
commit 42c3501d54

@ -42,10 +42,10 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@ -70,7 +70,7 @@ class OfflineFirstNewsRepositoryTest {
@get:Rule
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
@Before
@BeforeTest
fun setup() {
niaPreferencesDataSource = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore(testScope),

@ -50,9 +50,6 @@ class OfflineFirstTopicsRepositoryTest {
private lateinit var synchronizer: Synchronizer
@get:Rule
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
@BeforeTest
fun setup() {
topicDao = TestTopicDao()

@ -18,10 +18,11 @@ package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import com.russhwolf.settings.MapSettings
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
@ -35,7 +36,12 @@ import kotlin.test.assertTrue
class OfflineFirstUserDataRepositoryTest {
private val testScope = TestScope(UnconfinedTestDispatcher())
@OptIn(ExperimentalCoroutinesApi::class)
private val dispatcher = UnconfinedTestDispatcher()
private val settings = MapSettings()
private val testScope = TestScope(dispatcher)
private lateinit var subject: OfflineFirstUserDataRepository
@ -43,13 +49,11 @@ class OfflineFirstUserDataRepositoryTest {
private val analyticsHelper = NoOpAnalyticsHelper()
@get:Rule
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
@BeforeTest
fun setup() {
niaPreferencesDataSource = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore(testScope),
settings,
dispatcher,
)
subject = OfflineFirstUserDataRepository(

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.core.data.testdoubles
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDaoInterface
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource
@ -32,7 +33,7 @@ val nonPresentInterestsIds = setOf("2")
/**
* Test double for [NewsResourceDao]
*/
class TestNewsResourceDao : NewsResourceDao {
class TestNewsResourceDao : NewsResourceDaoInterface {
private val entitiesStateFlow = MutableStateFlow(emptyList<NewsResourceEntity>())
@ -92,6 +93,10 @@ class TestNewsResourceDao : NewsResourceDao {
result.map { it.entity.id }
}
override suspend fun insertOrIgnoreNewsResources(entities: List<NewsResourceEntity>): List<Long> {
TODO("Not yet implemented")
}
override suspend fun upsertNewsResources(newsResourceEntities: List<NewsResourceEntity>) {
entitiesStateFlow.update { oldValues ->
// New values come first so they overwrite old values

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.core.data.testdoubles
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDaoInterface
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@ -26,7 +27,7 @@ import kotlinx.coroutines.flow.update
/**
* Test double for [TopicDao]
*/
class TestTopicDao : TopicDao {
class TestTopicDao : TopicDaoInterface {
private val entitiesStateFlow = MutableStateFlow(emptyList<TopicEntity>())

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.datetime.Instant
import org.junit.Test
import kotlin.test.Test
import kotlin.test.assertEquals
class PopulatedNewsResourceKtTest {

@ -30,17 +30,17 @@ import kotlinx.datetime.Instant
/**
* DAO for [NewsResource] and [NewsResourceEntity] access
*/
class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher): NewsResourceDaoInterface {
private val query = db.newsResourceQueries
/**
* Fetches news resources that match the query parameters
*/
fun getNewsResources(
useFilterTopicIds: Boolean = false,
filterTopicIds: Set<String> = emptySet(),
useFilterNewsIds: Boolean = false,
filterNewsIds: Set<String> = emptySet(),
override fun getNewsResources(
useFilterTopicIds: Boolean,
filterTopicIds: Set<String>,
useFilterNewsIds: Boolean,
filterNewsIds: Set<String>,
): Flow<List<PopulatedNewsResource>> {
return query.getNewsResources(
useFilterTopicIds = useFilterTopicIds,
@ -69,11 +69,11 @@ class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatch
/**
* Fetches ids of news resources that match the query parameters
*/
fun getNewsResourceIds(
useFilterTopicIds: Boolean = false,
filterTopicIds: Set<String> = emptySet(),
useFilterNewsIds: Boolean = false,
filterNewsIds: Set<String> = emptySet(),
override fun getNewsResourceIds(
useFilterTopicIds: Boolean,
filterTopicIds: Set<String>,
useFilterNewsIds: Boolean,
filterNewsIds: Set<String>,
): Flow<List<String>> {
return query.getNewsResourceIds(
useFilterTopicIds = useFilterTopicIds,
@ -88,7 +88,7 @@ class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatch
/**
* Inserts [entities] into the db if they don't exist, and ignores those that do
*/
suspend fun insertOrIgnoreNewsResources(entities: List<NewsResourceEntity>): List<Long> {
override suspend fun insertOrIgnoreNewsResources(entities: List<NewsResourceEntity>): List<Long> {
entities.forEach {
query.insertOrIgnoreNewsResource(
id = it.id,
@ -109,7 +109,7 @@ class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatch
/**
* Inserts or updates [newsResourceEntities] in the db under the specified primary keys
*/
suspend fun upsertNewsResources(newsResourceEntities: List<NewsResourceEntity>) {
override suspend fun upsertNewsResources(newsResourceEntities: List<NewsResourceEntity>) {
newsResourceEntities.forEach {
query.upsertNewsResource(
id = it.id,
@ -123,7 +123,7 @@ class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatch
}
}
suspend fun insertOrIgnoreTopicCrossRefEntities(
override suspend fun insertOrIgnoreTopicCrossRefEntities(
newsResourceTopicCrossReferences: List<NewsResourceTopicCrossRef>,
) {
newsResourceTopicCrossReferences.forEach {
@ -137,7 +137,7 @@ class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatch
/**
* Deletes rows in the db matching the specified [ids]
*/
suspend fun deleteNewsResources(ids: List<String>) {
override suspend fun deleteNewsResources(ids: List<String>) {
query.deleteNewsResources(ids)
}
}

@ -0,0 +1,43 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.dao
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource
import kotlinx.coroutines.flow.Flow
interface NewsResourceDaoInterface {
fun getNewsResources(
useFilterTopicIds: Boolean = false,
filterTopicIds: Set<String> = emptySet(),
useFilterNewsIds: Boolean = false,
filterNewsIds: Set<String> = emptySet()
): Flow<List<PopulatedNewsResource>>
fun getNewsResourceIds(
useFilterTopicIds: Boolean = false,
filterTopicIds: Set<String> = emptySet(),
useFilterNewsIds: Boolean = false,
filterNewsIds: Set<String> = emptySet()
): Flow<List<String>>
suspend fun insertOrIgnoreNewsResources(entities: List<NewsResourceEntity>): List<Long>
suspend fun upsertNewsResources(newsResourceEntities: List<NewsResourceEntity>)
suspend fun insertOrIgnoreTopicCrossRefEntities(newsResourceTopicCrossReferences: List<NewsResourceTopicCrossRef>)
suspend fun deleteNewsResources(ids: List<String>)
}

@ -28,9 +28,9 @@ import kotlinx.coroutines.flow.map
/**
* DAO for [NewsResourceFtsEntity] access.
*/
class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher): NewsResourceFtsDaoInterface {
private val dbQuery = db.newsResourceFtsQueries
suspend fun insertAll(newsResources: List<NewsResourceFtsEntity>) {
override suspend fun insertAll(newsResources: List<NewsResourceFtsEntity>) {
newsResources.forEach {
dbQuery.insert(
news_resource_id = it.newsResourceId,
@ -40,13 +40,13 @@ class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispa
}
}
fun searchAllNewsResources(query: String): Flow<List<String>> {
override fun searchAllNewsResources(query: String): Flow<List<String>> {
return dbQuery.searchAllNewsResources(query)
.asFlow()
.mapToList(dispatcher)
}
fun getCount(): Flow<Int> {
override fun getCount(): Flow<Int> {
return dbQuery.getCount()
.asFlow()
.mapToOneNotNull(dispatcher)

@ -0,0 +1,26 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.dao
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsEntity
import kotlinx.coroutines.flow.Flow
interface NewsResourceFtsDaoInterface {
suspend fun insertAll(newsResources: List<NewsResourceFtsEntity>)
fun searchAllNewsResources(query: String): Flow<List<String>>
fun getCount(): Flow<Int>
}

@ -28,11 +28,11 @@ import kotlinx.datetime.Instant
/**
* DAO for [RecentSearchQueryEntity] access
*/
class RecentSearchQueryDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
class RecentSearchQueryDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher): RecentSearchQueryDaoInterface {
private val query = db.recentSearchQueryQueries
fun getRecentSearchQueryEntities(limit: Int): Flow<List<RecentSearchQueryEntity>> {
override fun getRecentSearchQueryEntities(limit: Int): Flow<List<RecentSearchQueryEntity>> {
return query.getRecentSearchQueryEntities(limit.toLong()) { query, timestamp ->
RecentSearchQueryEntity(
query = query,
@ -43,7 +43,7 @@ class RecentSearchQueryDao(db: NiaDatabase, private val dispatcher: CoroutineDis
.mapToList(dispatcher)
}
suspend fun insertOrReplaceRecentSearchQuery(recentSearchQuery: RecentSearchQueryEntity) {
override suspend fun insertOrReplaceRecentSearchQuery(recentSearchQuery: RecentSearchQueryEntity) {
query.insertOrReplaceRecentSearchQuery(
recent_search_query = Recent_search_query(
query = recentSearchQuery.query,
@ -52,7 +52,7 @@ class RecentSearchQueryDao(db: NiaDatabase, private val dispatcher: CoroutineDis
)
}
suspend fun clearRecentSearchQueries() {
override suspend fun clearRecentSearchQueries() {
query.clearRecentSearchQueries()
}
}

@ -0,0 +1,26 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.dao
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
import kotlinx.coroutines.flow.Flow
interface RecentSearchQueryDaoInterface {
fun getRecentSearchQueryEntities(limit: Int): Flow<List<RecentSearchQueryEntity>>
suspend fun insertOrReplaceRecentSearchQuery(recentSearchQuery: RecentSearchQueryEntity)
suspend fun clearRecentSearchQueries()
}

@ -29,11 +29,11 @@ import kotlinx.coroutines.flow.map
* DAO for [TopicEntity] access
*/
class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher): TopicDaoInterface {
private val query = db.topicsQueries
fun getTopicEntity(topicId: String): Flow<TopicEntity> {
override fun getTopicEntity(topicId: String): Flow<TopicEntity> {
return query.getTopicEntity(topicId) { id, name, shortDescription, longDescription, url, imageUrl ->
TopicEntity(
id = id,
@ -48,7 +48,7 @@ class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
.mapToOne(dispatcher)
}
fun getTopicEntities(): Flow<List<TopicEntity>> {
override fun getTopicEntities(): Flow<List<TopicEntity>> {
return query.getOneOffTopicEntities { id, name, shortDescription, longDescription, url, imageUrl ->
TopicEntity(
id = id,
@ -63,7 +63,7 @@ class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
.mapToList(dispatcher)
}
suspend fun getOneOffTopicEntities(): List<TopicEntity> {
override suspend fun getOneOffTopicEntities(): List<TopicEntity> {
// TODO: Use flow?
return query.getOneOffTopicEntities { id, name, shortDescription, longDescription, url, imageUrl ->
TopicEntity(
@ -77,7 +77,7 @@ class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
}.executeAsList()
}
fun getTopicEntities(ids: Set<String>): Flow<List<TopicEntity>> {
override fun getTopicEntities(ids: Set<String>): Flow<List<TopicEntity>> {
return query.getTopicEntities { id, name, shortDescription, longDescription, url, imageUrl ->
TopicEntity(
id = id,
@ -98,7 +98,7 @@ class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
/**
* Inserts [topicEntities] into the db if they don't exist, and ignores those that do
*/
suspend fun insertOrIgnoreTopics(topicEntities: List<TopicEntity>): List<Long> {
override suspend fun insertOrIgnoreTopics(topicEntities: List<TopicEntity>): List<Long> {
topicEntities.map {
query.insertOrIgnoreTopic(
id = it.id,
@ -116,7 +116,7 @@ class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
/**
* Inserts or updates [entities] in the db under the specified primary keys
*/
suspend fun upsertTopics(entities: List<TopicEntity>) {
override suspend fun upsertTopics(entities: List<TopicEntity>) {
entities.forEach {
query.upsertTopic(
id = it.id,
@ -132,7 +132,7 @@ class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
/**
* Deletes rows in the db matching the specified [ids]
*/
suspend fun deleteTopics(ids: List<String>) {
override suspend fun deleteTopics(ids: List<String>) {
query.deleteTopics(ids)
}
}

@ -0,0 +1,30 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.dao
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import kotlinx.coroutines.flow.Flow
interface TopicDaoInterface {
fun getTopicEntity(topicId: String): Flow<TopicEntity>
fun getTopicEntities(): Flow<List<TopicEntity>>
suspend fun getOneOffTopicEntities(): List<TopicEntity>
fun getTopicEntities(ids: Set<String>): Flow<List<TopicEntity>>
suspend fun insertOrIgnoreTopics(topicEntities: List<TopicEntity>): List<Long>
suspend fun upsertTopics(entities: List<TopicEntity>)
suspend fun deleteTopics(ids: List<String>)
}

@ -28,11 +28,11 @@ import kotlinx.coroutines.flow.map
/**
* DAO for [TopicFtsEntity] access.
*/
class TopicFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) {
class TopicFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher): TopicFtsDaoInterface {
private val dbQuery = db.topicFtsQueries
suspend fun insertAll(topics: List<TopicFtsEntity>) {
override suspend fun insertAll(topics: List<TopicFtsEntity>) {
topics.forEach {
dbQuery.insert(
topic_id = it.topicId,
@ -43,7 +43,7 @@ class TopicFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher)
}
}
fun searchAllTopics(query: String): Flow<List<String>> {
override fun searchAllTopics(query: String): Flow<List<String>> {
return dbQuery.searchAllTopics(query) {
it.orEmpty()
}
@ -51,7 +51,7 @@ class TopicFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher)
.mapToList(dispatcher)
}
fun getCount(): Flow<Int> {
override fun getCount(): Flow<Int> {
return dbQuery.getCount()
.asFlow()
.mapToOne(dispatcher)

@ -0,0 +1,26 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.dao
import com.google.samples.apps.nowinandroid.core.database.model.TopicFtsEntity
import kotlinx.coroutines.flow.Flow
interface TopicFtsDaoInterface {
suspend fun insertAll(topics: List<TopicFtsEntity>)
fun searchAllTopics(query: String): Flow<List<String>>
fun getCount(): Flow<Int>
}
Loading…
Cancel
Save