Skip to content

Commit

Permalink
Fix engine upload sync (#1893)
Browse files Browse the repository at this point in the history
* Added Body annotation for retrofit upload api

* Logging exception when uploading

* Added tests

* Updated tests and removed some dependency

* Update engine/src/test/java/com/google/android/fhir/sync/remote/RemoteFhirServiceTest.kt

* Update engine/src/test/java/com/google/android/fhir/sync/remote/RemoteFhirServiceTest.kt

---------

Co-authored-by: PallaviGanorkar <[email protected]>
Co-authored-by: Jing Tang <[email protected]>
  • Loading branch information
3 people committed Mar 9, 2023
1 parent 848754f commit f87fa04
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 5 deletions.
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ object Dependencies {

object Retrofit {
const val coreRetrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
const val gsonConverter = "com.squareup.retrofit2:converter-gson:${Versions.retrofit}"
}

object Room {
Expand Down Expand Up @@ -180,6 +179,7 @@ object Dependencies {
const val guava = "com.google.guava:guava:${Versions.guava}"
const val httpInterceptor = "com.squareup.okhttp3:logging-interceptor:${Versions.http}"
const val http = "com.squareup.okhttp3:okhttp:${Versions.http}"
const val mockWebServer = "com.squareup.okhttp3:mockwebserver:${Versions.http}"

const val jsonToolsPatch = "com.github.java-json-tools:json-patch:${Versions.jsonToolsPatch}"
const val kotlinPoet = "com.squareup:kotlinpoet:${Versions.kotlinPoet}"
Expand Down
2 changes: 1 addition & 1 deletion engine/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ dependencies {
implementation(Dependencies.Kotlin.stdlib)
implementation(Dependencies.Lifecycle.liveDataKtx)
implementation(Dependencies.Retrofit.coreRetrofit)
implementation(Dependencies.Retrofit.gsonConverter)
implementation(Dependencies.Room.ktx)
implementation(Dependencies.Room.runtime)
implementation(Dependencies.androidFhirCommon)
Expand All @@ -130,6 +129,7 @@ dependencies {
testImplementation(Dependencies.junit)
testImplementation(Dependencies.mockitoInline)
testImplementation(Dependencies.mockitoKotlin)
testImplementation(Dependencies.mockWebServer)
testImplementation(Dependencies.robolectric)
testImplementation(Dependencies.truth)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import okhttp3.logging.HttpLoggingInterceptor
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.Resource
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Url
Expand All @@ -36,7 +36,7 @@ internal interface RemoteFhirService : DataSource {

@GET override suspend fun download(@Url path: String): Resource

@POST(".") override suspend fun upload(bundle: Bundle): Resource
@POST(".") override suspend fun upload(@Body bundle: Bundle): Resource

class Builder(
private val baseUrl: String,
Expand Down Expand Up @@ -81,7 +81,6 @@ internal interface RemoteFhirService : DataSource {
.baseUrl(baseUrl)
.client(client)
.addConverterFactory(FhirConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RemoteFhirService::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.OperationOutcome
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
import timber.log.Timber

/** [Uploader] implementation to work with Fhir [Bundle]. */
internal class BundleUploader(
Expand All @@ -51,6 +52,7 @@ internal class BundleUploader(
completed += bundle.entry.size
emit(getUploadResult(response, localChangeTokens, total, completed))
} catch (e: Exception) {
Timber.e(e)
emit(UploadResult.Failure(ResourceSyncException(ResourceType.Bundle, e)))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* 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.sync.remote

import ca.uhn.fhir.context.FhirContext
import com.google.android.fhir.NetworkConfiguration
import com.google.android.fhir.logicalId
import com.google.common.truth.Truth.assertThat
import java.net.HttpURLConnection
import kotlinx.coroutines.test.runTest
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.HumanName
import org.hl7.fhir.r4.model.Patient
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class RemoteFhirServiceTest {

private val mockWebServer = MockWebServer()

private val apiService by lazy {
RemoteFhirService.Builder(mockWebServer.url("/").toString(), NetworkConfiguration()).build()
}

private val parser = FhirContext.forR4Cached().newJsonParser()

@Before
fun setUp() {
mockWebServer.start()
}

@After
fun shutdown() {
mockWebServer.shutdown()
}

@Test // https://github.com/google/android-fhir/issues/1892
fun `should assemble download request correctly`() = runTest {
// checks that a download request can be made successfully with parameters without exception
val mockResponse =
MockResponse().apply {
setResponseCode(HttpURLConnection.HTTP_OK)
setBody(
parser.encodeResourceToString(
Patient().apply {
id = "patient-001"
addName(
HumanName().apply {
addGiven("John")
family = "Doe"
}
)
}
)
)
}
mockWebServer.enqueue(mockResponse)

val result = apiService.download("Patient/patient-001")

// No exception should occur
assertThat(result).isInstanceOf(Patient::class.java)
}

@Test // https://github.com/google/android-fhir/issues/1892
fun `should assemble upload request correctly`() = runTest {
// checks that a upload request can be made successfully with parameters without exception
val mockResponse =
MockResponse().apply {
setResponseCode(HttpURLConnection.HTTP_OK)
setBody(
parser.encodeResourceToString(
Bundle().apply {
id = "transaction-response-1"
type = Bundle.BundleType.TRANSACTIONRESPONSE
}
)
)
}
mockWebServer.enqueue(mockResponse)
val request =
Bundle().apply {
id = "transaction-1"
type = Bundle.BundleType.TRANSACTION
}

val result = apiService.upload(request)

// No exception has occurred
assertThat(result).isInstanceOf(Bundle::class.java)
}

@Test
fun `should use fhir converter to serialize and deserialize request and response for fhir resources`() =
runTest {
val mockResponse =
MockResponse().apply {
setResponseCode(HttpURLConnection.HTTP_OK)
setBody(
parser.encodeResourceToString(
Bundle().apply {
id = "transaction-response-1"
type = Bundle.BundleType.TRANSACTIONRESPONSE
}
)
)
}
mockWebServer.enqueue(mockResponse)
val request =
Bundle().apply {
id = "transaction-1"
type = Bundle.BundleType.TRANSACTION
}

val result = apiService.upload(request)

assertThat(result).isInstanceOf(Bundle::class.java)
assertThat((result as Bundle).type).isEqualTo(Bundle.BundleType.TRANSACTIONRESPONSE)
assertThat((result).logicalId).isEqualTo("transaction-response-1")
}
}

0 comments on commit f87fa04

Please sign in to comment.