Skip to content

Commit

Permalink
Retrieve user assignments from contained field (#3707)
Browse files Browse the repository at this point in the history
* Retrieve assigned resources from Practitioner Details contained field

* Retrieve assigned resources from PractionerDetails contained field

* Add unit tests

* Run spotless Apply

---------

Co-authored-by: Benjamin Mwalimu <[email protected]>
  • Loading branch information
Rkareko and dubdabasoduba authored Feb 3, 2025
1 parent a790c9d commit 55dbd93
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ import io.mockk.MockKAnnotations
import io.mockk.impl.annotations.MockK
import java.lang.reflect.Type
import okhttp3.ResponseBody.Companion.toResponseBody
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.Location
import org.hl7.fhir.r4.model.Organization
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.StringType
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.smartregister.fhircore.engine.data.remote.fhir.resource.FhirConverter
import org.smartregister.fhircore.engine.data.remote.fhir.resource.FhirConverterFactory
import org.smartregister.fhircore.engine.util.extension.getCustomJsonParser
import org.smartregister.model.practitioner.PractitionerDetails
import retrofit2.Retrofit

class FhirResourceConverterTest {
Expand Down Expand Up @@ -70,6 +76,258 @@ class FhirResourceConverterTest {
Assert.assertEquals("12345", result.logicalId)
}

@Test
fun testCustomJsonParserConvertsUserAssignmentsInPractitionerDetailsContainedFieldCorrectly() {
val parser = FhirContext.forCached(FhirVersionEnum.R4).getCustomJsonParser()

val practDetails =
"""
{
"resourceType": "Bundle",
"total": 1,
"entry": [
{
"resource": {
"resourceType": "PractitionerDetail",
"id": "8e53855c-779d-491d-9b73-5727517602a8",
"meta": {
"profile": [
"http://hl7.org/fhir/profiles/custom-resource"
]
},
"contained": [
{
"resourceType": "Location",
"id": "e347e698-f9d6-471c-9e73-44b80043e0ba",
"meta": {
"versionId": "2",
"lastUpdated": "2024-12-09T12:55:53.061+00:00",
"source": "#7304eb3f27706df5"
},
"status": "active",
"name": "Gurudola Test GND",
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/location-physical-type",
"code": "jdn",
"display": "Jurisdiction"
}
]
}
],
"physicalType": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/location-physical-type",
"code": "jdn",
"display": "Jurisdiction"
}
]
},
"partOf": {
"reference": "Location/099c05b2-f509-46c6-8072-f636ebf0c595",
"display": "Matugama Test Facility"
}
},
{
"resourceType": "Location",
"id": "4d7bcfa0-e6ed-44bd-a592-d9b7ba0f2cef",
"meta": {
"versionId": "1",
"lastUpdated": "2024-12-09T12:57:06.865+00:00",
"source": "#93280ef9aeb965c3"
},
"status": "active",
"name": "Henpita Test GND",
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/location-physical-type",
"code": "jdn",
"display": "Jurisdiction"
}
]
}
],
"physicalType": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/location-physical-type",
"code": "jdn",
"display": "Jurisdiction"
}
]
},
"partOf": {
"reference": "Location/866ab27d-461a-4c5d-b1d0-89ade3058758",
"display": "Katugahahena Test Facility"
}
},
{
"resourceType": "Location",
"id": "6c964cc1-b583-4739-9411-9838d4107d31",
"meta": {
"versionId": "1",
"lastUpdated": "2024-12-09T12:57:36.985+00:00",
"source": "#f64849744c57b2a0"
},
"status": "active",
"name": "Diyagala Test GND",
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/location-physical-type",
"code": "jdn",
"display": "Jurisdiction"
}
]
}
],
"physicalType": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/location-physical-type",
"code": "jdn",
"display": "Jurisdiction"
}
]
},
"partOf": {
"reference": "Location/866ab27d-461a-4c5d-b1d0-89ade3058758",
"display": "Katugahahena Test Facility"
}
},
{
"resourceType": "Organization",
"id": "51fc72f2-68ab-4feb-80d3-cd67bec87795",
"meta": {
"versionId": "1",
"lastUpdated": "2024-12-10T08:12:22.786+00:00",
"source": "#8a10e75e64c7d3e7"
},
"identifier": [
{
"use": "official",
"value": "43e03c23-04c8-4ea6-acae-691418dca6f2"
}
],
"active": true,
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
"code": "team"
}
]
}
],
"name": "Gurudola Org"
},
{
"resourceType": "Organization",
"id": "592499d8-0837-4632-8a98-cf2c19316c86",
"meta": {
"versionId": "1",
"lastUpdated": "2024-12-10T08:13:06.313+00:00",
"source": "#6e9a830646ffd5b5"
},
"identifier": [
{
"use": "official",
"value": "92823980-8adb-4aa0-b9ee-972913bcd4d9"
}
],
"active": true,
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
"code": "team"
}
]
}
],
"name": "Diyagala"
},
{
"resourceType": "Organization",
"id": "8992f2a8-6fed-4945-9f4b-98612d43d7ee",
"meta": {
"versionId": "1",
"lastUpdated": "2024-12-10T08:13:48.854+00:00",
"source": "#27259eb01172d208"
},
"identifier": [
{
"use": "official",
"value": "819fdfc2-9d24-48cf-ac21-537697274be5"
}
],
"active": true,
"type": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
"code": "team"
}
]
}
],
"name": "Henpita"
}
],
"fhir": {
"id": "8e53855c-779d-491d-9b73-5727517602a8",
"careteams": [],
"teams": [],
"locations": [],
"locationHierarchyList": [],
"practitionerRoles": [],
"groups": [],
"practitioner": [],
"organizationAffiliation": []
}
}
}
]
}
"""
.trimIndent()
val input = practDetails.toByteArray().toResponseBody()
Bundle()

val result =
FhirConverterFactory(parser).responseBodyConverter(type, annotations, retrofit).convert(input)
as Resource
val parsedPractitionerDetails = ((result as Bundle).entry[0].resource as PractitionerDetails)
Assert.assertEquals(6, parsedPractitionerDetails.contained.size)
Assert.assertEquals(
"Gurudola Test GND",
(parsedPractitionerDetails.contained[0] as Location).name,
)
Assert.assertEquals(
"Henpita Test GND",
(parsedPractitionerDetails.contained[1] as Location).name,
)
Assert.assertEquals(
"Diyagala Test GND",
(parsedPractitionerDetails.contained[2] as Location).name,
)
Assert.assertEquals(
"Gurudola Org",
(parsedPractitionerDetails.contained[3] as Organization).name,
)
Assert.assertEquals("Diyagala", (parsedPractitionerDetails.contained[4] as Organization).name)
Assert.assertEquals("Henpita", (parsedPractitionerDetails.contained[5] as Organization).name)
}

private fun buildPatient(): Patient {
return Patient().apply {
id = "12345"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.hl7.fhir.r4.model.Bundle as FhirR4ModelBundle
import org.hl7.fhir.r4.model.CareTeam
import org.hl7.fhir.r4.model.Group
import org.hl7.fhir.r4.model.Location
import org.hl7.fhir.r4.model.Organization
import org.hl7.fhir.r4.model.OrganizationAffiliation
import org.hl7.fhir.r4.model.Practitioner
import org.hl7.fhir.r4.model.PractitionerRole
import org.hl7.fhir.r4.model.ResourceType
import org.smartregister.fhircore.engine.R
import org.smartregister.fhircore.engine.configuration.ConfigType
Expand Down Expand Up @@ -316,15 +323,73 @@ constructor(
viewModelScope.launch {
bundle.entry.forEach { entry ->
val practitionerDetails = entry.resource as PractitionerDetails
val careTeams = practitionerDetails.fhirPractitionerDetails?.careTeams ?: listOf()
val organizations = practitionerDetails.fhirPractitionerDetails?.organizations ?: listOf()
val locations = practitionerDetails.fhirPractitionerDetails?.locations ?: listOf()
val practitioners = practitionerDetails.fhirPractitionerDetails?.practitioners ?: listOf()
val containedResources = practitionerDetails.contained
val careTeams = mutableListOf<CareTeam>()
val organizations = mutableListOf<Organization>()
val locations = mutableListOf<Location>()
val practitioners = mutableListOf<Practitioner>()
val groups = mutableListOf<Group>()
val practitionerRoles = mutableListOf<PractitionerRole>()
val organizationAffiliations = mutableListOf<OrganizationAffiliation>()
val practitionerId =
practitionerDetails.fhirPractitionerDetails?.practitionerId.valueToString()
val locationHierarchies =
practitionerDetails.fhirPractitionerDetails?.locationHierarchyList ?: listOf()

if (containedResources.isNullOrEmpty()) {
/**
* This block is retained for backward compatibility with FHIR Gateway lower than v2.2.6
* The user assignments in those versions are not stored in the
* [PractitionerDetails.contained] field
*/
practitionerDetails.fhirPractitionerDetails?.careTeams?.let { careTeams.addAll(it) }
practitionerDetails.fhirPractitionerDetails?.organizations?.let {
organizations.addAll(it)
}
practitionerDetails.fhirPractitionerDetails?.locations?.let { locations.addAll(it) }
practitionerDetails.fhirPractitionerDetails?.practitioners?.let {
practitioners.addAll(it)
}
practitionerDetails.fhirPractitionerDetails?.groups?.let { groups.addAll(it) }
practitionerDetails.fhirPractitionerDetails?.practitionerRoles?.let {
practitionerRoles.addAll(
it,
)
}
practitionerDetails.fhirPractitionerDetails?.organizationAffiliations?.let {
organizationAffiliations.addAll(
it,
)
}
} else {
containedResources.forEach { resource ->
when (resource.resourceType) {
ResourceType.CareTeam -> {
careTeams.add(resource as CareTeam)
}
ResourceType.Organization -> {
organizations.add(resource as Organization)
}
ResourceType.Location -> {
locations.add(resource as Location)
}
ResourceType.Practitioner -> {
practitioners.add(resource as Practitioner)
}
ResourceType.Group -> {
groups.add(resource as Group)
}
ResourceType.PractitionerRole -> {
practitionerRoles.add(resource as PractitionerRole)
}
ResourceType.OrganizationAffiliation -> {
organizationAffiliations.add(resource as OrganizationAffiliation)
}
else -> {}
}
}
}

val careTeamIds =
defaultRepository.createRemote(false, *careTeams.toTypedArray()).run {
careTeams.map { it.id.extractLogicalIdUuid() }
Expand Down Expand Up @@ -356,15 +421,9 @@ constructor(
}

defaultRepository.createRemote(false, *practitioners.toTypedArray())
practitionerDetails.fhirPractitionerDetails?.groups?.toTypedArray()?.let {
defaultRepository.createRemote(false, *it)
}
practitionerDetails.fhirPractitionerDetails?.practitionerRoles?.toTypedArray()?.let {
defaultRepository.createRemote(false, *it)
}
practitionerDetails.fhirPractitionerDetails?.organizationAffiliations?.toTypedArray()?.let {
defaultRepository.createRemote(false, *it)
}
defaultRepository.createRemote(false, *groups.toTypedArray())
defaultRepository.createRemote(false, *practitionerRoles.toTypedArray())
defaultRepository.createRemote(false, *organizationAffiliations.toTypedArray())

if (practitionerId.isNotEmpty()) {
writePractitionerDetailsToShredPref(
Expand Down
Loading

0 comments on commit 55dbd93

Please sign in to comment.