diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 5f1fa9d8c78..15fda06d443 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.parser; * #L% */ -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; import java.io.Reader; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java index 2e3e7cc7671..b778239350f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java @@ -125,7 +125,7 @@ public abstract class BaseFhirDao implements IDao { private FhirContext myContext; -// @PersistenceContext(name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT") + // @PersistenceContext(name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT") @PersistenceContext(type = PersistenceContextType.TRANSACTION) private EntityManager myEntityManager; @@ -195,8 +195,7 @@ public abstract class BaseFhirDao implements IDao { String typeString = nextValue.getReference().getResourceType(); if (isBlank(typeString)) { - throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " - + nextValue.getReference().getValue()); + throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReference().getValue()); } Class type = getContext().getResourceDefinition(typeString).getImplementingClass(); String id = nextValue.getReference().getIdPart(); @@ -347,7 +346,7 @@ public abstract class BaseFhirDao implements IDao { CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = builder.createQuery(TagDefinition.class); Root from = cq.from(TagDefinition.class); - + //@formatter:off if (isNotBlank(theScheme)) { cq.where( @@ -365,7 +364,7 @@ public abstract class BaseFhirDao implements IDao { ); } //@formatter:on - + TypedQuery q = myEntityManager.createQuery(cq); try { return q.getSingleResult(); @@ -530,7 +529,7 @@ public abstract class BaseFhirDao implements IDao { if (pids.isEmpty()) { return new ArrayList(); } - + CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = builder.createQuery(ResourceTable.class); Root from = cq.from(ResourceTable.class); @@ -689,6 +688,18 @@ public abstract class BaseFhirDao implements IDao { continue; } + if (Constants.PARAM_COUNT.equals(nextParamName)) { + if (paramList.size() > 0 && paramList.get(0).size() > 0) { + String intString = paramList.get(0).get(0); + try { + paramMap.setCount(Integer.parseInt(intString)); + } catch (NumberFormatException e) { + throw new InvalidRequestException("Invalid " + Constants.PARAM_COUNT + " value: " + intString); + } + } + continue; + } + RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName); if (paramDef == null) { throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName); @@ -886,7 +897,7 @@ public abstract class BaseFhirDao implements IDao { RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType()); return toResource(type.getImplementingClass(), theEntity); } - + protected T toResource(Class theResourceType, BaseHasResource theEntity) { String resourceText = null; switch (theEntity.getEncoding()) { @@ -1067,9 +1078,9 @@ public abstract class BaseFhirDao implements IDao { quantityParams = extractSearchParamQuantity(entity, theResource); dateParams = extractSearchParamDates(entity, theResource); -// ourLog.info("Indexing resource: {}", entity.getId()); + // ourLog.info("Indexing resource: {}", entity.getId()); ourLog.trace("Storing string indexes: {}", stringParams); - + tokenParams = new ArrayList(); for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(entity, theResource)) { if (next instanceof ResourceIndexedSearchParamToken) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java index 4ea6b497ce3..ffc55840685 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; @@ -24,6 +25,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.jmx.access.InvalidInvocationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -560,11 +562,56 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest { nextEntry = resp.getEntry().get(3); assertEquals(Bundle.class, nextEntry.getResource().getClass()); - Bundle respBundle = (Bundle) nextEntry.getResource(); assertEquals(1, respBundle.getTotal().intValue()); } + @Test + public void testTransactionSearchWithCount() { + String methodName = "testTransactionSearchWithCount"; + + Patient p = new Patient(); + p.addIdentifier().setSystem("urn:system").setValue(methodName); + p.setId("Patient/" + methodName); + IdDt idv1 = ourPatientDao.update(p).getId(); + ourLog.info("Created patient, got id: {}", idv1); + + p = new Patient(); + p.addIdentifier().setSystem("urn:system").setValue(methodName); + p.addName().addFamily("Family Name"); + p.setId("Patient/" + methodName); + IdDt idv2 = ourPatientDao.update(p).getId(); + ourLog.info("Updated patient, got id: {}", idv2); + + Bundle request = new Bundle(); + request.addEntry().getTransaction().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1"); + Bundle resp = ourSystemDao.transaction(request); + + assertEquals(2, resp.getEntry().size()); + + Entry nextEntry = resp.getEntry().get(1); + assertEquals(Bundle.class, nextEntry.getResource().getClass()); + Bundle respBundle = (Bundle) nextEntry.getResource(); + assertEquals(1, respBundle.getTotal().intValue()); + + // Invalid _count + + request = new Bundle(); + request.addEntry().getTransaction().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=GKJGKJG"); + try { + ourSystemDao.transaction(request); + } catch (InvalidRequestException e) { + assertEquals(e.getMessage(), ("Invalid _count value: GKJGKJG")); + } + + // Empty _count + + request = new Bundle(); + request.addEntry().getTransaction().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "="); + respBundle = ourSystemDao.transaction(request); + assertThat(respBundle.getEntry().size(), greaterThan(0)); + } + @Test public void testTransactionReadWithIfNoneMatch() { String methodName = "testTransactionReadWithIfNoneMatch"; diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4ee01a01744..51dab472a25 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -130,6 +130,9 @@ precision than the equivalent header, but the client previously gave the header priority. + + JPA server supports _count parameter in transaction containing search URL (nested search) +