Skip to content

Commit

Permalink
Give unique name for periodic and oneTime sync worker (#2053)
Browse files Browse the repository at this point in the history
* distinct name for onetime

new stuff

Fix the version collision in Knowledge Manager  (#2043)

* Url is not a unique identifier for the library and should be used together with version. Made necessary changes to reflect that

* Truncate the input test file to fit the memory. Use the first library with the given url if the version wasn't explicitly provided

* Refactored text overflow in dropdown (#1987)

* Refactored text overflow in dropdown

* Removed unnecessary attributes from drop down list item xml

* Update datacapture/src/main/res/layout/drop_down_list_item.xml

---------

Co-authored-by: Jing Tang <[email protected]>

* Spotless apply

---------

Co-authored-by: Omar Ismail <[email protected]>
Co-authored-by: khyativyasargus <[email protected]>
Co-authored-by: Jing Tang <[email protected]>

Support basic HTTP authentication (#2038)

* Create a more generic and consistent way of providing authorization header

* Update engine/src/main/java/com/google/android/fhir/sync/HttpAuthenticator.kt

Co-authored-by: Omar Ismail <[email protected]>

* Update doc for HttpAuthenticator

---------

Co-authored-by: Omar Ismail <[email protected]>

Engine codelab (#2059)

* Initial version

* make app work

* add update function

* Run spotless apply

* Remove unnecessary header in FHIR Engine codelab README.md

---------

Co-authored-by: omarismail <[email protected]>

Simplify codelab code (#2060)

* Initial version

* make app work

* add update function

* Run spotless apply

* Remove unnecessary header in FHIR Engine codelab README.md

* Simplify engine codelab code

* Update codelabs/engine/app/src/main/java/com/google/android/fhir/codelabs/engine/DownloadWorkManagerImpl.kt

Co-authored-by: Omar Ismail <[email protected]>

---------

Co-authored-by: omarismail <[email protected]>
Co-authored-by: Omar Ismail <[email protected]>

Engine codelab (#2061)

* Initial version

* make app work

* add update function

* Run spotless apply

* Remove unnecessary header in FHIR Engine codelab README.md

* Simplify engine codelab code

* Update codelabs/engine/app/src/main/java/com/google/android/fhir/codelabs/engine/DownloadWorkManagerImpl.kt

Co-authored-by: Omar Ismail <[email protected]>

* Update engine codelab

* Run spotless

---------

Co-authored-by: omarismail <[email protected]>
Co-authored-by: Omar Ismail <[email protected]>

* address comments
  • Loading branch information
omarismail94 authored Jul 4, 2023
1 parent 36eb2ba commit dc5c2ba
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ import com.google.android.fhir.NetworkConfiguration
import com.google.android.fhir.ServerConfiguration
import com.google.android.fhir.datacapture.DataCaptureConfig
import com.google.android.fhir.datacapture.XFhirQueryResolver
import com.google.android.fhir.demo.data.FhirSyncWorker
import com.google.android.fhir.search.search
import com.google.android.fhir.sync.Sync
import com.google.android.fhir.sync.remote.HttpLogger
import org.hl7.fhir.r4.model.Patient
import timber.log.Timber

class FhirApplication : Application(), DataCaptureConfig.Provider {
Expand All @@ -46,7 +43,6 @@ class FhirApplication : Application(), DataCaptureConfig.Provider {
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Patient.IDENTIFIER
FhirEngineProvider.init(
FhirEngineConfiguration(
enableEncryptionIfSupported = true,
Expand All @@ -63,7 +59,6 @@ class FhirApplication : Application(), DataCaptureConfig.Provider {
)
)
)
Sync.oneTimeSync<FhirSyncWorker>(this)

dataCaptureConfig =
DataCaptureConfig().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.work.Constraints
import com.google.android.fhir.demo.data.FhirSyncWorker
import com.google.android.fhir.demo.data.DemoFhirSyncWorker
import com.google.android.fhir.sync.PeriodicSyncConfiguration
import com.google.android.fhir.sync.RepeatInterval
import com.google.android.fhir.sync.Sync
Expand All @@ -50,12 +50,13 @@ class MainActivityViewModel(application: Application) : AndroidViewModel(applica

init {
viewModelScope.launch {
Sync.periodicSync<FhirSyncWorker>(
Sync.periodicSync<DemoFhirSyncWorker>(
application.applicationContext,
PeriodicSyncConfiguration(
syncConstraints = Constraints.Builder().build(),
repeat = RepeatInterval(interval = 15, timeUnit = TimeUnit.MINUTES)
)
periodicSyncConfiguration =
PeriodicSyncConfiguration(
syncConstraints = Constraints.Builder().build(),
repeat = RepeatInterval(interval = 15, timeUnit = TimeUnit.MINUTES)
)
)
.shareIn(this, SharingStarted.Eagerly, 10)
.collect { _pollState.emit(it) }
Expand All @@ -64,7 +65,7 @@ class MainActivityViewModel(application: Application) : AndroidViewModel(applica

fun triggerOneTimeSync() {
viewModelScope.launch {
Sync.oneTimeSync<FhirSyncWorker>(getApplication())
Sync.oneTimeSync<DemoFhirSyncWorker>(getApplication())
.shareIn(this, SharingStarted.Eagerly, 10)
.collect { _pollState.emit(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.google.android.fhir.sync.AcceptLocalConflictResolver
import com.google.android.fhir.sync.DownloadWorkManager
import com.google.android.fhir.sync.FhirSyncWorker

class FhirSyncWorker(appContext: Context, workerParams: WorkerParameters) :
class DemoFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) :
FhirSyncWorker(appContext, workerParams) {

override fun getDownloadWorkManager(): DownloadWorkManager {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* 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://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.sync

import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.work.Constraints
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.testing.WorkManagerTestInitHelper
import com.google.android.fhir.FhirEngine
import com.google.android.fhir.testing.TestDataSourceImpl
import com.google.android.fhir.testing.TestDownloadManagerImpl
import com.google.android.fhir.testing.TestFhirEngineImpl
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class SyncInstrumentedTest {

private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext

class TestSyncWorker(appContext: Context, workerParams: WorkerParameters) :
FhirSyncWorker(appContext, workerParams) {

override fun getFhirEngine(): FhirEngine = TestFhirEngineImpl
override fun getDataSource(): DataSource = TestDataSourceImpl
override fun getDownloadWorkManager(): DownloadWorkManager = TestDownloadManagerImpl()
override fun getConflictResolver() = AcceptRemoteConflictResolver
}

@Test
fun oneTime_worker_runs() {
WorkManagerTestInitHelper.initializeTestWorkManager(context)
val workManager = WorkManager.getInstance(context)
runBlocking {
Sync.oneTimeSync<TestSyncWorker>(context = context)
.transformWhile {
emit(it is SyncJobStatus.Finished)
it !is SyncJobStatus.Finished
}
.shareIn(this, SharingStarted.Eagerly, 5)
}

assertThat(workManager.getWorkInfosByTag(TestSyncWorker::class.java.name).get().first().state)
.isEqualTo(WorkInfo.State.SUCCEEDED)
}

@Test
fun periodic_worker_still_queued_to_run_after_oneTime_worker_started() {
WorkManagerTestInitHelper.initializeTestWorkManager(context)
val workManager = WorkManager.getInstance(context)
// run and wait for periodic worker to finish
runBlocking {
Sync.periodicSync<TestSyncWorker>(
context = context,
periodicSyncConfiguration =
PeriodicSyncConfiguration(
syncConstraints = Constraints.Builder().build(),
repeat = RepeatInterval(interval = 15, timeUnit = TimeUnit.MINUTES)
),
)
.transformWhile {
emit(it)
it !is SyncJobStatus.Finished
}
.shareIn(this, SharingStarted.Eagerly, 5)
}

// Verify the periodic worker completed the run, and is queued to run again
val periodicWorkerId =
workManager.getWorkInfosByTag(TestSyncWorker::class.java.name).get().first().id
assertThat(workManager.getWorkInfoById(periodicWorkerId).get().state)
.isEqualTo(WorkInfo.State.ENQUEUED)

// Start and complete a oneTime job, and verify it does not remove the periodic worker
runBlocking {
Sync.oneTimeSync<TestSyncWorker>(context = context)
.transformWhile {
emit(it)
it !is SyncJobStatus.Finished
}
.shareIn(this, SharingStarted.Eagerly, 5)
}
assertThat(workManager.getWorkInfosByTag(TestSyncWorker::class.java.name).get().size)
.isEqualTo(2)

// Move forward to the next epoch to trigger the periodic sync
val testDriver = WorkManagerTestInitHelper.getTestDriver(context)
testDriver?.setPeriodDelayMet(periodicWorkerId)

assertThat(workManager.getWorkInfoById(periodicWorkerId).get().state)
.isEqualTo(WorkInfo.State.RUNNING)
}
}
24 changes: 13 additions & 11 deletions engine/src/main/java/com/google/android/fhir/sync/Sync.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ object Sync {
context: Context,
retryConfiguration: RetryConfiguration? = defaultRetryConfiguration
): Flow<SyncJobStatus> {
val flow = getWorkerInfo<W>(context)
val uniqueWorkName = "${W::class.java.name}-oneTimeSync"
val flow = getWorkerInfo(context, uniqueWorkName)
WorkManager.getInstance(context)
.enqueueUniqueWork(
W::class.java.name,
uniqueWorkName,
ExistingWorkPolicy.KEEP,
createOneTimeWorkRequest(retryConfiguration, W::class.java)
)
Expand All @@ -80,20 +81,21 @@ object Sync {
context: Context,
periodicSyncConfiguration: PeriodicSyncConfiguration
): Flow<SyncJobStatus> {
val flow = getWorkerInfo<W>(context)
val uniqueWorkName = "${W::class.java.name}-periodicSync"
val flow = getWorkerInfo(context, uniqueWorkName)
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
W::class.java.name,
uniqueWorkName,
ExistingPeriodicWorkPolicy.KEEP,
createPeriodicWorkRequest(periodicSyncConfiguration, W::class.java)
)
return flow
}

/** Gets the worker info for the [FhirSyncWorker] */
inline fun <reified W : FhirSyncWorker> getWorkerInfo(context: Context) =
fun getWorkerInfo(context: Context, workName: String) =
WorkManager.getInstance(context)
.getWorkInfosForUniqueWorkLiveData(W::class.java.name)
.getWorkInfosForUniqueWorkLiveData(workName)
.asFlow()
.flatMapConcat { it.asFlow() }
.mapNotNull { workInfo ->
Expand All @@ -106,11 +108,6 @@ object Sync {
}
}

/** Gets the timestamp of the last sync job. */
fun getLastSyncTimestamp(context: Context): OffsetDateTime? {
return DatastoreUtil(context).readLastSyncTimestamp()
}

@PublishedApi
internal inline fun <W : FhirSyncWorker> createOneTimeWorkRequest(
retryConfiguration: RetryConfiguration?,
Expand Down Expand Up @@ -155,4 +152,9 @@ object Sync {
}
return periodicWorkRequestBuilder.build()
}

/** Gets the timestamp of the last sync job. */
fun getLastSyncTimestamp(context: Context): OffsetDateTime? {
return DatastoreUtil(context).readLastSyncTimestamp()
}
}

0 comments on commit dc5c2ba

Please sign in to comment.