diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 7de8452ee3b..a2badff5ed0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -76,13 +76,6 @@ import ca.uhn.fhir.context.RuntimeChildChoiceDefinition; import ca.uhn.fhir.context.RuntimeChildResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.dao.IDao; -import ca.uhn.fhir.jpa.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.ISearchBuilder; -import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao; import ca.uhn.fhir.jpa.entity.BaseHasResource; @@ -1102,7 +1095,7 @@ public class SearchBuilder implements ISearchBuilder { if (!isBlank(unitsValue)) { code = theBuilder.equal(theFrom.get("myUnits"), unitsValue); } - + cmpValue = ObjectUtils.defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL); final Expression path = theFrom.get("myValue"); String invalidMessageName = "invalidQuantityPrefix"; @@ -1768,6 +1761,39 @@ public class SearchBuilder implements ISearchBuilder { } private void searchForIdsWithAndOr(String theResourceName, String theParamName, List> theAndOrParams) { + + for (int andListIdx = 0; andListIdx < theAndOrParams.size(); andListIdx++) { + List nextOrList = theAndOrParams.get(andListIdx); + + for (int orListIdx = 0; orListIdx < nextOrList.size(); orListIdx++) { + IQueryParameterType nextOr = nextOrList.get(orListIdx); + boolean hasNoValue = false; + if (nextOr.getMissing() != null) { + continue; + } + if (nextOr instanceof QuantityParam) { + if (isBlank(((QuantityParam) nextOr).getValueAsString())) { + hasNoValue = true; + } + } + + if (hasNoValue) { + ourLog.debug("Ignoring empty parameter: {}", theParamName); + nextOrList.remove(orListIdx); + orListIdx--; + } + } + + if (nextOrList.isEmpty()) { + theAndOrParams.remove(andListIdx); + andListIdx--; + } + } + + if (theAndOrParams.isEmpty()) { + return; + } + if (theParamName.equals(BaseResource.SP_RES_ID)) { addPredicateResourceId(theAndOrParams); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index f7c721fa503..7d048e7f8c2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -178,14 +178,41 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { myDaoConfig.setAllowMultipleDelete(true); } - + @Test + public void testSearchWithEmptyParameter() throws Exception { + Observation obs= new Observation(); + obs.setStatus(ObservationStatus.FINAL); + obs.getCode().addCoding().setSystem("foo").setCode("bar"); + ourClient.create().resource(obs).execute(); + + testSearchWithEmptyParameter("/Observation?value-quantity="); + testSearchWithEmptyParameter("/Observation?code=bar&value-quantity="); + testSearchWithEmptyParameter("/Observation?value-date="); + testSearchWithEmptyParameter("/Observation?code=bar&value-date="); + testSearchWithEmptyParameter("/Observation?value-concept="); + testSearchWithEmptyParameter("/Observation?code=bar&value-concept="); + } + + private void testSearchWithEmptyParameter(String url) throws IOException, ClientProtocolException { + HttpGet get = new HttpGet(ourServerBase + url); + CloseableHttpResponse resp = ourHttpClient.execute(get); + try { + assertEquals(200, resp.getStatusLine().getStatusCode()); + String respString = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8); + Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, respString); + assertEquals(1, bundle.getEntry().size()); + } finally { + IOUtils.closeQuietly(resp.getEntity().getContent()); + } + } + @Test public void testSearchWithMissingDate2() throws Exception { MedicationRequest mr1 = new MedicationRequest(); mr1.getCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01"); IIdType id1 = myMedicationRequestDao.create(mr1).getId().toUnqualifiedVersionless(); - + MedicationRequest mr2 = new MedicationRequest(); mr2.getCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); IIdType id2 = myMedicationRequestDao.create(mr2).getId().toUnqualifiedVersionless(); @@ -195,38 +222,34 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { try { assertEquals(200, resp.getStatusLine().getStatusCode()); Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8)); - + List ids = toUnqualifiedVersionlessIdValues(bundle); assertThat(ids, contains(id1.getValue())); } finally { IOUtils.closeQuietly(resp); } - - } - + @Test public void testEverythingWithOnlyPatient() { Patient p = new Patient(); p.setActive(true); IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); - + myFhirCtx.getRestfulClientFactory().setSocketTimeout(300 * 1000); - + Bundle response = ourClient - .operation() - .onInstance(id) - .named("everything") - .withNoParameters(Parameters.class) - .returnResourceType(Bundle.class) - .execute(); - + .operation() + .onInstance(id) + .named("everything") + .withNoParameters(Parameters.class) + .returnResourceType(Bundle.class) + .execute(); + assertEquals(1, response.getEntry().size()); } - - @Test public void testSaveAndRetrieveResourceWithExtension() { Patient nextPatient = new Patient(); @@ -239,7 +262,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { ourClient.update().resource(nextPatient).execute(); Patient p = ourClient.read().resource(Patient.class).withId("B").execute(); - + String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p); ourLog.info(encoded); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 7dc713b157d..edf36e28bf2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -24,6 +24,10 @@ looked like before the update. This change was made to support the change above, but seems like a useful feature all around. + + Fix HTTP 500 error in JPA server if a numeric search parameter was supplied with no value, e.g. + GET /Observation?value-quantity=]]> +