diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java index 2d8f4e90077..b89132dc997 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java @@ -24,6 +24,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Map; @@ -47,6 +48,7 @@ import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.param.CollectionBinder; +import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ResourceParameter; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -66,6 +68,7 @@ public class OperationParameter implements IParameter { private Class myParameterType; private String myParamType; private FhirContext myContext; + private boolean myAllowGet; public OperationParameter(FhirContext theCtx, String theOperationName, OperationParam theOperationParam) { this(theCtx, theOperationName, theOperationParam.name(), theOperationParam.min(), theOperationParam.max()); @@ -107,6 +110,8 @@ public class OperationParameter implements IParameter { myMax = 1; } + myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType); + /* * The parameter can be of type string for validation methods - This is a bit * weird. See ValidateDstu2Test. We should probably clean this up.. @@ -114,6 +119,10 @@ public class OperationParameter implements IParameter { if (!myParameterType.equals(IBase.class) && !myParameterType.equals(String.class)) { if (IBaseResource.class.isAssignableFrom(myParameterType) && myParameterType.isInterface()) { myParamType = "Resource"; + } else if (DateRangeParam.class.isAssignableFrom(myParameterType)) { + myParamType = "date"; + myMax = 2; + myAllowGet = true; } else if (!IBase.class.isAssignableFrom(myParameterType) || myParameterType.isInterface() || Modifier.isAbstract(myParameterType.getModifiers())) { throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName()); } else if (myParameterType.equals(ValidationModeEnum.class)) { @@ -153,13 +162,25 @@ public class OperationParameter implements IParameter { if (theRequest.getRequestType() == RequestTypeEnum.GET) { String[] paramValues = theRequest.getParameters().get(myName); if (paramValues != null && paramValues.length > 0) { - if (IPrimitiveType.class.isAssignableFrom(myParameterType)) { - for (String nextValue : paramValues) { - FhirContext ctx = theRequest.getServer().getFhirContext(); - RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition((Class) myParameterType); - IPrimitiveType instance = def.newInstance(); - instance.setValueAsString(nextValue); - matchingParamValues.add(instance); + if (myAllowGet) { + + if (DateRangeParam.class.isAssignableFrom(myParameterType)) { + List parameters = new ArrayList(); + parameters.add(QualifiedParamList.singleton(paramValues[0])); + if (paramValues.length > 1) { + parameters.add(QualifiedParamList.singleton(paramValues[1])); + } + DateRangeParam dateRangeParam = new DateRangeParam(); + dateRangeParam.setValuesAsQueryTokens(parameters); + matchingParamValues.add(dateRangeParam); + } else { + for (String nextValue : paramValues) { + FhirContext ctx = theRequest.getServer().getFhirContext(); + RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition((Class) myParameterType); + IPrimitiveType instance = def.newInstance(); + instance.setValueAsString(nextValue); + matchingParamValues.add(instance); + } } } else { HapiLocalizer localizer = theRequest.getServer().getFhirContext().getLocalizer(); diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/Medication.html b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/Medication.html index 5bdc76e9351..71ee40a1ea3 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/Medication.html +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/Medication.html @@ -11,7 +11,14 @@ a browser.
-
+ + +
+
+ +
+
+
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 615d3c22adb..bd4f9efb6bf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -1227,11 +1227,11 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } else { theOrders.add(theBuilder.desc(theFrom.get("myUpdated"))); } - + createSort(theBuilder, theFrom, theSort.getChain(), theOrders, thePredicates); return; } - + RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceType); RuntimeSearchParam param = resourceDef.getSearchParam(theSort.getParamName()); if (param == null) { @@ -1615,7 +1615,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH /** * THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet()) */ - private HashSet loadReverseIncludes(List theMatches, Set theRevIncludes, boolean theReverseMode) { + private HashSet loadReverseIncludes(Collection theMatches, Set theRevIncludes, boolean theReverseMode) { if (theMatches.size() == 0) { return new HashSet(); } @@ -1902,7 +1902,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH Long pid = translateForcedIdToPid(theId); BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid); - + if (entity == null) { throw new ResourceNotFoundException(theId); } @@ -2023,6 +2023,14 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } } + // Load _include and _revinclude before filter and sort in everything mode + if (theParams.isEverythingMode() == true) { + if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { + loadPids.addAll(loadReverseIncludes(loadPids, theParams.getRevIncludes(), true)); + loadPids.addAll(loadReverseIncludes(loadPids, theParams.getIncludes(), false)); + } + } + // Handle _lastUpdated DateRangeParam lu = theParams.getLastUpdated(); if (lu != null && (lu.getLowerBoundAsInstant() != null || lu.getUpperBoundAsInstant() != null)) { @@ -2047,59 +2055,22 @@ public abstract class BaseHapiFhirResourceDao extends BaseH for (Long next : query.getResultList()) { loadPids.add(next); } - + if (loadPids.isEmpty()) { return new SimpleBundleProvider(); } } // Handle sorting if any was provided - final List pids; - if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) { - List orders = new ArrayList(); - List predicates = new ArrayList(); - CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery cq = builder.createTupleQuery(); - Root from = cq.from(ResourceTable.class); - predicates.add(from.get("myId").in(loadPids)); - createSort(builder, from, theParams.getSort(), orders, predicates); - if (orders.size() > 0) { - Set originalPids = loadPids; - loadPids = new LinkedHashSet(); - cq.multiselect(from.get("myId").as(Long.class)); - cq.where(predicates.toArray(new Predicate[0])); - cq.orderBy(orders); - - TypedQuery query = myEntityManager.createQuery(cq); - - for (Tuple next : query.getResultList()) { - loadPids.add(next.get(0, Long.class)); - } - - ourLog.debug("Sort PID order is now: {}", loadPids); - - pids = new ArrayList(loadPids); - - // Any ressources which weren't matched by the sort get added to the bottom - for (Long next : originalPids) { - if (loadPids.contains(next) == false) { - pids.add(next); - } - } - - } else { - pids = new ArrayList(loadPids); - } - } else { - pids = new ArrayList(loadPids); - } + final List pids = processSort(theParams, loadPids); // Load _revinclude resources final Set revIncludedPids; - if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { - revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true); - if (theParams.isEverythingMode()) { - revIncludedPids.addAll(loadReverseIncludes(pids, theParams.getIncludes(), false)); + if (theParams.isEverythingMode() == false) { + if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { + revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true); + } else { + revIncludedPids = new HashSet(); } } else { revIncludedPids = new HashSet(); @@ -2155,6 +2126,49 @@ public abstract class BaseHapiFhirResourceDao extends BaseH return retVal; } + private List processSort(final SearchParameterMap theParams, Set loadPids) { + final List pids; + if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) { + List orders = new ArrayList(); + List predicates = new ArrayList(); + CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = builder.createTupleQuery(); + Root from = cq.from(ResourceTable.class); + predicates.add(from.get("myId").in(loadPids)); + createSort(builder, from, theParams.getSort(), orders, predicates); + if (orders.size() > 0) { + Set originalPids = loadPids; + loadPids = new LinkedHashSet(); + cq.multiselect(from.get("myId").as(Long.class)); + cq.where(predicates.toArray(new Predicate[0])); + cq.orderBy(orders); + + TypedQuery query = myEntityManager.createQuery(cq); + + for (Tuple next : query.getResultList()) { + loadPids.add(next.get(0, Long.class)); + } + + ourLog.debug("Sort PID order is now: {}", loadPids); + + pids = new ArrayList(loadPids); + + // Any ressources which weren't matched by the sort get added to the bottom + for (Long next : originalPids) { + if (loadPids.contains(next) == false) { + pids.add(next); + } + } + + } else { + pids = new ArrayList(loadPids); + } + } else { + pids = new ArrayList(loadPids); + } + return pids; + } + @Override public IBundleProvider search(String theParameterName, IQueryParameterType theValue) { return search(Collections.singletonMap(theParameterName, theValue)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java index cb6745fda13..aeb2a65ffe7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java @@ -8,13 +8,15 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.UnsignedIntDt; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.IBundleProvider; public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2implements IFhirResourceDaoPatient { @Override - public IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount) { + public IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) { SearchParameterMap paramMap = new SearchParameterMap(); if (theCount != null) { paramMap.setCount(theCount.getValue()); @@ -23,6 +25,8 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2im paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); paramMap.setEverythingMode(true); + paramMap.setSort(theSort); + paramMap.setLastUpdated(theLastUpdated); paramMap.add("_id", new StringParam(theId.getIdPart())); ca.uhn.fhir.rest.server.IBundleProvider retVal = search(paramMap); return retVal; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java index e2e75f12253..171cf86bd13 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java @@ -26,10 +26,12 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.UnsignedIntDt; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.server.IBundleProvider; public interface IFhirResourceDaoPatient extends IFhirResourceDao { - IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount); + IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdate, SortSpec theSort); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java index 7bd6d564c67..c12f0d41139 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java @@ -51,12 +51,12 @@ public class JpaValidationSupportDstu2 implements IValidationSupport { private FhirContext myDstu2Ctx; @Override - public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) { + public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) { return null; } @Override - public ValueSet fetchCodeSystem(String theSystem) { + public ValueSet fetchCodeSystem(FhirContext theCtx, String theSystem) { return null; } @@ -85,20 +85,19 @@ public class JpaValidationSupportDstu2 implements IValidationSupport { /* * Validator wants RI structures and not HAPI ones, so convert * - * TODO: we really need a more efficient way of converting.. Or maybe this will - * just go away when we move to RI structures + * TODO: we really need a more efficient way of converting.. Or maybe this will just go away when we move to RI structures */ String encoded = myDstu2Ctx.newJsonParser().encodeResourceToString(res); return myRiCtx.newJsonParser().parseResource(theClass, encoded); } @Override - public boolean isCodeSystemSupported(String theSystem) { + public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) { return false; } @Override - public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) { + public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) { return null; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java index bd9e329c6ff..954a07f91b2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java @@ -6,6 +6,10 @@ import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.server.Constants; public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu2 { @@ -18,12 +22,21 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu ca.uhn.fhir.model.primitive.IdDt theId, @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = "_count") - ca.uhn.fhir.model.primitive.UnsignedIntDt theCount) { + @OperationParam(name = Constants.PARAM_COUNT) + ca.uhn.fhir.model.primitive.UnsignedIntDt theCount, + + @Description(shortDefinition="Only return resources which were last updated as specified by the given range") + @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) + DateRangeParam theLastUpdated, + +// @OperationParam(name = Constants.PARAM_SORT, min=0, max=1) + @Sort + SortSpec theSortSpec + ) { startRequest(theServletRequest); try { - return ((IFhirResourceDaoPatient)getDao()).everything(theServletRequest, theId, theCount); + return ((IFhirResourceDaoPatient)getDao()).everything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-subscription-config-dstu2.xml b/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-subscription-config-dstu2.xml index 676e6b97d20..830235c6601 100644 --- a/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-subscription-config-dstu2.xml +++ b/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-subscription-config-dstu2.xml @@ -13,7 +13,7 @@ - +