From 1c5a07b5a0aab8d8fb5bf96a627c989afe2e5e54 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 30 Jan 2018 16:20:25 -0600 Subject: [PATCH] Fix #822 - Respect chained method params in a transaction search --- .../uhn/fhir/rest/param/TokenOrListParam.java | 8 +- .../server/util/JaxRsResponseDstu3Test.java | 4 +- .../jaxrs/server/util/JaxRsResponseTest.java | 4 +- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 2 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 2 +- .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 4 +- .../provider/ServletSubRequestDetails.java | 4 +- .../java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java | 3 +- .../fhir/jpa/dao/r4/FhirSystemDaoR4Test.java | 151 ++++++++------- ...SystemProviderTransactionSearchR4Test.java | 176 ++++++++++++------ .../fhir/rest/api/server/RequestDetails.java | 68 ++++--- .../uhn/fhir/rest/server/RestfulServer.java | 4 +- .../ResponseHighlighterInterceptor.java | 4 +- src/changes/changes.xml | 5 + 14 files changed, 261 insertions(+), 178 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenOrListParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenOrListParam.java index 17f572646cd..153cb035ff7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenOrListParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenOrListParam.java @@ -69,14 +69,12 @@ public class TokenOrListParam extends BaseOrListParam getListAsCodings() { diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java index cfcf62001d9..94126fa48d7 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java @@ -114,7 +114,7 @@ public class JaxRsResponseDstu3Test { boolean allowPrefer = true; String resourceName = "Patient"; MethodOutcome methodOutcome = new MethodOutcome(theId); - response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML}); + response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML}); boolean addContentLocationHeader = true; boolean respondGzip = true; Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request); @@ -126,7 +126,7 @@ public class JaxRsResponseDstu3Test { @Test public void testNoOutcomeXml() throws IOException { - response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML}); + response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML}); boolean addContentLocationHeader = true; boolean respondGzip = true; Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), null, theSummaryMode, 204, addContentLocationHeader, respondGzip, this.request); diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java index 1a4bffb1ad9..b9979da676b 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java @@ -88,7 +88,7 @@ public class JaxRsResponseTest { @Test public void testReturnResponseAsXml() throws IOException { - response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML }); + response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML }); boolean addContentLocationHeader = true; boolean respondGzip = true; Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request); @@ -100,7 +100,7 @@ public class JaxRsResponseTest { @Test public void testNoOutcomeXml() throws IOException { - response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML }); + response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML }); boolean addContentLocationHeader = true; boolean respondGzip = true; Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index 6b9225bad9e..b044f6d57c2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -532,7 +532,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { } for (java.util.Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) { String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); - requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue); + requestDetails.addParameter(nextParamEntry.getKey(), nextValue); } url = url.substring(0, qIndex); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 13c06ba885d..70ed8ec955b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -260,7 +260,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } for (java.util.Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) { String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); - requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue); + requestDetails.addParameter(nextParamEntry.getKey(), nextValue); } url = url.substring(0, qIndex); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 7ddf0286dda..6b2ad8519f1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -266,7 +266,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } for (java.util.Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) { String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); - requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue); + requestDetails.addParameter(nextParamEntry.getKey(), nextValue); } url = url.substring(0, qIndex); } @@ -290,7 +290,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch()); } - Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {}", url); + Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {0}", url); try { IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails); if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java index 254b30683ef..666200cb0a5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java @@ -29,13 +29,13 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; public class ServletSubRequestDetails extends ServletRequestDetails { - private Map> myHeaders = new HashMap>(); + private Map> myHeaders = new HashMap<>(); public void addHeader(String theName, String theValue) { String lowerCase = theName.toLowerCase(); ArrayList list = myHeaders.get(lowerCase); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); myHeaders.put(lowerCase, list); } list.add(theValue); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index bca7e69e457..c8a7c35f75c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -158,7 +158,7 @@ public abstract class BaseJpaTest { } protected List toUnqualifiedVersionlessIds(IBundleProvider theFound) { - List retVal = new ArrayList(); + List retVal = new ArrayList<>(); Integer size = theFound.size(); StopWatch sw = new StopWatch(); while (size == null) { @@ -171,6 +171,7 @@ public abstract class BaseJpaTest { } catch (InterruptedException theE) { //ignore } + size = theFound.size(); } ourLog.info("Found {} results", size); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index e499b7571b8..31cfcb39d78 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.SearchParameterMap; -import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; @@ -12,11 +11,11 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; -import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; @@ -25,7 +24,6 @@ import org.hl7.fhir.r4.model.Observation.ObservationStatus; import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; import org.junit.*; import org.mockito.ArgumentCaptor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; @@ -57,46 +55,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); } - /** - * See #811 - */ - @Test - public void testUpdatePreviouslyDeletedResourceInBatch() { - AllergyIntolerance ai = new AllergyIntolerance(); - ai.setId("AIA1914009"); - ai.setClinicalStatus(AllergyIntolerance.AllergyIntoleranceClinicalStatus.ACTIVE); - IIdType id = myAllergyIntoleranceDao.update(ai).getId(); - assertEquals("1", id.getVersionIdPart()); - - id = myAllergyIntoleranceDao.delete(ai.getIdElement().toUnqualifiedVersionless()).getId(); - assertEquals("2", id.getVersionIdPart()); - - try { - myAllergyIntoleranceDao.read(ai.getIdElement().toUnqualifiedVersionless()); - fail(); - } catch (ResourceGoneException e) { - // good - } - - Bundle batch = new Bundle(); - batch.setType(BundleType.BATCH); - ai = new AllergyIntolerance(); - ai.setId("AIA1914009"); - ai.setClinicalStatus(AllergyIntolerance.AllergyIntoleranceClinicalStatus.ACTIVE); - batch - .addEntry() - .setFullUrl("AllergyIntolerance/AIA1914009") - .setResource(ai) - .getRequest() - .setUrl("AllergyIntolerance/AIA1914009") - .setMethod(HTTPVerb.PUT); - mySystemDao.transaction(mySrd, batch); - - id = myAllergyIntoleranceDao.read(ai.getIdElement().toUnqualifiedVersionless()).getIdElement(); - assertEquals("3", id.getVersionIdPart()); - - } - @Before public void beforeDisableResultReuse() { myDaoConfig.setReuseCachedSearchResultsForMillis(null); @@ -2489,7 +2447,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { /* * Make sure we are able to handle placeholder IDs in match URLs, e.g. - * + * * "request": { * "method": "PUT", * "url": "Observation?subject=urn:uuid:8dba64a8-2aca-48fe-8b4e-8c7bf2ab695a&code=http%3A%2F%2Floinc.org|29463-7&date=2011-09-03T11:13:00-04:00" @@ -2519,7 +2477,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { /* * Make sure we are able to handle placeholder IDs in match URLs, e.g. - * + * * "request": { * "method": "PUT", * "url": "Observation?subject=urn:uuid:8dba64a8-2aca-48fe-8b4e-8c7bf2ab695a&code=http%3A%2F%2Floinc.org|29463-7&date=2011-09-03T11:13:00-04:00" @@ -2583,7 +2541,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString()); } - @Test public void testTransactionWithReferenceResource() { Bundle request = new Bundle(); @@ -2611,7 +2568,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { assertEquals(1, found.size().intValue()); } - @Test public void testTransactionWithReferenceToCreateIfNoneExist() { Bundle bundle = new Bundle(); @@ -2635,9 +2591,9 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { IdType medId1 = new IdType(outcome.getEntry().get(0).getResponse().getLocation()); IdType medOrderId1 = new IdType(outcome.getEntry().get(1).getResponse().getLocation()); - /* - * Again! - */ + /* + * Again! + */ bundle = new Bundle(); bundle.setType(BundleType.TRANSACTION); @@ -2665,6 +2621,33 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { assertNotEquals(medOrderId1, medOrderId2); } + @Test + public void testTransactionWithReferenceUuid() { + Bundle request = new Bundle(); + + Patient p = new Patient(); + p.setActive(true); + p.setId(IdType.newRandomUuid()); + request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId()); + + Observation o = new Observation(); + o.getCode().setText("Some Observation"); + o.getSubject().setReference(p.getId()); + request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST); + + Bundle resp = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + + String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue(); + assertThat(patientId, startsWith("Patient/")); + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("subject", new ReferenceParam(patientId)); + IBundleProvider found = myObservationDao.search(params); + assertEquals(1, found.size().intValue()); + } + // // // /** @@ -2767,34 +2750,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { // // } - - @Test - public void testTransactionWithReferenceUuid() { - Bundle request = new Bundle(); - - Patient p = new Patient(); - p.setActive(true); - p.setId(IdType.newRandomUuid()); - request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId()); - - Observation o = new Observation(); - o.getCode().setText("Some Observation"); - o.getSubject().setReference(p.getId()); - request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST); - - Bundle resp = mySystemDao.transaction(mySrd, request); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); - - String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue(); - assertThat(patientId, startsWith("Patient/")); - - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - params.add("subject", new ReferenceParam(patientId)); - IBundleProvider found = myObservationDao.search(params); - assertEquals(1, found.size().intValue()); - } - @Test public void testTransactionWithRelativeOidIds() throws Exception { Bundle res = new Bundle(); @@ -2959,6 +2914,46 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { } + /** + * See #811 + */ + @Test + public void testUpdatePreviouslyDeletedResourceInBatch() { + AllergyIntolerance ai = new AllergyIntolerance(); + ai.setId("AIA1914009"); + ai.setClinicalStatus(AllergyIntolerance.AllergyIntoleranceClinicalStatus.ACTIVE); + IIdType id = myAllergyIntoleranceDao.update(ai).getId(); + assertEquals("1", id.getVersionIdPart()); + + id = myAllergyIntoleranceDao.delete(ai.getIdElement().toUnqualifiedVersionless()).getId(); + assertEquals("2", id.getVersionIdPart()); + + try { + myAllergyIntoleranceDao.read(ai.getIdElement().toUnqualifiedVersionless()); + fail(); + } catch (ResourceGoneException e) { + // good + } + + Bundle batch = new Bundle(); + batch.setType(BundleType.BATCH); + ai = new AllergyIntolerance(); + ai.setId("AIA1914009"); + ai.setClinicalStatus(AllergyIntolerance.AllergyIntoleranceClinicalStatus.ACTIVE); + batch + .addEntry() + .setFullUrl("AllergyIntolerance/AIA1914009") + .setResource(ai) + .getRequest() + .setUrl("AllergyIntolerance/AIA1914009") + .setMethod(HTTPVerb.PUT); + mySystemDao.transaction(mySrd, batch); + + id = myAllergyIntoleranceDao.read(ai.getIdElement().toUnqualifiedVersionless()).getIdElement(); + assertEquals("3", id.getVersionIdPart()); + + } + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java index 0576915716b..7219a0025a4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java @@ -1,35 +1,41 @@ package ca.uhn.fhir.jpa.provider.r4; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.dao.SearchParameterMap; +import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; +import ca.uhn.fhir.jpa.rp.r4.*; +import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.util.TestUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Bundle.*; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Bundle.BundleType; +import org.hl7.fhir.r4.model.Bundle.HTTPVerb; import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; -import org.hl7.fhir.r4.model.Patient; -import org.junit.*; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; -import ca.uhn.fhir.jpa.rp.r4.*; -import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.util.TestUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { @@ -43,7 +49,6 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor; - @SuppressWarnings("deprecation") @After public void after() { @@ -57,7 +62,7 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor(); ourClient.registerInterceptor(mySimpleHeaderInterceptor); } - + @Before public void beforeStartServer() throws Exception { if (myRestServer == null) { @@ -73,8 +78,14 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { OrganizationResourceProvider organizationRp = new OrganizationResourceProvider(); organizationRp.setDao(myOrganizationDao); + MedicationResourceProvider medicationRp = new MedicationResourceProvider(); + medicationRp.setDao(myMedicationDao); + + MedicationRequestResourceProvider medicationRequestRp = new MedicationRequestResourceProvider(); + medicationRequestRp.setDao(myMedicationRequestDao); + RestfulServer restServer = new RestfulServer(ourCtx); - restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp); + restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp, medicationRequestRp, medicationRp); restServer.setPlainProviders(mySystemProvider); @@ -106,11 +117,10 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { ourClient.setLogRequestAndResponse(true); myRestServer = restServer; } - + myRestServer.setDefaultResponseEncoding(EncodingEnum.XML); myRestServer.setPagingProvider(myPagingProvider); } - private List create20Patients() { List ids = new ArrayList(); @@ -120,7 +130,7 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { patient.setId("" + letter); patient.setGender(AdministrativeGender.MALE); patient.addIdentifier().setSystem("urn:foo").setValue("A"); - patient.addName().setFamily("abcdefghijklmnopqrstuvwxyz".substring(i, i+1)); + patient.addName().setFamily("abcdefghijklmnopqrstuvwxyz".substring(i, i + 1)); String id = myPatientDao.update(patient).getId().toUnqualifiedVersionless().getValue(); ids.add(id); } @@ -130,7 +140,7 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { @Test public void testBatchWithGetHardLimitLargeSynchronous() { List ids = create20Patients(); - + Bundle input = new Bundle(); input.setType(BundleType.BATCH); input @@ -138,12 +148,12 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { .getRequest() .setMethod(HTTPVerb.GET) .setUrl("Patient?_count=5&_sort=_id"); - + myDaoConfig.setMaximumSearchResultCountInTransaction(100); - + Bundle output = ourClient.transaction().withBundle(input).execute(); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + assertEquals(1, output.getEntry().size()); Bundle respBundle = (Bundle) output.getEntry().get(0).getResource(); assertEquals(5, respBundle.getEntry().size()); @@ -151,11 +161,11 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { List actualIds = toIds(respBundle); assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0]))); } - + @Test public void testBatchWithGetNormalSearch() { List ids = create20Patients(); - + Bundle input = new Bundle(); input.setType(BundleType.BATCH); input @@ -163,16 +173,16 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { .getRequest() .setMethod(HTTPVerb.GET) .setUrl("Patient?_count=5&_sort=name"); - + Bundle output = ourClient.transaction().withBundle(input).execute(); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + assertEquals(1, output.getEntry().size()); Bundle respBundle = (Bundle) output.getEntry().get(0).getResource(); assertEquals(5, respBundle.getEntry().size()); List actualIds = toIds(respBundle); assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0]))); - + String nextPageLink = respBundle.getLink("next").getUrl(); output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute(); respBundle = output; @@ -188,7 +198,7 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { public void testBatchWithManyGets() { List ids = create20Patients(); - + Bundle input = new Bundle(); input.setType(BundleType.BATCH); for (int i = 0; i < 30; i++) { @@ -198,10 +208,10 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { .setMethod(HTTPVerb.GET) .setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i); } - + Bundle output = ourClient.transaction().withBundle(input).execute(); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + assertEquals(30, output.getEntry().size()); for (int i = 0; i < 30; i++) { Bundle respBundle = (Bundle) output.getEntry().get(i).getResource(); @@ -212,10 +222,72 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { } } + /** + * See #822 + */ + @Test + public void testSearchByBatch() { + Patient p = new Patient(); + p.setId("P3000254749"); + p.setActive(true); + myPatientDao.update(p); + + Medication med = new Medication(); + med.setId("MED19795"); + med.getCode().addCoding().setCode("00093-0058-05").setSystem("http://hl7.org/fhir/sid/ndc"); + myMedicationDao.update(med); + + med = new Medication(); + med.setId("MED20344"); + med.getCode().addCoding().setCode("50580-0449-23").setSystem("http://hl7.org/fhir/sid/ndc"); + myMedicationDao.update(med); + + MedicationRequest medRequest = new MedicationRequest(); + medRequest.setId("MR142528"); + medRequest.setMedication(new Reference("Medication/MED19795")); + medRequest.setSubject(new Reference("Patient/P3000254749")); + medRequest.setIntent(MedicationRequest.MedicationRequestIntent.ORDER); + myMedicationRequestDao.update(medRequest); + + medRequest = new MedicationRequest(); + medRequest.setId("MR635079"); + medRequest.setMedication(new Reference("Medication/MED20344")); + medRequest.setSubject(new Reference("Patient/P3000254749")); + medRequest.setIntent(MedicationRequest.MedicationRequestIntent.ORDER); + myMedicationRequestDao.update(medRequest); + + SearchParameterMap map = new SearchParameterMap(); + map.add(MedicationRequest.SP_INTENT, new TokenOrListParam().add(null, "plan").add(null, "order")); + map.add(MedicationRequest.SP_MEDICATION, new ReferenceParam().setChain("code").setValue("50580-0449-23")); + Bundle b = ourClient + .search() + .forResource("MedicationRequest") + .where(MedicationRequest.INTENT.exactly().codes("plan", "order")) + .and(MedicationRequest.MEDICATION.hasChainedProperty(Medication.CODE.exactly().code("50580-0449-23"))) + .returnBundle(Bundle.class) + .execute(); + assertEquals(1, b.getEntry().size()); + assertEquals("MedicationRequest/MR635079", b.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + + b = new Bundle(); + b.setType(BundleType.BATCH); + b.addEntry() + .setFullUrl(IdType.newRandomUuid().getValueAsString()) + .getRequest() + .setMethod(HTTPVerb.GET) + .setUrl("MedicationRequest?intent=plan,order&medication.code=50580-0449-23&patient=P3000254749"); + Bundle resp = ourClient.transaction().withBundle(b).execute(); + + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + b = (Bundle) resp.getEntry().get(0).getResource(); + assertEquals(1, b.getEntry().size()); + assertEquals("MedicationRequest/MR635079", b.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + } + @Test public void testTransactionWithGetHardLimitLargeSynchronous() { List ids = create20Patients(); - + Bundle input = new Bundle(); input.setType(BundleType.TRANSACTION); input @@ -223,12 +295,12 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { .getRequest() .setMethod(HTTPVerb.GET) .setUrl("Patient?_count=5&_sort=_id"); - + myDaoConfig.setMaximumSearchResultCountInTransaction(100); - + Bundle output = ourClient.transaction().withBundle(input).execute(); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + assertEquals(1, output.getEntry().size()); Bundle respBundle = (Bundle) output.getEntry().get(0).getResource(); assertEquals(5, respBundle.getEntry().size()); @@ -236,11 +308,11 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { List actualIds = toIds(respBundle); assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0]))); } - + @Test public void testTransactionWithGetNormalSearch() { List ids = create20Patients(); - + Bundle input = new Bundle(); input.setType(BundleType.TRANSACTION); input @@ -248,16 +320,16 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { .getRequest() .setMethod(HTTPVerb.GET) .setUrl("Patient?_count=5&_sort=name"); - + Bundle output = ourClient.transaction().withBundle(input).execute(); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + assertEquals(1, output.getEntry().size()); Bundle respBundle = (Bundle) output.getEntry().get(0).getResource(); assertEquals(5, respBundle.getEntry().size()); List actualIds = toIds(respBundle); assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0]))); - + String nextPageLink = respBundle.getLink("next").getUrl(); output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute(); respBundle = output; @@ -273,7 +345,7 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { public void testTransactionWithManyGets() { List ids = create20Patients(); - + Bundle input = new Bundle(); input.setType(BundleType.TRANSACTION); for (int i = 0; i < 30; i++) { @@ -283,10 +355,10 @@ public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test { .setMethod(HTTPVerb.GET) .setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i); } - + Bundle output = ourClient.transaction().withBundle(input).execute(); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + assertEquals(30, output.getEntry().size()); for (int i = 0; i < 30; i++) { Bundle respBundle = (Bundle) output.getEntry().get(i).getResource(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java index b1b957a2b09..f05fa1a376f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.server.IRestfulServerDefaults; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor; +import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -30,9 +31,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank; * 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. @@ -63,6 +64,11 @@ public abstract class RequestDetails { private Map> myUnqualifiedToQualifiedNames; private Map myUserData; + public void addParameter(String theName, String[] theValues) { + getParameters(); + myParameters.put(theName, theValues); + } + protected abstract byte[] getByteStreamRequestContents(); /** @@ -170,37 +176,14 @@ public abstract class RequestDetails { public Map getParameters() { if (myParameters == null) { - return Collections.emptyMap(); + myParameters = new HashMap<>(); } - return myParameters; + return Collections.unmodifiableMap(myParameters); } public void setParameters(Map theParams) { myParameters = theParams; - - for (String next : theParams.keySet()) { - for (int i = 0; i < next.length(); i++) { - char nextChar = next.charAt(i); - if (nextChar == ':' || nextChar == '.') { - if (myUnqualifiedToQualifiedNames == null) { - myUnqualifiedToQualifiedNames = new HashMap<>(); - } - String unqualified = next.substring(0, i); - List list = myUnqualifiedToQualifiedNames.get(unqualified); - if (list == null) { - list = new ArrayList<>(4); - myUnqualifiedToQualifiedNames.put(unqualified, list); - } - list.add(next); - break; - } - } - } - - if (myUnqualifiedToQualifiedNames == null) { - myUnqualifiedToQualifiedNames = Collections.emptyMap(); - } - + myUnqualifiedToQualifiedNames = null; } /** @@ -296,6 +279,29 @@ public abstract class RequestDetails { } public Map> getUnqualifiedToQualifiedNames() { + for (String next : myParameters.keySet()) { + for (int i = 0; i < next.length(); i++) { + char nextChar = next.charAt(i); + if (nextChar == ':' || nextChar == '.') { + if (myUnqualifiedToQualifiedNames == null) { + myUnqualifiedToQualifiedNames = new HashMap<>(); + } + String unqualified = next.substring(0, i); + List list = myUnqualifiedToQualifiedNames.get(unqualified); + if (list == null) { + list = new ArrayList<>(4); + myUnqualifiedToQualifiedNames.put(unqualified, list); + } + list.add(next); + break; + } + } + } + + if (myUnqualifiedToQualifiedNames == null) { + myUnqualifiedToQualifiedNames = Collections.emptyMap(); + } + return myUnqualifiedToQualifiedNames; } @@ -359,6 +365,12 @@ public abstract class RequestDetails { return myRequestContents; } + public void removeParameter(String theName) { + Validate.notNull(theName, "theName must not be null"); + getParameters(); + myParameters.remove(theName); + } + /** * This method may be used to modify the contents of the incoming * request by hardcoding a value which will be used instead of the diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index cfa5c0da1b6..52d998878dd 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -938,8 +938,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer + + Searches which were embedded in a Bundle as a transaction or batch operation did + not respect any chained method parameters (e.g. MedicationRequest?medication.code=123). + Thanks to @manjusampath for reporting! +