Skip to content

Commit

Permalink
Changes in engine to Remove SyncedResourceEntity from database. (#1902)
Browse files Browse the repository at this point in the history
* Changes in engine to not store lastupdate per resource in the db anymore for sync purpose

* spotless apply

* Review comment changes

* moved function inline
  • Loading branch information
aditya-07 committed Mar 24, 2023
1 parent 48ede49 commit 7b9d8cd
Show file tree
Hide file tree
Showing 21 changed files with 229 additions and 299 deletions.
1 change: 1 addition & 0 deletions demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dependencies {
implementation(Dependencies.Androidx.activity)
implementation(Dependencies.Androidx.appCompat)
implementation(Dependencies.Androidx.constraintLayout)
implementation(Dependencies.Androidx.datastorePref)
implementation(Dependencies.Androidx.fragmentKtx)
implementation(Dependencies.Androidx.recyclerView)
implementation(Dependencies.Androidx.workRuntimeKtx)
Expand Down
46 changes: 46 additions & 0 deletions demo/src/main/java/com/google/android/fhir/demo/DemoDataStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.android.fhir.demo

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.fhir.sync.DownloadWorkManager
import kotlinx.coroutines.flow.first
import org.hl7.fhir.r4.model.ResourceType

private val Context.dataStorage: DataStore<Preferences> by
preferencesDataStore(name = "demo_app_storage")

/**
* Stores the lastUpdated timestamp per resource to be used by [DownloadWorkManager]'s
* implementation for optimal sync. See
* [_lastUpdated](https://build.fhir.org/search.html#_lastUpdated).
*/
class DemoDataStore(private val context: Context) {

suspend fun saveLastUpdatedTimestamp(resourceType: ResourceType, timestamp: String) {
context.dataStorage.edit { pref -> pref[stringPreferencesKey(resourceType.name)] = timestamp }
}

suspend fun getLasUpdateTimestamp(resourceType: ResourceType): String? {
return context.dataStorage.data.first()[stringPreferencesKey(resourceType.name)]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class FhirApplication : Application(), DataCaptureConfig.Provider {

private var dataCaptureConfig: DataCaptureConfig? = null

private val dataStore by lazy { DemoDataStore(this) }

override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Expand Down Expand Up @@ -69,6 +71,8 @@ class FhirApplication : Application(), DataCaptureConfig.Provider {

companion object {
fun fhirEngine(context: Context) = (context.applicationContext as FhirApplication).fhirEngine

fun dataStore(context: Context) = (context.applicationContext as FhirApplication).dataStore
}

override fun getDataCaptureConfig(): DataCaptureConfig = dataCaptureConfig ?: DataCaptureConfig()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class FhirSyncWorker(appContext: Context, workerParams: WorkerParameters) :
FhirSyncWorker(appContext, workerParams) {

override fun getDownloadWorkManager(): DownloadWorkManager {
return DownloadWorkManagerImpl()
return TimestampBasedDownloadWorkManagerImpl(FhirApplication.dataStore(applicationContext))
}

override fun getConflictResolver() = AcceptLocalConflictResolver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@

package com.google.android.fhir.demo.data

import com.google.android.fhir.SyncDownloadContext
import com.google.android.fhir.demo.DemoDataStore
import com.google.android.fhir.sync.DownloadWorkManager
import com.google.android.fhir.sync.SyncDataParams
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.LinkedList
import java.util.Locale
import org.hl7.fhir.exceptions.FHIRException
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.ListResource
Expand All @@ -28,25 +32,29 @@ import org.hl7.fhir.r4.model.Reference
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType

class DownloadWorkManagerImpl : DownloadWorkManager {
class TimestampBasedDownloadWorkManagerImpl(private val dataStore: DemoDataStore) :
DownloadWorkManager {
private val resourceTypeList = ResourceType.values().map { it.name }
private val urls =
LinkedList(listOf("Patient?address-city=NAIROBI", "Binary?_id=android-fhir-thermometer-image"))
LinkedList(
listOf(
"Patient?address-city=NAIROBI&_sort=_lastUpdated",
"Binary?_id=android-fhir-thermometer-image"
)
)

override suspend fun getNextRequestUrl(context: SyncDownloadContext): String? {
override suspend fun getNextRequestUrl(): String? {
var url = urls.poll() ?: return null

val resourceTypeToDownload =
ResourceType.fromCode(url.findAnyOf(resourceTypeList, ignoreCase = true)!!.second)
context.getLatestTimestampFor(resourceTypeToDownload)?.let {
url = affixLastUpdatedTimestamp(url!!, it)
dataStore.getLasUpdateTimestamp(resourceTypeToDownload)?.let {
url = affixLastUpdatedTimestamp(url, it)
}
return url
}

override suspend fun getSummaryRequestUrls(
context: SyncDownloadContext
): Map<ResourceType, String> {
override suspend fun getSummaryRequestUrls(): Map<ResourceType, String> {
return urls.associate {
ResourceType.fromCode(it.substringBefore("?")) to
it.plus("&${SyncDataParams.SUMMARY_KEY}=${SyncDataParams.SUMMARY_COUNT_VALUE}")
Expand Down Expand Up @@ -86,10 +94,26 @@ class DownloadWorkManagerImpl : DownloadWorkManager {
// Finally, extract the downloaded resources from the bundle.
var bundleCollection: Collection<Resource> = mutableListOf()
if (response is Bundle && response.type == Bundle.BundleType.SEARCHSET) {
bundleCollection = response.entry.map { it.resource }
bundleCollection =
response.entry
.map { it.resource }
.also { extractAndSaveLastUpdateTimestampToFetchFutureUpdates(it) }
}
return bundleCollection
}

private suspend fun extractAndSaveLastUpdateTimestampToFetchFutureUpdates(
resources: List<Resource>
) {
resources
.groupBy { it.resourceType }
.entries.map { map ->
dataStore.saveLastUpdatedTimestamp(
map.key,
map.value.maxOfOrNull { it.meta.lastUpdated }?.toTimeZoneString() ?: ""
)
}
}
}

/**
Expand Down Expand Up @@ -121,3 +145,10 @@ private fun affixLastUpdatedTimestamp(url: String, lastUpdated: String): String

return downloadUrl
}

private fun Date.toTimeZoneString(): String {
val simpleDateFormat =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.getDefault())
.withZone(ZoneId.systemDefault())
return simpleDateFormat.format(this.toInstant())
}
9 changes: 4 additions & 5 deletions engine/src/main/java/com/google/android/fhir/DatastoreUtil.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Google LLC
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,11 +26,10 @@ import java.time.OffsetDateTime
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "FHIR_ENGINE_PREF_DATASTORE"
)
private val Context.dataStore: DataStore<Preferences> by
preferencesDataStore(name = "FHIR_ENGINE_PREF_DATASTORE")

class DatastoreUtil(private val context: Context) {
internal class DatastoreUtil(private val context: Context) {
private val lastSyncTimestampKey by lazy { stringPreferencesKey("LAST_SYNC_TIMESTAMP") }

fun readLastSyncTimestamp(): OffsetDateTime? {
Expand Down
6 changes: 1 addition & 5 deletions engine/src/main/java/com/google/android/fhir/FhirEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ interface FhirEngine {
*/
suspend fun syncDownload(
conflictResolver: ConflictResolver,
download: suspend (SyncDownloadContext) -> Flow<List<Resource>>
download: suspend () -> Flow<List<Resource>>
)

/**
Expand Down Expand Up @@ -127,7 +127,3 @@ suspend inline fun <reified R : Resource> FhirEngine.get(id: String): R {
suspend inline fun <reified R : Resource> FhirEngine.delete(id: String) {
delete(getResourceType(R::class.java), id)
}

interface SyncDownloadContext {
suspend fun getLatestTimestampFor(type: ResourceType): String?
}
13 changes: 1 addition & 12 deletions engine/src/main/java/com/google/android/fhir/db/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import com.google.android.fhir.db.impl.dao.LocalChangeToken
import com.google.android.fhir.db.impl.dao.SquashedLocalChange
import com.google.android.fhir.db.impl.entities.LocalChangeEntity
import com.google.android.fhir.db.impl.entities.ResourceEntity
import com.google.android.fhir.db.impl.entities.SyncedResourceEntity
import com.google.android.fhir.search.SearchQuery
import java.time.Instant
import org.hl7.fhir.r4.model.Resource
Expand Down Expand Up @@ -79,22 +78,12 @@ internal interface Database {
@Throws(ResourceNotFoundException::class)
suspend fun selectEntity(type: ResourceType, id: String): ResourceEntity

/**
* Return the last update data of a resource based on the resource type. If no resource of
* [resourceType] is inserted, return `null`.
* @param resourceType The resource type
*/
suspend fun lastUpdate(resourceType: ResourceType): String?

/**
* Insert resources that were synchronised.
*
* @param syncedResources The synced resource
*/
suspend fun insertSyncedResources(
syncedResources: List<SyncedResourceEntity>,
resources: List<Resource>
)
suspend fun insertSyncedResources(resources: List<Resource>)

/**
* Deletes the FHIR resource of type `clazz` with `id`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import com.google.android.fhir.db.impl.dao.LocalChangeUtils
import com.google.android.fhir.db.impl.dao.SquashedLocalChange
import com.google.android.fhir.db.impl.entities.LocalChangeEntity
import com.google.android.fhir.db.impl.entities.ResourceEntity
import com.google.android.fhir.db.impl.entities.SyncedResourceEntity
import com.google.android.fhir.index.ResourceIndexer
import com.google.android.fhir.logicalId
import com.google.android.fhir.search.SearchQuery
Expand Down Expand Up @@ -103,7 +102,7 @@ internal class DatabaseImpl(
it.resourceIndexer = resourceIndexer
}
}
private val syncedResourceDao = db.syncedResourceDao()

private val localChangeDao = db.localChangeDao().also { it.iParser = iParser }

override suspend fun <R : Resource> insert(vararg resource: R): List<String> {
Expand Down Expand Up @@ -154,18 +153,8 @@ internal class DatabaseImpl(
} as Resource
}

override suspend fun lastUpdate(resourceType: ResourceType): String? {
return db.withTransaction { syncedResourceDao.getLastUpdate(resourceType) }
}

override suspend fun insertSyncedResources(
syncedResources: List<SyncedResourceEntity>,
resources: List<Resource>
) {
db.withTransaction {
syncedResourceDao.insertAll(syncedResources)
insertRemote(*resources.toTypedArray())
}
override suspend fun insertSyncedResources(resources: List<Resource>) {
db.withTransaction { insertRemote(*resources.toTypedArray()) }
}

override suspend fun delete(type: ResourceType, id: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Google LLC
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,7 +21,6 @@ import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.google.android.fhir.db.impl.dao.LocalChangeDao
import com.google.android.fhir.db.impl.dao.ResourceDao
import com.google.android.fhir.db.impl.dao.SyncedResourceDao
import com.google.android.fhir.db.impl.entities.DateIndexEntity
import com.google.android.fhir.db.impl.entities.DateTimeIndexEntity
import com.google.android.fhir.db.impl.entities.LocalChangeEntity
Expand All @@ -31,7 +30,6 @@ import com.google.android.fhir.db.impl.entities.QuantityIndexEntity
import com.google.android.fhir.db.impl.entities.ReferenceIndexEntity
import com.google.android.fhir.db.impl.entities.ResourceEntity
import com.google.android.fhir.db.impl.entities.StringIndexEntity
import com.google.android.fhir.db.impl.entities.SyncedResourceEntity
import com.google.android.fhir.db.impl.entities.TokenIndexEntity
import com.google.android.fhir.db.impl.entities.UriIndexEntity

Expand All @@ -47,15 +45,14 @@ import com.google.android.fhir.db.impl.entities.UriIndexEntity
DateIndexEntity::class,
DateTimeIndexEntity::class,
NumberIndexEntity::class,
SyncedResourceEntity::class,
LocalChangeEntity::class,
PositionIndexEntity::class],
PositionIndexEntity::class
],
version = 1,
exportSchema = false
)
@TypeConverters(DbTypeConverters::class)
internal abstract class ResourceDatabase : RoomDatabase() {
abstract fun resourceDao(): ResourceDao
abstract fun syncedResourceDao(): SyncedResourceDao
abstract fun localChangeDao(): LocalChangeDao
}

This file was deleted.

Loading

0 comments on commit 7b9d8cd

Please sign in to comment.