Fix #196 - Support deep chained parameters in JPA. Also add support for

Prefer header.
This commit is contained in:
James Agnew 2015-07-07 10:41:07 -04:00
parent 70222ecf15
commit 0f76ba81e1
26 changed files with 1313 additions and 625 deletions

View File

@ -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 (

View File

@ -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;
}
/**
* <b>From a client response:</b> 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;
}
/**
* <b>In a server response</b>: 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.
* <p>
* 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.
* </p>
*/
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)}
*/

View File

@ -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 <a href="https://tools.ietf.org/html/rfc7240#section-4.2">HTTP Prefer header</a>.
*/
public enum PreferReturnEnum {
REPRESENTATION("representation"), MINIMAL("minimal");
private String myHeaderValue;
private static HashMap<String, PreferReturnEnum> ourValues;
private PreferReturnEnum(String theHeaderValue) {
myHeaderValue = theHeaderValue;
}
public static PreferReturnEnum fromHeaderValue(String theHeaderValue) {
if (ourValues == null) {
HashMap<String, PreferReturnEnum> values = new HashMap<String, PreferReturnEnum>();
for (PreferReturnEnum next : PreferReturnEnum.values()) {
values.put(next.getHeaderValue(), next);
}
ourValues = values;
}
return ourValues.get(theHeaderValue);
}
public String getHeaderValue() {
return myHeaderValue;
}
}

View File

@ -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<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
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<BaseOperationOutcome> {
@Override
public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> 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<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> 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<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType, null);
@ -1443,7 +1459,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> 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<Include> myInclude = new ArrayList<Include>();
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<Include> myRevInclude = new ArrayList<Include>();
private SearchStyleEnum mySearchStyle;
private DateRangeParam myLastUpdated;
private List<SortInternal> mySort = new ArrayList<SortInternal>();
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<TagList> {
@Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> 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");

View File

@ -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<ICreateTyped, MethodOutcome> {
/**
* 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.
*
* <p>
* 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.
* </p>
* @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 <code>[ResourceType]?[Parameters]</code>,
* for example: <code>Patient?name=Smith&amp;identifier=13.2.4.11.4%7C847366</code>
* @since HAPI 0.9 / FHIR DSTU 2
*/
ICreateTyped conditionalByUrl(String theSearchUrl);
/**
* Add a <code>Prefer</code> 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<ICreateTyped, MethodOutc
ICreateTyped withId(IdDt theId);
/**
* 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 <code>[ResourceType]?[Parameters]</code>,
* for example: <code>Patient?name=Smith&amp;identifier=13.2.4.11.4%7C847366</code>
* @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.
*
* <p>
* 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.
* </p>
*/
ICreateTyped conditionalByUrl(String theSearchUrl);
/**
* @since HAPI 0.9 / FHIR DSTU 2
*/
ICreateWithQuery conditional();
ICreateTyped withId(String theId);
}

View File

@ -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<IUpdateExecutable, MethodOutcome>{
/**
* Add a <code>Prefer</code> 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);
}

View File

@ -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<Metho
}
}
IBaseOperationOutcome outcome = response != null ? response.getOperationOutcome() : null;
IBaseResource outcome = response != null ? response.getOperationOutcome() : null;
IBaseResource resource = response != null ? response.getResource() : null;
for (int i = theServer.getInterceptors().size() - 1; i >= 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<Metho
}
}
boolean allowPrefer = false;
switch (getResourceOperationType()) {
case CREATE:
if (response == null) {
@ -191,6 +194,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
}
addContentLocationHeaders(theRequest, servletResponse, response);
allowPrefer = true;
break;
case UPDATE:
@ -200,6 +204,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
}
addContentLocationHeaders(theRequest, servletResponse, response);
allowPrefer = true;
break;
case VALIDATE:
@ -222,6 +227,16 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
theServer.addHeadersToResponse(servletResponse);
if (resource != null && allowPrefer) {
String prefer = theRequest.getServletRequest().getHeader(Constants.HEADER_PREFER);
PreferReturnEnum preferReturn = RestfulServerUtils.parsePreferHeader(prefer);
if (preferReturn != null) {
if (preferReturn == PreferReturnEnum.REPRESENTATION) {
outcome = resource;
}
}
}
if (outcome != null) {
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
servletResponse.setContentType(encoding.getResourceContentType());
@ -229,7 +244,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
IParser parser = encoding.newParser(getContext());
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
try {
parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
parser.encodeResourceToWriter(outcome, writer);
} finally {
writer.close();
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.method;
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.io.IOException;
import java.io.PushbackReader;
@ -747,6 +748,8 @@ public class MethodUtil {
IBaseResource outcome = parser.parseResource(reader);
if (outcome instanceof BaseOperationOutcome) {
retVal.setOperationOutcome((BaseOperationOutcome) outcome);
} else {
retVal.setResource(outcome);
}
}

View File

@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.param;
import static org.apache.commons.lang3.StringUtils.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
@ -30,8 +32,8 @@ import ca.uhn.fhir.model.primitive.IdDt;
public class ReferenceParam extends IdDt implements IQueryParameterType {
private String myChain;
private BaseParam myBase=new BaseParam.ComposableBaseParam();
private String myChain;
public ReferenceParam() {
}
@ -58,6 +60,11 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
return myChain;
}
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override
public String getQueryParameterQualifier() {
if (myBase.getMissing()!=null) {
@ -79,6 +86,13 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
return null;
}
public Class<? extends IBaseResource> 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.
* <p>
* See <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_operations.html#dynamic_chains">Dynamic Chains</a>
* in the HAPI FHIR documentation for an example of how to use this method.
* </p>
*/
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.
* <p>
* See <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_operations.html#dynamic_chains">Dynamic Chains</a>
* in the HAPI FHIR documentation for an example of how to use this method.
* </p>
*/
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.
* <p>
* See <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_operations.html#dynamic_chains">Dynamic Chains</a>
* in the HAPI FHIR documentation for an example of how to use this method.
* </p>
*/
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.
* <p>
* See <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_operations.html#dynamic_chains">Dynamic Chains</a>
* in the HAPI FHIR documentation for an example of how to use this method.
* </p>
*/
public TokenParam toTokenParam() {
TokenParam retVal = new TokenParam();
retVal.setValueAsQueryToken(null, getValueAsQueryToken());
return retVal;
}
}

View File

@ -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<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -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<Include> 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<String, String[]> 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<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.get(Constants.PARAM_PRETTY);

View File

@ -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;

View File

@ -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 {

View File

@ -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.

View File

@ -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<T extends IResource> 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<T extends IResource> extends BaseFhirD
cq.select(from.get("myId").as(Long.class));
Subquery<Long> 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<T extends IResource> 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<Long> q = myEntityManager.createQuery(cq);
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
@ -404,15 +405,15 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
Subquery<Long> subQ = cq.subquery(Long.class);
Root<ResourceLink> subQfrom = subQ.from(ResourceLink.class);
Root<ResourceLink> 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<T extends IResource> extends BaseFhirD
} else {
cq.where(builder.and(typePredicate, joinPredicate));
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
@ -433,7 +434,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> 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<T extends IResource> extends BaseFhirD
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
String systemValue;
String unitsValue;
QuantityCompararatorEnum cmpValue;
@ -569,7 +570,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> 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<T extends IResource> 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<Class<? extends IBaseResource>> resourceTypes;
if (isBlank(ref.getResourceType())) {
@ -601,9 +603,20 @@ public abstract class BaseFhirResourceDao<T extends IResource> 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<T extends IResource> extends BaseFhirD
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName(), param);
continue;
}
IQueryParameterType chainValue = toParameterType(param, resourceId);
Set<Long> 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<Long> pids = dao.searchForIds(chain, chainValue);
if (pids.isEmpty()) {
continue;
}
@ -624,10 +652,14 @@ public abstract class BaseFhirResourceDao<T extends IResource> 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<T extends IResource> 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<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates,
IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
@ -707,7 +740,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return missingFalse;
}
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates,
IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
@ -740,7 +774,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> 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<T extends IResource> 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<T extends IResource> 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<T extends IResource> extends BaseFhirD
retVal.add(current);
}
TypedQuery<ResourceHistoryTable> 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<ResourceHistoryTable> 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<T extends IResource> 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<T extends IResource> extends BaseFhirD
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> 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.<Date>get("myUpdated"), lu.getLowerBoundAsInstant()) : null;
Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from.<Date>get("myUpdated"), lu.getUpperBoundAsInstant()) : null;
Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), lu.getLowerBoundAsInstant()) : null;
Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from.<Date> 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<T extends IResource> 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<IIdType> previouslyLoadedPids = new HashSet<IIdType>();
@ -1960,6 +1994,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> 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<T extends IResource> extends BaseFhirD
qp = new CompositeParam<IQueryParameterType, IQueryParameterType>(leftParam, rightParam);
break;
case REFERENCE:
qp = new ReferenceParam();
break;
default:
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
}

View File

@ -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;
}
}

View File

@ -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<Bundle> {
FhirTerser terser = getContext().newTerser();
for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) {
IResource nextResource = nextOutcome.getResource();
IResource nextResource = (IResource) nextOutcome.getResource();
if (nextResource == null) {
continue;
}

View File

@ -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<Device> ourDeviceDao;
@ -90,28 +106,11 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
private static IFhirResourceDao<Observation> ourObservationDao;
private static IFhirResourceDao<Organization> ourOrganizationDao;
private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirSystemDao<Bundle> ourSystemDao;
private static IFhirResourceDao<Questionnaire> ourQuestionnaireDao;
@SuppressWarnings("unused")
private static IFhirResourceDao<QuestionnaireAnswers> ourQuestionnaireAnswersDao;
private static IFhirResourceDao<Questionnaire> ourQuestionnaireDao;
private static IFhirSystemDao<Bundle> 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<IResource> 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<IResource> 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<Patient> 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<IBaseResource> 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<IBaseResource> 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<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(false);
params.put(Patient.SP_FAMILY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(true);
params.put(Patient.SP_FAMILY, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
QuantityParam param = new QuantityParam();
param.setMissing(false);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
QuantityParam param = new QuantityParam();
param.setMissing(true);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
TokenParam param = new TokenParam();
param.setMissing(false);
params.put(Observation.SP_CODE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
TokenParam param = new TokenParam();
param.setMissing(true);
params.put(Observation.SP_CODE, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
DateParam param = new DateParam();
param.setMissing(false);
params.put(Patient.SP_BIRTHDATE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
DateParam param = new DateParam();
param.setMissing(true);
params.put(Patient.SP_BIRTHDATE, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
ReferenceParam param = new ReferenceParam();
param.setMissing(false);
params.put(Patient.SP_ORGANIZATION, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
ReferenceParam param = new ReferenceParam();
param.setMissing(true);
params.put(Patient.SP_ORGANIZATION, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
DateParam param = new DateParam();
param.setMissing(false);
params.put(Patient.SP_BIRTHDATE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
DateParam param = new DateParam();
param.setMissing(true);
params.put(Patient.SP_BIRTHDATE, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
QuantityParam param = new QuantityParam();
param.setMissing(false);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
QuantityParam param = new QuantityParam();
param.setMissing(true);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
ReferenceParam param = new ReferenceParam();
param.setMissing(false);
params.put(Patient.SP_ORGANIZATION, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
ReferenceParam param = new ReferenceParam();
param.setMissing(true);
params.put(Patient.SP_ORGANIZATION, param);
List<IIdType> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(false);
params.put(Patient.SP_FAMILY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(true);
params.put(Patient.SP_FAMILY, param);
List<IIdType> 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<IBaseResource> 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<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
TokenParam param = new TokenParam();
param.setMissing(false);
params.put(Observation.SP_CODE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
TokenParam param = new TokenParam();
param.setMissing(true);
params.put(Observation.SP_CODE, param);
List<IIdType> 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<IIdType> 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<IIdType> 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<IBaseResource> res = value.getResources(0, 0);
assertTrue(res.isEmpty());
}
}

View File

@ -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 {

View File

@ -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\""));
}
}

View File

@ -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<HttpUriRequest> 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<ReaderInputStream>() {
@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<HttpUriRequest> 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<ReaderInputStream>() {
@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<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -268,56 +328,6 @@ public class GenericClientDstu2Test {
idx++;
}
@Test
public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception {
//@formatter:off
final String input = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" +
" <id value=\"e2ee823b-ee4d-472d-b79d-495c23f16b99\"/>\n" +
" <meta>\n" +
" <lastUpdated value=\"2015-06-22T15:48:57.554-04:00\"/>\n" +
" </meta>\n" +
" <type value=\"searchset\"/>\n" +
" <base value=\"http://localhost:58109/fhir/context\"/>\n" +
" <total value=\"0\"/>\n" +
" <link>\n" +
" <relation value=\"self\"/>\n" +
" <url value=\"http://localhost:58109/fhir/context/Patient?_pretty=true\"/>\n" +
" </link>\n" +
"</Bundle>";
//@formatter:on
ArgumentCaptor<HttpUriRequest> 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<InputStream>() {
@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&param1=STRINGVALIN1b&param2=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<HttpUriRequest> 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 = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" +
" <id value=\"e2ee823b-ee4d-472d-b79d-495c23f16b99\"/>\n" +
" <meta>\n" +
" <lastUpdated value=\"2015-06-22T15:48:57.554-04:00\"/>\n" +
" </meta>\n" +
" <type value=\"searchset\"/>\n" +
" <base value=\"http://localhost:58109/fhir/context\"/>\n" +
" <total value=\"0\"/>\n" +
" <link>\n" +
" <relation value=\"self\"/>\n" +
" <url value=\"http://localhost:58109/fhir/context/Patient?_pretty=true\"/>\n" +
" </link>\n" +
"</Bundle>";
//@formatter:on
ArgumentCaptor<HttpUriRequest> 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<InputStream>() {
@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<HttpUriRequest> 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<HttpUriRequest> 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<InputStream>() {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
@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("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", 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<HttpUriRequest> 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<ReaderInputStream>() {
@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("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx));
assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>",
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("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx));
assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>",
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<HttpUriRequest> 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<InputStream>() {
@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(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>",
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();

View File

@ -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("<Patient xmlns=\"http://hl7.org/fhir\"><id value=\"001\"/><meta><versionId value=\"002\"/></meta></Patient>", 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;
}
}

View File

@ -137,6 +137,15 @@
DSTU2 servers now indicate support for conditional create/update/delete in their
conformance statement.
</action>
<action type="fix">
Support for the Prefer header has been added to the server, client, and
JPA modules.
</action>
<action type="fix" issue="196">
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!
</action>
</release>
<release version="1.0" date="2015-May-8">
<action type="add">

View File

@ -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;
}

View File

@ -127,8 +127,8 @@
</menu>
<menu name="Get Help" inherit="top">
<item name="Google Group" href="https://groups.google.com/d/forum/hapi-fhir" />
<item name="Issue Tracker" href="https://github.com/jamesagnew/hapi-fhir/issues" />
<item name="Google Group (Ask Questions)" href="https://groups.google.com/d/forum/hapi-fhir" />
<item name="Issue Tracker (Report Bugs/Request Features)" href="https://github.com/jamesagnew/hapi-fhir/issues" />
</menu>
<menu name="Test Server">

View File

@ -195,6 +195,29 @@
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<a name="prefer"/>
<h4>Prefer Header / Returning the resource body</h4>
<p>
If you want to allow clients to request that the server return
the resource body as a result of the transaction, you may wish to
return the updated resource in the returned MethodOutcome.
</p>
<p>
In this type of request, the client adds a header containing
<code>Prefer: return=representation</code> which indicates to the server
that the client would like the resource returned in the response.
</p>
<p>
In order for the server to be able to honour this request, the
server method should add the updated resource to the MethodOutcome object
being returned, as shown in the example below.
</p>
<macro name="snippet">
<param name="id" value="updatePrefer" />
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<a name="instance_delete" />
</section>
@ -357,6 +380,13 @@
<code>http://fhir.example.com/Patient<br/>If-None-Exist: Patient?identifier=system%7C0001</code>
</p>
<h4>Prefer Header / Returning the resource body</h4>
<p>
If you wish to allow your server to honour the <code>Prefer</code>
header, the same mechanism shown above for
<a href="#prefer">Prefer Header for Updates</a> should be used.
</p>
<h4>Accessing The Raw Resource Payload</h4>
<p>
The create operation also supports access to the raw payload,