From 4c9667c92aa35dda43eb9843c79d5cda2080b798 Mon Sep 17 00:00:00 2001 From: samuelwlee2 <100384063+samuelwlee2@users.noreply.github.com> Date: Sat, 23 Apr 2022 10:47:04 -0600 Subject: [PATCH] 3524 export poll status returning the wrong partition in the url (#3526) * test added and bulk data export provider fixed * add test and change jira number * change test * modify tests --- ...urning-the-wrong-partition-in-the-url.yaml | 4 + .../provider/r4/MultitenantServerR4Test.java | 75 ++++++++++++++++++- .../provider/BulkDataExportProvider.java | 11 ++- 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3524-export-poll-status-returning-the-wrong-partition-in-the-url.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3524-export-poll-status-returning-the-wrong-partition-in-the-url.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3524-export-poll-status-returning-the-wrong-partition-in-the-url.yaml new file mode 100644 index 00000000000..442e3806e13 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3524-export-poll-status-returning-the-wrong-partition-in-the-url.yaml @@ -0,0 +1,4 @@ +type: fix +issue: 3524 +jira: SMILE-3672 +title: "Previously if partitioning was enabled, the URL returned from `$export-poll-status` provided a location in the wrong partition. This has been fixed and all Bulk Exports will be stored in the `DEFAULT` partition." diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index 51f6d09a45f..bccb0389c88 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -1,15 +1,27 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.batch.config.BatchConstants; +import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper; +import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; +import ca.uhn.fhir.jpa.bulk.export.model.BulkExportJobStatusEnum; +import ca.uhn.fhir.jpa.bulk.export.model.BulkExportResponseJson; +import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.test.utilities.ITestDataBuilder; +import ca.uhn.fhir.util.JsonUtil; +import com.google.common.collect.Sets; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; @@ -18,17 +30,23 @@ import org.hl7.fhir.r4.model.Condition; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Date; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -36,6 +54,11 @@ import static org.junit.jupiter.api.Assertions.fail; @SuppressWarnings("Duplicates") public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Test implements ITestDataBuilder { + @Autowired + private IBulkDataExportSvc myBulkDataExportSvc; + @Autowired + private IBulkDataExportJobSchedulingHelper myBulkDataExportJobSchedulingHelper; + @Override @AfterEach public void after() throws Exception { @@ -370,6 +393,56 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te assertThat(response.getEntry(), hasSize(2)); } + @Test + public void testBulkExportForDifferentPartitions() throws IOException { + setBulkDataExportProvider(); + testBulkExport(TENANT_A); + testBulkExport(TENANT_B); + testBulkExport(JpaConstants.DEFAULT_PARTITION_NAME); + } + + private void testBulkExport(String createInPartition) throws IOException { + // Create a patient + IBaseResource patientA = buildPatient(withActiveTrue()); + SystemRequestDetails requestDetails = new SystemRequestDetails(); + requestDetails.setTenantId(createInPartition); + myPatientDao.create((Patient) patientA, requestDetails); + + // Create a bulk job + BulkDataExportOptions options = new BulkDataExportOptions(); + options.setResourceTypes(Sets.newHashSet("Patient")); + options.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(options, false, requestDetails); + assertNotNull(jobDetails.getJobId()); + + // Run a scheduled pass to build the export and wait for completion + myBulkDataExportJobSchedulingHelper.startSubmittedJobs(); + myBatchJobHelper.awaitAllBulkJobCompletions( + BatchConstants.BULK_EXPORT_JOB_NAME + ); + + //perform export-poll-status + HttpGet get = new HttpGet(buildExportUrl(createInPartition, jobDetails.getJobId())); + try (CloseableHttpResponse response = ourHttpClient.execute(get)) { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + BulkExportResponseJson responseJson = JsonUtil.deserialize(responseString, BulkExportResponseJson.class); + assertThat(responseJson.getOutput().get(0).getUrl(), containsString(JpaConstants.DEFAULT_PARTITION_NAME + "/Binary/")); + } + } + + private void setBulkDataExportProvider() { + BulkDataExportProvider provider = new BulkDataExportProvider(); + provider.setBulkDataExportSvcForUnitTests(myBulkDataExportSvc); + provider.setFhirContextForUnitTest(myFhirContext); + ourRestServer.registerProvider(provider); + } + + private String buildExportUrl(String createInPartition, String jobId) { + return myClient.getServerBase() + "/" + createInPartition + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + jobId; + } + private void createConditionWithAllowedUnqualified(IIdType idA) { myPartitionSettings.setAllowReferencesAcrossPartitions(PartitionSettings.CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED); IIdType idB = createResource("Condition", withTenant(TENANT_A), withObservationCode("http://cs", "A")); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java index 18cdff8d907..b1a31517cee 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java @@ -106,6 +106,15 @@ public class BulkDataExportProvider { return StringUtils.removeEnd(theRequestDetails.getServerBaseForRequest(), "/"); } + private String getDefaultPartitionServerBase(ServletRequestDetails theRequestDetails) { + if (theRequestDetails.getTenantId() == null || theRequestDetails.getTenantId().equals(JpaConstants.DEFAULT_PARTITION_NAME)) { + return getServerBase(theRequestDetails); + } + else { + return StringUtils.removeEnd(theRequestDetails.getServerBaseForRequest().replace(theRequestDetails.getTenantId(), JpaConstants.DEFAULT_PARTITION_NAME), "/"); + } + } + /** * Group/Id/$export */ @@ -197,7 +206,7 @@ public class BulkDataExportProvider { bulkResponseDocument.setTransactionTime(status.getStatusTime()); bulkResponseDocument.setRequest(status.getRequest()); for (IBulkDataExportSvc.FileEntry nextFile : status.getFiles()) { - String serverBase = getServerBase(theRequestDetails); + String serverBase = getDefaultPartitionServerBase(theRequestDetails); String nextUrl = serverBase + "/" + nextFile.getResourceId().toUnqualifiedVersionless().getValue(); bulkResponseDocument .addOutput()