Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FhirEngine interface method 'withTransaction' #2535

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
857f112
Add FhirEngine interface method 'withTransaction'
LZRS May 6, 2024
5f2e163
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS May 26, 2024
72be126
Only allow FHIREngine CRUD api for withTransaction
LZRS May 26, 2024
e7bff9c
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS May 30, 2024
868877f
Rename BaseFhirEngine to CrudFhirEngine
LZRS May 30, 2024
75b41b5
Move count method back to FhirEngine class
LZRS May 30, 2024
7b769c1
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS Jun 1, 2024
22f0e57
Merge branch 'master' into 2531-fhirengine-interface
LZRS Jun 3, 2024
f048d86
Merge branch 'master' into 2531-fhirengine-interface
LZRS Jun 3, 2024
4b1e808
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS Jun 8, 2024
f336d2d
Merge branch 'master' into 2531-fhirengine-interface
LZRS Jun 13, 2024
c1aad5e
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS Jun 14, 2024
892ed27
Update 'withTransaction' tests
LZRS Jun 14, 2024
ddc3ecb
Merge branch 'master' into 2531-fhirengine-interface
LZRS Jun 26, 2024
2d2eec0
Merge branch 'master' into 2531-fhirengine-interface
LZRS Jul 1, 2024
52d4147
Merge branch 'master' into 2531-fhirengine-interface
LZRS Aug 22, 2024
ff0f0e1
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS Oct 4, 2024
99d3eb0
Merge remote-tracking branch 'upstream/master' into 2531-fhirengine-i…
LZRS Oct 4, 2024
b520d30
Revert to using FhirEngine interface
LZRS Oct 4, 2024
fe0cee6
Merge branch 'master' into 2531-fhirengine-interface
LZRS Oct 8, 2024
faacaec
Simplify tests for fhirengine's withTransaction
LZRS Oct 8, 2024
c2e5f30
Merge branch 'master' into 2531-fhirengine-interface
LZRS Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 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 @@ -202,6 +202,12 @@ interface FhirEngine {
* back and no record is purged.
*/
suspend fun purge(type: ResourceType, ids: Set<String>, forcePurge: Boolean = false)

/**
* Adds support for performing actions on `FhirEngine` as a single atomic transaction where the
* entire set of changes succeed or fail as a single entity
*/
suspend fun withTransaction(block: suspend FhirEngine.() -> Unit)
LZRS marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ internal class FhirEngineImpl(private val database: Database, private val contex
}
}

override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) {
database.withTransaction { this.block() }
}

private suspend fun saveResolvedResourcesToDatabase(resolved: List<Resource>?) {
resolved?.let {
database.deleteUpdates(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ internal object TestFhirEngineImpl : FhirEngine {
download().collect()
}

override suspend fun withTransaction(block: suspend FhirEngine.() -> Unit) {}

override suspend fun count(search: Search): Long {
return 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ import kotlinx.coroutines.test.runTest
import org.hl7.fhir.exceptions.FHIRException
import org.hl7.fhir.r4.model.Address
import org.hl7.fhir.r4.model.CanonicalType
import org.hl7.fhir.r4.model.CodeableConcept
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.DateTimeType
import org.hl7.fhir.r4.model.Encounter
import org.hl7.fhir.r4.model.Enumerations
import org.hl7.fhir.r4.model.HumanName
import org.hl7.fhir.r4.model.Meta
import org.hl7.fhir.r4.model.Observation
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.Reference
import org.hl7.fhir.r4.model.ResourceType
import org.junit.Assert.assertThrows
import org.junit.Before
Expand Down Expand Up @@ -783,6 +787,71 @@ class FhirEngineImplTest {
assertThat(services.database.getLocalChangesCount()).isEqualTo(0)
}

@Test
fun `withTransaction saves changes successfully`() = runTest {
fhirEngine.withTransaction {
val patient01 =
Patient().apply {
id = "patient-01"
gender = Enumerations.AdministrativeGender.FEMALE
}
this.create(patient01)

val patient01Observation =
Observation().apply {
id = "patient-01-observation"
status = Observation.ObservationStatus.FINAL
code = CodeableConcept()
subject = Reference(patient01)
}
this.create(patient01Observation)
}

assertThat(
fhirEngine.get<Patient>("patient-01"),
)
.isNotNull()
assertThat(fhirEngine.get<Observation>("patient-01-observation")).isNotNull()
assertThat(
fhirEngine.get<Observation>("patient-01-observation").subject.reference,
)
.isEqualTo("Patient/patient-01")
}

@Test
fun `withTransaction rolls back changes when an error occurs`() = runTest {
val patient01 =
Patient().apply {
id = "patient-01"
gender = Enumerations.AdministrativeGender.FEMALE
}

fhirEngine.create(patient01)

try {
fhirEngine.withTransaction {
val patientEncounter =
Encounter().apply {
id = "enc-01"
status = Encounter.EncounterStatus.FINISHED
class_ = Coding()
subject = Reference(patient01)
}

this.create(patientEncounter)

// Update encounter to reference non-existent subject to force ResourceNotFoundException
val nonExistentSubject = this.get(ResourceType.Patient, "non_existent_id") as Patient
patientEncounter.subject = Reference(nonExistentSubject)
this.update(patientEncounter)
}
} catch (_: ResourceNotFoundException) {}

assertThrows(ResourceNotFoundException::class.java) {
runBlocking { fhirEngine.get<Encounter>("enc-01") }
}
}

companion object {
private const val TEST_PATIENT_1_ID = "test_patient_1"
private var TEST_PATIENT_1 =
Expand Down
Loading