Skip to content

Commit

Permalink
Update the purge API to accept a list of resource IDs (#2462)
Browse files Browse the repository at this point in the history
* Update the purge API to accept a list of resource IDs

* Code clean up

* Documentation clean up
  • Loading branch information
ndegwamartin authored Apr 18, 2024
1 parent 6ff0643 commit be22519
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class DatabaseImplTest {

@Test
fun purge_withLocalChangeAndForcePurgeTrue_shouldPurgeResource() = runBlocking {
database.purge(ResourceType.Patient, TEST_PATIENT_1_ID, true)
database.purge(ResourceType.Patient, setOf(TEST_PATIENT_1_ID), true)
// after purge the resource is not available in database
val resourceNotFoundException =
assertThrows(ResourceNotFoundException::class.java) {
Expand All @@ -300,7 +300,7 @@ class DatabaseImplTest {
fun purge_withLocalChangeAndForcePurgeFalse_shouldThrowIllegalStateException() = runBlocking {
val resourceIllegalStateException =
assertThrows(IllegalStateException::class.java) {
runBlocking { database.purge(ResourceType.Patient, TEST_PATIENT_1_ID) }
runBlocking { database.purge(ResourceType.Patient, setOf(TEST_PATIENT_1_ID)) }
}
assertThat(resourceIllegalStateException.message)
.isEqualTo(
Expand All @@ -315,7 +315,7 @@ class DatabaseImplTest {
assertThat(database.getLocalChanges(ResourceType.Patient, TEST_PATIENT_2_ID)).isEmpty()
assertResourceEquals(TEST_PATIENT_2, database.select(ResourceType.Patient, TEST_PATIENT_2_ID))

database.purge(TEST_PATIENT_2.resourceType, TEST_PATIENT_2_ID)
database.purge(TEST_PATIENT_2.resourceType, setOf(TEST_PATIENT_2_ID))

val resourceNotFoundException =
assertThrows(ResourceNotFoundException::class.java) {
Expand All @@ -332,7 +332,7 @@ class DatabaseImplTest {

assertResourceEquals(TEST_PATIENT_2, database.select(ResourceType.Patient, TEST_PATIENT_2_ID))

database.purge(TEST_PATIENT_2.resourceType, TEST_PATIENT_2_ID, true)
database.purge(TEST_PATIENT_2.resourceType, setOf(TEST_PATIENT_2_ID), true)

val resourceNotFoundException =
assertThrows(ResourceNotFoundException::class.java) {
Expand All @@ -346,7 +346,7 @@ class DatabaseImplTest {
fun purge_resourceNotAvailable_shouldThrowResourceNotFoundException() = runBlocking {
val resourceNotFoundException =
assertThrows(ResourceNotFoundException::class.java) {
runBlocking { database.purge(ResourceType.Patient, TEST_PATIENT_2_ID) }
runBlocking { database.purge(ResourceType.Patient, setOf(TEST_PATIENT_2_ID)) }
}
assertThat(resourceNotFoundException.message)
.isEqualTo(
Expand Down
18 changes: 18 additions & 0 deletions engine/src/main/java/com/google/android/fhir/FhirEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,26 @@ interface FhirEngine {
* @param forcePurge If `true`, the resource will be purged even if it has local changes.
* Otherwise, an [IllegalStateException] will be thrown if local changes exist. Defaults to
* `false`.
*
* If you need to purge resources in bulk use the method
* [FhirEngine.purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean = false)]
*/
suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean = false)

/**
* Purges resources of the specified type from the database identified by their IDs without any
* deletion of data from the server.
*
* @param type The [ResourceType]
* @param ids The resource ids [Set]<[Resource.id]>
* @param forcePurge If `true`, the resource will be purged even if it has local changes.
* Otherwise, an [IllegalStateException] will be thrown if local changes exist. Defaults to
* `false`.
*
* In the case an exception is thrown by any entry in the list the whole transaction is rolled
* back and no record is purged.
*/
suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean = false)
}

/**
Expand Down
14 changes: 7 additions & 7 deletions engine/src/main/java/com/google/android/fhir/db/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,17 @@ internal interface Database {
suspend fun getLocalChanges(resourceUuid: UUID): List<LocalChange>

/**
* Purge resource from database based on resource type and id without any deletion of data from
* the server.
* Purges resources of the specified type from the database identified by their IDs without any
* deletion of data from the server.
*
* @param type The [ResourceType]
* @param id The resource id [Resource.id]
* @param isLocalPurge default value is false here resource will not be deleted from
* @param ids The resource ids [Set]<[Resource.id]>
* @param forcePurge default value is false, here resources will not be deleted from
* LocalChangeEntity table but it will throw IllegalStateException("Resource has local changes
* either sync with server or FORCE_PURGE required") if local change exists. If true this API
* will delete resource entry from LocalChangeEntity table.
* either sync with server or FORCE_PURGE required") if local changes exists. If true this API
* will delete resource entries from LocalChangeEntity table.
*/
suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean = false)
suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean = false)

/**
* @return List of [LocalChangeResourceReference] associated with the [LocalChangeEntity.id]s. A
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,27 +383,29 @@ internal class DatabaseImpl(
}
}

override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) {
override suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean) {
db.withTransaction {
// To check resource is present in DB else throw ResourceNotFoundException()
selectEntity(type, id)
val localChangeEntityList = localChangeDao.getLocalChanges(type, id)
// If local change is not available simply delete resource
if (localChangeEntityList.isEmpty()) {
resourceDao.deleteResource(resourceId = id, resourceType = type)
} else {
// local change is available with FORCE_PURGE the delete resource and discard changes from
// localChangeEntity table
if (forcePurge) {
ids.forEach { id ->
// To check resource is present in DB else throw ResourceNotFoundException()
selectEntity(type, id)
val localChangeEntityList = localChangeDao.getLocalChanges(type, id)
// If local change is not available simply delete resource
if (localChangeEntityList.isEmpty()) {
resourceDao.deleteResource(resourceId = id, resourceType = type)
localChangeDao.discardLocalChanges(
token = LocalChangeToken(localChangeEntityList.map { it.id }),
)
} else {
// local change is available but FORCE_PURGE = false then throw exception
throw IllegalStateException(
"Resource with type $type and id $id has local changes, either sync with server or FORCE_PURGE required",
)
// local change is available with FORCE_PURGE the delete resource and discard changes from
// localChangeEntity table
if (forcePurge) {
resourceDao.deleteResource(resourceId = id, resourceType = type)
localChangeDao.discardLocalChanges(
token = LocalChangeToken(localChangeEntityList.map { it.id }),
)
} else {
// local change is available but FORCE_PURGE = false then throw exception
throw IllegalStateException(
"Resource with type $type and id $id has local changes, either sync with server or FORCE_PURGE required",
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ internal class FhirEngineImpl(private val database: Database, private val contex
}

override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) {
database.purge(type, id, forcePurge)
database.purge(type, setOf(id), forcePurge)
}

override suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean) {
database.purge(type, ids, forcePurge)
}

override suspend fun syncDownload(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ object TestFhirEngineImpl : FhirEngine {
}

override suspend fun purge(type: ResourceType, id: String, forcePurge: Boolean) {}

override suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean) {}
}

object TestFailingDatasource : DataSource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,23 @@ class FhirEngineImplTest {
assertThat(fhirEngine.getLocalChanges(ResourceType.Patient, TEST_PATIENT_1_ID)).isEmpty()
}

@Test
fun `purge() multiple with local change and force purge true should purge resources`() =
runBlocking {
val ids = fhirEngine.create(TEST_PATIENT_1, TEST_PATIENT_2)

fhirEngine.purge(ResourceType.Patient, ids.toSet(), true)

assertThrows(ResourceNotFoundException::class.java) {
runBlocking { fhirEngine.get(ResourceType.Patient, TEST_PATIENT_1_ID) }
}
assertThrows(ResourceNotFoundException::class.java) {
runBlocking { fhirEngine.get(ResourceType.Patient, TEST_PATIENT_2_ID) }
}
assertThat(fhirEngine.getLocalChanges(ResourceType.Patient, TEST_PATIENT_1_ID)).isEmpty()
assertThat(fhirEngine.getLocalChanges(ResourceType.Patient, TEST_PATIENT_2_ID)).isEmpty()
}

@Test
fun `purge() with local change and force purge false should throw IllegalStateException`() =
runBlocking {
Expand Down

0 comments on commit be22519

Please sign in to comment.