From 0f76ba81e11c81c67055a5e726f2b5f1e5c24345 Mon Sep 17 00:00:00 2001
From: James Agnew
Date: Tue, 7 Jul 2015 10:41:07 -0400
Subject: [PATCH] Fix #196 - Support deep chained parameters in JPA. Also add
support for Prefer header.
---
.../RestfulPatientResourceProviderMore.java | 23 +
.../ca/uhn/fhir/rest/api/MethodOutcome.java | 62 +-
.../uhn/fhir/rest/api/PreferReturnEnum.java | 54 ++
.../uhn/fhir/rest/client/GenericClient.java | 49 +-
.../uhn/fhir/rest/gclient/ICreateTyped.java | 50 +-
.../fhir/rest/gclient/IUpdateExecutable.java | 11 +-
.../BaseOutcomeReturningMethodBinding.java | 21 +-
.../ca/uhn/fhir/rest/method/MethodUtil.java | 5 +-
.../uhn/fhir/rest/param/ReferenceParam.java | 96 ++-
.../ca/uhn/fhir/rest/server/Constants.java | 4 +
.../fhir/rest/server/RestfulServerUtils.java | 38 +-
.../uhn/fhir/util/OperationOutcomeUtil.java | 20 +
.../model/api/IBaseOperationOutcome.java | 20 +
.../ca/uhn/fhir/i18n/hapi-messages.properties | 1 +
.../uhn/fhir/jpa/dao/BaseFhirResourceDao.java | 119 ++-
.../ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java | 37 +-
.../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 7 +-
.../jpa/dao/FhirResourceDaoDstu2Test.java | 786 ++++++++++--------
.../provider/ResourceProviderDstu2Test.java | 48 ++
.../rest/server/RestfulServerUtilsTest.java | 29 +
.../rest/client/GenericClientDstu2Test.java | 336 +++++---
.../ca/uhn/fhir/rest/server/PreferTest.java | 75 +-
src/changes/changes.xml | 9 +
src/site/resources/hapi.css | 4 +
src/site/site.xml | 4 +-
src/site/xdoc/doc_rest_operations.xml | 30 +
26 files changed, 1313 insertions(+), 625 deletions(-)
create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java
diff --git a/examples/src/main/java/example/RestfulPatientResourceProviderMore.java b/examples/src/main/java/example/RestfulPatientResourceProviderMore.java
index fceb09d8f97..42070c2cd78 100644
--- a/examples/src/main/java/example/RestfulPatientResourceProviderMore.java
+++ b/examples/src/main/java/example/RestfulPatientResourceProviderMore.java
@@ -760,6 +760,29 @@ public MethodOutcome updatePatientConditional(
}
//END SNIPPET: updateConditional
+//START SNIPPET: updatePrefer
+@Update
+public MethodOutcome updatePatientPrefer(
+ @ResourceParam Patient thePatient,
+ @IdParam IdDt theId) {
+
+ // Save the patient to the database
+
+ // Update the version and last updated time on the resource
+ IdDt updatedId = theId.withVersion("123");
+ thePatient.setId(updatedId);
+ InstantDt lastUpdated = InstantDt.withCurrentTime();
+ ResourceMetadataKeyEnum.UPDATED.put(thePatient, lastUpdated);
+
+ // Add the resource to the outcome, so that it can be returned by the server
+ // if the client requests it
+ MethodOutcome outcome = new MethodOutcome();
+ outcome.setId(updatedId);
+ outcome.setResource(thePatient);
+ return outcome;
+}
+//END SNIPPET: updatePrefer
+
//START SNIPPET: updateRaw
@Update
public MethodOutcome updatePatientWithRawValue (
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java
index 3579a7703ac..68962458dfc 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java
@@ -21,16 +21,18 @@ package ca.uhn.fhir.rest.api;
*/
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
+import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.primitive.IdDt;
public class MethodOutcome {
+ private Boolean myCreated;
private IIdType myId;
private IBaseOperationOutcome myOperationOutcome;
+ private IBaseResource myResource;
private IdDt myVersionId;
- private Boolean myCreated;
/**
* Constructor
@@ -38,16 +40,6 @@ public class MethodOutcome {
public MethodOutcome() {
}
- /**
- * Constructor
- *
- * @param theId
- * The ID of the created/updated resource
- */
- public MethodOutcome(IIdType theId) {
- myId = theId;
- }
-
/**
* Constructor
*
@@ -115,6 +107,24 @@ public class MethodOutcome {
myOperationOutcome = theBaseOperationOutcome;
}
+ /**
+ * Constructor
+ *
+ * @param theId
+ * The ID of the created/updated resource
+ */
+ public MethodOutcome(IIdType theId) {
+ myId = theId;
+ }
+
+ /**
+ * This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are
+ * returned to client instances, if the server has responded with an HTTP 201 Created.
+ */
+ public Boolean getCreated() {
+ return myCreated;
+ }
+
public IIdType getId() {
return myId;
}
@@ -128,6 +138,15 @@ public class MethodOutcome {
return myOperationOutcome;
}
+ /**
+ * From a client response: If the method returned an actual resource body (e.g. a create/update with
+ * "Prefer: return=representation") this field will be populated with the
+ * resource itself.
+ */
+ public IBaseResource getResource() {
+ return myResource;
+ }
+
/**
* @deprecated {@link MethodOutcome#getId()} should return the complete ID including version if it is available
*/
@@ -136,14 +155,6 @@ public class MethodOutcome {
return myVersionId;
}
- /**
- * This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are
- * returned to client instances, if the server has responded with an HTTP 201 Created.
- */
- public Boolean getCreated() {
- return myCreated;
- }
-
/**
* If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called whether the
* result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
@@ -175,6 +186,19 @@ public class MethodOutcome {
myOperationOutcome = theBaseOperationOutcome;
}
+ /**
+ * In a server response: This field may be populated in server code with the final resource for operations
+ * where a resource body is being created/updated. E.g. for an update method, this field could be populated with
+ * the resource after the update is applied, with the new version ID, lastUpdate time, etc.
+ *
+ * This field is optional, but if it is populated the server will return the resource body if requested to
+ * do so via the HTTP Prefer header.
+ *
+ */
+ public void setResource(IBaseResource theResource) {
+ myResource = theResource;
+ }
+
/**
* @deprecated Put the ID and version ID into the same IdDt instance and pass it to {@link #setId(IdDt)}
*/
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java
new file mode 100644
index 00000000000..46748442147
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java
@@ -0,0 +1,54 @@
+package ca.uhn.fhir.rest.api;
+
+/*
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 - 2015 University Health Network
+ * %%
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.util.HashMap;
+
+/**
+ * Represents values for "return" value as provided in the the HTTP Prefer header.
+ */
+public enum PreferReturnEnum {
+
+ REPRESENTATION("representation"), MINIMAL("minimal");
+
+ private String myHeaderValue;
+ private static HashMap ourValues;
+
+ private PreferReturnEnum(String theHeaderValue) {
+ myHeaderValue = theHeaderValue;
+ }
+
+ public static PreferReturnEnum fromHeaderValue(String theHeaderValue) {
+ if (ourValues == null) {
+ HashMap values = new HashMap();
+ for (PreferReturnEnum next : PreferReturnEnum.values()) {
+ values.put(next.getHeaderValue(), next);
+ }
+ ourValues = values;
+ }
+ return ourValues.get(theHeaderValue);
+ }
+
+ public String getHeaderValue() {
+ return myHeaderValue;
+ }
+
+}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
index b745ff98b5b..2574b96e31d 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
@@ -64,6 +64,7 @@ import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate;
@@ -563,6 +564,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
params.get(parameterName).add(parameterValue);
}
+ private static void addPreferHeader(PreferReturnEnum thePrefer, BaseHttpClientInvocation theInvocation) {
+ if (thePrefer != null) {
+ theInvocation.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + thePrefer.getHeaderValue());
+ }
+ }
+
private abstract class BaseClientExecutable, Y> implements IClientExecutable {
private EncodingEnum myParamEncoding;
private Boolean myPrettyPrint;
@@ -650,6 +657,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private CriterionList myCriterionList;
private String myId;
+ private PreferReturnEnum myPrefer;
private IBaseResource myResource;
private String myResourceBody;
private String mySearchUrl;
@@ -693,6 +701,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext);
}
+ addPreferHeader(myPrefer, invocation);
+
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
@@ -703,6 +713,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
+ @Override
+ public ICreateTyped prefer(PreferReturnEnum theReturn) {
+ myPrefer = theReturn;
+ return this;
+ }
+
@Override
public ICreateTyped resource(IBaseResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
@@ -1250,7 +1266,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class OperationOutcomeResponseHandler implements IClientResponseHandler {
@Override
- public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, BaseServerResponseException {
+ public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
return null;
@@ -1278,7 +1294,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
- public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, BaseServerResponseException {
+ public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException {
MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) {
response.setCreated(true);
@@ -1418,7 +1434,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@SuppressWarnings("unchecked")
@Override
- public List invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, BaseServerResponseException {
+ public List invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Class extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
ResourceResponseHandler handler = new ResourceResponseHandler((Class) bundleType, null);
@@ -1443,7 +1459,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
- public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, BaseServerResponseException {
+ public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@@ -1463,6 +1479,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private String myCompartmentName;
private CriterionList myCriterion = new CriterionList();
private List myInclude = new ArrayList();
+ private DateRangeParam myLastUpdated;
private Integer myParamLimit;
private String myResourceId;
private String myResourceName;
@@ -1470,7 +1487,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
private Class extends IBaseBundle> myReturnBundleType;
private List myRevInclude = new ArrayList();
private SearchStyleEnum mySearchStyle;
- private DateRangeParam myLastUpdated;
private List mySort = new ArrayList();
public SearchInternal() {
@@ -1559,6 +1575,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
+ @Override
+ public IQuery lastUpdated(DateRangeParam theLastUpdated) {
+ myLastUpdated = theLastUpdated;
+ return this;
+ }
+
@Override
public IQuery limitTo(int theLimitTo) {
if (theLimitTo > 0) {
@@ -1621,12 +1643,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
- @Override
- public IQuery lastUpdated(DateRangeParam theLastUpdated) {
- myLastUpdated = theLastUpdated;
- return this;
- }
-
}
@SuppressWarnings("rawtypes")
@@ -1682,7 +1698,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class TagListResponseHandler implements IClientResponseHandler {
@Override
- public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, BaseServerResponseException {
+ public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@@ -1786,6 +1802,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private CriterionList myCriterionList;
private IIdType myId;
+ private PreferReturnEnum myPrefer;
private IBaseResource myResource;
private String myResourceBody;
private String mySearchUrl;
@@ -1834,6 +1851,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
}
+ addPreferHeader(myPrefer, invocation);
+
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
@@ -1844,6 +1863,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
+ @Override
+ public IUpdateExecutable prefer(PreferReturnEnum theReturn) {
+ myPrefer = theReturn;
+ return this;
+ }
+
@Override
public IUpdateTyped resource(IBaseResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ICreateTyped.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ICreateTyped.java
index 8c5495144dc..34169e46609 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ICreateTyped.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ICreateTyped.java
@@ -22,20 +22,34 @@ package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.client.IGenericClient;
public interface ICreateTyped extends IClientExecutable {
/**
- * If you want the explicitly state an ID for your created resource, put that ID here. You generally do not
- * need to invoke this method, so that the server will assign the ID itself.
- *
- *
- * Note that creating a resource by ID is no longer supported as of FHIR DSTU2. You should use the {@link IGenericClient#update()} operation
- * to create-by-ID in DSTU2.
- *
+ * @since HAPI 0.9 / FHIR DSTU 2
*/
- ICreateTyped withId(String theId);
+ ICreateWithQuery conditional();
+
+ /**
+ * Specifies that the create should be performed as a conditional create
+ * against a given search URL.
+ *
+ * @param theSearchUrl The search URL to use. The format of this URL should be of the form [ResourceType]?[Parameters],
+ * for example: Patient?name=Smith&identifier=13.2.4.11.4%7C847366
+ * @since HAPI 0.9 / FHIR DSTU 2
+ */
+ ICreateTyped conditionalByUrl(String theSearchUrl);
+
+ /**
+ * Add a Prefer header to the request, which requests that the server include
+ * or suppress the resource body as a part of the result. If a resource is returned by the server
+ * it will be parsed an accessible to the client via {@link MethodOutcome#getResource()}
+ *
+ * @since HAPI 1.1
+ */
+ ICreateTyped prefer(PreferReturnEnum theReturn);
/**
* If you want the explicitly state an ID for your created resource, put that ID here. You generally do not
@@ -49,18 +63,14 @@ public interface ICreateTyped extends IClientExecutable[ResourceType]?[Parameters],
- * for example: Patient?name=Smith&identifier=13.2.4.11.4%7C847366
- * @since HAPI 0.9 / FHIR DSTU 2
+ * If you want the explicitly state an ID for your created resource, put that ID here. You generally do not
+ * need to invoke this method, so that the server will assign the ID itself.
+ *
+ *
+ * Note that creating a resource by ID is no longer supported as of FHIR DSTU2. You should use the {@link IGenericClient#update()} operation
+ * to create-by-ID in DSTU2.
+ *
*/
- ICreateTyped conditionalByUrl(String theSearchUrl);
-
- /**
- * @since HAPI 0.9 / FHIR DSTU 2
- */
- ICreateWithQuery conditional();
+ ICreateTyped withId(String theId);
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IUpdateExecutable.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IUpdateExecutable.java
index f58959b7859..7087313eee1 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IUpdateExecutable.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IUpdateExecutable.java
@@ -21,8 +21,17 @@ package ca.uhn.fhir.rest.gclient;
*/
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
public interface IUpdateExecutable extends IClientExecutable{
-
+ /**
+ * Add a Prefer header to the request, which requests that the server include
+ * or suppress the resource body as a part of the result. If a resource is returned by the server
+ * it will be parsed an accessible to the client via {@link MethodOutcome#getResource()}
+ *
+ * @since HAPI 1.1
+ */
+ IUpdateExecutable prefer(PreferReturnEnum theReturn);
+
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java
index 502b32cef43..d9c56cd8009 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java
@@ -31,7 +31,7 @@ import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
-import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
+import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
@@ -39,6 +39,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
@@ -170,7 +171,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, outcome, theRequest.getServletRequest(), theRequest.getServletResponse());
@@ -179,6 +181,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding getResourceType(FhirContext theCtx) {
+ if (isBlank(getResourceType())) {
+ return null;
+ }
+ return theCtx.getResourceDefinition(getResourceType()).getImplementingClass();
+ }
+
@Override
public String getValueAsQueryToken() {
if (myBase.getMissing()!=null) {
@@ -95,11 +109,9 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
myChain = theChain;
}
- public Class extends IBaseResource> getResourceType(FhirContext theCtx) {
- if (isBlank(getResourceType())) {
- return null;
- }
- return theCtx.getResourceDefinition(getResourceType()).getImplementingClass();
+ @Override
+ public void setMissing(Boolean theMissing) {
+ myBase.setMissing(theMissing);
}
@Override
@@ -134,21 +146,6 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
}
}
- /**
- * Returns a new param containing the same value as this param, but with the type copnverted
- * to {@link TokenParam}. This is useful if you are using reference parameters and want to handle
- * chained parameters of different types in a single method.
- *
- * See Dynamic Chains
- * in the HAPI FHIR documentation for an example of how to use this method.
- *
- */
- public TokenParam toTokenParam() {
- TokenParam retVal = new TokenParam();
- retVal.setValueAsQueryToken(null, getValueAsQueryToken());
- return retVal;
- }
-
/**
* Returns a new param containing the same value as this param, but with the type copnverted
* to {@link DateParam}. This is useful if you are using reference parameters and want to handle
@@ -164,21 +161,6 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
return retVal;
}
- /**
- * Returns a new param containing the same value as this param, but with the type copnverted
- * to {@link StringParam}. This is useful if you are using reference parameters and want to handle
- * chained parameters of different types in a single method.
- *
- * See Dynamic Chains
- * in the HAPI FHIR documentation for an example of how to use this method.
- *
- */
- public StringParam toStringParam() {
- StringParam retVal = new StringParam();
- retVal.setValueAsQueryToken(null, getValueAsQueryToken());
- return retVal;
- }
-
/**
* Returns a new param containing the same value as this param, but with the type copnverted
* to {@link NumberParam}. This is useful if you are using reference parameters and want to handle
@@ -193,7 +175,7 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
retVal.setValueAsQueryToken(null, getValueAsQueryToken());
return retVal;
}
-
+
/**
* Returns a new param containing the same value as this param, but with the type copnverted
* to {@link QuantityParam}. This is useful if you are using reference parameters and want to handle
@@ -210,13 +192,43 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
}
@Override
- public Boolean getMissing() {
- return myBase.getMissing();
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
+ if (isNotBlank(myChain)) {
+ b.append("chain", myChain);
+ }
+ b.append("value", getValue());
+ return b.build();
}
- @Override
- public void setMissing(Boolean theMissing) {
- myBase.setMissing(theMissing);
+ /**
+ * Returns a new param containing the same value as this param, but with the type copnverted
+ * to {@link StringParam}. This is useful if you are using reference parameters and want to handle
+ * chained parameters of different types in a single method.
+ *
+ * See Dynamic Chains
+ * in the HAPI FHIR documentation for an example of how to use this method.
+ *
+ */
+ public StringParam toStringParam() {
+ StringParam retVal = new StringParam();
+ retVal.setValueAsQueryToken(null, getValueAsQueryToken());
+ return retVal;
+ }
+
+ /**
+ * Returns a new param containing the same value as this param, but with the type copnverted
+ * to {@link TokenParam}. This is useful if you are using reference parameters and want to handle
+ * chained parameters of different types in a single method.
+ *
+ * See Dynamic Chains
+ * in the HAPI FHIR documentation for an example of how to use this method.
+ *
+ */
+ public TokenParam toTokenParam() {
+ TokenParam retVal = new TokenParam();
+ retVal.setValueAsQueryToken(null, getValueAsQueryToken());
+ return retVal;
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java
index 7f90a5e45a4..c8b20e75b37 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java
@@ -133,6 +133,10 @@ public class Constants {
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata";
+ public static final String HEADER_PREFER = "Prefer";
+ public static final String HEADER_PREFER_RETURN = "return";
+ public static final String HEADER_PREFER_RETURN_MINIMAL = "minimal";
+ public static final String HEADER_PREFER_RETURN_REPRESENTATION = "representation";
static {
Map valToEncoding = new HashMap();
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java
index edabbac7454..42deb66785a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java
@@ -32,6 +32,7 @@ import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;
@@ -57,6 +58,7 @@ import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@@ -80,7 +82,7 @@ public class RestfulServerUtils {
}
}
}
-
+
public static String createPagingLink(Set theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
try {
StringBuilder b = new StringBuilder();
@@ -128,6 +130,8 @@ public class RestfulServerUtils {
}
}
+
+
public static RestfulServer.NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
Map requestParams = theRequest.getParameters();
String[] narrative = requestParams.get(Constants.PARAM_NARRATIVE);
@@ -281,6 +285,38 @@ public class RestfulServerUtils {
return writer;
}
+ public static PreferReturnEnum parsePreferHeader(String theValue) {
+ if (isBlank(theValue)) {
+ return null;
+ }
+
+ StringTokenizer tok = new StringTokenizer(theValue, ",");
+ while (tok.hasMoreTokens()) {
+ String next = tok.nextToken();
+ int eqIndex = next.indexOf('=');
+ if (eqIndex == -1 || eqIndex >= next.length() - 2) {
+ continue;
+ }
+
+ String key = next.substring(0, eqIndex).trim();
+ if (key.equals(Constants.HEADER_PREFER_RETURN) == false) {
+ continue;
+ }
+
+ String value = next.substring(eqIndex + 1).trim();
+ if (value.length() < 2) {
+ continue;
+ }
+ if ('"' == value.charAt(0) && '"' == value.charAt(value.length() - 1)) {
+ value = value.substring(1, value.length() - 1);
+ }
+
+ return PreferReturnEnum.fromHeaderValue(value);
+ }
+
+ return null;
+ }
+
public static boolean prettyPrintResponse(RestfulServer theServer, RequestDetails theRequest) {
Map requestParams = theRequest.getParameters();
String[] pretty = requestParams.get(Constants.PARAM_PRETTY);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java
index d78595b158f..87528092a81 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.util;
+/*
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 - 2015 University Health Network
+ * %%
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import static org.apache.commons.lang3.StringUtils.*;
import java.util.List;
diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseOperationOutcome.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseOperationOutcome.java
index daee24be886..f1e3bdfd1fa 100644
--- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseOperationOutcome.java
+++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseOperationOutcome.java
@@ -1,5 +1,25 @@
package org.hl7.fhir.instance.model.api;
+/*
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 - 2015 University Health Network
+ * %%
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
public interface IBaseOperationOutcome extends IBaseResource {
diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties
index 68a894dd8d0..7c995535071 100644
--- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties
+++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties
@@ -48,5 +48,6 @@ ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionInvalidUrl=Unable to perform {0
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.failedToCreateWithClientAssignedNumericId=Can not create resource with ID[{0}], no resource with this ID exists and clients may only assign IDs which begin with a non-numeric character on this server
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.failedToCreateWithClientAssignedId=Can not create resource with ID[{0}], ID must not be supplied on a create (POST) operation
+ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.invalidParameterChain=Invalid parameter chain: {0}
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Deletion failed.
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java
index d8f6453fbfc..d64ec877ff6 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java
@@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
-import static org.apache.commons.lang3.StringUtils.*;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -311,7 +312,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
-
+
if (params instanceof NumberParam) {
NumberParam param = (NumberParam) params;
@@ -374,7 +375,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
cq.select(from.get("myId").as(Long.class));
Subquery subQ = cq.subquery(Long.class);
- Root extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable);
+ Root extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable);
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName);
Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), resourceType);
@@ -383,16 +384,16 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
Predicate typePredicate = builder.equal(from.get("myResourceType"), resourceType);
Predicate notDeletedPredicate = builder.isNull(from.get("myDeleted"));
-
+
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, typePredicate, joinPredicate, notDeletedPredicate));
} else {
cq.where(builder.and(typePredicate, joinPredicate, notDeletedPredicate));
}
-
+
ourLog.info("Adding :missing qualifier for parameter '{}'", theParamName);
-
+
TypedQuery q = myEntityManager.createQuery(cq);
List resultList = q.getResultList();
HashSet retVal = new HashSet(resultList);
@@ -404,15 +405,15 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
CriteriaQuery cq = builder.createQuery(Long.class);
Root from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
-
+
Subquery subQ = cq.subquery(Long.class);
- Root subQfrom = subQ.from(ResourceLink.class);
+ Root subQfrom = subQ.from(ResourceLink.class);
subQ.select(subQfrom.get("mySourceResourcePid").as(Long.class));
-
-// subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
+
+ // subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
Predicate path = createResourceLinkPathPredicate(theParamName, builder, subQfrom);
subQ.where(path);
-
+
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName);
@@ -422,7 +423,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
} else {
cq.where(builder.and(typePredicate, joinPredicate));
}
-
+
TypedQuery q = myEntityManager.createQuery(cq);
List resultList = q.getResultList();
HashSet retVal = new HashSet(resultList);
@@ -433,7 +434,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
if (theList == null || theList.isEmpty()) {
return thePids;
}
-
+
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsQuantity", theParamName, ResourceIndexedSearchParamQuantity.class);
}
@@ -450,7 +451,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
-
+
String systemValue;
String unitsValue;
QuantityCompararatorEnum cmpValue;
@@ -569,7 +570,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
-
+
if (params instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) params;
@@ -587,10 +588,11 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
codePredicates.add(eq);
} else {
- String chain = getContext().getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath();
- BaseRuntimeChildDefinition def = getContext().newTerser().getDefinition(myResourceType, chain);
+
+ String paramPath = getContext().getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath();
+ BaseRuntimeChildDefinition def = getContext().newTerser().getDefinition(myResourceType, paramPath);
if (!(def instanceof RuntimeChildResourceDefinition)) {
- throw new ConfigurationException("Property " + chain + " of type " + myResourceName + " is not a resource: " + def.getClass());
+ throw new ConfigurationException("Property " + paramPath + " of type " + myResourceName + " is not a resource: " + def.getClass());
}
List> resourceTypes;
if (isBlank(ref.getResourceType())) {
@@ -601,9 +603,20 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(ref.getResourceType());
resourceTypes.add(resDef.getImplementingClass());
}
+
+ boolean foundChainMatch = false;
for (Class extends IBaseResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(nextType);
- RuntimeSearchParam param = typeDef.getSearchParam(ref.getChain());
+
+ String chain = ref.getChain();
+ String remainingChain = null;
+ int chainDotIndex = chain.indexOf('.');
+ if (chainDotIndex != -1) {
+ remainingChain = chain.substring(chainDotIndex + 1);
+ chain = chain.substring(0, chainDotIndex);
+ }
+
+ RuntimeSearchParam param = typeDef.getSearchParam(chain);
if (param == null) {
ourLog.debug("Type {} doesn't have search param {}", nextType.getSimpleName(), param);
continue;
@@ -613,9 +626,24 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName(), param);
continue;
}
-
- IQueryParameterType chainValue = toParameterType(param, resourceId);
- Set pids = dao.searchForIds(ref.getChain(), chainValue);
+
+ IQueryParameterType chainValue;
+ if (remainingChain != null) {
+ if (param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
+ ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[] { nextType.getSimpleName(), chain, remainingChain });
+ continue;
+ }
+
+ chainValue = new ReferenceParam();
+ chainValue.setValueAsQueryToken(null, resourceId);
+ ((ReferenceParam)chainValue).setChain(remainingChain);
+ } else {
+ chainValue = toParameterType(param, resourceId);
+ }
+
+ foundChainMatch = true;
+
+ Set pids = dao.searchForIds(chain, chainValue);
if (pids.isEmpty()) {
continue;
}
@@ -624,10 +652,14 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
codePredicates.add(eq);
}
+
+ if (!foundChainMatch) {
+ throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + ref.getChain()));
+ }
}
} else {
- throw new IllegalArgumentException("Invalid token type: " + params.getClass());
+ throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + params.getClass());
}
}
@@ -673,7 +705,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
-
+
Predicate singleCode = createPredicateString(theParameter, theParamName, builder, from);
codePredicates.add(singleCode);
}
@@ -693,7 +725,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
return new HashSet(q.getResultList());
}
- private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root extends BaseResourceIndexedSearchParam> from, List codePredicates, IQueryParameterType nextOr) {
+ private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root extends BaseResourceIndexedSearchParam> from, List codePredicates,
+ IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
@@ -707,7 +740,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
return missingFalse;
}
- private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root extends ResourceLink> from, List codePredicates, IQueryParameterType nextOr) {
+ private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root extends ResourceLink> from, List codePredicates,
+ IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
@@ -740,7 +774,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
-
+
if (nextOr instanceof TokenParam) {
TokenParam id = (TokenParam) nextOr;
if (id.isText()) {
@@ -1101,9 +1135,9 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing) {
StopWatch w = new StopWatch();
-
+
preProcessResourceForStorage(theResource);
-
+
ResourceTable entity = new ResourceTable();
entity.setResourceType(toResourceName(theResource));
@@ -1147,8 +1181,10 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
}
/**
- * May
- * @param theResource The resource that is about to be stored
+ * May
+ *
+ * @param theResource
+ * The resource that is about to be stored
*/
protected void preProcessResourceForStorage(T theResource) {
// nothing by default
@@ -1242,9 +1278,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
retVal.add(current);
}
- TypedQuery q = myEntityManager.createQuery(
- "SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END "
- + (theSince != null ? " AND h.myUpdated >= :SINCE" : "") + " ORDER BY h.myUpdated ASC", ResourceHistoryTable.class);
+ TypedQuery q = myEntityManager.createQuery("SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END "
+ + (theSince != null ? " AND h.myUpdated >= :SINCE" : "") + " ORDER BY h.myUpdated ASC", ResourceHistoryTable.class);
q.setParameter("PID", translateForcedIdToPid(theId));
q.setParameter("RESTYPE", resourceType);
q.setParameter("END", end.getValue(), TemporalType.TIMESTAMP);
@@ -1486,8 +1521,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
throw new ConfigurationException("Unknown search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "]");
}
if (sp.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
- throw new ConfigurationException("Search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName
- + "] is not a token type, only token is supported");
+ throw new ConfigurationException("Search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "] is not a token type, only token is supported");
}
}
@@ -1636,10 +1670,10 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
CriteriaQuery cq = builder.createQuery(Long.class);
Root from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
-
+
Predicate predicateIds = (from.get("myId").in(loadPids));
- Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from.get("myUpdated"), lu.getLowerBoundAsInstant()) : null;
- Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from.get("myUpdated"), lu.getUpperBoundAsInstant()) : null;
+ Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from. get("myUpdated"), lu.getLowerBoundAsInstant()) : null;
+ Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from. get("myUpdated"), lu.getUpperBoundAsInstant()) : null;
if (predicateLower != null && predicateUpper != null) {
cq.where(predicateIds, predicateLower, predicateUpper);
} else if (predicateLower != null) {
@@ -1719,8 +1753,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
loadResourcesByPid(pidsSubList, retVal, BundleEntrySearchModeEnum.MATCH);
/*
- * Load _include resources - Note that _revincludes are handled differently than _include ones, as they are counted towards the total count and paged, so they are loaded
- * outside the bundle provider
+ * Load _include resources - Note that _revincludes are handled differently than _include ones, as they are counted towards the total count and paged, so they are loaded outside the
+ * bundle provider
*/
if (theParams.getIncludes() != null && theParams.getIncludes().isEmpty() == false) {
Set previouslyLoadedPids = new HashSet();
@@ -1960,6 +1994,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
outcome.setResource(theResource);
if (theResource != null) {
theResource.setId(theEntity.getIdDt());
+ ResourceMetadataKeyEnum.UPDATED.put(theResource, theEntity.getUpdated());
}
return outcome;
}
@@ -1992,6 +2027,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
qp = new CompositeParam(leftParam, rightParam);
break;
case REFERENCE:
+ qp = new ReferenceParam();
+ break;
default:
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java
index d8c8b30be33..8ac1b597fbc 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java
@@ -21,36 +21,25 @@ package ca.uhn.fhir.jpa.dao;
*/
import ca.uhn.fhir.jpa.entity.ResourceTable;
-import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.api.MethodOutcome;
public class DaoMethodOutcome extends MethodOutcome {
- private ResourceTable myEntity;
- private IResource myResource;
+ private ResourceTable myEntity;
- public ResourceTable getEntity() {
- return myEntity;
- }
+ public ResourceTable getEntity() {
+ return myEntity;
+ }
- public IResource getResource() {
- return myResource;
- }
+ @Override
+ public DaoMethodOutcome setCreated(Boolean theCreated) {
+ super.setCreated(theCreated);
+ return this;
+ }
- @Override
- public DaoMethodOutcome setCreated(Boolean theCreated) {
- super.setCreated(theCreated);
- return this;
- }
-
- public DaoMethodOutcome setEntity(ResourceTable theEntity) {
- myEntity = theEntity;
- return this;
- }
-
- public DaoMethodOutcome setResource(IResource theResource) {
- myResource = theResource;
- return this;
- }
+ public DaoMethodOutcome setEntity(ResourceTable theEntity) {
+ myEntity = theEntity;
+ return this;
+ }
}
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 4f5b263e902..93690a9560f 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
@@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
-import static org.apache.commons.lang3.StringUtils.*;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.Date;
import java.util.HashMap;
@@ -32,8 +33,6 @@ import java.util.Set;
import javax.persistence.TypedQuery;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
-import org.springframework.jmx.access.InvalidInvocationException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -318,7 +317,7 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
FhirTerser terser = getContext().newTerser();
for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) {
- IResource nextResource = nextOutcome.getResource();
+ IResource nextResource = (IResource) nextOutcome.getResource();
if (nextResource == null) {
continue;
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
index f196cd8ecde..df08b6da1d2 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
@@ -1,7 +1,22 @@
package ca.uhn.fhir.jpa.dao;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsInRelativeOrder;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -20,6 +35,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.jpa.entity.ResourceIndexedSearchParamString;
@@ -78,7 +94,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@SuppressWarnings("unchecked")
-public class FhirResourceDaoDstu2Test extends BaseJpaTest {
+public class FhirResourceDaoDstu2Test extends BaseJpaTest {
private static ClassPathXmlApplicationContext ourCtx;
private static IFhirResourceDao ourDeviceDao;
@@ -90,28 +106,11 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
private static IFhirResourceDao ourObservationDao;
private static IFhirResourceDao ourOrganizationDao;
private static IFhirResourceDao ourPatientDao;
- private static IFhirSystemDao ourSystemDao;
- private static IFhirResourceDao ourQuestionnaireDao;
@SuppressWarnings("unused")
private static IFhirResourceDao ourQuestionnaireAnswersDao;
+ private static IFhirResourceDao ourQuestionnaireDao;
+ private static IFhirSystemDao ourSystemDao;
- @Test
- public void testQuestionnaireTitleGetsIndexed() {
- Questionnaire q = new Questionnaire();
- q.getGroup().setTitle("testQuestionnaireTitleGetsIndexedQ_TITLE");
- IIdType qid1 = ourQuestionnaireDao.create(q).getId().toUnqualifiedVersionless();
- q = new Questionnaire();
- q.getGroup().setTitle("testQuestionnaireTitleGetsIndexedQ_NOTITLE");
- IIdType qid2 = ourQuestionnaireDao.create(q).getId().toUnqualifiedVersionless();
-
- IBundleProvider results = ourQuestionnaireDao.search("title", new StringParam("testQuestionnaireTitleGetsIndexedQ_TITLE"));
- assertEquals(1, results.size());
- assertEquals(qid1, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
- assertNotEquals(qid2, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
-
- }
-
-
@Test
public void testChoiceParamConcept() {
Observation o1 = new Observation();
@@ -649,6 +648,51 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
}
+ /**
+ * See #196
+ */
+ @Test
+ public void testInvalidChainNames() {
+ ReferenceParam param = null;
+
+ // OK
+ param = new ReferenceParam("999999999999");
+ param.setChain("organization");
+ ourLocationDao.search("partof", param);
+
+ // OK
+ param = new ReferenceParam("999999999999");
+ param.setChain("organization.name");
+ ourLocationDao.search("partof", param);
+
+ try {
+ param = new ReferenceParam("999999999999");
+ param.setChain("foo");
+ ourLocationDao.search("partof", param);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), containsString("Invalid parameter chain: partof." + param.getChain()));
+ }
+
+ try {
+ param = new ReferenceParam("999999999999");
+ param.setChain("organization.foo");
+ ourLocationDao.search("partof", param);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), containsString("Invalid parameter chain: " + param.getChain()));
+ }
+
+ try {
+ param = new ReferenceParam("999999999999");
+ param.setChain("organization.name.foo");
+ ourLocationDao.search("partof", param);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), containsString("Invalid parameter chain: " + param.getChain()));
+ }
+ }
+
@Test
public void testOrganizationName() {
@@ -688,6 +732,21 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
+ @Test
+ public void testPersistContactPoint() {
+ List found = toList(ourPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
+ int initialSize2000 = found.size();
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("testPersistContactPoint");
+ patient.addTelecom().setValue("555-123-4567");
+ ourPatientDao.create(patient);
+
+ found = toList(ourPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
+ assertEquals(1 + initialSize2000, found.size());
+
+ }
+
@Test
public void testPersistResourceLink() {
Patient patient = new Patient();
@@ -728,21 +787,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
- @Test
- public void testPersistContactPoint() {
- List found = toList(ourPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
- int initialSize2000 = found.size();
-
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("testPersistContactPoint");
- patient.addTelecom().setValue("555-123-4567");
- ourPatientDao.create(patient);
-
- found = toList(ourPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
- assertEquals(1 + initialSize2000, found.size());
-
- }
-
@Test
public void testPersistSearchParamDate() {
List found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
@@ -852,6 +896,22 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
+ @Test
+ public void testQuestionnaireTitleGetsIndexed() {
+ Questionnaire q = new Questionnaire();
+ q.getGroup().setTitle("testQuestionnaireTitleGetsIndexedQ_TITLE");
+ IIdType qid1 = ourQuestionnaireDao.create(q).getId().toUnqualifiedVersionless();
+ q = new Questionnaire();
+ q.getGroup().setTitle("testQuestionnaireTitleGetsIndexedQ_NOTITLE");
+ IIdType qid2 = ourQuestionnaireDao.create(q).getId().toUnqualifiedVersionless();
+
+ IBundleProvider results = ourQuestionnaireDao.search("title", new StringParam("testQuestionnaireTitleGetsIndexedQ_TITLE"));
+ assertEquals(1, results.size());
+ assertEquals(qid1, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
+ assertNotEquals(qid2, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
+
+ }
+
@Test
public void testReadForcedIdVersionHistory() throws InterruptedException {
Patient p1 = new Patient();
@@ -875,29 +935,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
- @Test
- public void testReverseIncludes() {
- String methodName = "testReverseIncludes";
- Organization org = new Organization();
- org.setName("X" + methodName + "X");
- IIdType orgId = ourOrganizationDao.create(org).getId();
-
- Patient pat = new Patient();
- pat.addName().addFamily("X" + methodName + "X");
- pat.getManagingOrganization().setReference(orgId.toUnqualifiedVersionless());
- ourPatientDao.create(pat);
-
- SearchParameterMap map = new SearchParameterMap();
- map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
- map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
- IBundleProvider resultsP = ourOrganizationDao.search(map);
- assertEquals(2, resultsP.size());
- List results = resultsP.getResources(0, resultsP.size());
- assertEquals(2, results.size());
- assertEquals(Organization.class, results.get(0).getClass());
- assertEquals(Patient.class, results.get(1).getClass());
- }
-
@Test
public void testResourceInstanceMetaOperation() {
deleteEverything();
@@ -1136,6 +1173,29 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
+ @Test
+ public void testReverseIncludes() {
+ String methodName = "testReverseIncludes";
+ Organization org = new Organization();
+ org.setName("X" + methodName + "X");
+ IIdType orgId = ourOrganizationDao.create(org).getId();
+
+ Patient pat = new Patient();
+ pat.addName().addFamily("X" + methodName + "X");
+ pat.getManagingOrganization().setReference(orgId.toUnqualifiedVersionless());
+ ourPatientDao.create(pat);
+
+ SearchParameterMap map = new SearchParameterMap();
+ map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
+ map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
+ IBundleProvider resultsP = ourOrganizationDao.search(map);
+ assertEquals(2, resultsP.size());
+ List results = resultsP.getResources(0, resultsP.size());
+ assertEquals(2, results.size());
+ assertEquals(Organization.class, results.get(0).getClass());
+ assertEquals(Patient.class, results.get(1).getClass());
+ }
+
@Test
public void testSearchAll() {
{
@@ -1299,7 +1359,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
@Test
public void testSearchLastUpdatedParam() throws InterruptedException {
String methodName = "testSearchLastUpdatedParam";
-
+
int sleep = 100;
Thread.sleep(sleep);
@@ -1322,7 +1382,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
Thread.sleep(1100);
DateTimeDt beforeR2 = new DateTimeDt(new Date(), TemporalPrecisionEnum.MILLI);
Thread.sleep(1100);
-
+
IIdType id2;
{
Patient patient = new Patient();
@@ -1330,7 +1390,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
patient.addName().addFamily(methodName).addGiven("John");
id2 = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
-
+
{
SearchParameterMap params = new SearchParameterMap();
List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
@@ -1494,6 +1554,52 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
+ @Test
+ public void testSearchResourceLinkWithChainDouble() {
+ String methodName = "testSearchResourceLinkWithChainDouble";
+
+ Organization org = new Organization();
+ org.setName(methodName);
+ IIdType orgId01 = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
+
+ Location locParent = new Location();
+ locParent.setManagingOrganization(new ResourceReferenceDt(orgId01));
+ IIdType locParentId = ourLocationDao.create(locParent).getId().toUnqualifiedVersionless();
+
+ Location locChild = new Location();
+ locChild.setPartOf(new ResourceReferenceDt(locParentId));
+ IIdType locChildId = ourLocationDao.create(locChild).getId().toUnqualifiedVersionless();
+
+ Location locGrandchild = new Location();
+ locGrandchild.setPartOf(new ResourceReferenceDt(locChildId));
+ IIdType locGrandchildId = ourLocationDao.create(locGrandchild).getId().toUnqualifiedVersionless();
+
+ IBundleProvider found;
+ ReferenceParam param;
+
+ found = ourLocationDao.search("organization", new ReferenceParam(orgId01.getIdPart()));
+ assertEquals(1, found.size());
+ assertEquals(locParentId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
+
+ param = new ReferenceParam(orgId01.getIdPart());
+ param.setChain("organization");
+ found = ourLocationDao.search("partof", param);
+ assertEquals(1, found.size());
+ assertEquals(locChildId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
+
+ param = new ReferenceParam(orgId01.getIdPart());
+ param.setChain("partof.organization");
+ found = ourLocationDao.search("partof", param);
+ assertEquals(1, found.size());
+ assertEquals(locGrandchildId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
+
+ param = new ReferenceParam(methodName);
+ param.setChain("partof.organization.name");
+ found = ourLocationDao.search("partof", param);
+ assertEquals(1, found.size());
+ assertEquals(locGrandchildId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
+ }
+
@Test
public void testSearchResourceLinkWithChainWithMultipleTypes() {
Patient patient = new Patient();
@@ -1609,196 +1715,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
- @Test
- public void testSearchWithMissingString() {
- IIdType orgId = ourOrganizationDao.create(new Organization()).getId();
- IIdType notMissing;
- IIdType missing;
- {
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("001");
- missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
- }
- {
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("002");
- patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
- patient.setBirthDate(new DateDt("2011-01-01"));
- patient.getManagingOrganization().setReference(orgId);
- notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
- }
- // String Param
- {
- HashMap params = new HashMap();
- StringParam param = new StringParam();
- param.setMissing(false);
- params.put(Patient.SP_FAMILY, param);
- List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
- assertThat(patients, not(containsInRelativeOrder(missing)));
- assertThat(patients, containsInRelativeOrder(notMissing));
- }
- {
- Map params = new HashMap();
- StringParam param = new StringParam();
- param.setMissing(true);
- params.put(Patient.SP_FAMILY, param);
- List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
- assertThat(patients, containsInRelativeOrder(missing));
- assertThat(patients, not(containsInRelativeOrder(notMissing)));
- }
- }
-
- @Test
- public void testSearchWithMissingQuantity() {
- IIdType notMissing;
- IIdType missing;
- {
- Observation obs = new Observation();
- obs.addIdentifier().setSystem("urn:system").setValue("001");
- missing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
- }
- {
- Observation obs = new Observation();
- obs.addIdentifier().setSystem("urn:system").setValue("002");
- obs.setValue(new QuantityDt(123));
- notMissing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
- }
- // Quantity Param
- {
- HashMap params = new HashMap();
- QuantityParam param = new QuantityParam();
- param.setMissing(false);
- params.put(Observation.SP_VALUE_QUANTITY, param);
- List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
- assertThat(patients, not(containsInRelativeOrder(missing)));
- assertThat(patients, containsInRelativeOrder(notMissing));
- }
- {
- Map params = new HashMap();
- QuantityParam param = new QuantityParam();
- param.setMissing(true);
- params.put(Observation.SP_VALUE_QUANTITY, param);
- List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
- assertThat(patients, containsInRelativeOrder(missing));
- assertThat(patients, not(containsInRelativeOrder(notMissing)));
- }
- }
-
- @Test
- public void testSearchWithToken() {
- IIdType notMissing;
- IIdType missing;
- {
- Observation obs = new Observation();
- obs.addIdentifier().setSystem("urn:system").setValue("001");
- missing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
- }
- {
- Observation obs = new Observation();
- obs.addIdentifier().setSystem("urn:system").setValue("002");
- obs.getCode().addCoding().setSystem("urn:system").setCode("002");
- notMissing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
- }
- // Token Param
- {
- HashMap params = new HashMap();
- TokenParam param = new TokenParam();
- param.setMissing(false);
- params.put(Observation.SP_CODE, param);
- List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
- assertThat(patients, not(containsInRelativeOrder(missing)));
- assertThat(patients, containsInRelativeOrder(notMissing));
- }
- {
- Map params = new HashMap();
- TokenParam param = new TokenParam();
- param.setMissing(true);
- params.put(Observation.SP_CODE, param);
- List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
- assertThat(patients, containsInRelativeOrder(missing));
- assertThat(patients, not(containsInRelativeOrder(notMissing)));
- }
- }
-
- @Test
- public void testSearchWithMissingDate() {
- IIdType orgId = ourOrganizationDao.create(new Organization()).getId();
- IIdType notMissing;
- IIdType missing;
- {
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("001");
- missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
- }
- {
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("002");
- patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
- patient.setBirthDate(new DateDt("2011-01-01"));
- patient.getManagingOrganization().setReference(orgId);
- notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
- }
- // Date Param
- {
- HashMap params = new HashMap();
- DateParam param = new DateParam();
- param.setMissing(false);
- params.put(Patient.SP_BIRTHDATE, param);
- List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
- assertThat(patients, not(containsInRelativeOrder(missing)));
- assertThat(patients, containsInRelativeOrder(notMissing));
- }
- {
- Map params = new HashMap();
- DateParam param = new DateParam();
- param.setMissing(true);
- params.put(Patient.SP_BIRTHDATE, param);
- List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
- assertThat(patients, containsInRelativeOrder(missing));
- assertThat(patients, not(containsInRelativeOrder(notMissing)));
- }
- }
-
- @Test
- public void testSearchWithMissingReference() {
- IIdType orgId = ourOrganizationDao.create(new Organization()).getId().toUnqualifiedVersionless();
- IIdType notMissing;
- IIdType missing;
- {
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("001");
- missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
- }
- {
- Patient patient = new Patient();
- patient.addIdentifier().setSystem("urn:system").setValue("002");
- patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
- patient.setBirthDate(new DateDt("2011-01-01"));
- patient.getManagingOrganization().setReference(orgId);
- notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
- }
- // Reference Param
- {
- HashMap params = new HashMap();
- ReferenceParam param = new ReferenceParam();
- param.setMissing(false);
- params.put(Patient.SP_ORGANIZATION, param);
- List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
- assertThat(patients, not(containsInRelativeOrder(missing)));
- assertThat(patients, containsInRelativeOrder(notMissing));
- }
- {
- Map params = new HashMap();
- ReferenceParam param = new ReferenceParam();
- param.setMissing(true);
- params.put(Patient.SP_ORGANIZATION, param);
- List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
- assertThat(patients, containsInRelativeOrder(missing));
- assertThat(patients, not(containsInRelativeOrder(notMissing)));
- assertThat(patients, not(containsInRelativeOrder(orgId)));
- }
- }
-
@Test
public void testSearchStringParamWithNonNormalized() {
{
@@ -2046,6 +1962,221 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
+ @Test
+ public void testSearchWithMissingDate() {
+ IIdType orgId = ourOrganizationDao.create(new Organization()).getId();
+ IIdType notMissing;
+ IIdType missing;
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("001");
+ missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
+ }
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("002");
+ patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
+ patient.setBirthDate(new DateDt("2011-01-01"));
+ patient.getManagingOrganization().setReference(orgId);
+ notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
+ }
+ // Date Param
+ {
+ HashMap params = new HashMap();
+ DateParam param = new DateParam();
+ param.setMissing(false);
+ params.put(Patient.SP_BIRTHDATE, param);
+ List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
+ assertThat(patients, not(containsInRelativeOrder(missing)));
+ assertThat(patients, containsInRelativeOrder(notMissing));
+ }
+ {
+ Map params = new HashMap();
+ DateParam param = new DateParam();
+ param.setMissing(true);
+ params.put(Patient.SP_BIRTHDATE, param);
+ List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
+ assertThat(patients, containsInRelativeOrder(missing));
+ assertThat(patients, not(containsInRelativeOrder(notMissing)));
+ }
+ }
+
+ @Test
+ public void testSearchWithMissingQuantity() {
+ IIdType notMissing;
+ IIdType missing;
+ {
+ Observation obs = new Observation();
+ obs.addIdentifier().setSystem("urn:system").setValue("001");
+ missing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
+ }
+ {
+ Observation obs = new Observation();
+ obs.addIdentifier().setSystem("urn:system").setValue("002");
+ obs.setValue(new QuantityDt(123));
+ notMissing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
+ }
+ // Quantity Param
+ {
+ HashMap params = new HashMap();
+ QuantityParam param = new QuantityParam();
+ param.setMissing(false);
+ params.put(Observation.SP_VALUE_QUANTITY, param);
+ List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
+ assertThat(patients, not(containsInRelativeOrder(missing)));
+ assertThat(patients, containsInRelativeOrder(notMissing));
+ }
+ {
+ Map params = new HashMap();
+ QuantityParam param = new QuantityParam();
+ param.setMissing(true);
+ params.put(Observation.SP_VALUE_QUANTITY, param);
+ List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
+ assertThat(patients, containsInRelativeOrder(missing));
+ assertThat(patients, not(containsInRelativeOrder(notMissing)));
+ }
+ }
+
+ @Test
+ public void testSearchWithMissingReference() {
+ IIdType orgId = ourOrganizationDao.create(new Organization()).getId().toUnqualifiedVersionless();
+ IIdType notMissing;
+ IIdType missing;
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("001");
+ missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
+ }
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("002");
+ patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
+ patient.setBirthDate(new DateDt("2011-01-01"));
+ patient.getManagingOrganization().setReference(orgId);
+ notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
+ }
+ // Reference Param
+ {
+ HashMap params = new HashMap();
+ ReferenceParam param = new ReferenceParam();
+ param.setMissing(false);
+ params.put(Patient.SP_ORGANIZATION, param);
+ List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
+ assertThat(patients, not(containsInRelativeOrder(missing)));
+ assertThat(patients, containsInRelativeOrder(notMissing));
+ }
+ {
+ Map params = new HashMap();
+ ReferenceParam param = new ReferenceParam();
+ param.setMissing(true);
+ params.put(Patient.SP_ORGANIZATION, param);
+ List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
+ assertThat(patients, containsInRelativeOrder(missing));
+ assertThat(patients, not(containsInRelativeOrder(notMissing)));
+ assertThat(patients, not(containsInRelativeOrder(orgId)));
+ }
+ }
+
+ @Test
+ public void testSearchWithMissingString() {
+ IIdType orgId = ourOrganizationDao.create(new Organization()).getId();
+ IIdType notMissing;
+ IIdType missing;
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("001");
+ missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
+ }
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue("002");
+ patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
+ patient.setBirthDate(new DateDt("2011-01-01"));
+ patient.getManagingOrganization().setReference(orgId);
+ notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
+ }
+ // String Param
+ {
+ HashMap params = new HashMap();
+ StringParam param = new StringParam();
+ param.setMissing(false);
+ params.put(Patient.SP_FAMILY, param);
+ List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
+ assertThat(patients, not(containsInRelativeOrder(missing)));
+ assertThat(patients, containsInRelativeOrder(notMissing));
+ }
+ {
+ Map params = new HashMap();
+ StringParam param = new StringParam();
+ param.setMissing(true);
+ params.put(Patient.SP_FAMILY, param);
+ List patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
+ assertThat(patients, containsInRelativeOrder(missing));
+ assertThat(patients, not(containsInRelativeOrder(notMissing)));
+ }
+ }
+
+ @Test
+ public void testSearchWithNoResults() {
+ Device dev = new Device();
+ dev.addIdentifier().setSystem("Foo");
+ ourDeviceDao.create(dev);
+
+ IBundleProvider value = ourDeviceDao.search(new SearchParameterMap());
+ ourLog.info("Initial size: " + value.size());
+ for (IBaseResource next : value.getResources(0, value.size())) {
+ ourLog.info("Deleting: {}", next.getIdElement());
+ ourDeviceDao.delete((IIdType) next.getIdElement());
+ }
+
+ value = ourDeviceDao.search(new SearchParameterMap());
+ if (value.size() > 0) {
+ ourLog.info("Found: " + (value.getResources(0, 1).get(0).getIdElement()));
+ fail(ourFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(value.getResources(0, 1).get(0)));
+ }
+ assertEquals(0, value.size());
+
+ List res = value.getResources(0, 0);
+ assertTrue(res.isEmpty());
+
+ }
+
+ @Test
+ public void testSearchWithToken() {
+ IIdType notMissing;
+ IIdType missing;
+ {
+ Observation obs = new Observation();
+ obs.addIdentifier().setSystem("urn:system").setValue("001");
+ missing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
+ }
+ {
+ Observation obs = new Observation();
+ obs.addIdentifier().setSystem("urn:system").setValue("002");
+ obs.getCode().addCoding().setSystem("urn:system").setCode("002");
+ notMissing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
+ }
+ // Token Param
+ {
+ HashMap params = new HashMap();
+ TokenParam param = new TokenParam();
+ param.setMissing(false);
+ params.put(Observation.SP_CODE, param);
+ List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
+ assertThat(patients, not(containsInRelativeOrder(missing)));
+ assertThat(patients, containsInRelativeOrder(notMissing));
+ }
+ {
+ Map params = new HashMap();
+ TokenParam param = new TokenParam();
+ param.setMissing(true);
+ params.put(Observation.SP_CODE, param);
+ List patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
+ assertThat(patients, containsInRelativeOrder(missing));
+ assertThat(patients, not(containsInRelativeOrder(notMissing)));
+ }
+ }
+
@Test
public void testSortByDate() {
Patient p = new Patient();
@@ -2148,6 +2279,67 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertThat(actual, contains(id4, id3, id2, id1, idMethodName));
}
+ @Test
+ public void testSortByReference() {
+ String methodName = "testSortByReference";
+
+ Organization o1 = new Organization();
+ IIdType oid1 = ourOrganizationDao.create(o1).getId().toUnqualifiedVersionless();
+
+ Organization o2 = new Organization();
+ IIdType oid2 = ourOrganizationDao.create(o2).getId().toUnqualifiedVersionless();
+
+ Patient p = new Patient();
+ p.addIdentifier().setSystem("urn:system").setValue(methodName);
+ p.addName().addFamily("testSortF1").addGiven("testSortG1");
+ p.getManagingOrganization().setReference(oid1);
+ IIdType id1 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
+
+ p = new Patient();
+ p.addIdentifier().setSystem("urn:system").setValue(methodName);
+ p.addName().addFamily("testSortF2").addGiven("testSortG2");
+ p.getManagingOrganization().setReference(oid2);
+ IIdType id2 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
+
+ p = new Patient();
+ p.addIdentifier().setSystem("urn:system").setValue(methodName);
+ p.addName().addFamily("testSortF3").addGiven("testSortG3");
+ p.getManagingOrganization().setReference(oid1);
+ IIdType id3 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
+
+ p = new Patient();
+ p.addIdentifier().setSystem("urn:system").setValue(methodName);
+ p.getManagingOrganization().setReference(oid2);
+ IIdType id4 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
+
+ SearchParameterMap pm;
+ List actual;
+
+ pm = new SearchParameterMap();
+ pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
+ pm.setSort(new SortSpec(Patient.SP_ORGANIZATION));
+ actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
+ assertEquals(4, actual.size());
+ assertThat(actual.subList(0, 2), containsInAnyOrder(id1, id3));
+ assertThat(actual.subList(2, 4), containsInAnyOrder(id2, id4));
+
+ pm = new SearchParameterMap();
+ pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
+ pm.setSort(new SortSpec(Patient.SP_ORGANIZATION).setOrder(SortOrderEnum.ASC));
+ actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
+ assertEquals(4, actual.size());
+ assertThat(actual.subList(0, 2), containsInAnyOrder(id1, id3));
+ assertThat(actual.subList(2, 4), containsInAnyOrder(id2, id4));
+
+ pm = new SearchParameterMap();
+ pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
+ pm.setSort(new SortSpec(Patient.SP_ORGANIZATION).setOrder(SortOrderEnum.DESC));
+ actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
+ assertEquals(4, actual.size());
+ assertThat(actual.subList(0, 2), containsInAnyOrder(id2, id4));
+ assertThat(actual.subList(2, 4), containsInAnyOrder(id1, id3));
+ }
+
@Test
public void testSortByString() {
Patient p = new Patient();
@@ -2195,67 +2387,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertThat(actual, contains(id3, id2, id1, id4));
}
- @Test
- public void testSortByReference() {
- String methodName = "testSortByReference";
-
- Organization o1 = new Organization();
- IIdType oid1 = ourOrganizationDao.create(o1).getId().toUnqualifiedVersionless();
-
- Organization o2 = new Organization();
- IIdType oid2 = ourOrganizationDao.create(o2).getId().toUnqualifiedVersionless();
-
- Patient p = new Patient();
- p.addIdentifier().setSystem("urn:system").setValue(methodName);
- p.addName().addFamily("testSortF1").addGiven("testSortG1");
- p.getManagingOrganization().setReference(oid1);
- IIdType id1 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
-
- p = new Patient();
- p.addIdentifier().setSystem("urn:system").setValue(methodName);
- p.addName().addFamily("testSortF2").addGiven("testSortG2");
- p.getManagingOrganization().setReference(oid2);
- IIdType id2 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
-
- p = new Patient();
- p.addIdentifier().setSystem("urn:system").setValue(methodName);
- p.addName().addFamily("testSortF3").addGiven("testSortG3");
- p.getManagingOrganization().setReference(oid1);
- IIdType id3 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
-
- p = new Patient();
- p.addIdentifier().setSystem("urn:system").setValue(methodName);
- p.getManagingOrganization().setReference(oid2);
- IIdType id4 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
-
- SearchParameterMap pm;
- List actual;
-
- pm = new SearchParameterMap();
- pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
- pm.setSort(new SortSpec(Patient.SP_ORGANIZATION));
- actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
- assertEquals(4, actual.size());
- assertThat(actual.subList(0, 2), containsInAnyOrder(id1, id3));
- assertThat(actual.subList(2, 4), containsInAnyOrder(id2, id4));
-
- pm = new SearchParameterMap();
- pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
- pm.setSort(new SortSpec(Patient.SP_ORGANIZATION).setOrder(SortOrderEnum.ASC));
- actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
- assertEquals(4, actual.size());
- assertThat(actual.subList(0, 2), containsInAnyOrder(id1, id3));
- assertThat(actual.subList(2, 4), containsInAnyOrder(id2, id4));
-
- pm = new SearchParameterMap();
- pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
- pm.setSort(new SortSpec(Patient.SP_ORGANIZATION).setOrder(SortOrderEnum.DESC));
- actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
- assertEquals(4, actual.size());
- assertThat(actual.subList(0, 2), containsInAnyOrder(id2, id4));
- assertThat(actual.subList(2, 4), containsInAnyOrder(id1, id3));
- }
-
@Test
public void testStoreUnversionedResources() {
Organization o1 = new Organization();
@@ -2508,7 +2639,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertEquals(published, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(1)));
assertEquals(updated, ResourceMetadataKeyEnum.UPDATED.get((IResource) history.get(1)));
assertEquals("001", ((Patient) history.get(1)).getIdentifierFirstRep().getValue());
- assertEquals(published2, ResourceMetadataKeyEnum.PUBLISHED.get( (IResource) history.get(0)));
+ assertEquals(published2, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(0)));
assertEquals(updated2, ResourceMetadataKeyEnum.UPDATED.get((IResource) history.get(0)));
assertEquals("002", ((Patient) history.get(0)).getIdentifierFirstRep().getValue());
@@ -2683,29 +2814,4 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
FhirSystemDaoDstu2Test.doDeleteEverything(ourSystemDao);
}
- @Test
- public void testSearchWithNoResults() {
- Device dev = new Device();
- dev.addIdentifier().setSystem("Foo");
- ourDeviceDao.create(dev);
-
- IBundleProvider value = ourDeviceDao.search(new SearchParameterMap());
- ourLog.info("Initial size: " + value.size());
- for (IBaseResource next : value.getResources(0, value.size())) {
- ourLog.info("Deleting: {}", next.getIdElement());
- ourDeviceDao.delete((IIdType) next.getIdElement());
- }
-
- value = ourDeviceDao.search(new SearchParameterMap());
- if (value.size() > 0) {
- ourLog.info("Found: " + (value.getResources(0, 1).get(0).getIdElement()));
- fail(ourFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(value.getResources(0, 1).get(0)));
- }
- assertEquals(0, value.size());
-
- List res = value.getResources(0, 0);
- assertTrue(res.isEmpty());
-
- }
-
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
index 5f2d747524e..f5f070de649 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
@@ -989,6 +989,53 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
}
}
+
+
+ @Test
+ public void testUpdateResourceWithPrefer() throws IOException, Exception {
+ String methodName = "testUpdateResourceWithPrefer";
+
+ Patient pt = new Patient();
+ pt.addName().addFamily(methodName);
+ String resource = ourCtx.newXmlParser().encodeResourceToString(pt);
+
+ HttpPost post = new HttpPost(ourServerBase + "/Patient");
+ post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
+ CloseableHttpResponse response = ourHttpClient.execute(post);
+ IdDt id;
+ try {
+ assertEquals(201, response.getStatusLine().getStatusCode());
+ String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
+ assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
+ id = new IdDt(newIdString);
+ } finally {
+ response.close();
+ }
+
+ Date before = new Date();
+ Thread.sleep(100);
+
+ HttpPut put = new HttpPut(ourServerBase + "/Patient/" + id.getIdPart());
+ put.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION);
+ put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
+ response = ourHttpClient.execute(put);
+ try {
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ String responseString = IOUtils.toString(response.getEntity().getContent());
+ IOUtils.closeQuietly(response.getEntity().getContent());
+
+ Patient respPt = ourCtx.newXmlParser().parseResource(Patient.class, responseString);
+ assertEquals("2", respPt.getId().getVersionIdPart());
+
+ InstantDt updateTime = ResourceMetadataKeyEnum.UPDATED.get(respPt);
+ assertTrue(updateTime.getValue().after(before));
+
+ } finally {
+ response.close();
+ }
+
+ }
+
@Test
public void testUpdateWithClientSuppliedIdWhichDoesntExist() {
@@ -1036,6 +1083,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
}
}
+
@Test
public void testValidateResourceWithId() throws IOException {
diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java
index c98994426a9..34cdf6b4168 100644
--- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java
+++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java
@@ -6,6 +6,8 @@ import java.util.regex.Matcher;
import org.junit.Test;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
+
public class RestfulServerUtilsTest {
@Test
@@ -28,4 +30,31 @@ public class RestfulServerUtilsTest {
assertEquals("application/json+fhir", m.group(1));
}
+ @Test
+ public void testParsePreferHeaderBad() {
+ assertEquals(null, RestfulServerUtils.parsePreferHeader(null));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader(""));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("foo"));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("foo,bar"));
+
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return"));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return="));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return= "));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return= "));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return= "));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return= "));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return= "));
+ assertEquals(null, RestfulServerUtils.parsePreferHeader("return =\"minimal"));
+ }
+
+ @Test
+ public void testParsePreferHeaderGood() {
+ assertEquals(PreferReturnEnum.MINIMAL, RestfulServerUtils.parsePreferHeader("return=minimal"));
+ assertEquals(PreferReturnEnum.REPRESENTATION, RestfulServerUtils.parsePreferHeader("return=representation"));
+ assertEquals(PreferReturnEnum.MINIMAL, RestfulServerUtils.parsePreferHeader("return = minimal "));
+ assertEquals(PreferReturnEnum.MINIMAL, RestfulServerUtils.parsePreferHeader("return = \"minimal\" "));
+ assertEquals(PreferReturnEnum.MINIMAL, RestfulServerUtils.parsePreferHeader("return =\"minimal\""));
+ assertEquals(PreferReturnEnum.MINIMAL, RestfulServerUtils.parsePreferHeader("return =\"minimal\""));
+ }
+
}
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java
index 1a1fc811706..1c11667db61 100644
--- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java
@@ -53,6 +53,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.XmlParserDstu2Test;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.Constants;
@@ -178,6 +179,65 @@ public class GenericClientDstu2Test {
}
+ @Test
+ public void testCreatePrefer() throws Exception {
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ int idx = 0;
+
+ Patient p = new Patient();
+ p.addName().addFamily("FOOFAMILY");
+
+ client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute();
+ assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER).length);
+ assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER)[0].getValue());
+ idx++;
+
+ client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute();
+ assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER).length);
+ assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER)[0].getValue());
+ idx++;
+
+ }
+
+ @Test
+ public void testCreateReturningResourceBody() throws Exception {
+ Patient p = new Patient();
+ p.setId("123");
+ final String formatted = ourCtx.newXmlParser().encodeResourceToString(p);
+
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_200_OK, "OK"));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(formatted), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ p = new Patient();
+ p.setId(new IdDt("1"));
+ p.addName().addFamily("FOOFAMILY");
+
+ MethodOutcome output = client.create().resource(p).execute();
+ assertNotNull(output.getResource());
+ assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue());
+ }
+
@Test
public void testDeleteConditional() throws Exception {
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@@ -268,56 +328,6 @@ public class GenericClientDstu2Test {
idx++;
}
-
- @Test
- public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception {
-
- //@formatter:off
- final String input = "\n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- "";
- //@formatter:on
-
- ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
- when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
- when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
- when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
- when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {
- new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT")
- });
- when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
- @Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
- return new ReaderInputStream(new StringReader(input), Charset.forName("UTF-8"));
- }
- });
-
- IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
-
- ca.uhn.fhir.model.dstu2.resource.Bundle response;
-
- //@formatter:off
- response = client
- .search()
- .forResource(Patient.class)
- .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
- .execute();
- //@formatter:on
-
- assertEquals("2015-06-22T15:48:57.554-04:00", ResourceMetadataKeyEnum.UPDATED.get(response).getValueAsString());
- }
-
-
@Test
public void testOperationAsGetWithInParameters() throws Exception {
IParser p = ourCtx.newXmlParser();
@@ -401,7 +411,7 @@ public class GenericClientDstu2Test {
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString());
idx++;
}
-
+
@Test
public void testOperationAsGetWithNoInParameters() throws Exception {
IParser p = ourCtx.newXmlParser();
@@ -483,13 +493,9 @@ public class GenericClientDstu2Test {
@Test
public void testOperationWithBundleResponseJson() throws Exception {
-
- final String resp = "{\n" +
- " \"resourceType\":\"Bundle\",\n" +
- " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" +
- " \"base\":\"http://fhirtest.uhn.ca/baseDstu2\"\n" +
- "}";
-
+
+ final String resp = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://fhirtest.uhn.ca/baseDstu2\"\n" + "}";
+
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
@@ -502,7 +508,7 @@ public class GenericClientDstu2Test {
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
-
+
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
@@ -829,40 +835,52 @@ public class GenericClientDstu2Test {
}
-
- /**
- * See #191
- */
@Test
- public void testSearchReturningDstu2Bundle() throws Exception {
- String msg =IOUtils.toString(XmlParserDstu2Test.class.getResourceAsStream("/bundle_orion.xml"));
+ public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception {
+
+ //@formatter:off
+ final String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+ //@formatter:on
+
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
- when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML+ "; charset=UTF-8"));
- when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") });
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(input), Charset.forName("UTF-8"));
+ }
+ });
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ ca.uhn.fhir.model.dstu2.resource.Bundle response;
+
//@formatter:off
- ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search()
- .forResource("Observation")
- .where(Patient.NAME.matches().value("FOO"))
- .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
- .execute();
+ response = client
+ .search()
+ .forResource(Patient.class)
+ .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
+ .execute();
//@formatter:on
- Link link = response.getLink().get(0);
- assertEquals("just trying add link", link.getRelation());
- assertEquals("blarion", link.getUrl());
-
- Entry entry = response.getEntry().get(0);
- link = entry.getLink().get(0);
- assertEquals("orionhealth.edit", link.getRelation());
- assertEquals("Observation", link.getUrl());
+ assertEquals("2015-06-22T15:48:57.554-04:00", ResourceMetadataKeyEnum.UPDATED.get(response).getValueAsString());
}
-
@Test
public void testSearchByString() throws Exception {
String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
@@ -887,6 +905,38 @@ public class GenericClientDstu2Test {
}
+ /**
+ * See #191
+ */
+ @Test
+ public void testSearchReturningDstu2Bundle() throws Exception {
+ String msg = IOUtils.toString(XmlParserDstu2Test.class.getResourceAsStream("/bundle_orion.xml"));
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ //@formatter:off
+ ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search()
+ .forResource("Observation")
+ .where(Patient.NAME.matches().value("FOO"))
+ .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
+ .execute();
+ //@formatter:on
+
+ Link link = response.getLink().get(0);
+ assertEquals("just trying add link", link.getRelation());
+ assertEquals("blarion", link.getUrl());
+
+ Entry entry = response.getEntry().get(0);
+ link = entry.getLink().get(0);
+ assertEquals("orionhealth.edit", link.getRelation());
+ assertEquals("Observation", link.getUrl());
+ }
+
@Test
public void testSearchWithLastUpdated() throws Exception {
String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
@@ -1142,45 +1192,63 @@ public class GenericClientDstu2Test {
}
@Test
- public void testValidateNonFluent() throws Exception {
-
- OperationOutcome oo = new OperationOutcome();
- oo.addIssue().setDetails("FOOBAR");
- final String msg = ourCtx.newXmlParser().encodeResourceToString(oo);
-
+ public void testUpdatePrefer() throws Exception {
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
- when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
- when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
- when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
- return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
- Patient p = new Patient();
- p.addName().addGiven("GIVEN");
-
int idx = 0;
- MethodOutcome response;
- //@formatter:off
- response = client.validate(p);
- //@formatter:on
-
- assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
- assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
- assertEquals("", extractBody(capt, idx));
- assertNotNull(response.getOperationOutcome());
- assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
+ Patient p = new Patient();
+ p.setId(new IdDt("1"));
+ p.addName().addFamily("FOOFAMILY");
+
+ client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute();
+ assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER).length);
+ assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER)[0].getValue());
idx++;
+
+ client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute();
+ assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER).length);
+ assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_PREFER)[0].getValue());
+ idx++;
+
}
- private OperationOutcome toOo(IBaseOperationOutcome theOperationOutcome) {
- return (OperationOutcome) theOperationOutcome;
+ @Test
+ public void testUpdateReturningResourceBody() throws Exception {
+ Patient p = new Patient();
+ p.setId("123");
+ final String formatted = ourCtx.newXmlParser().encodeResourceToString(p);
+
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_200_OK, "OK"));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(formatted), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ p = new Patient();
+ p.setId(new IdDt("1"));
+ p.addName().addFamily("FOOFAMILY");
+
+ MethodOutcome output = client.update().resource(p).execute();
+ assertNotNull(output.getResource());
+ assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
@Test
@@ -1205,14 +1273,16 @@ public class GenericClientDstu2Test {
Patient p = new Patient();
p.addName().addGiven("GIVEN");
-
+
int idx = 0;
MethodOutcome response;
response = client.validate().resource(p).execute();
assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
- assertEquals("", extractBody(capt, idx));
+ assertEquals(
+ "",
+ extractBody(capt, idx));
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
idx++;
@@ -1220,7 +1290,9 @@ public class GenericClientDstu2Test {
response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute();
assertEquals("http://example.com/fhir/Patient/$validate?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
- assertEquals("", extractBody(capt, idx));
+ assertEquals(
+ "",
+ extractBody(capt, idx));
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
idx++;
@@ -1242,6 +1314,50 @@ public class GenericClientDstu2Test {
idx++;
}
+ @Test
+ public void testValidateNonFluent() throws Exception {
+
+ OperationOutcome oo = new OperationOutcome();
+ oo.addIssue().setDetails("FOOBAR");
+ final String msg = ourCtx.newXmlParser().encodeResourceToString(oo);
+
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient p = new Patient();
+ p.addName().addGiven("GIVEN");
+
+ int idx = 0;
+ MethodOutcome response;
+
+ //@formatter:off
+ response = client.validate(p);
+ //@formatter:on
+
+ assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
+ assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
+ assertEquals(
+ "",
+ extractBody(capt, idx));
+ assertNotNull(response.getOperationOutcome());
+ assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
+ idx++;
+ }
+
+ private OperationOutcome toOo(IBaseOperationOutcome theOperationOutcome) {
+ return (OperationOutcome) theOperationOutcome;
+ }
+
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu2();
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java
index 6daf96cba5b..c99bf5eb648 100644
--- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java
@@ -1,6 +1,12 @@
package ca.uhn.fhir.rest.server;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.emptyOrNullString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isEmptyOrNullString;
+import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import java.util.concurrent.TimeUnit;
@@ -30,9 +36,6 @@ import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
-/**
- * Created by dsotnikov on 2/25/2014.
- */
public class PreferTest {
private static CloseableHttpClient ourClient;
@@ -65,6 +68,55 @@ public class PreferTest {
}
+ @Test
+ public void testCreatePreferMinimal() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setValue("002");
+
+ HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
+ httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_MINIMAL);
+ httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
+
+ HttpResponse status = ourClient.execute(httpPost);
+
+ String responseContent = IOUtils.toString(status.getEntity().getContent());
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ ourLog.info("Response was:\n{}", responseContent);
+
+ assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode());
+ assertThat(responseContent, is(emptyOrNullString()));
+ assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), not(containsString("fhir")));
+ assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
+ assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
+
+ }
+
+ @Test
+ public void testCreatePreferRepresentation() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setValue("002");
+
+ HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
+ httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION);
+ httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
+
+ HttpResponse status = ourClient.execute(httpPost);
+
+ String responseContent = IOUtils.toString(status.getEntity().getContent());
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ ourLog.info("Response was:\n{}", responseContent);
+
+ assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode());
+ assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), containsString(Constants.CT_FHIR_XML));
+ assertEquals("", responseContent);
+ assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
+ assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
+
+ }
@AfterClass
@@ -104,13 +156,26 @@ public class PreferTest {
@Create()
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
- MethodOutcome retVal = new MethodOutcome(new IdDt("Patient/001/_history/002"));
+ IdDt id = new IdDt("Patient/001/_history/002");
+ MethodOutcome retVal = new MethodOutcome(id);
+
+ Patient pt = new Patient();
+ pt.setId(id);
+ retVal.setResource(pt);
+
return retVal;
}
@Update()
public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @IdParam IdDt theIdParam) {
- return new MethodOutcome(new IdDt("Patient/001/_history/002"));
+ IdDt id = new IdDt("Patient/001/_history/002");
+ MethodOutcome retVal = new MethodOutcome(id);
+
+ Patient pt = new Patient();
+ pt.setId(id);
+ retVal.setResource(pt);
+
+ return retVal;
}
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index e42ca35f058..210bae5cbe4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -137,6 +137,15 @@
DSTU2 servers now indicate support for conditional create/update/delete in their
conformance statement.
+
+ Support for the Prefer header has been added to the server, client, and
+ JPA modules.
+
+
+ JPA server failed to search for deep chained parameters across multiple references,
+ e.g. "Location.partof.partof.organization". Thanks to Ismael Sarmento Jr for
+ reporting!
+
diff --git a/src/site/resources/hapi.css b/src/site/resources/hapi.css
index 25fce5416ce..630f51ceca5 100644
--- a/src/site/resources/hapi.css
+++ b/src/site/resources/hapi.css
@@ -157,6 +157,10 @@ DIV.main-body DIV.row DIV.span8 DIV.body-content {
background-color: #F8F8F8 !important;
}
+.syntaxhighlighter .code .container:before {
+ display: block;
+}
+
.table th, .table td {
padding: 2px;
}
diff --git a/src/site/site.xml b/src/site/site.xml
index 1a00b94344a..438dc874a46 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -127,8 +127,8 @@
+
Prefer Header / Returning the resource body
+
+ If you wish to allow your server to honour the Prefer
+ header, the same mechanism shown above for
+ Prefer Header for Updates should be used.
+
+
Accessing The Raw Resource Payload
The create operation also supports access to the raw payload,