Merge pull request #299 from android/refactor/remove_episodes
Remove episodes related codepull/307/head
commit
f914d6a412
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.data.model
|
|
||||||
|
|
||||||
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity
|
|
||||||
import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisode
|
|
||||||
import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisodeExpanded
|
|
||||||
|
|
||||||
fun NetworkEpisode.asEntity() = EpisodeEntity(
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
publishDate = publishDate,
|
|
||||||
alternateVideo = alternateVideo,
|
|
||||||
alternateAudio = alternateAudio,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun NetworkEpisodeExpanded.asEntity() = EpisodeEntity(
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
publishDate = publishDate,
|
|
||||||
alternateVideo = alternateVideo,
|
|
||||||
alternateAudio = alternateAudio,
|
|
||||||
)
|
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.data.testdoubles
|
|
||||||
|
|
||||||
import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao
|
|
||||||
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity
|
|
||||||
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test double for [EpisodeDao]
|
|
||||||
*/
|
|
||||||
class TestEpisodeDao : EpisodeDao {
|
|
||||||
|
|
||||||
private var entitiesStateFlow = MutableStateFlow(
|
|
||||||
listOf(
|
|
||||||
EpisodeEntity(
|
|
||||||
id = "1",
|
|
||||||
name = "Episode",
|
|
||||||
publishDate = Instant.fromEpochMilliseconds(0),
|
|
||||||
alternateVideo = null,
|
|
||||||
alternateAudio = null,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getEpisodesStream(): Flow<List<PopulatedEpisode>> =
|
|
||||||
entitiesStateFlow.map {
|
|
||||||
it.map(EpisodeEntity::asPopulatedEpisode)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun insertOrIgnoreEpisodes(episodeEntities: List<EpisodeEntity>): List<Long> {
|
|
||||||
entitiesStateFlow.value = episodeEntities
|
|
||||||
// Assume no conflicts on insert
|
|
||||||
return episodeEntities.map { it.id.toLong() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun updateEpisodes(entities: List<EpisodeEntity>) {
|
|
||||||
throw NotImplementedError("Unused in tests")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deleteEpisodes(ids: List<String>) {
|
|
||||||
val idSet = ids.toSet()
|
|
||||||
entitiesStateFlow.update { entities ->
|
|
||||||
entities.filterNot { idSet.contains(it.id) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun EpisodeEntity.asPopulatedEpisode() = PopulatedEpisode(
|
|
||||||
entity = this,
|
|
||||||
newsResources = emptyList(),
|
|
||||||
authors = emptyList(),
|
|
||||||
)
|
|
@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.model
|
|
||||||
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.Author
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.Episode
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class PopulatedEpisodeKtTest {
|
|
||||||
@Test
|
|
||||||
fun populated_episode_can_be_mapped_to_episode() {
|
|
||||||
val populatedEpisode = PopulatedEpisode(
|
|
||||||
entity = EpisodeEntity(
|
|
||||||
id = "0",
|
|
||||||
name = "Test",
|
|
||||||
publishDate = Instant.fromEpochMilliseconds(1),
|
|
||||||
alternateAudio = "audio",
|
|
||||||
alternateVideo = "video"
|
|
||||||
),
|
|
||||||
newsResources = listOf(
|
|
||||||
NewsResourceEntity(
|
|
||||||
id = "1",
|
|
||||||
episodeId = "0",
|
|
||||||
title = "news",
|
|
||||||
content = "Hilt",
|
|
||||||
url = "url",
|
|
||||||
headerImageUrl = "headerImageUrl",
|
|
||||||
type = Video,
|
|
||||||
publishDate = Instant.fromEpochMilliseconds(1),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
authors = listOf(
|
|
||||||
AuthorEntity(
|
|
||||||
id = "2",
|
|
||||||
name = "name",
|
|
||||||
imageUrl = "imageUrl",
|
|
||||||
twitter = "twitter",
|
|
||||||
mediumPage = "mediumPage",
|
|
||||||
bio = "bio",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val episode = populatedEpisode.asExternalModel()
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
Episode(
|
|
||||||
id = "0",
|
|
||||||
name = "Test",
|
|
||||||
publishDate = Instant.fromEpochMilliseconds(1),
|
|
||||||
alternateAudio = "audio",
|
|
||||||
alternateVideo = "video",
|
|
||||||
newsResources = listOf(
|
|
||||||
NewsResource(
|
|
||||||
id = "1",
|
|
||||||
episodeId = "0",
|
|
||||||
title = "news",
|
|
||||||
content = "Hilt",
|
|
||||||
url = "url",
|
|
||||||
headerImageUrl = "headerImageUrl",
|
|
||||||
type = Video,
|
|
||||||
publishDate = Instant.fromEpochMilliseconds(1),
|
|
||||||
authors = listOf(),
|
|
||||||
topics = listOf()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
authors = listOf(
|
|
||||||
Author(
|
|
||||||
id = "2",
|
|
||||||
name = "name",
|
|
||||||
imageUrl = "imageUrl",
|
|
||||||
twitter = "twitter",
|
|
||||||
mediumPage = "mediumPage",
|
|
||||||
bio = "bio",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
episode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,314 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 11,
|
||||||
|
"identityHash": "2f83f889f6d8a96243f4ce387adbc604",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "authors",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `image_url` TEXT NOT NULL, `twitter` TEXT NOT NULL DEFAULT '', `medium_page` TEXT NOT NULL DEFAULT '', `bio` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "imageUrl",
|
||||||
|
"columnName": "image_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "twitter",
|
||||||
|
"columnName": "twitter",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "mediumPage",
|
||||||
|
"columnName": "medium_page",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bio",
|
||||||
|
"columnName": "bio",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "news_resources_authors",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`news_resource_id` TEXT NOT NULL, `author_id` TEXT NOT NULL, PRIMARY KEY(`news_resource_id`, `author_id`), FOREIGN KEY(`news_resource_id`) REFERENCES `news_resources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`author_id`) REFERENCES `authors`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "newsResourceId",
|
||||||
|
"columnName": "news_resource_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "authorId",
|
||||||
|
"columnName": "author_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"news_resource_id",
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_news_resources_authors_news_resource_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"news_resource_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_authors_news_resource_id` ON `${TABLE_NAME}` (`news_resource_id`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_news_resources_authors_author_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_authors_author_id` ON `${TABLE_NAME}` (`author_id`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "news_resources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"news_resource_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "authors",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"author_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "news_resources",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `url` TEXT NOT NULL, `header_image_url` TEXT, `publish_date` INTEGER NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headerImageUrl",
|
||||||
|
"columnName": "header_image_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "publishDate",
|
||||||
|
"columnName": "publish_date",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "news_resources_topics",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`news_resource_id` TEXT NOT NULL, `topic_id` TEXT NOT NULL, PRIMARY KEY(`news_resource_id`, `topic_id`), FOREIGN KEY(`news_resource_id`) REFERENCES `news_resources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`topic_id`) REFERENCES `topics`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "newsResourceId",
|
||||||
|
"columnName": "news_resource_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "topicId",
|
||||||
|
"columnName": "topic_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"news_resource_id",
|
||||||
|
"topic_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_news_resources_topics_news_resource_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"news_resource_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_topics_news_resource_id` ON `${TABLE_NAME}` (`news_resource_id`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_news_resources_topics_topic_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"topic_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_news_resources_topics_topic_id` ON `${TABLE_NAME}` (`topic_id`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "news_resources",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"news_resource_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "topics",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"topic_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "topics",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `longDescription` TEXT NOT NULL DEFAULT '', `url` TEXT NOT NULL DEFAULT '', `imageUrl` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shortDescription",
|
||||||
|
"columnName": "shortDescription",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "longDescription",
|
||||||
|
"columnName": "longDescription",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "imageUrl",
|
||||||
|
"columnName": "imageUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2f83f889f6d8a96243f4ce387adbc604')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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 androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import androidx.room.Transaction
|
|
||||||
import androidx.room.Update
|
|
||||||
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity
|
|
||||||
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.Episode
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DAO for [EpisodeEntity] and [Episode] access
|
|
||||||
*/
|
|
||||||
@Dao
|
|
||||||
interface EpisodeDao {
|
|
||||||
@Transaction
|
|
||||||
@Query(value = "SELECT * FROM episodes")
|
|
||||||
fun getEpisodesStream(): Flow<List<PopulatedEpisode>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts [episodeEntities] into the db if they don't exist, and ignores those that do
|
|
||||||
*/
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
||||||
suspend fun insertOrIgnoreEpisodes(episodeEntities: List<EpisodeEntity>): List<Long>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates [entities] in the db that match the primary key, and no-ops if they don't
|
|
||||||
*/
|
|
||||||
@Update
|
|
||||||
suspend fun updateEpisodes(entities: List<EpisodeEntity>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts or updates [entities] in the db under the specified primary keys
|
|
||||||
*/
|
|
||||||
@Transaction
|
|
||||||
suspend fun upsertEpisodes(entities: List<EpisodeEntity>) = upsert(
|
|
||||||
items = entities,
|
|
||||||
insertMany = ::insertOrIgnoreEpisodes,
|
|
||||||
updateMany = ::updateEpisodes
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes rows in the db matching the specified [ids]
|
|
||||||
*/
|
|
||||||
@Query(
|
|
||||||
value = """
|
|
||||||
DELETE FROM episodes
|
|
||||||
WHERE id in (:ids)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
suspend fun deleteEpisodes(ids: List<String>)
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.model
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Index
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cross reference for many to many relationship between [EpisodeEntity] and [AuthorEntity]
|
|
||||||
*/
|
|
||||||
@Entity(
|
|
||||||
tableName = "episodes_authors",
|
|
||||||
primaryKeys = ["episode_id", "author_id"],
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = EpisodeEntity::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["episode_id"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
ForeignKey(
|
|
||||||
entity = AuthorEntity::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["author_id"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
],
|
|
||||||
indices = [
|
|
||||||
Index(value = ["episode_id"]),
|
|
||||||
Index(value = ["author_id"]),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
data class EpisodeAuthorCrossRef(
|
|
||||||
@ColumnInfo(name = "episode_id")
|
|
||||||
val episodeId: String,
|
|
||||||
@ColumnInfo(name = "author_id")
|
|
||||||
val authorId: String,
|
|
||||||
)
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.model
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines an NiA episode.
|
|
||||||
* It is a parent in a 1 to many relationship with [NewsResourceEntity]
|
|
||||||
*/
|
|
||||||
@Entity(
|
|
||||||
tableName = "episodes",
|
|
||||||
)
|
|
||||||
data class EpisodeEntity(
|
|
||||||
@PrimaryKey
|
|
||||||
val id: String,
|
|
||||||
val name: String,
|
|
||||||
@ColumnInfo(name = "publish_date")
|
|
||||||
val publishDate: Instant,
|
|
||||||
@ColumnInfo(name = "alternate_video")
|
|
||||||
val alternateVideo: String?,
|
|
||||||
@ColumnInfo(name = "alternate_audio")
|
|
||||||
val alternateAudio: String?,
|
|
||||||
)
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.model
|
|
||||||
|
|
||||||
import androidx.room.Embedded
|
|
||||||
import androidx.room.Junction
|
|
||||||
import androidx.room.Relation
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.Episode
|
|
||||||
|
|
||||||
/**
|
|
||||||
* External data layer representation of an NiA episode
|
|
||||||
*/
|
|
||||||
data class PopulatedEpisode(
|
|
||||||
@Embedded
|
|
||||||
val entity: EpisodeEntity,
|
|
||||||
@Relation(
|
|
||||||
parentColumn = "id",
|
|
||||||
entityColumn = "episode_id"
|
|
||||||
)
|
|
||||||
val newsResources: List<NewsResourceEntity>,
|
|
||||||
@Relation(
|
|
||||||
parentColumn = "id",
|
|
||||||
entityColumn = "id",
|
|
||||||
associateBy = Junction(
|
|
||||||
value = EpisodeAuthorCrossRef::class,
|
|
||||||
parentColumn = "episode_id",
|
|
||||||
entityColumn = "author_id",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val authors: List<AuthorEntity>
|
|
||||||
)
|
|
||||||
|
|
||||||
fun PopulatedEpisode.asExternalModel() = Episode(
|
|
||||||
id = entity.id,
|
|
||||||
name = entity.name,
|
|
||||||
publishDate = entity.publishDate,
|
|
||||||
alternateVideo = entity.alternateVideo,
|
|
||||||
alternateAudio = entity.alternateAudio,
|
|
||||||
newsResources = newsResources.map(NewsResourceEntity::asExternalModel),
|
|
||||||
authors = authors.map(AuthorEntity::asExternalModel)
|
|
||||||
)
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.model.data
|
|
||||||
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
|
|
||||||
/**
|
|
||||||
* External data layer representation of an NiA episode
|
|
||||||
*/
|
|
||||||
data class Episode(
|
|
||||||
val id: String,
|
|
||||||
val name: String,
|
|
||||||
val publishDate: Instant,
|
|
||||||
val alternateVideo: String?,
|
|
||||||
val alternateAudio: String?,
|
|
||||||
val newsResources: List<NewsResource>,
|
|
||||||
val authors: List<Author>
|
|
||||||
)
|
|
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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.network.model
|
|
||||||
|
|
||||||
import com.google.samples.apps.nowinandroid.core.model.data.Episode
|
|
||||||
import com.google.samples.apps.nowinandroid.core.network.model.util.InstantSerializer
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Network representation of [Episode] when fetched from /episodes
|
|
||||||
*/
|
|
||||||
@Serializable
|
|
||||||
data class NetworkEpisode(
|
|
||||||
val id: String,
|
|
||||||
val name: String,
|
|
||||||
@Serializable(InstantSerializer::class)
|
|
||||||
val publishDate: Instant,
|
|
||||||
val alternateVideo: String?,
|
|
||||||
val alternateAudio: String?,
|
|
||||||
val newsResources: List<String> = listOf(),
|
|
||||||
val authors: List<String> = listOf(),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Network representation of [Episode] when fetched from /episodes/{id}
|
|
||||||
*/
|
|
||||||
@Serializable
|
|
||||||
data class NetworkEpisodeExpanded(
|
|
||||||
val id: String,
|
|
||||||
val name: String,
|
|
||||||
@Serializable(InstantSerializer::class)
|
|
||||||
val publishDate: Instant,
|
|
||||||
val alternateVideo: String,
|
|
||||||
val alternateAudio: String,
|
|
||||||
val newsResources: List<NetworkNewsResource> = listOf(),
|
|
||||||
val authors: List<NetworkAuthor> = listOf(),
|
|
||||||
)
|
|
Loading…
Reference in new issue