Skip to content

Commit

Permalink
feat: support refreshing event
Browse files Browse the repository at this point in the history
  • Loading branch information
lydavid committed Oct 11, 2024
1 parent 23912b7 commit 3698449
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,8 @@ class EventDao(
ended = ended,
),
)

fun delete(id: String) {
transacter.delete(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ SELECT
end,
ended
FROM event WHERE id = ?;

delete:
DELETE FROM event
WHERE id = :id;
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
package ly.david.musicsearch.data.repository.event

import ly.david.musicsearch.data.musicbrainz.models.core.EventMusicBrainzModel
import ly.david.musicsearch.data.musicbrainz.api.MusicBrainzApi
import ly.david.musicsearch.shared.domain.event.EventDetailsModel
import ly.david.musicsearch.data.database.dao.EventDao
import ly.david.musicsearch.data.musicbrainz.api.LookupApi
import ly.david.musicsearch.data.musicbrainz.models.core.EventMusicBrainzModel
import ly.david.musicsearch.data.repository.internal.toRelationWithOrderList
import ly.david.musicsearch.shared.domain.event.EventDetailsModel
import ly.david.musicsearch.shared.domain.event.EventRepository
import ly.david.musicsearch.shared.domain.relation.RelationRepository

class EventRepositoryImpl(
private val musicBrainzApi: MusicBrainzApi,
private val eventDao: EventDao,
private val relationRepository: RelationRepository,
private val lookupApi: LookupApi,
) : EventRepository {

override suspend fun lookupEvent(eventId: String): EventDetailsModel {
override suspend fun lookupEvent(
eventId: String,
forceRefresh: Boolean,
): EventDetailsModel {
if (forceRefresh) {
delete(eventId)
}

val event = eventDao.getEventForDetails(eventId)
val urlRelations = relationRepository.getEntityUrlRelationships(eventId)
val hasUrlsBeenSavedForEntity = relationRepository.hasUrlsBeenSavedFor(eventId)
if (event != null && hasUrlsBeenSavedForEntity) {
if (event != null &&
hasUrlsBeenSavedForEntity &&
!forceRefresh
) {
return event.copy(
urls = urlRelations,
)
}

val eventMusicBrainzModel = musicBrainzApi.lookupEvent(eventId)
val eventMusicBrainzModel = lookupApi.lookupEvent(eventId)
cache(eventMusicBrainzModel)
return lookupEvent(eventId)
return lookupEvent(
eventId = eventId,
forceRefresh = false,
)
}

private fun delete(id: String) {
eventDao.withTransaction {
eventDao.delete(id)
relationRepository.deleteUrlRelationshipsByEntity(entityId = id)
}
}

private fun cache(event: EventMusicBrainzModel) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package ly.david.musicsearch.data.repository.event

import kotlinx.coroutines.test.runTest
import ly.david.data.test.api.FakeLookupApi
import ly.david.musicsearch.data.database.dao.EntityHasRelationsDao
import ly.david.musicsearch.data.database.dao.EntityHasUrlsDao
import ly.david.musicsearch.data.database.dao.EventDao
import ly.david.musicsearch.data.database.dao.RelationDao
import ly.david.musicsearch.data.musicbrainz.models.UrlMusicBrainzModel
import ly.david.musicsearch.data.musicbrainz.models.common.LifeSpanMusicBrainzModel
import ly.david.musicsearch.data.musicbrainz.models.core.EventMusicBrainzModel
import ly.david.musicsearch.data.musicbrainz.models.relation.Direction
import ly.david.musicsearch.data.musicbrainz.models.relation.RelationMusicBrainzModel
import ly.david.musicsearch.data.musicbrainz.models.relation.SerializableMusicBrainzEntity
import ly.david.musicsearch.data.repository.KoinTestRule
import ly.david.musicsearch.data.repository.RelationRepositoryImpl
import ly.david.musicsearch.shared.domain.LifeSpanUiModel
import ly.david.musicsearch.shared.domain.event.EventDetailsModel
import ly.david.musicsearch.shared.domain.listitem.RelationListItemModel
import ly.david.musicsearch.shared.domain.network.MusicBrainzEntity
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.koin.test.KoinTest
import org.koin.test.inject

class EventRepositoryImplTest : KoinTest {

@get:Rule(order = 0)
val koinTestRule = KoinTestRule()

private val entityHasRelationsDao: EntityHasRelationsDao by inject()
private val entityHasUrlsDao: EntityHasUrlsDao by inject()
private val relationDao: RelationDao by inject()
private val eventDao: EventDao by inject()

private fun createRepositoryWithFakeNetworkData(
musicBrainzModel: EventMusicBrainzModel,
): EventRepositoryImpl {
val relationRepository = RelationRepositoryImpl(
lookupApi = object : FakeLookupApi() {
override suspend fun lookupEvent(
eventId: String,
include: String?,
): EventMusicBrainzModel {
return musicBrainzModel
}
},
entityHasRelationsDao = entityHasRelationsDao,
entityHasUrlsDao = entityHasUrlsDao,
relationDao = relationDao,
)
return EventRepositoryImpl(
eventDao = eventDao,
relationRepository = relationRepository,
lookupApi = object : FakeLookupApi() {
override suspend fun lookupEvent(
eventId: String,
include: String?,
): EventMusicBrainzModel {
return musicBrainzModel
}
},
)
}

@Test
fun `lookup is cached, and force refresh invalidates cache`() = runTest {
val sparseRepository = createRepositoryWithFakeNetworkData(
musicBrainzModel = EventMusicBrainzModel(
id = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
name = "The Eras Tour: Toronto (night 1)",
),
)
val sparseDetailsModel = sparseRepository.lookupEvent(
eventId = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
forceRefresh = false,
)
assertEquals(
EventDetailsModel(
id = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
name = "The Eras Tour: Toronto (night 1)",
),
sparseDetailsModel,
)

val allDataRepository = createRepositoryWithFakeNetworkData(
musicBrainzModel = EventMusicBrainzModel(
id = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
name = "The Eras Tour: Toronto (night 1)",
lifeSpan = LifeSpanMusicBrainzModel(
begin = "2024-11-14",
),
time = "19:00",
relations = listOf(
RelationMusicBrainzModel(
type = "ticketing",
typeId = "bf0f91b9-d97e-4a7b-9114-f1db1e0b61de",
direction = Direction.FORWARD,
targetType = SerializableMusicBrainzEntity.URL,
url = UrlMusicBrainzModel(
resource = "https://www.ticketmaster.ca/event/10005F01FAA14AE2",
id = "674fd59c-9986-4561-a255-a2959d14a5fe",
),
),
),
),
)
var allDataArtistDetailsModel = allDataRepository.lookupEvent(
eventId = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
forceRefresh = false,
)
assertEquals(
EventDetailsModel(
id = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
name = "The Eras Tour: Toronto (night 1)",
),
allDataArtistDetailsModel,
)
allDataArtistDetailsModel = allDataRepository.lookupEvent(
eventId = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
forceRefresh = true,
)
assertEquals(
EventDetailsModel(
id = "c1fd93a7-d48d-49e1-b87e-55d4e81e9f86",
name = "The Eras Tour: Toronto (night 1)",
lifeSpan = LifeSpanUiModel(
begin = "2024-11-14",
),
time = "19:00",
urls = listOf(
RelationListItemModel(
id = "674fd59c-9986-4561-a255-a2959d14a5fe_0",
linkedEntityId = "674fd59c-9986-4561-a255-a2959d14a5fe",
label = "ticketing",
name = "https://www.ticketmaster.ca/event/10005F01FAA14AE2",
disambiguation = null,
attributes = "",
additionalInfo = null,
linkedEntity = MusicBrainzEntity.URL,
),
),
),
allDataArtistDetailsModel,
)
}
}
2 changes: 1 addition & 1 deletion docs/all_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ TODO: screenshot of list screen
| area |||
| artist |||
| collection |||
| event | ||
| event | ||
| instrument |||
| label | ⬜️ ||
| place |||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ data class EventDetailsModel(
override val type: String? = null,
override val time: String? = null,
override val cancelled: Boolean? = null,
override val lifeSpan: LifeSpanUiModel? = null,
override val lifeSpan: LifeSpanUiModel = LifeSpanUiModel(),
val urls: List<RelationListItemModel> = listOf(),
) : Event
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package ly.david.musicsearch.shared.domain.event

interface EventRepository {
suspend fun lookupEvent(eventId: String): EventDetailsModel
suspend fun lookupEvent(
eventId: String,
forceRefresh: Boolean,
): EventDetailsModel
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ internal class EventPresenter(

LaunchedEffect(forceRefreshDetails) {
try {
val eventListItemModel = repository.lookupEvent(screen.id)
val eventListItemModel = repository.lookupEvent(
screen.id,
forceRefreshDetails,
)
title = eventListItemModel.getNameWithDisambiguation()
event = eventListItemModel
isError = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ly.david.musicsearch.ui.common.event

import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import ly.david.musicsearch.shared.domain.LifeSpanUiModel
Expand Down Expand Up @@ -32,7 +33,7 @@ internal class EventPreviewParameterProvider : PreviewParameterProvider<EventLis
)
}

@DefaultPreviews
@PreviewLightDark
@Composable
private fun Preview(
@PreviewParameter(EventPreviewParameterProvider::class) event: EventListItemModel,
Expand Down

0 comments on commit 3698449

Please sign in to comment.