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

Migrate to latest FHIR engine SDK #2718

Merged
merged 31 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3d54a4e
Upgrade dependencies
ellykits Aug 23, 2023
bef9c87
Fix app crash
ellykits Aug 24, 2023
7e740a7
Upgrade more gradle dependencies
ellykits Aug 24, 2023
5536c50
Upgrade test dependencies
ellykits Aug 24, 2023
32559a4
Merge branch 'main' into upgrade-gradle-dependencies
ellykits Aug 24, 2023
128d1be
Delete unused class
ellykits Aug 25, 2023
afeb385
Merge branch 'main' into upgrade-gradle-dependencies
ellykits Aug 25, 2023
f8dcfbf
Merge branch 'main' into upgrade-gradle-dependencies
ellykits Aug 25, 2023
a29d5a5
Refactor repositories to use new Search API SearchResult
ellykits Aug 29, 2023
79e01fb
Refactor code with latest SDK changes
ellykits Aug 29, 2023
3ceadb8
Fix failing tests
ellykits Aug 30, 2023
ce78e63
Merge branch 'main' into upgrade-gradle-dependencies
ellykits Aug 30, 2023
40abb62
Merge branch 'main' into refactor-repositories
ellykits Aug 30, 2023
ece42e7
Fix focus requester on PinCell UI
ellykits Aug 31, 2023
c12e92b
Merge branch 'main' into upgrade-gradle-dependencies
ellykits Aug 31, 2023
8af3518
Merge branch 'upgrade-gradle-dependencies' into refactor-repositories
ellykits Aug 31, 2023
de24f4b
Refactor geowidget and engine tests
ellykits Sep 1, 2023
ffd9b29
Merge branch 'main' into refactor-repositories
ellykits Sep 1, 2023
053835d
Refactor quest module tests
ellykits Sep 1, 2023
048b8c0
Fix failing tests for engine module
ellykits Sep 6, 2023
5337c39
Delete duplicated test classes
ellykits Sep 6, 2023
1ee8eea
Merge branch 'main' into refactor-repositories
ellykits Sep 6, 2023
2b15b78
Fix package name on CI script
ellykits Sep 6, 2023
29604a6
Resolve failing tests in quest module
ellykits Sep 6, 2023
0cc66f4
Refactor QuestionnaireResponse/Questionnaire validation implementation
ellykits Sep 7, 2023
0587295
Merge branch 'main' into refactor-repositories
ellykits Sep 12, 2023
b0d5b53
Merge branch 'main' into refactor-repositories
ellykits Sep 12, 2023
d5d55f0
Format code
ellykits Sep 12, 2023
c653ebe
Replace the resource.db file in assets
ellykits Sep 12, 2023
14fe317
Cleanup unused code
ellykits Sep 13, 2023
fd51f26
Replace resources.zip file
ellykits Sep 13, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/performance_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/Users/runner/Library/Android/sdk/platform-tools/adb install quest/build/outputs/apk/androidTest/ecbis/debug/quest-ecbis-debug-androidTest.apk && \
./gradlew :quest:assembleEcbisDebug --stacktrace && \
/Users/runner/Library/Android/sdk/platform-tools/adb install quest/build/outputs/apk/ecbis/debug/quest-ecbis-debug.apk && \
/Users/runner/Library/Android/sdk/platform-tools/adb shell am instrument -w -e package org.smartregister.fhircore.performance -e "androidx.benchmark.suppressErrors" ACTIVITY-MISSING,CODE-COVERAGE,DEBUGGABLE,UNLOCKED,EMULATOR -e additionalTestOutputDir "/sdcard/Download/" org.smartregister.opensrp.ecbis.test/org.smartregister.fhircore.quest.QuestTestRunner && \
/Users/runner/Library/Android/sdk/platform-tools/adb shell am instrument -w -e package org.smartregister.fhircore.quest.performance -e "androidx.benchmark.suppressErrors" ACTIVITY-MISSING,CODE-COVERAGE,DEBUGGABLE,UNLOCKED,EMULATOR -e additionalTestOutputDir "/sdcard/Download/" org.smartregister.opensrp.ecbis.test/org.smartregister.fhircore.quest.QuestTestRunner && \
/Users/runner/Library/Android/sdk/platform-tools/adb shell ls /sdcard/Download/ && \
/Users/runner/Library/Android/sdk/platform-tools/adb pull /sdcard/Download/org.smartregister.opensrp.ecbis-benchmarkData.json quest/ && \
./gradlew :quest:assembleOpensrpDebug --stacktrace
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import ca.uhn.fhir.context.api.BundleInclusionRule
import ca.uhn.fhir.model.valueset.BundleTypeEnum
import ca.uhn.fhir.rest.api.BundleLinks
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory
import com.google.android.fhir.get
import com.google.android.fhir.logicalId
import com.google.common.collect.Lists
import javax.inject.Inject
Expand Down Expand Up @@ -249,7 +248,7 @@ class LibraryEvaluator @Inject constructor(val defaultRepository: DefaultReposit
listOfNotNull(
patient,
*data.entry.map { it.resource }.toTypedArray(),
*defaultRepository.search(library.dataRequirementFirstRep).toTypedArray(),
*defaultRepository.searchCondition(library.dataRequirementFirstRep).toTypedArray(),
),
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion
import com.google.android.fhir.search.filter.TokenParamFilterCriterion
import com.google.android.fhir.search.has
import com.google.android.fhir.search.include
import com.google.android.fhir.search.revInclude
import com.google.android.fhir.search.search
import java.util.Date
import java.util.LinkedList
Expand Down Expand Up @@ -103,47 +105,43 @@
}
}

suspend inline fun <reified T : Resource> searchResourceFor(
subjectId: String,
subjectType: ResourceType = ResourceType.Patient,
subjectParam: ReferenceClientParam,
filters: List<DataQuery>? = null,
configComputedRuleValues: Map<String, Any> = emptyMap(),
): List<T> =
withContext(dispatcherProvider.io()) {
fhirEngine.search {
filterByResourceTypeId(subjectParam, subjectType, subjectId)
filters?.forEach { filterBy(it, configComputedRuleValues = configComputedRuleValues) }
}
}

suspend inline fun <reified T : Resource> searchResourceFor(
token: TokenClientParam,
subjectType: ResourceType,
subjectId: String,
filters: List<DataQuery> = listOf(),
dataQueries: List<DataQuery> = listOf(),

Check warning on line 112 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L112

Added line #L112 was not covered by tests
configComputedRuleValues: Map<String, Any>,
): List<T> =
withContext(dispatcherProvider.io()) {
fhirEngine.search {
filterByResourceTypeId(token, subjectType, subjectId)
filters.forEach { filterBy(it, configComputedRuleValues) }
}
fhirEngine
.search<T> {
filterByResourceTypeId(token, subjectType, subjectId)
dataQueries.forEach {
filterBy(
dataQuery = it,
configComputedRuleValues = configComputedRuleValues,

Check warning on line 122 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L120-L122

Added lines #L120 - L122 were not covered by tests
)
}
}
.map { it.resource }
}

suspend fun search(dataRequirement: DataRequirement) =
suspend fun searchCondition(dataRequirement: DataRequirement) =
when (dataRequirement.type) {
Enumerations.ResourceType.CONDITION.toCode() ->
fhirEngine.search<Condition> {
dataRequirement.codeFilter.forEach {
filter(TokenClientParam(it.path), { value = of(it.codeFirstRep) })
fhirEngine
.search<Condition> {
dataRequirement.codeFilter.forEach {
filter(TokenClientParam(it.path), { value = of(it.codeFirstRep) })
}
// TODO handle date filter
}
// TODO handle date filter
}
.map { it.resource }
else -> listOf()
}

suspend inline fun <reified R : Resource> search(search: Search) = fhirEngine.search<R>(search)
suspend inline fun <reified R : Resource> search(search: Search) =
fhirEngine.search<R>(search).map { it.resource }

Check warning on line 144 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L144

Added line #L144 was not covered by tests

/**
* Saves a resource in the database. It also updates the [Resource.meta.lastUpdated] and generates
Expand Down Expand Up @@ -246,22 +244,20 @@
}
}

suspend fun loadManagingEntity(group: Group, configComputedRuleValues: Map<String, Any>) =
suspend fun loadManagingEntity(group: Group) =
group.managingEntity?.let { reference ->
searchResourceFor<RelatedPerson>(
token = RelatedPerson.RES_ID,
subjectType = ResourceType.RelatedPerson,
subjectId = reference.extractId(),
configComputedRuleValues = configComputedRuleValues,
)
fhirEngine
.search<RelatedPerson> {
filter(RelatedPerson.RES_ID, { value = of(reference.extractId()) })
}
.map { it.resource }
.firstOrNull()
?.let { relatedPerson ->
searchResourceFor<Patient>(
token = Patient.RES_ID,
subjectType = ResourceType.Patient,
subjectId = relatedPerson.patient.extractId(),
configComputedRuleValues = configComputedRuleValues,
)
fhirEngine
.search<Patient> {
filter(Patient.RES_ID, { value = of(relatedPerson.patient.extractId()) })
}
.map { it.resource }
.firstOrNull()
}
}
Expand Down Expand Up @@ -416,7 +412,10 @@
resourceConfig.nestedSearchResources?.forEach {
has(it.resourceType, ReferenceClientParam((it.referenceParam))) {
it.dataQueries?.forEach { dataQuery ->
filterBy(dataQuery = dataQuery, configComputedRuleValues = configComputedRuleValues)
(this as Search).filterBy(
dataQuery = dataQuery,
configComputedRuleValues = configComputedRuleValues,

Check warning on line 417 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L415-L417

Added lines #L415 - L417 were not covered by tests
)
}
}
}
Expand Down Expand Up @@ -449,19 +448,6 @@
relatedResourceWrapper: RelatedResourceWrapper,
configComputedRuleValues: Map<String, Any>,
): RelatedResourceWrapper {
// Forward include related resources e.g. Members (Patient) referenced in Group resource
val forwardIncludeResourceConfigs =
relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(false)
if (!forwardIncludeResourceConfigs.isNullOrEmpty()) {
searchWithRevInclude(
isRevInclude = false,
relatedResourcesConfigs = forwardIncludeResourceConfigs,
resources = resources,
relatedResourceWrapper = relatedResourceWrapper,
configComputedRuleValues = configComputedRuleValues,
)
}

val countResourceConfigs = relatedResourcesConfigs?.filter { it.resultAsCount }
countResourceConfigs?.forEach { resourceConfig ->
if (resourceConfig.searchParameter.isNullOrEmpty()) {
Expand All @@ -480,7 +466,10 @@
}
apply
}
filter(ReferenceClientParam(resourceConfig.searchParameter), *filters.toTypedArray())
filter(
ReferenceClientParam(resourceConfig.searchParameter),
*filters.toTypedArray(),

Check warning on line 471 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L469-L471

Added lines #L469 - L471 were not covered by tests
)
applyConfiguredSortAndFilters(
resourceConfig = resourceConfig,
sortData = false,
Expand Down Expand Up @@ -511,18 +500,12 @@
}
}

// Reverse include related resources e.g. All CarePlans, Immunization for Patient resource
val reverseIncludeResourceConfigs =
relatedResourcesConfigs?.revIncludeRelatedResourceConfigs(true)
if (!reverseIncludeResourceConfigs.isNullOrEmpty()) {
searchWithRevInclude(
isRevInclude = true,
relatedResourcesConfigs = reverseIncludeResourceConfigs,
resources = resources,
relatedResourceWrapper = relatedResourceWrapper,
configComputedRuleValues = configComputedRuleValues,
)
}
searchIncludedResources(
relatedResourcesConfigs = relatedResourcesConfigs,
resources = resources,
relatedResourceWrapper = relatedResourceWrapper,
configComputedRuleValues = configComputedRuleValues,
)

return relatedResourceWrapper
}
Expand Down Expand Up @@ -582,13 +565,11 @@
}

/**
* If [isRevInclude] is set to false, the forward include search API will be used; otherwise
* reverse include is used to retrieve related resources. [relatedResourceWrapper] is a data class
* that wraps the maps used to store Search Query results. The [relatedResourcesConfigs]
* configures which resources to load.
* This function searches for reverse/forward included resources as per the configuration;
* [RelatedResourceWrapper] data class is then used to wrap the maps used to store Search Query
* results. The [relatedResourcesConfigs] configures which resources to load.
*/
private suspend fun searchWithRevInclude(
isRevInclude: Boolean,
private suspend fun searchIncludedResources(
relatedResourcesConfigs: List<ResourceConfig>?,
resources: List<Resource>,
relatedResourceWrapper: RelatedResourceWrapper,
Expand All @@ -610,26 +591,43 @@
filter(Resource.RES_ID, *filters.toTypedArray())
}

relatedResourcesConfigs.forEach { resourceConfig ->
search.apply {
applyConfiguredSortAndFilters(
resourceConfig = resourceConfig,
sortData = true,
configComputedRuleValues = configComputedRuleValues,
)
if (isRevInclude) {
revInclude(
resourceConfig.resource,
ReferenceClientParam(resourceConfig.searchParameter),
// Forward include related resources e.g. Members (Patient) referenced in Group resource
val forwardIncludeResourceConfigs =
relatedResourcesConfigs.revIncludeRelatedResourceConfigs(false)

// Reverse include related resources e.g. All CarePlans, Immunization for Patient resource
val reverseIncludeResourceConfigs =
relatedResourcesConfigs.revIncludeRelatedResourceConfigs(true)

search.apply {
reverseIncludeResourceConfigs.forEach { resourceConfig ->
revInclude(
resourceConfig.resource,
ReferenceClientParam(resourceConfig.searchParameter),
) {
(this as Search).applyConfiguredSortAndFilters(
resourceConfig = resourceConfig,
sortData = true,
configComputedRuleValues = configComputedRuleValues,
)
}
}

forwardIncludeResourceConfigs.forEach { resourceConfig ->
include(
resourceConfig.resource,
ReferenceClientParam(resourceConfig.searchParameter),
) {
(this as Search).applyConfiguredSortAndFilters(
resourceConfig = resourceConfig,
sortData = true,
configComputedRuleValues = configComputedRuleValues,
)
} else {
include(ReferenceClientParam(resourceConfig.searchParameter), resourceConfig.resource)
}
}
}

searchRelatedResources(
isRevInclude = isRevInclude,
search = search,
relatedResourcesConfigsMap = relatedResourcesConfigsMap,
relatedResourceWrapper = relatedResourceWrapper,
Expand All @@ -639,16 +637,24 @@
}

private suspend fun searchRelatedResources(
isRevInclude: Boolean,
search: Search,
relatedResourcesConfigsMap: Map<ResourceType, List<ResourceConfig>>,
relatedResourceWrapper: RelatedResourceWrapper,
configComputedRuleValues: Map<String, Any>,
) {
kotlin
.runCatching { fhirEngine.searchWithRevInclude<Resource>(isRevInclude, search) }
.runCatching { fhirEngine.search<Resource>(search) }
.onSuccess { searchResult ->
searchResult.values.forEach { theRelatedResourcesMap: Map<ResourceType, List<Resource>> ->
searchResult.forEach { currentSearchResult ->
val includedResources: Map<ResourceType, List<Resource>>? =
currentSearchResult.included?.values?.flatten()?.groupBy { it.resourceType }
val reverseIncludedResources: Map<ResourceType, List<Resource>>? =
currentSearchResult.revIncluded?.values?.flatten()?.groupBy { it.resourceType }
val theRelatedResourcesMap =
mutableMapOf<ResourceType, List<Resource>>().apply {
includedResources?.let { putAll(it) }
reverseIncludedResources?.let { putAll(it) }
}
theRelatedResourcesMap.forEach { entry ->
val currentResourceConfigs = relatedResourcesConfigsMap[entry.key]

Expand Down Expand Up @@ -700,8 +706,8 @@
*/
val computedValuesMap = mutableMapOf<String, Any>()
initialComputedValuesMap.forEach { entry ->
computedValuesMap[entry.key] =
"${entry.value.toString().substringBefore("/")}/${entry.value.toString().extractLogicalIdUuid()}"

Check warning on line 710 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L709-L710

Added lines #L709 - L710 were not covered by tests
}

Timber.i("Computed values map = ${computedValuesMap.values}")
Expand All @@ -714,7 +720,7 @@
configComputedRuleValues = computedValuesMap,
)
}
val resources = fhirEngine.search<Resource>(search)
val resources = fhirEngine.search<Resource>(search).map { it.resource }
resources.forEach {
Timber.i("Closing Resource type ${it.resourceType.name} and id ${it.id}")
closeResource(it, resourceConfig)
Expand All @@ -737,7 +743,7 @@
"Closing related Resource type ${resource.resourceType.name} and id ${resource.id}",
)
if (filterRelatedResource(resource, resourceConfig)) {
closeResource(resource, resourceConfig)

Check warning on line 746 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L746

Added line #L746 was not covered by tests
}
}
}
Expand Down Expand Up @@ -774,8 +780,8 @@

fun filterRelatedResource(resource: Resource, resourceConfig: ResourceConfig): Boolean {
return resourceConfig.filterFhirPathExpressions?.any { filterFhirPathExpression ->
fhirPathDataExtractor.extractValue(resource, filterFhirPathExpression.key) ==
filterFhirPathExpression.value

Check warning on line 784 in android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt#L783-L784

Added lines #L783 - L784 were not covered by tests
} == true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,24 +113,26 @@ constructor(
.onFailure { Timber.e(it, "Error retrieving resources. Empty list returned by default") }
.getOrDefault(emptyList())

return baseFhirResources.map { baseFhirResource ->
return baseFhirResources.map { searchResult ->
val retrievedRelatedResources =
withContext(dispatcherProvider.io()) {
retrieveRelatedResources(
resources = listOf(baseFhirResource),
resources = listOf(searchResult.resource),
relatedResourcesConfigs = relatedResourcesConfig,
relatedResourceWrapper = RelatedResourceWrapper(),
configComputedRuleValues = configComputedRuleValues,
)
}
RepositoryResourceData(
resourceRulesEngineFactId = baseResourceConfig.id ?: baseResourceConfig.resource.name,
resource = baseFhirResource,
resource = searchResult.resource,
relatedResourcesMap = retrievedRelatedResources.relatedResourceMap,
relatedResourcesCountMap = retrievedRelatedResources.relatedResourceCountMap,
secondaryRepositoryResourceData =
withContext(dispatcherProvider.io()) {
secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(filterActiveResources)
secondaryResourceConfigs.retrieveSecondaryRepositoryResourceData(
filterActiveResources,
)
},
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ package org.smartregister.fhircore.engine.di

import android.accounts.AccountManager
import android.content.Context
import androidx.work.WorkManager
import ca.uhn.fhir.context.FhirContext
import ca.uhn.fhir.context.FhirVersionEnum
import com.google.android.fhir.FhirEngine
import com.google.android.fhir.knowledge.KnowledgeManager
import com.google.android.fhir.sync.Sync
import com.google.android.fhir.workflow.FhirOperator
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -69,6 +67,4 @@ class CoreModule {
@Provides
fun provideFhirOperator(fhirEngine: FhirEngine): FhirOperator =
FhirOperator(fhirContext = FhirContext.forCached(FhirVersionEnum.R4), fhirEngine = fhirEngine)

@Singleton @Provides fun provideSync(workManager: WorkManager) = Sync(workManager)
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class FhirEngineModule {
FhirEngineProvider.init(
FhirEngineConfiguration(
enableEncryptionIfSupported = !BuildConfig.DEBUG,
DatabaseErrorStrategy.UNSPECIFIED,
databaseErrorStrategy = DatabaseErrorStrategy.UNSPECIFIED,
ServerConfiguration(
baseUrl = configService.provideAuthConfiguration().fhirServerBaseUrl,
authenticator = tokenAuthenticator,
Expand Down
Loading
Loading