Merge branch 'master' of github.com:jamesagnew/hapi-fhir into hl7org_structs

This commit is contained in:
jamesagnew 2015-05-07 21:24:34 -04:00
commit dfbe2415d1
104 changed files with 2388 additions and 1357 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -17,12 +17,12 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>

View File

@ -33,6 +33,22 @@ public class ClientExamples {
// END SNIPPET: proxy
}
@SuppressWarnings("unused")
public void createTimeouts() {
// START SNIPPET: timeouts
FhirContext ctx = new FhirContext();
// Set how long to try and establish the initial TCP connection (in ms)
ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000);
// Set how long to block for individual read/write operations (in ms)
ctx.getRestfulClientFactory().setSocketTimeout(20 * 1000);
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: timeouts
}
@SuppressWarnings("unused")
public void createSecurity() {
// START SNIPPET: security

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -18,7 +18,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
@ -37,12 +37,12 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.phloc</groupId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -91,7 +91,7 @@ public abstract class BaseElement implements IElement, ISupportsUndeclaredExtens
if (myUndeclaredExtensions == null) {
myUndeclaredExtensions = new ArrayList<ExtensionDt>();
}
return Collections.unmodifiableList(myUndeclaredExtensions);
return (myUndeclaredExtensions);
}
@Override
@ -111,7 +111,7 @@ public abstract class BaseElement implements IElement, ISupportsUndeclaredExtens
if (myUndeclaredModifierExtensions == null) {
myUndeclaredModifierExtensions = new ArrayList<ExtensionDt>();
}
return Collections.unmodifiableList(myUndeclaredModifierExtensions);
return (myUndeclaredModifierExtensions);
}
/**

View File

@ -59,4 +59,16 @@ public interface IQueryParameterType {
*/
public String getQueryParameterQualifier();
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
*/
Boolean getMissing();
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
*/
void setMissing(Boolean theMissing);
}

View File

@ -27,22 +27,28 @@ import org.hl7.fhir.instance.model.api.IBaseDatatype;
public interface ISupportsUndeclaredExtensions extends IElement {
/**
* Returns a list containing all undeclared non-modifier extensions
* Returns a list containing all undeclared non-modifier extensions. The returned list
* is mutable, so it may be modified (e.g. to add or remove an extension).
*/
List<ExtensionDt> getUndeclaredExtensions();
/**
* Returns a list containing all undeclared extensions (modifier and non-modifier) by extension URL
* Returns an <b>immutable</b> list containing all undeclared extensions (modifier and non-modifier) by extension URL
*
* @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions
*/
List<ExtensionDt> getUndeclaredExtensionsByUrl(String theUrl);
/**
* Returns an <b>immutable</b> list containing all extensions (modifier and non-modifier)
* Returns an <b>immutable</b> list containing all extensions (modifier and non-modifier).
*
* @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions
*/
List<ExtensionDt> getAllUndeclaredExtensions();
/**
* Returns a list containing all undeclared modifier extensions
* Returns a list containing all undeclared modifier extensions. The returned list
* is mutable, so it may be modified (e.g. to add or remove an extension).
*/
List<ExtensionDt> getUndeclaredModifierExtensions();
@ -65,6 +71,8 @@ public interface ISupportsUndeclaredExtensions extends IElement {
/**
* Adds an extension to this object
*
* @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions
*/
ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl, IBaseDatatype theValue);
@ -72,6 +80,8 @@ public interface ISupportsUndeclaredExtensions extends IElement {
* Adds an extension to this object. This method is intended for use when
* an extension is being added which will contain child extensions, as opposed to
* a datatype.
*
* @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions
*/
ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl);

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.param.TokenParam;
public abstract class BaseCodingDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType {
@ -172,4 +173,28 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
public abstract BaseCodingDt setSystem(String theUri);
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link TokenParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link TokenParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
}

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.param.StringParam;
public abstract class BaseIdentifierDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType {
@ -112,4 +113,29 @@ public abstract class BaseIdentifierDt extends BaseIdentifiableElement implement
}
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
}

View File

@ -33,6 +33,8 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.StringParam;
public abstract class BaseQuantityDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType {
@ -208,4 +210,30 @@ public abstract class BaseQuantityDt extends BaseIdentifiableElement implements
* </p>
*/
public abstract DecimalDt getValueElement();
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link QuantityParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link QuantityParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
}

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.rest.param.StringParam;
@DatatypeDef(name = "string")
public class StringDt extends BasePrimitive<String> implements IQueryParameterType {
@ -122,4 +123,28 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
return theValue;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
}

View File

@ -153,10 +153,14 @@ public abstract class BaseClient implements IRestfulClient {
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse);
}
void forceConformanceCheck() {
myFactory.validateServerBase(myUrlBase, myClient, this);
}
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint, boolean theLogRequestAndResponse) {
if (!myDontValidateConformance) {
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient);
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
}
// TODO: handle non 2xx status codes by throwing the correct exception,
@ -441,4 +445,8 @@ public abstract class BaseClient implements IRestfulClient {
return reader;
}
public List<IClientInterceptor> getInterceptors() {
return Collections.unmodifiableList(myInterceptors);
}
}

View File

@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.client.HttpClient;
@ -144,11 +145,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
myContext = theContext;
}
@Override
public BaseConformance conformance() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
throw new IllegalArgumentException("Must call conformance(" + IBaseConformance.class.getSimpleName() + ") for HL7.org structures");
throw new IllegalArgumentException("Must call conformance(" + IBaseConformance.class.getSimpleName() + ") instead of conformance() for HL7.org structures");
}
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
@ -164,6 +164,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp;
}
@Override
public void forceConformanceCheck() {
super.forceConformanceCheck();
}
@Override
public ICreate create() {
return new CreateInternal();
@ -588,24 +593,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp;
}
protected IBaseResource parseResourceBody(String theResourceBody) {
EncodingEnum encoding = null;
for (int i = 0; i < theResourceBody.length() && encoding == null; i++) {
switch (theResourceBody.charAt(i)) {
case '<':
encoding = EncodingEnum.XML;
break;
case '{':
encoding = EncodingEnum.JSON;
break;
}
}
protected IResource parseResourceBody(String theResourceBody) {
EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody);
if (encoding == null) {
throw new InvalidRequestException("FHIR client can't determine resource encoding");
}
return encoding.newParser(myContext).parseResource(theResourceBody);
}
@SuppressWarnings("unchecked")
@Override
public T prettyPrint() {
@ -635,6 +631,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private final class StringResponseHandler implements IClientResponseHandler<String> {
@Override
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
return IOUtils.toString(theResponseReader);
}
}
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
private CriterionList myCriterionList;
@ -1575,6 +1580,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private Bundle myBundle;
private List<IBaseResource> myResources;
private IBaseBundle myBaseBundle;
private String myRawBundle;
private EncodingEnum myRawBundleEncoding;
public TransactionExecutable(Bundle theResources) {
myBundle = theResources;
@ -1588,6 +1595,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
myBaseBundle = theBundle;
}
public TransactionExecutable(String theBundle) {
myRawBundle = theBundle;
myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle);
if (myRawBundleEncoding == null) {
throw new IllegalArgumentException("Can not determine encoding of raw resource body");
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public T execute() {
@ -1600,6 +1615,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
ResourceResponseHandler binding = new ResourceResponseHandler(myBaseBundle.getClass(), null);
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBaseBundle, myContext);
return (T) invoke(params, binding, invocation);
} else if (myRawBundle != null) {
StringResponseHandler binding = new StringResponseHandler();
/*
* If the user has explicitly requested a given encoding, we may need to reencode the raw string
*/
if (getParamEncoding() != null) {
if (MethodUtil.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) {
IResource parsed = parseResourceBody(myRawBundle);
myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed);
}
}
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myRawBundle, myContext);
return (T) invoke(params, binding, invocation);
} else {
BundleResponseHandler binding = new BundleResponseHandler(null);
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBundle, myContext);
@ -1629,6 +1657,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new TransactionExecutable<T>(theBundle);
}
@Override
public ITransactionTyped<String> withBundle(String theBundle) {
Validate.notBlank(theBundle, "theBundle must not be null");
return new TransactionExecutable<String>(theBundle);
}
}
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {

View File

@ -34,6 +34,8 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientInnapropriateForServerException;
import ca.uhn.fhir.rest.gclient.ICreate;
import ca.uhn.fhir.rest.gclient.IDelete;
import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped;
@ -109,6 +111,16 @@ public interface IGenericClient extends IRestfulClient {
@Deprecated
MethodOutcome delete(Class<? extends IResource> theType, String theId);
/**
* Force the client to fetch the server's conformance statement and validate that it is appropriate for this client.
*
* @throws FhirClientConnectionException
* if the conformance statement cannot be read, or if the client
* @throws FhirClientInnapropriateForServerException
* If the conformance statement indicates that the server is inappropriate for this client (e.g. it implements the wrong version of FHIR)
*/
void forceConformanceCheck() throws FhirClientConnectionException;
/**
* Fluent method for the "get tags" operation
*/
@ -123,17 +135,15 @@ public interface IGenericClient extends IRestfulClient {
* Implementation of the "history instance" method.
*
* @param theType
* The type of resource to return the history for, or
* <code>null</code> to search for history across all resources
* The type of resource to return the history for, or <code>null</code> to search for history across all resources
* @param theId
* The ID of the resource to return the history for, or <code>null</code> to search for all resource
* instances. Note that if this param is not null, <code>theType</code> must also not be null
* The ID of the resource to return the history for, or <code>null</code> to search for all resource instances. Note that if this param is not null, <code>theType</code> must also not
* be null
* @param theSince
* If not null, request that the server only return resources updated since this time
* @param theLimit
* If not null, request that the server return no more than this number of resources. Note that the
* server may return less even if more are available, but should not return more according to the FHIR
* specification.
* If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more
* according to the FHIR specification.
* @return A bundle containing returned resources
* @deprecated As of 0.9, use the fluent {@link #history()} method instead
*/
@ -144,31 +154,21 @@ public interface IGenericClient extends IRestfulClient {
* Implementation of the "history instance" method.
*
* @param theType
* The type of resource to return the history for, or
* <code>null</code> to search for history across all resources
* The type of resource to return the history for, or <code>null</code> to search for history across all resources
* @param theId
* The ID of the resource to return the history for, or <code>null</code> to search for all resource
* instances. Note that if this param is not null, <code>theType</code> must also not be null
* The ID of the resource to return the history for, or <code>null</code> to search for all resource instances. Note that if this param is not null, <code>theType</code> must also not
* be null
* @param theSince
* If not null, request that the server only return resources updated since this time
* @param theLimit
* If not null, request that the server return no more than this number of resources. Note that the
* server may return less even if more are available, but should not return more according to the FHIR
* specification.
* If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more
* according to the FHIR specification.
* @return A bundle containing returned resources
* @deprecated As of 0.9, use the fluent {@link #history()} method instead
*/
@Deprecated
<T extends IResource> Bundle history(Class<T> theType, String theId, DateTimeDt theSince, Integer theLimit);
/**
* Loads the previous/next bundle of resources from a paged set, using the link specified in the "link type=next"
* tag within the atom bundle.
*
* @see Bundle#getLinkNext()
*/
IGetPage loadPage();
// /**
// * Implementation of the "instance read" method. This method will only ever do a "read" for the latest version of a
// * given resource instance, even if the ID passed in contains a version. If you wish to request a specific version
@ -187,6 +187,13 @@ public interface IGenericClient extends IRestfulClient {
// */
// <T extends IBaseResource> T read(Class<T> theType, IdDt theId);
/**
* Loads the previous/next bundle of resources from a paged set, using the link specified in the "link type=next" tag within the atom bundle.
*
* @see Bundle#getLinkNext()
*/
IGetPage loadPage();
/**
* Implementation of the FHIR "extended operations" action
*/
@ -229,8 +236,7 @@ public interface IGenericClient extends IRestfulClient {
IBaseResource read(UriDt theUrl);
/**
* Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security
* headers, or pre-process responses, etc.
* Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
@ -259,8 +265,8 @@ public interface IGenericClient extends IRestfulClient {
Bundle search(UriDt theUrl);
/**
* If set to <code>true</code>, the client will log all requests and all responses. This is probably not a good
* production setting since it will result in a lot of extra logging, but it can be useful for troubleshooting.
* If set to <code>true</code>, the client will log all requests and all responses. This is probably not a good production setting since it will result in a lot of extra logging, but it can be
* useful for troubleshooting.
*
* @param theLogRequestAndResponse
* Should requests and responses be logged
@ -277,8 +283,7 @@ public interface IGenericClient extends IRestfulClient {
*
* @param theResources
* The resources to create/update in a single transaction
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other
* {@link IResource#getResourceMetadata() metadata}
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other {@link IResource#getResourceMetadata() metadata}
* @deprecated Use {@link #transaction()}
*
*/
@ -286,8 +291,7 @@ public interface IGenericClient extends IRestfulClient {
List<IBaseResource> transaction(List<IBaseResource> theResources);
/**
* Remove an intercaptor that was previously registered using
* {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
@ -328,18 +332,16 @@ public interface IGenericClient extends IRestfulClient {
MethodOutcome validate(IResource theResource);
/**
* Implementation of the "instance vread" method. Note that this method expects <code>theId</code> to contain a
* resource ID as well as a version ID, and will fail if it does not.
* Implementation of the "instance vread" method. Note that this method expects <code>theId</code> to contain a resource ID as well as a version ID, and will fail if it does not.
* <p>
* Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the
* resource type and ID) the server base for the client will be ignored, and the URL passed in will be queried.
* Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the resource type and ID) the server base for the client will be ignored, and the URL
* passed in will be queried.
* </p>
*
* @param theType
* The type of resource to load
* @param theId
* The ID to load, including the resource ID and the resource version ID. Valid values include
* "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222"
* The ID to load, including the resource ID and the resource version ID. Valid values include "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222"
* @return The resource
*/
<T extends IBaseResource> T vread(Class<T> theType, IdDt theId);

View File

@ -72,6 +72,12 @@ public interface IRestfulClientFactory {
*/
HttpClient getHttpClient();
/**
* @deprecated Use {@link #getServerValidationMode()} instead
*/
@Deprecated
ServerValidationModeEnum getServerValidationModeEnum();
/**
* Gets the server validation mode for any clients created from this factory. Server
* validation involves the client requesting the server's conformance statement
@ -79,8 +85,10 @@ public interface IRestfulClientFactory {
* <p>
* The default value for this setting is defined by {@link #DEFAULT_SERVER_VALIDATION_MODE}
* </p>
*
* @since 1.0
*/
ServerValidationModeEnum getServerValidationModeEnum();
ServerValidationModeEnum getServerValidationMode();
/**
* Gets the socket timeout, in milliseconds. This is the SO_TIMEOUT time, which is the amount of time that a
@ -158,6 +166,12 @@ public interface IRestfulClientFactory {
*/
void setProxyCredentials(String theUsername, String thePassword);
/**
* @deprecated Use {@link #setServerValidationMode(ServerValidationModeEnum)} instead. This method was incorrectly named.
*/
@Deprecated
void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode);
/**
* Sets the server validation mode for any clients created from this factory. Server
* validation involves the client requesting the server's conformance statement
@ -165,8 +179,10 @@ public interface IRestfulClientFactory {
* <p>
* The default value for this setting is defined by {@link #DEFAULT_SERVER_VALIDATION_MODE}
* </p>
*
* @since 1.0
*/
void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode);
void setServerValidationMode(ServerValidationModeEnum theServerValidationMode);
/**
* Sets the socket timeout, in milliseconds. This is the SO_TIMEOUT time, which is the amount of time that a

View File

@ -51,6 +51,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientInnapropriateForServerException;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.FhirTerser;
@ -137,7 +138,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
}
@Override
public ServerValidationModeEnum getServerValidationModeEnum() {
public ServerValidationModeEnum getServerValidationMode() {
return myServerValidationMode;
}
@ -195,25 +196,29 @@ public class RestfulClientFactory implements IRestfulClientFactory {
/**
* This method is internal to HAPI - It may change in future versions, use with caution.
*/
public void validateServerBaseIfConfiguredToDoSo(String theServerBase, HttpClient theHttpClient) {
String serverBase = theServerBase;
if (!serverBase.endsWith("/")) {
serverBase = serverBase + "/";
}
public void validateServerBaseIfConfiguredToDoSo(String theServerBase, HttpClient theHttpClient, BaseClient theClient) {
String serverBase = normalizeBaseUrlForMap(theServerBase);
switch (myServerValidationMode) {
case NEVER:
break;
case ONCE:
if (!myValidatedServerBaseUrls.contains(serverBase)) {
validateServerBase(serverBase, theHttpClient);
myValidatedServerBaseUrls.add(serverBase);
validateServerBase(serverBase, theHttpClient, theClient);
}
break;
}
}
private String normalizeBaseUrlForMap(String theServerBase) {
String serverBase = theServerBase;
if (!serverBase.endsWith("/")) {
serverBase = serverBase + "/";
}
return serverBase;
}
@Override
public synchronized void setConnectionRequestTimeout(int theConnectionRequestTimeout) {
myConnectionRequestTimeout = theConnectionRequestTimeout;
@ -258,7 +263,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
}
@Override
public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) {
public void setServerValidationMode(ServerValidationModeEnum theServerValidationMode) {
Validate.notNull(theServerValidationMode, "theServerValidationMode may not be null");
myServerValidationMode = theServerValidationMode;
}
@ -269,10 +274,12 @@ public class RestfulClientFactory implements IRestfulClientFactory {
myHttpClient = null;
}
@SuppressWarnings("unchecked")
private void validateServerBase(String theServerBase, HttpClient theHttpClient) {
void validateServerBase(String theServerBase, HttpClient theHttpClient, BaseClient theClient) {
GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this);
for (IClientInterceptor interceptor : theClient.getInterceptors()) {
client.registerInterceptor(interceptor);
}
client.setDontValidateConformance(true);
IBaseResource conformance;
@ -296,7 +303,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
} else {
if (serverFhirVersionString.startsWith("0.80") || serverFhirVersionString.startsWith("0.0.8")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU1;
} else if (serverFhirVersionString.startsWith("0.4") || serverFhirVersionString.startsWith("0.5")) {
} else if (serverFhirVersionString.startsWith("0.4")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2;
} else if (serverFhirVersionString.startsWith("0.5")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2;
@ -309,9 +316,22 @@ public class RestfulClientFactory implements IRestfulClientFactory {
if (serverFhirVersionEnum != null) {
FhirVersionEnum contextFhirVersion = myContext.getVersion().getVersion();
if (!contextFhirVersion.isEquivalentTo(serverFhirVersionEnum)) {
throw new FhirClientConnectionException(myContext.getLocalizer().getMessage(RestfulClientFactory.class, "wrongVersionInConformance", theServerBase + Constants.URL_TOKEN_METADATA, serverFhirVersionString, serverFhirVersionEnum, contextFhirVersion));
}
throw new FhirClientInnapropriateForServerException(myContext.getLocalizer().getMessage(RestfulClientFactory.class, "wrongVersionInConformance", theServerBase + Constants.URL_TOKEN_METADATA, serverFhirVersionString, serverFhirVersionEnum, contextFhirVersion));
}
}
myValidatedServerBaseUrls.add(normalizeBaseUrlForMap(theServerBase));
}
@Override
public ServerValidationModeEnum getServerValidationModeEnum() {
return getServerValidationMode();
}
@Override
public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) {
setServerValidationMode(theServerValidationMode);
}
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.rest.client.exceptions;
/*
* #%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 ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
/**
* This exception will be thrown by FHIR clients if the client attempts to
* communicate with a server which is a valid FHIR server but is incompatible
* with this client for some reason.
*/
public class FhirClientInnapropriateForServerException extends BaseServerResponseException {
private static final long serialVersionUID = 1L;
public FhirClientInnapropriateForServerException(Throwable theCause) {
super(0, theCause);
}
public FhirClientInnapropriateForServerException(String theMessage, Throwable theCause) {
super(0, theMessage, theCause);
}
public FhirClientInnapropriateForServerException(String theMessage) {
super(0, theMessage);
}
}

View File

@ -0,0 +1,53 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%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 ca.uhn.fhir.rest.server.Constants;
abstract class BaseClientParam implements IParam {
@Override
public ICriterion<?> isMissing(boolean theMissing) {
return new MissingCriterion(theMissing ? Constants.PARAMQUALIFIER_MISSING_TRUE : Constants.PARAMQUALIFIER_MISSING_FALSE);
}
private class MissingCriterion implements ICriterion<IParam>, ICriterionInternal
{
private String myParameterValue;
public MissingCriterion(String theParameterValue) {
myParameterValue = theParameterValue;
}
@Override
public String getParameterValue() {
return myParameterValue;
}
@Override
public String getParameterName() {
return BaseClientParam.this.getParamName() + Constants.PARAMQUALIFIER_MISSING;
}
}
}

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.rest.gclient;
/**
* Composite parameter type for use in fluent client interfaces
*/
public class CompositeClientParam<A extends IParam, B extends IParam> implements IParam {
public class CompositeClientParam<A extends IParam, B extends IParam> extends BaseClientParam implements IParam {
private String myName;

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt;
/**
* Date parameter type for use in fluent client interfaces
*/
public class DateClientParam implements IParam {
public class DateClientParam extends BaseClientParam implements IParam {
private String myParamName;

View File

@ -22,6 +22,16 @@ package ca.uhn.fhir.rest.gclient;
public interface IParam {
/**
* Returns the name of this parameter
*/
String getParamName();
/**
* Sets the <code>:missing</code> qualifier for this parameter. Set this to <code>true</code>
* to indicate that the server should return resources with this value <p>populated</p>. Set this to
* <code>false</code> to indicate that the server should return resources with this value <b>missing</b>.
*/
ICriterion<?> isMissing(boolean theMissing);
}

View File

@ -44,9 +44,9 @@ public interface ITransaction {
*/
<T extends IBaseBundle> ITransactionTyped<T> withBundle(T theBundleResource);
// *****
// TODO: add withString version
// If we add a withString version, make sure to auto-detect content type!
// *****
/**
* Use the given raw text (should be a Bundle resource) as the transaction input
*/
ITransactionTyped<String> withBundle(String theBundle);
}

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.rest.gclient;
/**
* Token parameter type for use in fluent client interfaces
*/
public class NumberClientParam implements IParam {
public class NumberClientParam extends BaseClientParam implements IParam {
private String myParamName;

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam.IMatches;
/**
* Token parameter type for use in fluent client interfaces
*/
public class QuantityClientParam implements IParam {
public class QuantityClientParam extends BaseClientParam implements IParam {
private String myParamName;

View File

@ -23,7 +23,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
*/
public class ReferenceClientParam implements IParam {
public class ReferenceClientParam extends BaseClientParam implements IParam {
private String myName;

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.rest.server.Constants;
* @author james
*
*/
public class StringClientParam implements IParam {
public class StringClientParam extends BaseClientParam implements IParam {
private final String myParamName;

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
/**
* Token parameter type for use in fluent client interfaces
*/
public class TokenClientParam implements IParam {
public class TokenClientParam extends BaseClientParam implements IParam {
private String myParamName;

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
/**
*
*/
public class UriClientParam implements IParam {
public class UriClientParam extends BaseClientParam implements IParam {
//TODO: handle :above and :below

View File

@ -37,6 +37,7 @@ import org.hl7.fhir.instance.model.IBaseResource;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
@ -47,6 +48,7 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.FhirValidator;
/**
* @author James Agnew
@ -270,7 +272,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
} else if (myContents != null) {
contents = myContents;
if (myContentsIsBundle) {
if (myContentsIsBundle && myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
contentType = encoding.getBundleContentType();
} else {
contentType = encoding.getResourceContentType();

View File

@ -173,6 +173,25 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
*/
public abstract String getResourceName();
/**
* Returns the value of {@link #getResourceOperationType()} or {@link #getSystemOperationType()} or {@link #getOtherOperationType()}
*/
public String getResourceOrSystemOperationType() {
Enum<?> retVal = getResourceOperationType();
if (retVal != null) {
return retVal.name();
}
retVal = getSystemOperationType();
if (retVal != null) {
return retVal.name();
}
retVal = getOtherOperationType();
if (retVal != null) {
return retVal.name();
}
return null;
}
public abstract RestfulOperationTypeEnum getResourceOperationType();
public abstract RestfulOperationSystemEnum getSystemOperationType();

View File

@ -20,9 +20,12 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.Enumeration;
@ -32,6 +35,7 @@ import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.IBaseResource;
@ -53,6 +57,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
@ -65,7 +70,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
if (!theMethod.getReturnType().equals(MethodOutcome.class)) {
if (!allowVoidReturnType()) {
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName() + " method but it does not return " + MethodOutcome.class);
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName()
+ " method but it does not return " + MethodOutcome.class);
} else if (theMethod.getReturnType() == void.class) {
myReturnVoid = true;
}
@ -101,8 +107,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
protected abstract BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
/**
* For servers, this method will match only incoming requests that match the given operation, or which have no
* operation in the URL if this method returns null.
* For servers, this method will match only incoming requests that match the given operation, or which have no operation in the URL if this method returns null.
*/
protected abstract String getMatchingOperation();
@ -126,7 +131,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
}
@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 IOException,
BaseServerResponseException {
switch (theResponseStatusCode) {
case Constants.STATUS_HTTP_200_OK:
case Constants.STATUS_HTTP_201_CREATED:
@ -198,7 +204,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
switch (getResourceOperationType()) {
case CREATE:
if (response == null) {
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName()
+ " returned null, which is not allowed for create operation");
}
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
@ -271,10 +278,40 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
/**
* @throws IOException
*/
protected IBaseResource parseIncomingServerResource(Request theRequest) throws IOException {
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
Reader requestReader;
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest);
if (encoding == null) {
String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
if (ctValue != null) {
if (ctValue.startsWith("application/x-www-form-urlencoded")) {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBinding.class, "invalidContentTypeInRequest", ctValue, getResourceOrSystemOperationType());
throw new InvalidRequestException(msg);
}
}
if (isBlank(ctValue)) {
/*
* If the client didn't send a content type, try to guess
*/
requestReader = theRequest.getServletRequest().getReader();
String body = IOUtils.toString(requestReader);
encoding = MethodUtil.detectEncodingNoDefault(body);
if (encoding == null) {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBinding.class, "noContentTypeInRequest", getResourceOrSystemOperationType());
throw new InvalidRequestException(msg);
} else {
requestReader = new StringReader(body);
}
} else {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBinding.class, "invalidContentTypeInRequest", ctValue, getResourceOrSystemOperationType());
throw new InvalidRequestException(msg);
}
} else {
requestReader = theRequest.getServletRequest().getReader();
}
IParser parser = encoding.newParser(getContext());
BufferedReader requestReader = theRequest.getServletRequest().getReader();
Class<? extends IBaseResource> wantedResourceType = requestContainsResourceType();
IBaseResource retVal;
@ -305,7 +342,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
return null;
}
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, Request theRequest) throws IOException {
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, Request theRequest)
throws IOException {
theResponse.setStatus(theE.getStatusCode());
theServer.addHeadersToResponse(theResponse);

View File

@ -249,15 +249,26 @@ public class MethodUtil {
}
public static EncodingEnum detectEncoding(String theBody) {
for (int i = 0; i < theBody.length(); i++) {
EncodingEnum retVal = detectEncodingNoDefault(theBody);
if (retVal == null) {
retVal = EncodingEnum.XML;
}
return retVal;
}
public static EncodingEnum detectEncodingNoDefault(String theBody) {
EncodingEnum retVal = null;
for (int i = 0; i < theBody.length() && retVal == null; i++) {
switch (theBody.charAt(i)) {
case '<':
return EncodingEnum.XML;
retVal = EncodingEnum.XML;
break;
case '{':
return EncodingEnum.JSON;
retVal = EncodingEnum.JSON;
break;
}
}
return EncodingEnum.XML;
return retVal;
}
public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {

View File

@ -199,4 +199,8 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
return new HttpPostClientInvocation(theContext, theResources, BundleTypeEnum.TRANSACTION);
}
public static BaseHttpClientInvocation createTransactionInvocation(String theRawBundle, FhirContext theContext) {
return new HttpPostClientInvocation(theContext, theRawBundle, true, "");
}
}

View File

@ -26,54 +26,76 @@ import ca.uhn.fhir.rest.server.Constants;
/**
* Base class for RESTful operation parameter types
*/
public class BaseParam implements IQueryParameterType {
abstract class BaseParam implements IQueryParameterType {
private Boolean myMissing;
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale instead of a normal value
*/
@Override
public Boolean getMissing() {
return myMissing;
}
@Override
public String getQueryParameterQualifier() {
public final String getQueryParameterQualifier() {
if (myMissing != null) {
return Constants.PARAMQUALIFIER_MISSING;
}
return null;
return doGetQueryParameterQualifier();
}
abstract String doGetQueryParameterQualifier();
abstract String doGetValueAsQueryToken();
@Override
public String getValueAsQueryToken() {
public final String getValueAsQueryToken() {
if (myMissing != null) {
return myMissing ? "true" : "false";
return myMissing ? Constants.PARAMQUALIFIER_MISSING_TRUE : Constants.PARAMQUALIFIER_MISSING_FALSE;
}
return null;
return doGetValueAsQueryToken();
}
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale instead of a normal value
*/
@Override
public void setMissing(Boolean theMissing) {
myMissing = theMissing;
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
public final void setValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_MISSING.equals(theQualifier)) {
myMissing = "true".equals(theValue);
doSetValueAsQueryToken(null, null);
} else {
myMissing = null;
doSetValueAsQueryToken(theQualifier, theValue);
}
}
abstract void doSetValueAsQueryToken(String theQualifier, String theValue);
static class ComposableBaseParam extends BaseParam{
@Override
String doGetQueryParameterQualifier() {
return null;
}
@Override
String doGetValueAsQueryToken() {
return null;
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
// nothing
}
}
}

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class CompositeParam<A extends IQueryParameterType, B extends IQueryParameterType> implements IQueryParameterType {
public class CompositeParam<A extends IQueryParameterType, B extends IQueryParameterType> extends BaseParam implements IQueryParameterType {
private A myLeftType;
private B myRightType;
@ -59,27 +59,13 @@ public class CompositeParam<A extends IQueryParameterType, B extends IQueryParam
}
}
/**
* @return Returns the left value for this parameter (the first of two parameters in this composite)
*/
public A getLeftValue() {
return myLeftType;
}
@Override
public String getQueryParameterQualifier() {
String doGetQueryParameterQualifier() {
return null;
}
/**
* @return Returns the right value for this parameter (the second of two parameters in this composite)
*/
public B getRightValue() {
return myRightType;
}
@Override
public String getValueAsQueryToken() {
String doGetValueAsQueryToken() {
StringBuilder b = new StringBuilder();
if (myLeftType != null) {
b.append(myLeftType.getValueAsQueryToken());
@ -92,7 +78,7 @@ public class CompositeParam<A extends IQueryParameterType, B extends IQueryParam
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
void doSetValueAsQueryToken(String theQualifier, String theValue) {
if (isBlank(theValue)) {
myLeftType.setValueAsQueryToken(theQualifier, "");
myRightType.setValueAsQueryToken(theQualifier, "");
@ -108,4 +94,18 @@ public class CompositeParam<A extends IQueryParameterType, B extends IQueryParam
}
}
/**
* @return Returns the left value for this parameter (the first of two parameters in this composite)
*/
public A getLeftValue() {
return myLeftType;
}
/**
* @return Returns the right value for this parameter (the second of two parameters in this composite)
*/
public B getRightValue() {
return myRightType;
}
}

View File

@ -36,7 +36,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateParam extends DateTimeDt implements IQueryParameterType, IQueryParameterOr<DateParam> {
private QuantityCompararatorEnum myComparator;
private BaseParam myBase=new BaseParam();
private BaseParam myBase=new BaseParam.ComposableBaseParam();
/**
* Constructor
@ -191,4 +191,14 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return new DateTimeDt(getValueAsString());
}
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override
public void setMissing(Boolean theMissing) {
myBase.setMissing(theMissing);
}
}

View File

@ -365,6 +365,16 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype
return getDisplay();
}
@Override
public Boolean getMissing() {
throw new UnsupportedOperationException();
}
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException();
}

View File

@ -414,6 +414,16 @@ class InternalQuantityDt
return this;
}
@Override
public Boolean getMissing() {
throw new UnsupportedOperationException();
}
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException();
}

View File

@ -45,32 +45,24 @@ public class NumberParam extends BaseParam implements IQueryParameterType {
}
@Override
public String toString() {
String doGetQueryParameterQualifier() {
return null;
}
@Override
String doGetValueAsQueryToken() {
StringBuilder b = new StringBuilder();
b.append(getClass().getSimpleName());
b.append("[");
if (myQuantity.getComparatorElement().isEmpty() == false) {
b.append(myQuantity.getComparatorElement().getValue());
}
if (myQuantity.getValueElement().isEmpty() == false) {
b.append(myQuantity.getValueElement().toString());
}
b.append("]");
return b.toString();
}
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
}
public BigDecimal getValue() {
return myQuantity.getValueElement().getValue();
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
super.setValueAsQueryToken(theQualifier, theValue);
void doSetValueAsQueryToken(String theQualifier, String theValue) {
if (getMissing() != null && isBlank(theValue)) {
return;
}
@ -92,25 +84,28 @@ public class NumberParam extends BaseParam implements IQueryParameterType {
}
}
@Override
public String getValueAsQueryToken() {
if (getMissing() != null) {
return super.getQueryParameterQualifier();
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
}
public BigDecimal getValue() {
return myQuantity.getValueElement().getValue();
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(getClass().getSimpleName());
b.append("[");
if (myQuantity.getComparatorElement().isEmpty() == false) {
b.append(myQuantity.getComparatorElement().getValue());
}
if (myQuantity.getValueElement().isEmpty() == false) {
b.append(myQuantity.getValueElement().toString());
}
b.append("]");
return b.toString();
}
@Override
public String getQueryParameterQualifier() {
return super.getQueryParameterQualifier();
}
}

View File

@ -142,33 +142,13 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
myApproximate = false;
}
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
@Override
String doGetQueryParameterQualifier() {
return null;
}
@Override
public String getQueryParameterQualifier() {
return super.getQueryParameterQualifier();
}
public UriDt getSystem() {
return myQuantity.getSystemElement();
}
public String getUnits() {
return myQuantity.getUnitsElement().getValue();
}
public DecimalDt getValue() {
return myQuantity.getValueElement();
}
@Override
public String getValueAsQueryToken() {
if (super.getMissing() != null) {
return super.getValueAsQueryToken();
}
String doGetValueAsQueryToken() {
StringBuilder b = new StringBuilder();
if (myApproximate) {
b.append('~');
@ -191,6 +171,62 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
return b.toString();
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
clear();
if (theValue == null) {
return;
}
List<String> parts = ParameterUtil.splitParameterString(theValue, '|', true);
if (parts.size() > 0 && StringUtils.isNotBlank(parts.get(0))) {
if (parts.get(0).startsWith("~")) {
myQuantity.setComparator((QuantityCompararatorEnum) null);
myApproximate = true;
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else if (parts.get(0).startsWith("<=")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith("<")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN);
String valStr = parts.get(0).substring(1);
myQuantity.setValue(new BigDecimal(valStr));
} else if (parts.get(0).startsWith(">=")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith(">")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else {
myQuantity.setValue(new BigDecimal(parts.get(0)));
}
}
if (parts.size() > 1 && StringUtils.isNotBlank(parts.get(1))) {
myQuantity.setSystem(parts.get(1));
}
if (parts.size() > 2 && StringUtils.isNotBlank(parts.get(2))) {
myQuantity.setUnits(parts.get(2));
}
}
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
}
public UriDt getSystem() {
return myQuantity.getSystemElement();
}
public String getUnits() {
return myQuantity.getUnitsElement().getValue();
}
public DecimalDt getValue() {
return myQuantity.getValueElement();
}
public boolean isApproximate() {
return myApproximate;
}
@ -253,51 +289,6 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
return this;
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
clear();
super.setValueAsQueryToken(theQualifier, theValue);
if (getMissing() != null) {
return;
}
if (theValue == null) {
return;
}
List<String> parts = ParameterUtil.splitParameterString(theValue, '|', true);
if (parts.size() > 0 && StringUtils.isNotBlank(parts.get(0))) {
if (parts.get(0).startsWith("~")) {
myQuantity.setComparator((QuantityCompararatorEnum) null);
myApproximate = true;
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else if (parts.get(0).startsWith("<=")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith("<")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN);
String valStr = parts.get(0).substring(1);
myQuantity.setValue(new BigDecimal(valStr));
} else if (parts.get(0).startsWith(">=")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith(">")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else {
myQuantity.setValue(new BigDecimal(parts.get(0)));
}
}
if (parts.size() > 1 && StringUtils.isNotBlank(parts.get(1))) {
myQuantity.setSystem(parts.get(1));
}
if (parts.size() > 2 && StringUtils.isNotBlank(parts.get(2))) {
myQuantity.setUnits(parts.get(2));
}
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
public class ReferenceParam extends IdDt implements IQueryParameterType {
private String myChain;
private BaseParam myBase=new BaseParam();
private BaseParam myBase=new BaseParam.ComposableBaseParam();
public ReferenceParam() {
}
@ -206,4 +206,14 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
return retVal;
}
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override
public void setMissing(Boolean theMissing) {
myBase.setMissing(theMissing);
}
}

View File

@ -48,23 +48,35 @@ public class StringParam extends BaseParam implements IQueryParameterType {
}
@Override
public String getQueryParameterQualifier() {
if (getMissing() != null) {
return super.getQueryParameterQualifier();
}else if (isExact()) {
String doGetQueryParameterQualifier() {
if (isExact()) {
return Constants.PARAMQUALIFIER_STRING_EXACT;
} else {
return null;
}
}
@Override
String doGetValueAsQueryToken() {
return ParameterUtil.escape(myValue);
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_STRING_EXACT.equals(theQualifier)) {
setExact(true);
} else {
setExact(false);
}
myValue = ParameterUtil.unescape(theValue);
}
public String getValue() {
return myValue;
}
@Override
public String getValueAsQueryToken() {
return ParameterUtil.escape(myValue);
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
public String getValueNotNull() {
@ -87,16 +99,6 @@ public class StringParam extends BaseParam implements IQueryParameterType {
myValue = theValue;
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_STRING_EXACT.equals(theQualifier)) {
setExact(true);
} else {
setExact(false);
}
myValue = ParameterUtil.unescape(theValue);
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -104,11 +106,10 @@ public class StringParam extends BaseParam implements IQueryParameterType {
if (myExact) {
builder.append("exact", myExact);
}
if (getMissing() != null) {
builder.append("missing", getMissing().booleanValue());
}
return builder.toString();
}
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
}

View File

@ -41,21 +41,6 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
public TokenParam() {
}
public TokenParam(String theSystem, String theValue) {
setSystem(theSystem);
setValue(theValue);
}
public TokenParam(String theSystem, String theValue, boolean theText) {
if (theText && isNotBlank(theSystem)) {
throw new IllegalArgumentException(
"theSystem can not be non-blank if theText is true (:text searches do not include a system). In other words, set the first parameter to null for a text search");
}
setSystem(theSystem);
setValue(theValue);
setText(theText);
}
/**
* Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a parameter
*
@ -76,21 +61,56 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
this(toSystemValue(theIdentifierDt.getSystemElement()), theIdentifierDt.getValueElement().getValue());
}
private static String toSystemValue(UriDt theSystem) {
return theSystem.getValueAsString();
public TokenParam(String theSystem, String theValue) {
setSystem(theSystem);
setValue(theValue);
}
public TokenParam(String theSystem, String theValue, boolean theText) {
if (theText && isNotBlank(theSystem)) {
throw new IllegalArgumentException(
"theSystem can not be non-blank if theText is true (:text searches do not include a system). In other words, set the first parameter to null for a text search");
}
setSystem(theSystem);
setValue(theValue);
setText(theText);
}
@Override
public String getQueryParameterQualifier() {
if (getMissing() != null) {
return super.getQueryParameterQualifier();
} else if (isText()) {
String doGetQueryParameterQualifier() {
if (isText()) {
return Constants.PARAMQUALIFIER_TOKEN_TEXT;
} else {
return null;
}
}
/**
* {@inheritDoc}
*/
@Override
String doGetValueAsQueryToken() {
if (getSystem() != null) {
return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue());
} else {
return ParameterUtil.escape(getValue());
}
}
/**
* {@inheritDoc}
*/
@Override
void doSetValueAsQueryToken(String theQualifier, String theParameter) {
int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
if (barIndex != -1) {
setSystem(theParameter.substring(0, barIndex));
setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
} else {
setValue(ParameterUtil.unescape(theParameter));
}
}
public String getSystem() {
return mySystem;
}
@ -99,18 +119,8 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
return myValue;
}
/**
* {@inheritDoc}
*/
@Override
public String getValueAsQueryToken() {
if (getMissing() != null) {
return super.getValueAsQueryToken();
} else if (getSystem() != null) {
return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue());
} else {
return ParameterUtil.escape(getValue());
}
public InternalCodingDt getValueAsCoding() {
return new InternalCodingDt(mySystem, myValue);
}
public String getValueNotNull() {
@ -137,25 +147,6 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
myValue = theValue;
}
/**
* {@inheritDoc}
*/
@Override
public void setValueAsQueryToken(String theQualifier, String theParameter) {
super.setValueAsQueryToken(theQualifier, theParameter);
if (getMissing() != null) {
return;
}
int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
if (barIndex != -1) {
setSystem(theParameter.substring(0, barIndex));
setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
} else {
setValue(ParameterUtil.unescape(theParameter));
}
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -170,8 +161,8 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
return builder.toString();
}
public InternalCodingDt getValueAsCoding() {
return new InternalCodingDt(mySystem, myValue);
private static String toSystemValue(UriDt theSystem) {
return theSystem.getValueAsString();
}
}

View File

@ -42,21 +42,30 @@ public class UriParam extends BaseParam implements IQueryParameterType {
}
@Override
public String getQueryParameterQualifier() {
if (getMissing() != null) {
return super.getQueryParameterQualifier();
} else {
String doGetQueryParameterQualifier() {
return null;
}
@Override
String doGetValueAsQueryToken() {
return ParameterUtil.escape(myValue);
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
myValue = ParameterUtil.unescape(theValue);
}
public String getValue() {
return myValue;
}
@Override
public String getValueAsQueryToken() {
return ParameterUtil.escape(myValue);
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
public UriDt getValueAsUriDt() {
return new UriDt(myValue);
}
public String getValueNotNull() {
@ -71,11 +80,6 @@ public class UriParam extends BaseParam implements IQueryParameterType {
myValue = theValue;
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
myValue = ParameterUtil.unescape(theValue);
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -83,12 +87,4 @@ public class UriParam extends BaseParam implements IQueryParameterType {
return builder.toString();
}
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
public UriDt getValueAsUriDt() {
return new UriDt(myValue);
}
}

View File

@ -114,6 +114,7 @@ public class Constants {
public static final int STATUS_HTTP_304_NOT_MODIFIED = 304;
public static final int STATUS_HTTP_400_BAD_REQUEST = 400;
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_403_FORBIDDEN= 403;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
@ -124,6 +125,8 @@ 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 PARAMQUALIFIER_MISSING_TRUE = "true";
public static final String PARAMQUALIFIER_MISSING_FALSE = "false";
static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -205,9 +205,18 @@ public class RestfulServerUtils {
}
public static EncodingEnum determineRequestEncoding(Request theReq) {
EncodingEnum retVal = determineRequestEncodingNoDefault(theReq);
if (retVal != null) {
return retVal;
}
return EncodingEnum.XML;
}
public static EncodingEnum determineRequestEncodingNoDefault(Request theReq) {
EncodingEnum retVal = null;
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
while (acceptValues.hasMoreElements() && retVal == null) {
String nextAcceptHeaderValue = acceptValues.nextElement();
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
for (String nextPart : nextAcceptHeaderValue.split(",")) {
@ -219,16 +228,16 @@ public class RestfulServerUtils {
nextPart = nextPart.substring(0, scIdx);
}
nextPart = nextPart.trim();
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
if (retVal != null) {
break;
}
}
}
}
}
return retVal;
}
}
}
}
}
return EncodingEnum.XML;
}
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
try {

View File

@ -50,6 +50,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
registerExceptionType(PreconditionFailedException.STATUS_CODE, PreconditionFailedException.class);
registerExceptionType(ResourceVersionConflictException.STATUS_CODE, ResourceVersionConflictException.class);
registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class);
registerExceptionType(ForbiddenOperationException.STATUS_CODE, ForbiddenOperationException.class);
}
private List<String> myAdditionalMessages = null;

View File

@ -0,0 +1,59 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
* #%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%
*/
/**
* This Represents an <b>HTTP 403 Forbidden</b> response, which generally indicates one of two conditions:
* <ul>
* <li>Authentication was provided, but the authenticated user is not permitted to perform the requested operation.</li>
* <li>The operation is forbidden to all users. Repeating the request with authentication would serve no purpose.</li>
* </ul>
*
* <p>
* Note that a complete list of RESTful exceptions is available in the <a href="./package-summary.html">Package
* Summary</a>.
* </p>
*/
public class ForbiddenOperationException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_403_FORBIDDEN;
private static final long serialVersionUID = 1L;
public ForbiddenOperationException(String theMessage) {
super(STATUS_CODE, theMessage);
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome
* The OperationOutcome resource to return to the client
*/
public ForbiddenOperationException(String theMessage, BaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
}

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 javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirContext;

View File

@ -1,43 +0,0 @@
package org.hl7.fhir.instance.model.api;
import org.hl7.fhir.instance.model.IPrimitiveType;
/*
* #%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%
*/
<<<<<<< HEAD:hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/IBaseResource.java
/**
* For now, this is a simple marker interface indicating that a class is a resource type.
* There are two concrete types of implementations of this interrface. The first are
* HL7.org's Resource structures (e.g.
* <code>org.hl7.fhir.instance.model.Patient</code>) and
* the second are HAPI's Resource structures, e.g.
* <code>ca.uhn.fhir.model.dstu.resource.Patient</code>)
*/
public interface IBaseResource extends IBase {
IIdType
=======
public interface IBaseEnumeration<T extends Enum<?>> extends IPrimitiveType<T> {
// Marker interface
>>>>>>> 2edc7eadab64d171ddc1b7c971ff36b9eb55ce67:hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseEnumeration.java
}

View File

@ -12,6 +12,9 @@ ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable
ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server's metadata statement during client initialization. URL used was: {0}
ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext).
ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.noContentTypeInRequest=No Content-Type header was provided in the request. This is required for "{0}" operation
ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.invalidContentTypeInRequest=Incorrect Content-Type header value of "{0}" was provided in the request. A FHIR Content-Type is required for "{1}" operation
ca.uhn.fhir.rest.method.OperationMethodBinding.methodNotSupported=HTTP Method {0} is not allowed for this operation. Allowed method(s): {1}
ca.uhn.fhir.rest.method.OperationParamBinder.urlParamNotPrimitive=Can not invoke operation {0} using HTTP GET because parameter {1} is not a primitive datatype
ca.uhn.fhir.rest.method.IncludeParameter.invalidIncludeNameInRequest=Invalid {2} parameter value: "{0}". Valid values are: {1}
@ -39,4 +42,5 @@ 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.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

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
@ -40,7 +40,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<exclusions>
<exclusion>
<artifactId>woodstox-core-asl</artifactId>
@ -51,7 +51,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<exclusions>
<exclusion>
<artifactId>woodstox-core-asl</artifactId>
@ -62,7 +62,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<exclusions>
<exclusion>
<artifactId>woodstox-core-asl</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -18,17 +18,17 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -31,7 +31,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
@ -42,13 +42,13 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<optional>true</optional>
</dependency>
@ -373,7 +373,7 @@
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<executions>
<execution>
<id>build_dstu1</id>
@ -404,12 +404,12 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>

View File

@ -23,8 +23,10 @@ package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collection;
@ -424,16 +426,14 @@ public abstract class BaseFhirDao implements IDao {
// Get list of IDs
searchHistoryCurrentVersion(theResourceName, theId, theSince, end.getValue(), limit, tuples);
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
ourLog.info("Retrieved {} history IDs from current versions in {} ms", tuples.size(), timer.getMillisAndRestart());
searchHistoryHistory(theResourceName, theId, theSince, end.getValue(), limit, tuples);
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
ourLog.info("Retrieved {} history IDs from previous versions in {} ms", tuples.size(), timer.getMillisAndRestart());
// Sort merged list
Collections.sort(tuples, Collections.reverseOrder());
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated()) : tuples.toString();
return new IBundleProvider() {
@ -641,7 +641,12 @@ public abstract class BaseFhirDao implements IDao {
SearchParameterMap paramMap = new SearchParameterMap();
List<NameValuePair> parameters;
try {
parameters = URLEncodedUtils.parse(new URI(theMatchUrl), "UTF-8");
String matchUrl = theMatchUrl;
if (matchUrl.indexOf('?') == -1) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: URL does not contain any parameters ('?' not detected)");
}
matchUrl = matchUrl.replace("|", "%7C");
parameters = URLEncodedUtils.parse(new URI(matchUrl), "UTF-8");
} catch (URISyntaxException e) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString());
}
@ -1045,6 +1050,9 @@ public abstract class BaseFhirDao implements IDao {
quantityParams = extractSearchParamQuantity(entity, theResource);
dateParams = extractSearchParamDates(entity, theResource);
ourLog.info("Indexing resource: {}", entity.getId());
ourLog.info("Storing string indexes: {}", stringParams);
tokenParams = new ArrayList<ResourceIndexedSearchParamToken>();
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(entity, theResource)) {
if (next instanceof ResourceIndexedSearchParamToken) {

View File

@ -51,6 +51,7 @@ import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
@ -71,6 +72,7 @@ import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
@ -140,6 +142,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
private String mySecondaryPrimaryKeyParamName;
private Set<Long> addPredicateComposite(RuntimeSearchParam theParamDef, Set<Long> thePids, List<? extends IQueryParameterType> theNextAnd) {
// TODO: fail if missing is set for a composite query
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
@ -177,6 +181,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return thePids;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsDate", theParamName, ResourceIndexedSearchParamDate.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class);
@ -184,6 +192,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
IQueryParameterType params = nextOr;
Predicate p = createPredicateDate(builder, from, params);
codePredicates.add(p);
@ -204,50 +216,6 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private Predicate addPredicateDateFromRange(CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theFrom, DateRangeParam theRange) {
Date lowerBound = theRange.getLowerBoundAsInstant();
Date upperBound = theRange.getUpperBoundAsInstant();
Predicate lb = null;
if (lowerBound != null) {
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueLow"), lowerBound);
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueHigh"), lowerBound);
lb = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate hbo = builder.or(lt, lin);
// lb = builder.and(lbo, hbo);
}
Predicate ub = null;
if (upperBound != null) {
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueLow"), upperBound);
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueHigh"), upperBound);
ub = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate ubo = builder.or(lt, lin);
// ub = builder.and(ubo, lbo);
}
if (lb != null && ub != null) {
return (theBuilder.and(lb, ub));
} else if (lb != null) {
return (lb);
} else {
return (ub);
}
}
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends
// IQueryParameterType> theList) {
// }
private Set<Long> addPredicateId(Set<Long> theExistingPids, Set<Long> thePids) {
if (thePids == null || thePids.isEmpty()) {
return Collections.emptySet();
@ -272,6 +240,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return found;
}
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends
// IQueryParameterType> theList) {
// }
private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList) {
if (theList == null || theList.isEmpty()) {
return thePids;
@ -322,6 +294,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return thePids;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsNumber", theParamName, ResourceIndexedSearchParamNumber.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamNumber> from = cq.from(ResourceIndexedSearchParamNumber.class);
@ -331,6 +307,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (params instanceof NumberParam) {
NumberParam param = (NumberParam) params;
@ -384,11 +364,76 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private Set<Long> addPredicateParamMissing(Set<Long> thePids, String joinName, String theParamName, Class<? extends BaseResourceIndexedSearchParam> theParamTable) {
String resourceType = getContext().getResourceDefinition(getResourceType()).getName();
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
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<? 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);
subQ.where(builder.and(subQtype, subQname));
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
Predicate typePredicate = builder.equal(from.get("myResourceType"), resourceType);
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, typePredicate, joinPredicate));
} else {
cq.where(builder.and(typePredicate, joinPredicate));
}
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);
return retVal;
}
private Set<Long> addPredicateParamMissingResourceLink(Set<Long> thePids, String joinName, String theParamName) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
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);
subQ.select(subQfrom.get("mySourceResourcePid").as(Long.class));
// subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
subQ.where(createResourceLinkPathPredicate(theParamName, builder, subQfrom));
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, joinPredicate));
} else {
cq.where(joinPredicate);
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
return retVal;
}
private Set<Long> addPredicateQuantity(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) {
return thePids;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsQuantity", theParamName, ResourceIndexedSearchParamQuantity.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamQuantity> from = cq.from(ResourceIndexedSearchParamQuantity.class);
@ -398,6 +443,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
String systemValue;
String unitsValue;
QuantityCompararatorEnum cmpValue;
@ -499,6 +548,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return pidsToRetain;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissingResourceLink(thePids, "myResourceLinks", theParamName);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceLink> from = cq.from(ResourceLink.class);
@ -509,6 +562,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (params instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) params;
@ -573,10 +630,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
Predicate type = createResourceLinkPathPredicate(theParamName, builder, from);
if (pidsToRetain.size() > 0) {
Predicate inPids = (from.get("mySourceResourcePid").in(pidsToRetain));
cq.where(builder.and(type, masterCodePredicate, inPids));
@ -588,11 +642,23 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private Predicate createResourceLinkPathPredicate(String theParamName, CriteriaBuilder builder, Root<? extends ResourceLink> from) {
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
return type;
}
private Set<Long> addPredicateString(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) {
return thePids;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsString", theParamName, ResourceIndexedSearchParamString.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamString> from = cq.from(ResourceIndexedSearchParamString.class);
@ -601,6 +667,9 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType theParameter = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
Predicate singleCode = createPredicateString(theParameter, theParamName, builder, from);
codePredicates.add(singleCode);
@ -621,11 +690,43 @@ 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) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
}
Predicate singleCode = from.get("myId").isNotNull();
Predicate name = theBuilder.equal(from.get("myParamName"), theParamName);
codePredicates.add(theBuilder.and(name, singleCode));
missingFalse = true;
}
return missingFalse;
}
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) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
}
Predicate singleCode = from.get("mySourceResource").isNotNull();
Predicate name = createResourceLinkPathPredicate(theParamName, theBuilder, from);
codePredicates.add(theBuilder.and(name, singleCode));
missingFalse = true;
}
return missingFalse;
}
private Set<Long> addPredicateToken(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) {
return thePids;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsToken", theParamName, ResourceIndexedSearchParamToken.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamToken> from = cq.from(ResourceIndexedSearchParamToken.class);
@ -633,6 +734,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (nextOr instanceof TokenParam) {
TokenParam id = (TokenParam) nextOr;
if (id.isText()) {
@ -758,20 +863,60 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
DateParam date = (DateParam) theParam;
if (!date.isEmpty()) {
DateRangeParam range = new DateRangeParam(date);
p = addPredicateDateFromRange(theBuilder, theFrom, range);
p = createPredicateDateFromRange(theBuilder, theFrom, range);
} else {
// TODO: handle missing date param?
p = null;
}
} else if (theParam instanceof DateRangeParam) {
DateRangeParam range = (DateRangeParam) theParam;
p = addPredicateDateFromRange(theBuilder, theFrom, range);
p = createPredicateDateFromRange(theBuilder, theFrom, range);
} else {
throw new IllegalArgumentException("Invalid token type: " + theParam.getClass());
}
return p;
}
private Predicate createPredicateDateFromRange(CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theFrom, DateRangeParam theRange) {
Date lowerBound = theRange.getLowerBoundAsInstant();
Date upperBound = theRange.getUpperBoundAsInstant();
Predicate lb = null;
if (lowerBound != null) {
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueLow"), lowerBound);
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueHigh"), lowerBound);
lb = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate hbo = builder.or(lt, lin);
// lb = builder.and(lbo, hbo);
}
Predicate ub = null;
if (upperBound != null) {
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueLow"), upperBound);
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueHigh"), upperBound);
ub = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate ubo = builder.or(lt, lin);
// ub = builder.and(ubo, lbo);
}
if (lb != null && ub != null) {
return (theBuilder.and(lb, ub));
} else if (lb != null) {
return (lb);
} else {
return (ub);
}
}
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
String rawSearchTerm;
@ -1531,9 +1676,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<IdDt> previouslyLoadedPids = new HashSet<IdDt>();
@ -1861,7 +2005,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
entity = myEntityManager.find(ResourceTable.class, pid);
resourceId = entity.getIdDt();
} else {
return create(theResource);
return create(theResource, null, thePerformIndexing);
}
} else {
resourceId = theResource.getId();
@ -1874,7 +2018,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
if (Character.isDigit(theResource.getId().getIdPart().charAt(0))) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart()));
}
return doCreate(theResource, null, true);
return doCreate(theResource, null, thePerformIndexing);
}
}

View File

@ -201,7 +201,10 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
if (parts.getResourceId() != null) {
// if (res.getId().hasIdPart() && isBlank(parts.getResourceId())) {
// parts.setResourceId(res.getId().getIdPart());
// }
if (isNotBlank(parts.getResourceId())) {
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
outcome = resourceDao.update(res, null, false);
} else {

View File

@ -22,6 +22,9 @@ package ca.uhn.fhir.jpa.dao;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
class HistoryTuple implements Comparable<HistoryTuple> {
private Long myId;
@ -64,4 +67,13 @@ class HistoryTuple implements Comparable<HistoryTuple> {
myUpdated = theUpdated;
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("id", myId);
b.append("history", myIsHistory);
b.append("updated", myUpdated);
return b.build();
}
}

View File

@ -446,6 +446,22 @@ class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISea
}
systems.add(null);
codes.add(nextValue.getValueAsString());
} else if (nextObject instanceof CodingDt) {
CodingDt nextValue = (CodingDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
String nextSystem = nextValue.getSystemElement().getValueAsString();
String nextCode = nextValue.getCodeElement().getValue();
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
systems.add(nextSystem);
codes.add(nextCode);
}
if (!nextValue.getDisplayElement().isEmpty()) {
systems.add(null);
codes.add(nextValue.getDisplayElement().getValue());
}
} else if (nextObject instanceof CodeableConceptDt) {
CodeableConceptDt nextCC = (CodeableConceptDt) nextObject;
if (!nextCC.getTextElement().isEmpty()) {

View File

@ -27,7 +27,7 @@
-->
</bean>
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<property name="persistenceUnitName" value="FHIR_UT" />
<property name="jpaVendorAdapter">
@ -41,7 +41,7 @@
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao;
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;
@ -217,8 +218,8 @@ public class FhirResourceDaoDstu2Test {
}
@Test
public void testCreateWithIfNoneExist() {
String methodName = "testCreateWithIfNoneExist";
public void testCreateWithIfNoneExistBasic() {
String methodName = "testCreateWithIfNoneExistBasic";
MethodOutcome results;
Patient p = new Patient();
@ -1522,6 +1523,195 @@ public class FhirResourceDaoDstu2Test {
}
@Test
public void testSearchWithMissingString() {
IdDt orgId = ourOrganizationDao.create(new Organization()).getId();
IdDt notMissing;
IdDt 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<IdDt> 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<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingQuantity() {
IdDt notMissing;
IdDt 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<IdDt> 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<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithToken() {
IdDt notMissing;
IdDt 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<IdDt> 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<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingDate() {
IdDt orgId = ourOrganizationDao.create(new Organization()).getId();
IdDt notMissing;
IdDt 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<IdDt> 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<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingReference() {
IdDt orgId = ourOrganizationDao.create(new Organization()).getId();
IdDt notMissing;
IdDt 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<IdDt> 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<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchStringParamWithNonNormalized() {
{
@ -2233,36 +2423,36 @@ public class FhirResourceDaoDstu2Test {
@Test
public void testUpdateMaintainsSearchParams() throws InterruptedException {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsAAA");
p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsAAA");
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2AAA");
p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2AAA");
IdDt p1id = ourPatientDao.create(p1).getId();
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsBBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsBBB");
p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2BBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2BBB");
ourPatientDao.create(p2).getId();
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2AAA"));
assertEquals(1, ids.size());
assertThat(ids, contains(p1id.getIdPartAsLong()));
// Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB");
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsDstu2BBB");
MethodOutcome update2 = ourPatientDao.update(p1);
IdDt p1id2 = update2.getId();
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2AAA"));
assertEquals(0, ids.size());
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB"));
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2BBB"));
assertEquals(2, ids.size());
// Make sure vreads work
p1 = ourPatientDao.read(p1id);
assertEquals("testUpdateMaintainsSearchParamsAAA", p1.getNameFirstRep().getGivenAsSingleString());
assertEquals("testUpdateMaintainsSearchParamsDstu2AAA", p1.getNameFirstRep().getGivenAsSingleString());
p1 = ourPatientDao.read(p1id2);
assertEquals("testUpdateMaintainsSearchParamsBBB", p1.getNameFirstRep().getGivenAsSingleString());
assertEquals("testUpdateMaintainsSearchParamsDstu2BBB", p1.getNameFirstRep().getGivenAsSingleString());
}
@ -2344,14 +2534,12 @@ public class FhirResourceDaoDstu2Test {
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())) {

View File

@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@ -11,10 +12,11 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.instance.model.IBaseResource;
import org.apache.commons.io.IOUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@ -23,10 +25,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.provider.SystemProviderTest;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
@ -95,6 +99,30 @@ public class FhirSystemDaoDstu2Test {
}
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve.xml");
String bundleStr = IOUtils.toString(bundleRes);
Bundle bundle = ourFhirContext.newXmlParser().parseResource(Bundle.class, bundleStr);
Bundle resp = ourSystemDao.transaction(bundle);
ourLog.info(ourFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
OperationOutcome oo = (OperationOutcome) resp.getEntry().get(0).getResource();
assertThat(oo.getIssue().get(0).getDetailsElement().getValue(), containsString("Transaction completed"));
assertThat(resp.getEntry().get(1).getTransactionResponse().getLocation(), startsWith("Patient/a555-44-4444/_history/"));
assertThat(resp.getEntry().get(2).getTransactionResponse().getLocation(), startsWith("Patient/temp6789/_history/"));
assertThat(resp.getEntry().get(3).getTransactionResponse().getLocation(), startsWith("Organization/GHH/_history/"));
Patient p = ourPatientDao.read(new IdDt("Patient/a555-44-4444/_history/1"));
assertEquals("Patient/temp6789", p.getLink().get(0).getOther().getReference().getValue());
}
@Test
public void testTransactionReadAndSearch() {
String methodName = "testTransactionReadAndSearch";
@ -489,12 +517,12 @@ public class FhirSystemDaoDstu2Test {
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("urn:"+methodName);
p.setId(methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.PUT).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getCode().setText("Some Observation");
o.getSubject().setReference("Patient/urn:"+methodName);
o.getSubject().setReference("Patient/"+methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);

View File

@ -4,7 +4,15 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
@ -13,6 +21,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
@ -32,11 +41,14 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.common.net.UrlEscapers;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.resource.Device;
@ -89,6 +101,7 @@ public class ResourceProviderDstu2Test {
private static DaoConfig ourDaoConfig;
private static CloseableHttpClient ourHttpClient;
private static String ourServerBase;
private static int ourPort;
// private static JpaConformanceProvider ourConfProvider;
@ -142,6 +155,7 @@ public class ResourceProviderDstu2Test {
String resource = ourFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?name=" + methodName);
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
IdDt id;
@ -161,7 +175,7 @@ public class ResourceProviderDstu2Test {
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
assertEquals(id.getValue(), newIdString);
assertEquals(id.getValue(), newIdString); // version should match for conditional create
} finally {
response.close();
}
@ -176,7 +190,7 @@ public class ResourceProviderDstu2Test {
pt.addName().addFamily(methodName);
String resource = ourFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPost post = new HttpPost(ourServerBase + "/Patient");
HttpPost post = new HttpPost(ourServerBase + "/Patient?name=" + methodName);
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
IdDt id;
@ -195,7 +209,7 @@ public class ResourceProviderDstu2Test {
try {
assertEquals(200, response.getStatusLine().getStatusCode());
IdDt newId = new IdDt(response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue());
assertEquals(id.toVersionless(), newId.toVersionless());
assertEquals(id.toVersionless(), newId.toVersionless()); // version shouldn't match for conditional update
assertNotEquals(id, newId);
} finally {
response.close();
@ -204,8 +218,8 @@ public class ResourceProviderDstu2Test {
}
@Test
public void testDeleteResourceConditional() throws IOException {
String methodName = "testDeleteResourceConditional";
public void testDeleteResourceConditional1() throws IOException {
String methodName = "testDeleteResourceConditional1";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
@ -243,6 +257,67 @@ public class ResourceProviderDstu2Test {
}
/**
* Based on email from Rene Spronk
*/
@Test
public void testDeleteResourceConditional2() throws IOException, Exception {
String methodName = "testDeleteResourceConditional2";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
pt.addIdentifier().setSystem("http://ghh.org/patient").setValue("555-44-4444");
String resource = ourFhirCtx.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();
}
/*
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL
* but we want to make sure that works too..
*/
Socket sock = new Socket();
try {
sock.connect(new InetSocketAddress("localhost", ourPort));
sock.getOutputStream().write(("DELETE " + "/fhir/context/Patient?identifier=" + ("http://ghh.org/patient|555-44-4444")).getBytes("UTF-8"));
sock.getOutputStream().write("\n\n".getBytes("UTF-8"));
sock.getOutputStream().flush();
InputStream inputStream = sock.getInputStream();
byte[] buf = new byte[10000];
int count;
StringBuilder b = new StringBuilder();
while ((count = inputStream.read(buf)) != -1) {
b.append(new String(buf, 0, count, Charset.forName("UTF-8")));
}
String resp = b.toString();
ourLog.info("Resp: {}", resp);
} finally {
sock.close();
}
HttpGet read = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart());
response = ourHttpClient.execute(read);
try {
ourLog.info(response.toString());
assertEquals(Constants.STATUS_HTTP_410_GONE, response.getStatusLine().getStatusCode());
} finally {
response.close();
}
}
/**
* Test for issue #60
*/
@ -313,6 +388,59 @@ public class ResourceProviderDstu2Test {
assertEquals(BundleEntrySearchModeEnum.INCLUDE, found.getEntries().get(1).getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE));
}
//@Test
public void testSearchWithMissing() throws Exception {
String methodName = "testSearchWithMissing";
Organization org = new Organization();
org.addIdentifier().setSystem("urn:system:rpdstu2").setValue(methodName + "01");
org.setName(methodName + "name");
IdDt orgNotMissing = ourClient.create().resource(org).prettyPrint().encodedXml().execute().getId().toUnqualifiedVersionless();
org = new Organization();
org.addIdentifier().setSystem("urn:system:rpdstu2").setValue(methodName + "01");
IdDt orgMissing = ourClient.create().resource(org).prettyPrint().encodedXml().execute().getId().toUnqualifiedVersionless();
{
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Organization.class)
.where(Organization.NAME.isMissing(false))
.prettyPrint()
.execute();
//@formatter:on
List<IdDt> list = toIdListUnqualifiedVersionless(found);
ourLog.info(methodName + ": " + list.toString());
assertThat(list, containsInRelativeOrder(orgNotMissing));
assertThat(list, not(containsInRelativeOrder(orgMissing)));
}
{
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Organization.class)
.where(Organization.NAME.isMissing(true))
.prettyPrint()
.execute();
//@formatter:on
List<IdDt> list = toIdListUnqualifiedVersionless(found);
ourLog.info(methodName + ": " + list.toString());
assertThat(list, not(containsInRelativeOrder(orgNotMissing)));
assertThat("Wanted " + orgMissing + " but found: " + list, list, containsInRelativeOrder(orgMissing));
}
}
private List<IdDt> toIdListUnqualifiedVersionless(Bundle found) {
List<IdDt> list = new ArrayList<IdDt>();
for (BundleEntry next : found.getEntries()) {
list.add(next.getResource().getId().toUnqualifiedVersionless());
}
return list;
}
@Test
public void testEverythingOperation() throws Exception {
String methodName = "testEverythingOperation";
@ -802,13 +930,13 @@ public class ResourceProviderDstu2Test {
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() throws Exception {
int port = RandomServerPortProvider.findFreePort();
ourPort = RandomServerPortProvider.findFreePort();
RestfulServer restServer = new RestfulServer();
ourFhirCtx = FhirContext.forDstu2();
restServer.setFhirContext(ourFhirCtx);
ourServerBase = "http://localhost:" + port + "/fhir/context";
ourServerBase = "http://localhost:" + ourPort + "/fhir/context";
ourAppCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu2.xml", "fhir-jpabase-spring-test-config.xml");
@ -826,7 +954,7 @@ public class ResourceProviderDstu2Test {
restServer.setPagingProvider(new FifoMemoryPagingProvider(10));
ourServer = new Server(port);
ourServer = new Server(ourPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");

View File

@ -1,9 +1,10 @@
package ca.uhn.fhir.jpa.provider;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import static org.junit.Assert.*;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@ -15,25 +16,22 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1;
import ca.uhn.fhir.jpa.rp.dstu.ObservationResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu.OrganizationResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu.PatientResourceProvider;
import ca.uhn.fhir.jpa.provider.QuestionnaireResourceProvider;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer;
public class SystemProviderTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderTest.class);
private static Server ourServer;
private static ClassPathXmlApplicationContext ourAppCtx;
private static FhirContext ourCtx;
@ -42,14 +40,48 @@ public class SystemProviderTest {
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/test-server-seed-bundle.json");
Bundle bundle = FhirContext.forDstu1().newJsonParser().parseBundle(new InputStreamReader(bundleRes));
List<IResource> res = bundle.toListOfResources();
ourClient.transaction().withResources(res).execute();
InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve.xml");
String bundle = IOUtils.toString(bundleRes);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@Test
public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
String bundle = IOUtils.toString(bundleRes);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
IdDt id1_1 = new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation());
assertEquals("Provenance", id1_1.getResourceType());
IdDt id1_2 = new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation());
IdDt id1_3 = new IdDt(resp.getEntry().get(3).getTransactionResponse().getLocation());
IdDt id1_4 = new IdDt(resp.getEntry().get(4).getTransactionResponse().getLocation());
/*
* Same bundle!
*/
bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
bundle = IOUtils.toString(bundleRes);
response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
IdDt id2_1 = new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation());
IdDt id2_2 = new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation());
IdDt id2_3 = new IdDt(resp.getEntry().get(3).getTransactionResponse().getLocation());
IdDt id2_4 = new IdDt(resp.getEntry().get(4).getTransactionResponse().getLocation());
assertNotEquals(id1_1.toVersionless(), id2_1.toVersionless());
assertEquals("Provenance", id2_1.getResourceType());
assertEquals(id1_2.toVersionless(), id2_2.toVersionless());
assertEquals(id1_3.toVersionless(), id2_3.toVersionless());
assertEquals(id1_4.toVersionless(), id2_4.toVersionless());
}
@AfterClass
public static void afterClass() throws Exception {
@ -60,28 +92,28 @@ public class SystemProviderTest {
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() throws Exception {
ourAppCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml", "hapi-fhir-server-resourceproviders-dstu1.xml");
ourAppCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml", "hapi-fhir-server-resourceproviders-dstu2.xml");
IFhirResourceDao<Patient> patientDao = (IFhirResourceDao<Patient>) ourAppCtx.getBean("myPatientDaoDstu1", IFhirResourceDao.class);
IFhirResourceDao<Patient> patientDao = (IFhirResourceDao<Patient>) ourAppCtx.getBean("myPatientDaoDstu2", IFhirResourceDao.class);
PatientResourceProvider patientRp = new PatientResourceProvider();
patientRp.setDao(patientDao);
IFhirResourceDao<Questionnaire> questionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDaoDstu1", IFhirResourceDao.class);
IFhirResourceDao<Questionnaire> questionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class);
QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider();
questionnaireRp.setDao(questionnaireDao);
IFhirResourceDao<Observation> observationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class);
IFhirResourceDao<Observation> observationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDaoDstu2", IFhirResourceDao.class);
ObservationResourceProvider observationRp = new ObservationResourceProvider();
observationRp.setDao(observationDao);
IFhirResourceDao<Organization> organizationDao = (IFhirResourceDao<Organization>) ourAppCtx.getBean("myOrganizationDaoDstu1", IFhirResourceDao.class);
IFhirResourceDao<Organization> organizationDao = (IFhirResourceDao<Organization>) ourAppCtx.getBean("myOrganizationDaoDstu2", IFhirResourceDao.class);
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
organizationRp.setDao(organizationDao);
RestfulServer restServer = new RestfulServer();
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
JpaSystemProviderDstu1 systemProv = ourAppCtx.getBean(JpaSystemProviderDstu1.class, "mySystemProviderDstu1");
JpaSystemProviderDstu2 systemProv = ourAppCtx.getBean(JpaSystemProviderDstu2.class, "mySystemProviderDstu2");
restServer.setPlainProviders(systemProv);
int myPort = RandomServerPortProvider.findFreePort();
@ -96,10 +128,12 @@ public class SystemProviderTest {
servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourCtx = FhirContext.forDstu2();
restServer.setFhirContext(ourCtx);
ourServer.setHandler(proxyHandler);
ourServer.start();
ourCtx = restServer.getFhirContext();
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
ourClient = ourCtx.newRestfulGenericClient(serverBase);

View File

@ -30,7 +30,7 @@
-->
</bean>
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<property name="persistenceUnitName" value="FHIR_UT" />
<property name="jpaVendorAdapter">
@ -43,7 +43,7 @@
</property>
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -1,690 +0,0 @@
{
"resourceType":"Bundle",
"entry":[
{
"deleted":null,
"title":"Patient http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Patient/5556918",
"id":"Patient/5556918",
"link":[
{
"rel":"self",
"href":"Patient/5556918"
}
],
"published":"2014-05-29T10:49:32-04:00",
"content":{
"resourceType":"Patient",
"id":"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Patient/5556918",
"text":{
"status":"generated",
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"hapiHeaderText\"> Donald null <b>DUCK </b></div><table class=\"hapiPropertyTable\"><tbody><tr><td>Identifier</td><td>7000135</td></tr><tr><td>Address</td><td><span>10 Duxon Street </span><br/><span>VICTORIA </span><span>BC </span><span>Can </span></td></tr><tr><td>Date of birth</td><td><span>01 June 1980</span></td></tr></tbody></table></div>"
},
"identifier":[
{
"use":"official",
"label":"University Health Network MRN 7000135",
"system":"urn:oid:2.16.840.1.113883.3.239.18.148",
"value":"7000135",
"assigner":{
"resource":"Organization/1.3.6.1.4.1.12201"
}
}
],
"name":[
{
"family":[
"Duck"
],
"given":[
"Donald"
]
}
],
"telecom":[
{
"system":"phone",
"use":"home"
},
{
"system":"phone",
"use":"work"
},
{
"system":"phone",
"use":"mobile"
},
{
"system":"email",
"use":"home"
}
],
"gender":{
"coding":[
{
"system":"http://hl7.org/fhir/v3/AdministrativeGender",
"code":"M"
}
]
},
"birthDate":"1980-06-01T00:00:00",
"address":[
{
"use":"home",
"line":[
"10 Duxon Street"
],
"city":"VICTORIA",
"state":"BC",
"zip":"V8N 1Y4",
"country":"Can"
}
],
"managingOrganization":{
"resource":"Organization/1.3.6.1.4.1.12201"
}
}
},
{
"deleted":null,
"title":"DiagnosticReport http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/DiagnosticReport/5978827",
"id":"DiagnosticReport/5978827",
"link":[
{
"rel":"self",
"href":"DiagnosticReport/5978827"
}
],
"published":"2014-05-29T10:49:33-04:00",
"content":{
"resourceType":"DiagnosticReport",
"id":"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/DiagnosticReport/5978827",
"text":{
"status":"generated",
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"hapiHeaderText\"> C&amp;S </div><table class=\"hapiPropertyTable\"><tbody><tr><td>Status</td><td>partial</td></tr><tr><td>Issued</td><td> 29 April 2014 17:21:56 </td></tr></tbody></table><table class=\"hapiTableOfValues\"><thead><tr><td>Name</td><td>Value</td><td>Interpretation</td><td>Reference Range</td><td>Status</td></tr></thead><tbody><tr class=\"hapiTableOfValuesRowEven\"><td> Collection Info </td><td> Spec #102758: 26 Sep 08 1117</td><td/><td/><td>preliminary</td></tr><tr class=\"hapiTableOfValuesRowOdd\"><td> Direct Stain </td><td> pus cells</td><td/><td/><td>preliminary</td></tr><tr class=\"hapiTableOfValuesRowEven\"><td> Header </td><td> To view Culture &amp; Sensitivity Results, select</td><td/><td/><td/></tr><tr class=\"hapiTableOfValuesRowOdd\"><td> Header </td><td> (Y) Report Query. Do NOT select number beside</td><td/><td/><td/></tr><tr class=\"hapiTableOfValuesRowEven\"><td> Header </td><td> Prelim or Final Result field, as there is</td><td/><td/><td/></tr><tr class=\"hapiTableOfValuesRowOdd\"><td> Header </td><td> potential for viewing an incomplete report.</td><td/><td/><td/></tr><tr class=\"hapiTableOfValuesRowEven\"><td> Organism </td><td> Haemophilus influenzae</td><td/><td/><td>final</td></tr><tr class=\"hapiTableOfValuesRowOdd\"><td> Qualifier </td><td> =>10 x E6 cfu/L SIGNIFICANT RESULT. Organisms cultured in quantities =>10 x E6 cfu/L are consistent with pneumonia. beta-lactamase positive result suggests resistance to ampicillin but generally susceptible to amoxicillin- clavulanic and cefuroxime.</td><td/><td/><td>final</td></tr><tr class=\"hapiTableOfValuesRowEven\"><td> Sensitivities </td><td> _Beta-lactamase Pos: </td><td/><td/><td>final</td></tr><tr class=\"hapiTableOfValuesRowOdd\"><td> Test Comment </td><td> =>10 x E6 cfu/L Commensal flora</td><td/><td/><td>final</td></tr></tbody></table></div>"
},
"contained":[
{
"resourceType":"Observation",
"id":"f816a276-cfad-4eca-a9fa-f1dff844a196",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"146151.1"
}
],
"text":"Collection Info"
},
"valueString":"Spec #102758: 26 Sep 08 1117",
"interpretation":{
"coding":[
{
"code":"N"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"preliminary"
},
{
"resourceType":"Observation",
"id":"23b55496-1c2a-4d5f-9c24-8ca5042f4027",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"GM.2"
}
],
"text":"Direct Stain"
},
"valueString":"pus cells",
"interpretation":{
"coding":[
{
"code":"N"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"preliminary"
},
{
"resourceType":"Observation",
"id":"74e6791a-d810-4545-8410-e9eca41e81d6",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H1.3"
}
],
"text":"Header"
},
"valueString":"To view Culture & Sensitivity Results, select",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"cd8c6a6c-7ef5-446f-b07b-47a21bfe28ee",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H2.4"
}
],
"text":"Header"
},
"valueString":"(Y) Report Query. Do NOT select number beside",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"4a3d453d-3a18-432f-8f1f-d7657c50dcd4",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H3.5"
}
],
"text":"Header"
},
"valueString":"Prelim or Final Result field, as there is",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"0dd6cff6-f9db-42cc-89c9-2cd6ba6fe5af",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H4.6"
}
],
"text":"Header"
},
"valueString":"potential for viewing an incomplete report.",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"6d6b0117-220f-4b9a-abf3-5faf772cfa61",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"F/O.4"
}
],
"text":"Organism"
},
"valueString":"Haemophilus influenzae",
"interpretation":{
"coding":[
{
"code":"A"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"64068acf-57f4-42c8-b0e6-416247067b16",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"F/31266.5"
}
],
"text":"Qualifier"
},
"valueString":"=>10 x E6 cfu/L SIGNIFICANT RESULT. Organisms cultured in quantities =>10 x E6 cfu/L are consistent with pneumonia. beta-lactamase positive result suggests resistance to ampicillin but generally susceptible to amoxicillin- clavulanic and cefuroxime.",
"interpretation":{
"coding":[
{
"code":"A"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"0f9d254f-3ad1-404b-9be9-20258b3c242f",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"F/31415.6"
}
],
"text":"Sensitivities"
},
"valueString":"_Beta-lactamase Pos: ",
"interpretation":{
"coding":[
{
"code":"A"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"349bb02b-fbbe-4ce0-b190-3f545240dcc0",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"Q.3"
}
],
"text":"Test Comment"
},
"valueString":"=>10 x E6 cfu/L Commensal flora",
"interpretation":{
"coding":[
{
"code":"N"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"f816a276-cfad-4eca-a9fa-f1dff844a196",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"146151.1"
}
],
"text":"Collection Info"
},
"valueString":"Spec #102758: 26 Sep 08 1117",
"interpretation":{
"coding":[
{
"code":"N"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"preliminary"
},
{
"resourceType":"Observation",
"id":"23b55496-1c2a-4d5f-9c24-8ca5042f4027",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"GM.2"
}
],
"text":"Direct Stain"
},
"valueString":"pus cells",
"interpretation":{
"coding":[
{
"code":"N"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"preliminary"
},
{
"resourceType":"Observation",
"id":"74e6791a-d810-4545-8410-e9eca41e81d6",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H1.3"
}
],
"text":"Header"
},
"valueString":"To view Culture & Sensitivity Results, select",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"cd8c6a6c-7ef5-446f-b07b-47a21bfe28ee",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H2.4"
}
],
"text":"Header"
},
"valueString":"(Y) Report Query. Do NOT select number beside",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"4a3d453d-3a18-432f-8f1f-d7657c50dcd4",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H3.5"
}
],
"text":"Header"
},
"valueString":"Prelim or Final Result field, as there is",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"0dd6cff6-f9db-42cc-89c9-2cd6ba6fe5af",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"1H4.6"
}
],
"text":"Header"
},
"valueString":"potential for viewing an incomplete report.",
"issued":"2014-04-29T17:21:56"
},
{
"resourceType":"Observation",
"id":"6d6b0117-220f-4b9a-abf3-5faf772cfa61",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"F/O.4"
}
],
"text":"Organism"
},
"valueString":"Haemophilus influenzae",
"interpretation":{
"coding":[
{
"code":"A"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"64068acf-57f4-42c8-b0e6-416247067b16",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"F/31266.5"
}
],
"text":"Qualifier"
},
"valueString":"=>10 x E6 cfu/L SIGNIFICANT RESULT. Organisms cultured in quantities =>10 x E6 cfu/L are consistent with pneumonia. beta-lactamase positive result suggests resistance to ampicillin but generally susceptible to amoxicillin- clavulanic and cefuroxime.",
"interpretation":{
"coding":[
{
"code":"A"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"0f9d254f-3ad1-404b-9be9-20258b3c242f",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"F/31415.6"
}
],
"text":"Sensitivities"
},
"valueString":"_Beta-lactamase Pos: ",
"interpretation":{
"coding":[
{
"code":"A"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
},
{
"resourceType":"Observation",
"id":"349bb02b-fbbe-4ce0-b190-3f545240dcc0",
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.6",
"code":"Q.3"
}
],
"text":"Test Comment"
},
"valueString":"=>10 x E6 cfu/L Commensal flora",
"interpretation":{
"coding":[
{
"code":"N"
}
]
},
"issued":"2014-04-29T17:21:56",
"status":"final"
}
],
"name":{
"coding":[
{
"system":"urn:oid:1.3.6.1.4.1.12201.102.5",
"code":"4140"
}
],
"text":"C&S"
},
"status":"partial",
"issued":"2014-04-29T17:21:56",
"subject":{
"resource":"Patient/5556918"
},
"identifier":{
"value":"2363922"
},
"diagnosticDateTime":"2014-04-14T00:00:00-04:00",
"result":[
{
"resource":"#f816a276-cfad-4eca-a9fa-f1dff844a196"
},
{
"resource":"#23b55496-1c2a-4d5f-9c24-8ca5042f4027"
},
{
"resource":"#74e6791a-d810-4545-8410-e9eca41e81d6"
},
{
"resource":"#cd8c6a6c-7ef5-446f-b07b-47a21bfe28ee"
},
{
"resource":"#4a3d453d-3a18-432f-8f1f-d7657c50dcd4"
},
{
"resource":"#0dd6cff6-f9db-42cc-89c9-2cd6ba6fe5af"
},
{
"resource":"#6d6b0117-220f-4b9a-abf3-5faf772cfa61"
},
{
"resource":"#64068acf-57f4-42c8-b0e6-416247067b16"
},
{
"resource":"#0f9d254f-3ad1-404b-9be9-20258b3c242f"
},
{
"resource":"#349bb02b-fbbe-4ce0-b190-3f545240dcc0"
}
]
}
},
{
"deleted":null,
"title":"Organization http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Organization/1.3.6.1.4.1.12201",
"id":"Organization/1.3.6.1.4.1.12201",
"link":[
{
"rel":"self",
"href":"Organization/1.3.6.1.4.1.12201"
}
],
"published":"2014-05-29T10:49:32-04:00",
"content":{
"resourceType":"Organization",
"id":"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Organization/1.3.6.1.4.1.12201",
"extension":[
{
"url":"http://fhir.connectinggta.ca/Profile/organization#providerIdPool",
"valueUri":"1.3.6.1.4.1.12201.1"
}
],
"text":{
"status":"empty",
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\">No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/organization</div>"
},
"contained":[
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.1",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.1"
},
"name":"Toronto General Hospital"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.2",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.2"
},
"name":"Toronto Western Hospital"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.3",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.3"
},
"name":"Princess Margaret Hospital"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.4",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.4"
},
"name":"Toronto Rehab Institute"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.1",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.1"
},
"name":"Toronto General Hospital"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.2",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.2"
},
"name":"Toronto Western Hospital"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.3",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.3"
},
"name":"Princess Margaret Hospital"
},
{
"resourceType":"Location",
"id":"1.3.6.1.4.1.12201.100.4",
"identifier":{
"system":"urn:cgta:facility_ids",
"value":"1.3.6.1.4.1.12201.100.4"
},
"name":"Toronto Rehab Institute"
}
],
"name":"University Health Network",
"type":{
"coding":[
{
"code":"HOSPITAL"
}
]
},
"address":[
{
"line":[
"R. Fraser Elliott Building, 1st Floor",
"190 Elizabeth St."
],
"city":"Toronto",
"state":"ON",
"zip":"M5G 2C4"
}
],
"location":[
{
"resource":"#1.3.6.1.4.1.12201.100.1"
},
{
"resource":"#1.3.6.1.4.1.12201.100.2"
},
{
"resource":"#1.3.6.1.4.1.12201.100.3"
},
{
"resource":"#1.3.6.1.4.1.12201.100.4"
}
]
}
}
]
}

View File

@ -0,0 +1,114 @@
<Bundle xmlns="http://hl7.org/fhir">
<id value="a130877d-a636-4993-97e6-0eeb7734e5"/>
<type value="transaction"/>
<entry>
<resource>
<Patient>
<!-- PID|||555-44-4444||EVERYWOMAN^EVE^E^^^^L|JONES|19620320|F|||153 FERNWOOD DR.^
^STATESVILLE^OH^35292||(206)3345232|(206)752-121||||AC555444444||67-A4335^OH^20030520 -->
<id value="a555-44-4444"/>
<extension url="http://ihe.net/ITI-78/Profile/pdqm#mothersMaidenName">
<valueHumanName>
<family value="Jones"/>
</valueHumanName>
</extension>
<identifier>
<use value="official"/>
<system value="http://ghh.org/patient"/>
<value value="555-44-4444"/>
</identifier>
<identifier>
<use value="official"/>
<system value="http://www.ohio.gov/dmv/driverslicence"/>
<value value="67-A4335"/>
<period>
<end value="2003-05-20"/>
</period>
</identifier>
<name>
<use value="official"/>
<family value="Everywoman"/>
<given value="Eve E."/>
</name>
<telecom>
<system value="phone"/>
<value value="(206)3345232"/>
<use value="home"/>
</telecom>
<telecom>
<system value="phone"/>
<value value="(206)752-121"/>
<use value="work"/>
</telecom>
<gender value="female"/>
<birthDate value="1962-03-20"/>
<address>
<line value="153 Fernwood Dr."/>
<city value="Statesville"/>
<state value="OH"/>
<postalCode value="35292"/>
</address>
<managingOrganization>
<reference value="Organization/GHH"/>
<display value="Good Health Hospital"/>
</managingOrganization>
<link>
<other>
<reference value="Patient/temp6789"/>
</other>
<type value="seealso"/>
</link>
<active value="true"/>
</Patient>
</resource>
<transaction>
<method value="PUT"/>
<url value="Patient/a555-44-4444"/>
</transaction>
</entry>
<entry>
<resource>
<Patient>
<!-- Jane Doe registered in the emergency department -->
<id value="temp6789"/>
<identifier>
<use value="temp"/>
<system value="http://ghh.org/patient"/>
<value value="temp6789"/>
</identifier>
<name>
<use value="temp"/>
<family value="Doe 6789"/>
<given value="Jane"/>
</name>
<gender value="female"/>
<managingOrganization>
<reference value="Organization/GHH"/>
<display value="Good Health Hospital"/>
</managingOrganization>
<link>
<other>
<reference value="Patient/a555-44-4444"/>
</other>
<type value="replace"/>
</link>
<active value="true"/>
</Patient>
</resource>
<transaction>
<method value="PUT"/>
<url value="Patient/temp6789"/>
</transaction>
</entry>
<entry>
<resource>
<Organization>
<id value="GHH"/>
</Organization>
</resource>
<transaction>
<method value="PUT"/>
<url value="Organization/GHH"/>
</transaction>
</entry>
</Bundle>

View File

@ -0,0 +1,168 @@
<!-- Example Provided by Rene Spronk -->
<Bundle xmlns="http://hl7.org/fhir">
<id value="ringholm1430996763590912"/>
<type value="transaction"/>
<entry>
<resource>
<Provenance>
<id value="ringholm1430996763591053"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">Authored on 15-Feb 2015 by GHH.</div>
</text>
<target>
<!-- unversioned, part of a transaction -->
<reference value="Patient/555-44-4444"/>
</target>
<recorded value="2015-05-07T13:06:03+01:00"/>
<reason>
<text value="Patient validated demographics."/>
</reason>
<agent>
<role>
<system value="http://hl7.org/fhir/provenance-participant-role"/>
<code value="author"/>
</role>
<type>
<system value="http://hl7.org/fhir/provenance-participant-type"/>
<code value="patient"/>
</type>
<referenceReference>
<reference value="Patient/555-44-4444"/>
<display value="Patient Everywoman"/>
</referenceReference>
<display value="Eve Everywoman"/>
</agent>
</Provenance>
</resource>
<transaction>
<method value="POST"/>
</transaction>
</entry>
<entry>
<resource>
<Patient>
<id value="555-44-4444"/>
<extension url="http://ihe.net/ITI-78/Profile/pdqm#mothersMaidenName">
<valueHumanName>
<family value="Jones"/>
</valueHumanName>
</extension>
<identifier>
<use value="official"/>
<system value="http://ghh.org/patient"/>
<value value="555-44-4444"/>
</identifier>
<identifier>
<use value="official"/>
<system value="http://www.ohio.gov/dmv/driverslicence"/>
<value value="67-A4335"/>
<period>
<end value="2003-05-20"/>
</period>
</identifier>
<name>
<use value="official"/>
<family value="Everywoman"/>
<given value="Eve E."/>
</name>
<telecom>
<system value="phone"/>
<value value="(206)3345232"/>
<use value="home"/>
</telecom>
<telecom>
<system value="phone"/>
<value value="(206)752-121"/>
<use value="work"/>
</telecom>
<gender value="female"/>
<birthDate value="1962-03-20"/>
<address>
<line value="153 Fernwood Dr."/>
<city value="Statesville"/>
<state value="OH"/>
<postalCode value="35292"/>
</address>
<managingOrganization>
<reference value="Organization/GHH"/>
<display value="Good Health Hospital"/>
</managingOrganization>
<link>
<other>
<reference value="Patient/temp6789"/>
</other>
<type value="seealso"/>
</link>
<active value="true"/>
</Patient>
</resource>
<transaction>
<method value="PUT"/>
<url value="Patient/?identifier=http://ghh.org/patient%7C555-44-4444"/>
</transaction>
</entry>
<entry>
<resource>
<Patient>
<!-- Jane Doe registered in the emergency department -->
<id value="temp6789"/>
<identifier>
<use value="temp"/>
<system value="http://ghh.org/patient"/>
<value value="temp6789"/>
</identifier>
<name>
<use value="temp"/>
<family value="Doe 6789"/>
<given value="Jane"/>
</name>
<gender value="female"/>
<managingOrganization>
<reference value="Organization/GHH"/>
<display value="Good Health Hospital"/>
</managingOrganization>
<link>
<other>
<reference value="Patient/555-44-4444"/>
</other>
<type value="replace"/>
</link>
<active value="true"/>
</Patient>
</resource>
<transaction>
<method value="PUT"/>
<url value="Patient/?identifier=http://ghh.org/patient%7Ctemp6789"/>
</transaction>
</entry>
<entry>
<resource>
<Organization>
<id value="GHH"/>
<identifier>
<!-- Identifier for the GHH hospital -->
<use value="official"/>
<system value="http://ghh.org/department"/>
<value value="GHH"/>
</identifier>
<name value="Good Health Hospital"/>
<type>
<!-- GHH is a Hospital -->
<coding>
<system value="http://snomed.info/sct"/>
<code value="22232009"/>
<display value="Hospital"/>
</coding>
</type>
<active value="true"/>
</Organization>
</resource>
<transaction>
<method value="POST"/>
<url value="Organization"/>
<ifNoneExist value="Organization/?identifier=http://ghh.org/department%7CGHH"/>
</transaction>
</entry>
</Bundle>

View File

@ -41,19 +41,19 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<!-- At least one "structures" JAR must also be included -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<!--
@ -63,14 +63,14 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<!-- This dependency is used for the "FHIR Tester" web app overlay -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<type>war</type>
<scope>provided</scope>
</dependency>

View File

@ -17,7 +17,7 @@
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -32,7 +32,7 @@
database (e.g. Postgres). Consult the Hibernate documentation to see a list of
available dialects.
-->
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />
<property name="persistenceUnitName" value="FHIR_UT" />

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -18,22 +18,22 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<type>war</type>
<scope>provided</scope>
</dependency>

View File

@ -17,7 +17,7 @@
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -56,7 +56,7 @@
</bean>
-->
<bean depends-on="dbServer" id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean depends-on="dbServer" id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />
<property name="persistenceUnitName" value="FHIR_UT" />

View File

@ -16,7 +16,7 @@
<value>home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1</value>
<value>hidev , DSTU2 , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
<value>hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open</value>
<value>furored2 , DSTU1 , Spark - Furore (DSTU2 FHIR) , http://spark-dstu2.furore.com/fhir</value>
<value>furored2 , DSTU2 , Spark - Furore (DSTU2 FHIR) , http://spark-dstu2.furore.com/fhir</value>
<value>furore , DSTU1 , Spark - Furore (DSTU1 FHIR) , http://spark.furore.com/fhir</value>
<value>blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
<value>oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190</value>

View File

@ -34,7 +34,7 @@
<!-- <property name="url" value="jdbc:derby:directory:myUnitTestDB;create=true" /> -->
</bean>
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<property name="persistenceXmlLocation" value="classpath:fhir_jpatest_persistence.xml" />
<property name="persistenceUnitName" value="FHIR_UT" />
@ -47,7 +47,7 @@
</property>
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -18,7 +18,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
@ -189,7 +189,7 @@
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<executions>
<execution>
<goals>

View File

@ -147,6 +147,29 @@ public class GenericClientTest {
}
@Test
public void testMissing() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
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(getPatientFeedWithOneResult()), Charset.forName("UTF-8")));
}});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
client.search().forResource("Patient").where(Patient.NAME.isMissing(true)).execute();
assertEquals("http://example.com/fhir/Patient?name%3Amissing=true", capt.getValue().getRequestLine().getUri());
client.search().forResource("Patient").where(Patient.NAME.isMissing(false)).execute();
assertEquals("http://example.com/fhir/Patient?name%3Amissing=false", capt.getValue().getRequestLine().getUri());
}
@Test
public void testCreateWithStringAutoDetectsEncoding() throws Exception {

View File

@ -9,12 +9,14 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
@ -38,10 +40,13 @@ import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
@ -233,6 +238,75 @@ public class SearchTest {
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
}
/**
* See #164
*/
@Test
public void testSearchByPostWithParamsInBodyAndUrl() throws Exception {
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?name=Central");
// add parameters to the post method
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("_id", "aaa"));
UrlEncodedFormEntity sendentity = new UrlEncodedFormEntity(parameters, "UTF-8");
filePost.setEntity(sendentity);
HttpResponse status = ourClient.execute(filePost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals("idaaa", p.getName().get(0).getFamilyAsSingleString());
assertEquals("nameCentral", p.getName().get(1).getFamilyAsSingleString());
}
/**
* See #164
*/
@Test
public void testSearchByPostWithInvalidPostUrl() throws Exception {
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with _search
// add parameters to the post method
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("_id", "aaa"));
UrlEncodedFormEntity sendentity = new UrlEncodedFormEntity(parameters, "UTF-8");
filePost.setEntity(sendentity);
HttpResponse status = ourClient.execute(filePost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<details value=\"Incorrect Content-Type header value of &quot;application/x-www-form-urlencoded; charset=UTF-8&quot; was provided in the request. A FHIR Content-Type is required for &quot;CREATE&quot; operation\"/>"));
}
/**
* See #164
*/
@Test
public void testSearchByPostWithMissingContentType() throws Exception {
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with _search
HttpEntity sendentity = new ByteArrayEntity(new byte[] {1,2,3,4} );
filePost.setEntity(sendentity);
HttpResponse status = ourClient.execute(filePost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<details value=\"No Content-Type header was provided in the request. This is required for &quot;CREATE&quot; operation\"/>"));
}
@Test
public void testSearchCompartment() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/fooCompartment");
@ -354,6 +428,14 @@ public class SearchTest {
*/
public static class DummyPatientResourceProvider implements IResourceProvider {
/**
* Only needed for #164
*/
@Create
public MethodOutcome create(@ResourceParam Patient thePatient) {
throw new IllegalArgumentException();
}
@Search(compartmentName = "fooCompartment")
public List<Patient> compartment(@IdParam IdDt theId) {
ArrayList<Patient> retVal = new ArrayList<Patient>();
@ -383,7 +465,7 @@ public class SearchTest {
@Search
public List<Patient> findPatient(@RequiredParam(name = "_id") StringParam theParam) {
public List<Patient> findPatient(@RequiredParam(name = "_id") StringParam theParam, @OptionalParam(name="name") StringParam theName) {
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();
@ -391,6 +473,9 @@ public class SearchTest {
patient.addIdentifier("system", "identifier123");
if (theParam != null) {
patient.addName().addFamily("id" + theParam.getValue());
if (theName != null) {
patient.addName().addFamily("name" + theName.getValue());
}
}
retVal.add(patient);
return retVal;

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.StringParam;
/**
* HAPI/FHIR <b>Identifier</b> Datatype
@ -402,5 +403,29 @@ public class IdentifierDt
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
}

View File

@ -9,10 +9,12 @@ import ca.uhn.fhir.model.primitive.DateTimeDt;
import org.apache.commons.io.IOUtils;
import org.hamcrest.core.StringContains;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
@ -25,6 +27,7 @@ import static org.junit.Assert.fail;
public class ResourceValidatorTest {
private static FhirContext ourCtx = new FhirContext();
private static Locale ourDefaultLocale;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceValidatorTest.class);
@Test
@ -45,10 +48,29 @@ public class ResourceValidatorTest {
} catch (ValidationFailureException e) {
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
assertEquals(1, e.getOperationOutcome().getIssue().size());
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue(), containsString("Invalid content was found starting with element 'breed'"));
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue(), containsString("cvc-complex-type.2.4.a"));
}
}
@BeforeClass
public static void beforeClass() {
/*
* We cache the default locale, but temporarily set it to a random value during this test. This helps ensure that there are no
* language specific dependencies in the test.
*/
ourDefaultLocale = Locale.getDefault();
Locale[] available = Locale.getAvailableLocales();
Locale newLocale = available[(int)(Math.random() * available.length)];
Locale.setDefault(newLocale);
ourLog.info("Tests are running in locale: " + newLocale.getDisplayName());
}
public static void afterClass() {
Locale.setDefault(ourDefaultLocale);
}
/**
* See issue #50
*/
@ -92,7 +114,7 @@ public class ResourceValidatorTest {
} catch (ValidationFailureException e) {
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
assertEquals(1, e.getOperationOutcome().getIssue().size());
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue(), containsString("Inv-2: A system is required if a value is provided."));
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue(), containsString("Inv-2:"));
}
}
@ -114,7 +136,7 @@ public class ResourceValidatorTest {
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
assertEquals(1, operationOutcome.getIssue().size());
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2:"));
p.getTelecomFirstRep().setSystem(ContactSystemEnum.EMAIL);
validationResult = val.validateWithResult(p);
@ -155,7 +177,7 @@ public class ResourceValidatorTest {
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
assertEquals(1, operationOutcome.getIssue().size());
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2:"));
}
private FhirValidator createFhirValidator() {

View File

@ -10,7 +10,10 @@
<logger name="org.eclipse" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache" additivity="false" level="debug">
<logger name="org.apache" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.thymeleaf" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>

View File

@ -5,20 +5,20 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR Structures - DSTU2 (FHIR v0.4.0)</name>
<name>HAPI FHIR Structures - DSTU2 (FHIR v0.5.0)</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
@ -177,7 +177,7 @@
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<executions>
<execution>
<goals>

View File

@ -77,7 +77,7 @@ public class ClientServerValidationTestDstu2 {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
// don't load the conformance until the first time the client is actually used
@ -115,7 +115,7 @@ public class ClientServerValidationTestDstu2 {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
// don't load the conformance until the first time the client is actually used
@ -143,7 +143,7 @@ public class ClientServerValidationTestDstu2 {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
try {
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
fail();
@ -173,19 +173,75 @@ public class ClientServerValidationTestDstu2 {
myFirstResponse = false;
return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8"));
Patient resource = new Patient();
resource.addName().addFamily().setValue("FAM");
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(resource)), Charset.forName("UTF-8"));
}
}
});
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
client.registerInterceptor(new BasicAuthInterceptor("USER", "PASS"));
client.read(new UriDt("http://foo/Patient/123"));
Patient pt = (Patient) client.read(new UriDt("http://foo/Patient/123"));
assertEquals("FAM", pt.getNameFirstRep().getFamilyAsSingleString());
Header auth = capt.getValue().getFirstHeader("Authorization");
assertEquals(2, capt.getAllValues().size());
Header auth = capt.getAllValues().get(0).getFirstHeader("Authorization");
assertNotNull(auth);
assertEquals("Basic VVNFUjpQQVNT", auth.getValue());
auth = capt.getAllValues().get(1).getFirstHeader("Authorization");
assertNotNull(auth);
assertEquals("Basic VVNFUjpQQVNT", auth.getValue());
}
@Test
public void testForceConformanceCheck() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
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 {
if (myFirstResponse) {
myFirstResponse = false;
return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8"));
} else {
Patient resource = new Patient();
resource.addName().addFamily().setValue("FAM");
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(resource)), Charset.forName("UTF-8"));
}
}
});
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
client.registerInterceptor(new BasicAuthInterceptor("USER", "PASS"));
client.forceConformanceCheck();
assertEquals(1, capt.getAllValues().size());
Patient pt = (Patient) client.read(new UriDt("http://foo/Patient/123"));
assertEquals("FAM", pt.getNameFirstRep().getFamilyAsSingleString());
assertEquals(2, capt.getAllValues().size());
Header auth = capt.getAllValues().get(0).getFirstHeader("Authorization");
assertNotNull(auth);
assertEquals("Basic VVNFUjpQQVNT", auth.getValue());
auth = capt.getAllValues().get(1).getFirstHeader("Authorization");
assertNotNull(auth);
assertEquals("Basic VVNFUjpQQVNT", auth.getValue());
}

View File

@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
@ -35,7 +36,7 @@ import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
@ -616,6 +617,64 @@ public class GenericClientDstu2Test {
// assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue());
}
@Test
public void testTransactionWithString() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle req = new ca.uhn.fhir.model.dstu2.resource.Bundle();
req.addEntry().setResource(new Patient());
req.addEntry().setResource(new Observation());
String reqStringJson = ourCtx.newJsonParser().encodeResourceToString(req);
String reqStringXml = ourCtx.newXmlParser().encodeResourceToString(req);
ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle();
resp.addEntry().getTransactionResponse().setLocation("Patient/1/_history/1");
resp.addEntry().getTransactionResponse().setLocation("Patient/2/_history/2");
final String respStringJson = ourCtx.newJsonParser().encodeResourceToString(resp);
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_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respStringJson), Charset.forName("UTF-8"));
}});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
String response = client.transaction()
.withBundle(reqStringJson)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/", capt.getValue().getURI().toString());
assertEquals(respStringJson, response);
String requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent());
IOUtils.closeQuietly(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent());
assertEquals(reqStringJson, requestString);
assertEquals("application/json+fhir; charset=UTF-8", capt.getValue().getFirstHeader("Content-Type").getValue());
//@formatter:off
response = client.transaction()
.withBundle(reqStringJson)
.encodedXml()
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/?_format=xml", capt.getValue().getURI().toString());
assertEquals(respStringJson, response);
requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent());
IOUtils.closeQuietly(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent());
assertEquals(reqStringXml, requestString);
assertEquals("application/xml+fhir; charset=UTF-8", capt.getValue().getFirstHeader("Content-Type").getValue());
}
@Test
public void testTransactionWithTransactionResource() throws Exception {

View File

@ -10,6 +10,7 @@ import static org.junit.Assert.fail;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import org.apache.commons.io.IOUtils;
import org.hamcrest.core.StringContains;
@ -65,7 +66,7 @@ public class ResourceValidatorDstu2Test {
ourLog.info(resultString);
assertEquals(2, result.getOperationOutcome().getIssue().size());
assertThat(resultString, StringContains.containsString("cvc-pattern-valid: Value '2000-15-31'"));
assertThat(resultString, StringContains.containsString("'2000-15-31'"));
}
@SuppressWarnings("deprecation")
@ -90,7 +91,7 @@ public class ResourceValidatorDstu2Test {
} catch (ValidationFailureException e) {
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome());
ourLog.info(encoded);
assertThat(encoded, containsString("if there's a duration, there needs to be"));
assertThat(encoded, containsString("tim-1:"));
}
}
@ -115,7 +116,7 @@ public class ResourceValidatorDstu2Test {
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
ourLog.info(encoded);
assertThat(encoded, containsString("if there's a duration, there needs to be"));
assertThat(encoded, containsString("tim-1:"));
}
@Test
@ -157,7 +158,7 @@ public class ResourceValidatorDstu2Test {
} catch (ValidationFailureException e) {
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
assertEquals(1, e.getOperationOutcome().getIssue().size());
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue(), containsString("Invalid content was found starting with element 'breed'"));
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue(), containsString("cvc-complex-type"));
}
}
@ -179,7 +180,7 @@ public class ResourceValidatorDstu2Test {
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
assertEquals(1, operationOutcome.getIssue().size());
assertThat(operationOutcome.getIssueFirstRep().getDetails(), containsString("A system is required if a value is provided."));
assertThat(operationOutcome.getIssueFirstRep().getDetails(), containsString("cpt-2:"));
p.getTelecomFirstRep().setSystem(ContactPointSystemEnum.EMAIL);
validationResult = val.validateWithResult(p);

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>0.9-SNAPSHOT</version>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -27,7 +27,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>0.9-SNAPSHOT</version>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
@ -38,7 +38,7 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<version>${servlet_api_version}</version>
<scope>provided</scope>
</dependency>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -27,22 +27,22 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<!--<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>

View File

@ -127,7 +127,8 @@
$('#server-history-datetime').datetimepicker({
sideBySide: true,
use24hours: true,
showToday: true
showToday: true,
keepInvalid: true
});
});
$('#server-history-btn').click(

View File

@ -326,7 +326,8 @@
$('#resource-history-datetime').datetimepicker({
sideBySide: true,
use24hours: true,
showToday: true
showToday: true,
keepInvalid: true
});
});
$('#resource-history-btn').click(

View File

@ -145,6 +145,7 @@ PRE.resultBodyPre {
background-color: transparent;
overflow: visible;
/*white-space: normal;*/
white-space: pre-wrap;
}
/*

View File

@ -17,7 +17,7 @@
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -32,7 +32,7 @@
database (e.g. Postgres). Consult the Hibernate documentation to see a list of
available dialects.
-->
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<!--
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />

View File

@ -17,7 +17,7 @@
</bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />

View File

@ -32,7 +32,7 @@
database (e.g. Postgres). Consult the Hibernate documentation to see a list of
available dialects.
-->
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<!--
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -19,7 +19,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<!--
Because Tinder is a part of the HAPI FHIR build process (it generates

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -17,12 +17,12 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
@ -45,7 +45,7 @@
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<executions>
<execution>
<id>custom-structs</id>
@ -104,7 +104,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>

View File

@ -12,9 +12,9 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<name>HAPI-FHIR</name>
<url>http://hl7api.sourceforge.net/hapi-fhir/</url>
<url>http://jamesagnew.github.io/hapi-fhir/</url>
<organization>
<name>University Health Network</name>
@ -48,6 +48,10 @@
</dependencies>
<prerequisites>
<!--
TODO: the version number here should probably be
either 3.2 or 3.3. Check and fix
-->
<maven>3.0.1</maven>
</prerequisites>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -17,12 +17,12 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>

Some files were not shown because too many files have changed in this diff Show More