Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Thomas Andersen 2015-10-04 21:07:46 +02:00
commit e040ef3f43
85 changed files with 6920 additions and 1102 deletions

View File

@ -65,7 +65,6 @@
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>

View File

@ -6,7 +6,7 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> <classpathentry kind="src" output="target/classes" path="src/main/resources">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
@ -17,7 +17,7 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"> <classpathentry kind="src" output="target/test-classes" path="src/test/resources">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>

View File

@ -15,26 +15,6 @@
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (1).launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature> <nature>org.eclipse.jem.workbench.JavaEMFNature</nature>

View File

@ -37,25 +37,20 @@ public class BoundCodeDt<T extends Enum<?>> extends CodeDt {
public BoundCodeDt() { public BoundCodeDt() {
// nothing // nothing
} }
public BoundCodeDt(IValueSetEnumBinder<T> theBinder) { public BoundCodeDt(IValueSetEnumBinder<T> theBinder) {
Validate.notNull(theBinder, "theBinder must not be null"); Validate.notNull(theBinder, "theBinder must not be null");
myBinder = theBinder; myBinder = theBinder;
} }
public BoundCodeDt(IValueSetEnumBinder<T> theBinder, T theValue) { public BoundCodeDt(IValueSetEnumBinder<T> theBinder, T theValue) {
Validate.notNull(theBinder, "theBinder must not be null"); Validate.notNull(theBinder, "theBinder must not be null");
myBinder = theBinder; myBinder = theBinder;
setValueAsEnum(theValue); setValueAsEnum(theValue);
} }
public void setValueAsEnum(T theValue) { public IValueSetEnumBinder<T> getBinder() {
Validate.notNull(myBinder, "This object does not have a binder. Constructor BoundCodeDt() should not be called!"); return myBinder;
if (theValue==null) {
setValue(null);
} else {
setValue(myBinder.toCodeString(theValue));
}
} }
public T getValueAsEnum() { public T getValueAsEnum() {
@ -66,4 +61,13 @@ public class BoundCodeDt<T extends Enum<?>> extends CodeDt {
} }
return retVal; return retVal;
} }
public void setValueAsEnum(T theValue) {
Validate.notNull(myBinder, "This object does not have a binder. Constructor BoundCodeDt() should not be called!");
if (theValue==null) {
setValue(null);
} else {
setValue(myBinder.toCodeString(theValue));
}
}
} }

View File

@ -50,11 +50,6 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
// nothing // nothing
} }
@Override
public boolean isEmpty() {
return super.isBaseEmpty() && (getValue() == null || getValue().isEmpty());
}
/** /**
* Constructor which accepts a string code * Constructor which accepts a string code
* *
@ -65,27 +60,25 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
setValueAsString(theTextDiv); setValueAsString(theTextDiv);
} }
/**
* Accepts a textual DIV and parses it into XHTML events which are stored internally.
* <p>
* <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with an HTML tag (generally this would be a div tag), a div tag will be automatically placed
* surrounding the text.
* </p>
* <p>
* Also note that if the parsed text contains any entities (&amp;foo;) which are not a part of the entities defined in core XML (e.g. &amp;sect; which
* is valid in XHTML 1.0 but not in XML 1.0) they will be parsed and converted to their equivalent unicode character.
* </p>
*/
@Override @Override
public void setValueAsString(String theValue) throws DataFormatException { protected String encode(List<XMLEvent> theValue) {
if (theValue == null || theValue.isEmpty()) { try {
super.setValueAsString(null); StringWriter w = new StringWriter();
} else { XMLEventWriter ew = XmlUtil.createXmlFragmentWriter(w);
String value = theValue.trim();
if (value.charAt(0) != '<') { for (XMLEvent next : getValue()) {
value = "<div>" + value + "</div>"; if (next.isCharacters()) {
ew.add(next);
} else {
ew.add(next);
}
} }
super.setValueAsString(value); ew.close();
return w.toString();
} catch (XMLStreamException e) {
throw new DataFormatException("Problem with the contained XML events", e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException(e);
} }
} }
@ -93,6 +86,11 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
return getValue() != null && getValue().size() > 0; return getValue() != null && getValue().size() > 0;
} }
@Override
public boolean isEmpty() {
return super.isBaseEmpty() && (getValue() == null || getValue().isEmpty());
}
@Override @Override
protected List<XMLEvent> parse(String theValue) { protected List<XMLEvent> parse(String theValue) {
String val = theValue.trim(); String val = theValue.trim();
@ -128,24 +126,27 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
} }
} }
/**
* Accepts a textual DIV and parses it into XHTML events which are stored internally.
* <p>
* <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with an HTML tag (generally this would be a div tag), a div tag will be automatically placed
* surrounding the text.
* </p>
* <p>
* Also note that if the parsed text contains any entities (&amp;foo;) which are not a part of the entities defined in core XML (e.g. &amp;sect; which is valid in XHTML 1.0 but not in XML 1.0) they
* will be parsed and converted to their equivalent unicode character.
* </p>
*/
@Override @Override
protected String encode(List<XMLEvent> theValue) { public void setValueAsString(String theValue) throws DataFormatException {
try { if (theValue == null || theValue.isEmpty()) {
StringWriter w = new StringWriter(); super.setValueAsString(null);
XMLEventWriter ew = XmlUtil.createXmlWriter(w); } else {
for (XMLEvent next : getValue()) { String value = theValue.trim();
if (next.isCharacters()) { if (value.charAt(0) != '<') {
ew.add(next); value = "<div>" + value + "</div>";
} else {
ew.add(next);
}
} }
ew.close(); super.setValueAsString(value);
return w.toString();
} catch (XMLStreamException e) {
throw new DataFormatException("Problem with the contained XML events", e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException(e);
} }
} }

View File

@ -42,6 +42,7 @@ import org.apache.http.client.methods.HttpRequestBase;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -100,6 +101,7 @@ import ca.uhn.fhir.rest.gclient.IOperation;
import ca.uhn.fhir.rest.gclient.IOperationUnnamed; import ca.uhn.fhir.rest.gclient.IOperationUnnamed;
import ca.uhn.fhir.rest.gclient.IOperationUntyped; import ca.uhn.fhir.rest.gclient.IOperationUntyped;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput; import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
import ca.uhn.fhir.rest.gclient.IParam; import ca.uhn.fhir.rest.gclient.IParam;
import ca.uhn.fhir.rest.gclient.IQuery; import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.gclient.IRead; import ca.uhn.fhir.rest.gclient.IRead;
@ -229,7 +231,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
return delete(theType, new IdDt(theId)); return delete(theType, new IdDt(theId));
} }
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) { private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint,
SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) {
String resName = toResourceName(theType); String resName = toResourceName(theType);
IIdType id = theId; IIdType id = theId;
if (!id.hasBaseUrl()) { if (!id.hasBaseUrl()) {
@ -906,6 +909,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
myCriterionList.add((ICriterionInternal) theCriterion); myCriterionList.add((ICriterionInternal) theCriterion);
return this; return this;
} }
@Override
public IDeleteWithQuery resourceConditionalByType(Class<? extends IBaseResource> theResourceType) {
Validate.notNull(theResourceType, "theResourceType can not be null");
myCriterionList = new CriterionList();
myResourceType = myContext.getResourceDefinition(theResourceType).getName();
return this;
}
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
@ -1353,13 +1364,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput { private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput, IOperationUntypedWithInputAndPartialOutput {
private IIdType myId; private IIdType myId;
private String myOperationName; private String myOperationName;
private IBaseParameters myParameters; private IBaseParameters myParameters;
private Class<? extends IBaseResource> myType; private Class<? extends IBaseResource> myType;
private boolean myUseHttpGet; private boolean myUseHttpGet;
private RuntimeResourceDefinition myParametersDef;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -1455,6 +1467,51 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this; return this;
} }
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withParameter(Class<T> theParameterType, String theName, IBase theValue) {
Validate.notNull(theParameterType, "theParameterType must not be null");
Validate.notEmpty(theName, "theName must not be null");
Validate.notNull(theValue, "theValue must not be null");
myParametersDef = myContext.getResourceDefinition(theParameterType);
myParameters = (IBaseParameters) myParametersDef.newInstance();
addParam(theName, theValue);
return this;
}
@SuppressWarnings("unchecked")
private void addParam(String theName, IBase theValue) {
BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter");
BaseRuntimeElementCompositeDefinition<?> parameterElem = (BaseRuntimeElementCompositeDefinition<?>) parameterChild.getChildByName("parameter");
IBase parameter = parameterElem.newInstance();
parameterChild.getMutator().addValue(myParameters, parameter);
IPrimitiveType<String> name = (IPrimitiveType<String>) myContext.getElementDefinition("string").newInstance();
name.setValue(theName);
parameterElem.getChildByName("name").getMutator().setValue(parameter, name);
if (theValue instanceof IBaseDatatype) {
String childElementName = "value" + StringUtils.capitalize(myContext.getElementDefinition(theValue.getClass()).getName());
parameterElem.getChildByName(childElementName).getMutator().setValue(parameter, theValue);
} else if (theValue instanceof IBaseResource) {
parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue);
} else {
throw new IllegalArgumentException("Don't know how to handle parameter of type " + theValue.getClass());
}
}
@Override
public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) {
Validate.notEmpty(theName, "theName must not be null");
Validate.notNull(theValue, "theValue must not be null");
addParam(theName, theValue);
return this;
}
} }
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<BaseOperationOutcome> { private final class OperationOutcomeResponseHandler implements IClientResponseHandler<BaseOperationOutcome> {
@ -1506,7 +1563,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private RuntimeResourceDefinition myType; private RuntimeResourceDefinition myType;
@Override @Override
public Object execute() {//AAA public Object execute() {// AAA
if (myId.hasVersionIdPart()) { if (myId.hasVersionIdPart()) {
return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements());
} else { } else {
@ -1693,7 +1750,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text"); BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text");
IBase textInstance = textElement.newInstance(); IBase textInstance = textElement.newInstance();
textChild.getMutator().addValue(instance, textInstance); textChild.getMutator().addValue(instance, textInstance);
BaseRuntimeChildDefinition divChild = textElement.getChildByName("div"); BaseRuntimeChildDefinition divChild = textElement.getChildByName("div");
BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div"); BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div");
IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance(); IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance();

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.gclient; package ca.uhn.fhir.rest.gclient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
@ -43,8 +44,15 @@ public interface IDelete {
IDeleteTyped resourceConditionalByUrl(String theSearchUrl); IDeleteTyped resourceConditionalByUrl(String theSearchUrl);
/** /**
* Delete using a conditional/match URL. The query parameters will be added in the next part of the call chain.
* @since HAPI 0.9 / FHIR DSTU 2 * @since HAPI 0.9 / FHIR DSTU 2
*/ */
IDeleteWithQuery resourceConditionalByType(String theResourceType); IDeleteWithQuery resourceConditionalByType(String theResourceType);
/**
* Delete using a conditional/match URL. The query parameters will be added in the next part of the call chain.
* @since HAPI 1.3
*/
IDeleteWithQuery resourceConditionalByType(Class<? extends IBaseResource> theResourceType);
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.gclient; package ca.uhn.fhir.rest.gclient;
import org.hl7.fhir.instance.model.api.IBase;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -40,4 +42,16 @@ public interface IOperationUntyped {
*/ */
<T extends IBaseParameters> IOperationUntypedWithInput<T> withNoParameters(Class<T> theOutputParameterType); <T extends IBaseParameters> IOperationUntypedWithInput<T> withNoParameters(Class<T> theOutputParameterType);
/**
* Use chained method calls to construct a Parameters input. This form is a convenience
* in order to allow simple method chaining to be used to build up a parameters
* resource for the input of an operation without needing to manually construct one.
*
* @param theParameterType The type to use for the output parameters (this should be set to
* <code>Parameters.class</code> drawn from the version of the FHIR structures you are using)
* @param theName The first parameter name
* @param theValue The first parameter value
*/
<T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withParameter(Class<T> theParameterType, String theName, IBase theValue);
} }

View File

@ -0,0 +1,38 @@
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 org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters;
public interface IOperationUntypedWithInputAndPartialOutput<T extends IBaseParameters> extends IOperationUntypedWithInput<T> {
/**
* Use chained method calls to construct a Parameters input. This form is a convenience
* in order to allow simple method chaining to be used to build up a parameters
* resource for the input of an operation without needing to manually construct one.
*
* @param theName The first parameter name
* @param theValue The first parameter value
*/
IOperationUntypedWithInputAndPartialOutput<T> andParameter(String theName, IBase theValue);
}

View File

@ -73,6 +73,10 @@ public abstract class BaseServerResponseException extends RuntimeException {
private String myResponseMimeType; private String myResponseMimeType;
private int myStatusCode; private int myStatusCode;
public static void main (String[] args) {
BaseServerResponseException.class.getName();
}
/** /**
* Constructor * Constructor
* *

View File

@ -43,11 +43,13 @@ import ca.uhn.fhir.util.OperationOutcomeUtil;
public class ExceptionHandlingInterceptor extends InterceptorAdapter { public class ExceptionHandlingInterceptor extends InterceptorAdapter {
public static final String PROCESSING = "processing";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptor.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptor.class);
private Class<?>[] myReturnStackTracesForExceptionTypes; private Class<?>[] myReturnStackTracesForExceptionTypes;
@Override @Override
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException { public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theRequest, HttpServletResponse theResponse)
throws ServletException, IOException {
FhirContext ctx = theRequestDetails.getServer().getFhirContext(); FhirContext ctx = theRequestDetails.getServer().getFhirContext();
@ -116,12 +118,20 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
ourLog.error("Failure during REST processing", theException); ourLog.error("Failure during REST processing", theException);
populateDetails(ctx, theException, oo); populateDetails(ctx, theException, oo);
} else if (theException instanceof BaseServerResponseException) { } else if (theException instanceof BaseServerResponseException) {
ourLog.warn("Failure during REST processing: {}", theException); int statusCode = ((BaseServerResponseException) theException).getStatusCode();
// No stack traces for non-server internal errors
if (statusCode < 500) {
ourLog.warn("Failure during REST processing: {}", theException.toString());
} else {
ourLog.warn("Failure during REST processing: {}", theException);
}
BaseServerResponseException baseServerResponseException = (BaseServerResponseException) theException; BaseServerResponseException baseServerResponseException = (BaseServerResponseException) theException;
populateDetails(ctx, theException, oo); populateDetails(ctx, theException, oo);
if (baseServerResponseException.getAdditionalMessages() != null) { if (baseServerResponseException.getAdditionalMessages() != null) {
for (String next : baseServerResponseException.getAdditionalMessages()) { for (String next : baseServerResponseException.getAdditionalMessages()) {
OperationOutcomeUtil.addIssue(ctx, oo, "error", next); OperationOutcomeUtil.addIssue(ctx, oo, "error", next, null, PROCESSING);
} }
} }
} else { } else {
@ -143,19 +153,18 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
for (Class<?> next : myReturnStackTracesForExceptionTypes) { for (Class<?> next : myReturnStackTracesForExceptionTypes) {
if (next.isAssignableFrom(theException.getClass())) { if (next.isAssignableFrom(theException.getClass())) {
String detailsValue = theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException); String detailsValue = theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException);
OperationOutcomeUtil.addIssue(theCtx, theOo, "error", detailsValue); OperationOutcomeUtil.addIssue(theCtx, theOo, "error", detailsValue, null, PROCESSING);
return; return;
} }
} }
} }
OperationOutcomeUtil.addIssue(theCtx, theOo, "error", theException.getMessage()); OperationOutcomeUtil.addIssue(theCtx, theOo, "error", theException.getMessage(), null, PROCESSING);
} }
/** /**
* If any server methods throw an exception which extends any of the given exception types, the exception stack trace * If any server methods throw an exception which extends any of the given exception types, the exception stack trace will be returned to the user. This can be useful for helping to diagnose
* will be returned to the user. This can be useful for helping to diagnose issues, but may not be desirable for * issues, but may not be desirable for production situations.
* production situations.
* *
* @param theExceptionTypes * @param theExceptionTypes
* The exception types for which to return the stack trace to the user. * The exception types for which to return the stack trace to the user.

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.interceptor; package ca.uhn.fhir.rest.server.interceptor;
import java.io.IOException;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -24,6 +26,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -38,6 +41,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
/** /**
* Server interceptor which logs each request using a defined format * Server interceptor which logs each request using a defined format
@ -67,8 +71,8 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
* </tr> * </tr>
* <tr> * <tr>
* <td>${requestHeader.XXXX}</td> * <td>${requestHeader.XXXX}</td>
* <td>The value of the HTTP request header named XXXX. For example, a substitution variable named * <td>The value of the HTTP request header named XXXX. For example, a substitution variable named "${requestHeader.x-forwarded-for} will yield the value of the first header named "x-forwarded-for
* "${requestHeader.x-forwarded-for} will yield the value of the first header named "x-forwarded-for", or "" if none.</td> * ", or "" if none.</td>
* </tr> * </tr>
* <tr> * <tr>
* <td>${requestParameters}</td> * <td>${requestParameters}</td>
@ -82,15 +86,52 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
* <td>${servletPath}</td> * <td>${servletPath}</td>
* <td>The part of thre requesting URL that corresponds to the particular Servlet being called (see {@link HttpServletRequest#getServletPath()})</td> * <td>The part of thre requesting URL that corresponds to the particular Servlet being called (see {@link HttpServletRequest#getServletPath()})</td>
* </tr> * </tr>
* <tr>
* <td>${requestUrl}</td>
* <td>The complete URL of the request</td>
* </tr>
* <tr>
* <td>${requestVerb}</td>
* <td>The HTTP verb of the request</td>
* </tr>
* <tr>
* <td>${exceptionMessage}</td>
* <td>Applies only to an error message: The message from {@link Exception#getMessage()}</td>
* </tr>
* </table> * </table>
*/ */
public class LoggingInterceptor extends InterceptorAdapter { public class LoggingInterceptor extends InterceptorAdapter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class);
private String myErrorMessageFormat = "ERROR - ${idOrResourceName}";
private boolean myLogExceptions;
private Logger myLogger = ourLog; private Logger myLogger = ourLog;
private String myMessageFormat = "${operationType} - ${idOrResourceName}"; private String myMessageFormat = "${operationType} - ${idOrResourceName}";
/**
* Get the log message format to be used when logging exceptions
*/
public String getErrorMessageFormat() {
return myErrorMessageFormat;
}
@Override
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws ServletException, IOException {
if (myLogExceptions) {
// Perform any string substitutions from the message format
StrLookup<?> lookup = new MyLookup(theServletRequest, theException, theRequestDetails);
StrSubstitutor subs = new StrSubstitutor(lookup, "${", "}", '\\');
// Actuall log the line
String line = subs.replace(myErrorMessageFormat);
myLogger.info(line);
}
return true;
}
@Override @Override
public boolean incomingRequestPostProcessed(final RequestDetails theRequestDetails, final HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException { public boolean incomingRequestPostProcessed(final RequestDetails theRequestDetails, final HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
@ -105,6 +146,28 @@ public class LoggingInterceptor extends InterceptorAdapter {
return true; return true;
} }
/**
* Should exceptions be logged by this logger
*/
public boolean isLogExceptions() {
return myLogExceptions;
}
/**
* Set the log message format to be used when logging exceptions
*/
public void setErrorMessageFormat(String theErrorMessageFormat) {
Validate.notBlank(theErrorMessageFormat, "Message format can not be null/empty");
myErrorMessageFormat = theErrorMessageFormat;
}
/**
* Should exceptions be logged by this logger
*/
public void setLogExceptions(boolean theLogExceptions) {
myLogExceptions = theLogExceptions;
}
public void setLogger(Logger theLogger) { public void setLogger(Logger theLogger) {
Validate.notNull(theLogger, "Logger can not be null"); Validate.notNull(theLogger, "Logger can not be null");
myLogger = theLogger; myLogger = theLogger;
@ -125,12 +188,20 @@ public class LoggingInterceptor extends InterceptorAdapter {
} }
private static final class MyLookup extends StrLookup<String> { private static final class MyLookup extends StrLookup<String> {
private final Throwable myException;
private final HttpServletRequest myRequest; private final HttpServletRequest myRequest;
private final RequestDetails myRequestDetails; private final RequestDetails myRequestDetails;
private MyLookup(HttpServletRequest theRequest, RequestDetails theRequestDetails) { private MyLookup(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
myRequest = theRequest; myRequest = theRequest;
myRequestDetails = theRequestDetails; myRequestDetails = theRequestDetails;
myException = null;
}
public MyLookup(HttpServletRequest theServletRequest, BaseServerResponseException theException, RequestDetails theRequestDetails) {
myException = theException;
myRequestDetails = theRequestDetails;
myRequest = theServletRequest;
} }
@Override @Override
@ -204,6 +275,12 @@ public class LoggingInterceptor extends InterceptorAdapter {
} else { } else {
return ""; return "";
} }
} else if (theKey.equals("exceptionMessage")) {
return myException != null ? myException.getMessage() : null;
} else if (theKey.equals("requestUrl")) {
return myRequest.getRequestURL().toString();
} else if (theKey.equals("requestVerb")) {
return myRequest.getMethod();
} }
return "!VAL!"; return "!VAL!";

View File

@ -42,22 +42,23 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
*/ */
public class OperationOutcomeUtil { public class OperationOutcomeUtil {
/** // /**
* Add an issue to an OperationOutcome // * Add an issue to an OperationOutcome
* // *
* @param theCtx // * @param theCtx
* The fhir context // * The fhir context
* @param theOperationOutcome // * @param theOperationOutcome
* The OO resource to add to // * The OO resource to add to
* @param theSeverity // * @param theSeverity
* The severity (e.g. "error") // * The severity (e.g. "error")
* @param theDetails // * @param theDetails
* The details string // * The details string
*/ // * @param theCode
public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails) { // */
IBase issue = createIssue(theCtx, theOperationOutcome); // public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theCode) {
populateDetails(theCtx, issue, theSeverity, theDetails, null); // IBase issue = createIssue(theCtx, theOperationOutcome);
} // populateDetails(theCtx, issue, theSeverity, theDetails, null, theCode);
// }
/** /**
* Add an issue to an OperationOutcome * Add an issue to an OperationOutcome
@ -70,10 +71,11 @@ public class OperationOutcomeUtil {
* The severity (e.g. "error") * The severity (e.g. "error")
* @param theDetails * @param theDetails
* The details string * The details string
* @param theCode
*/ */
public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation) { public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
IBase issue = createIssue(theCtx, theOperationOutcome); IBase issue = createIssue(theCtx, theOperationOutcome);
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation); populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
} }
private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) { private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) {
@ -145,11 +147,16 @@ public class OperationOutcomeUtil {
} }
} }
private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation) { private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation, String theCode) {
BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass()); BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass());
BaseRuntimeChildDefinition detailsChild; BaseRuntimeChildDefinition detailsChild;
if (theCtx.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { if (theCtx.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
detailsChild = issueElement.getChildByName("diagnostics"); detailsChild = issueElement.getChildByName("diagnostics");
BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
codeElem.setValueAsString(theCode);
codeChild.getMutator().addValue(theIssue, codeElem);
} else { } else {
detailsChild = issueElement.getChildByName("details"); detailsChild = issueElement.getChildByName("details");
} }

View File

@ -173,17 +173,22 @@ public class UrlUtil {
*/ */
//@formatter:on //@formatter:on
public static UrlParts parseUrl(String theUrl) { public static UrlParts parseUrl(String theUrl) {
String url = theUrl;
if (url.matches("\\/[a-zA-Z]+\\?.*")) {
url = url.substring(1);
}
UrlParts retVal = new UrlParts(); UrlParts retVal = new UrlParts();
int nextStart = 0; int nextStart = 0;
boolean nextIsHistory = false; boolean nextIsHistory = false;
for (int idx = 0; idx < theUrl.length(); idx++) { for (int idx = 0; idx < url.length(); idx++) {
char nextChar = theUrl.charAt(idx); char nextChar = url.charAt(idx);
boolean atEnd = (idx + 1) == theUrl.length(); boolean atEnd = (idx + 1) == url.length();
if (nextChar == '?' || nextChar == '/' || atEnd) { if (nextChar == '?' || nextChar == '/' || atEnd) {
int endIdx = atEnd ? idx + 1 : idx; int endIdx = atEnd ? idx + 1 : idx;
String nextSubstring = theUrl.substring(nextStart, endIdx); String nextSubstring = url.substring(nextStart, endIdx);
if (retVal.getResourceType() == null) { if (retVal.getResourceType() == null) {
retVal.setResourceType(nextSubstring); retVal.setResourceType(nextSubstring);
} else if (retVal.getResourceId() == null) { } else if (retVal.getResourceId() == null) {
@ -194,12 +199,12 @@ public class UrlUtil {
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) { if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
nextIsHistory = true; nextIsHistory = true;
} else { } else {
throw new InvalidRequestException("Invalid FHIR resource URL: " + theUrl); throw new InvalidRequestException("Invalid FHIR resource URL: " + url);
} }
} }
if (nextChar == '?') { if (nextChar == '?') {
if (theUrl.length() > idx + 1) { if (url.length() > idx + 1) {
retVal.setParams(theUrl.substring(idx + 1, theUrl.length())); retVal.setParams(url.substring(idx + 1, url.length()));
} }
break; break;
} }

View File

@ -68,6 +68,7 @@ public class XmlUtil {
private static volatile XMLInputFactory ourInputFactory; private static volatile XMLInputFactory ourInputFactory;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
private static volatile XMLOutputFactory ourOutputFactory; private static volatile XMLOutputFactory ourOutputFactory;
private static XMLOutputFactory ourFragmentOutputFactory;
private static final Map<String, Integer> VALID_ENTITY_NAMES; private static final Map<String, Integer> VALID_ENTITY_NAMES;
private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver(); private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver();
@ -1539,6 +1540,12 @@ public class XmlUtil {
return retVal; return retVal;
} }
public static XMLEventWriter createXmlFragmentWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory outputFactory = getOrCreateFragmentOutputFactory();
XMLEventWriter retVal = outputFactory.createXMLEventWriter(theWriter);
return retVal;
}
public static XMLEventWriter createXmlWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException { public static XMLEventWriter createXmlWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory outputFactory = getOrCreateOutputFactory(); XMLOutputFactory outputFactory = getOrCreateOutputFactory();
XMLEventWriter retVal = outputFactory.createXMLEventWriter(theWriter); XMLEventWriter retVal = outputFactory.createXMLEventWriter(theWriter);
@ -1589,40 +1596,55 @@ public class XmlUtil {
return ourInputFactory; return ourInputFactory;
} }
private static XMLOutputFactory getOrCreateFragmentOutputFactory() throws FactoryConfigurationError {
XMLOutputFactory retVal = ourFragmentOutputFactory;
if (retVal == null) {
retVal = createOutputFactory();
retVal.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
ourFragmentOutputFactory = retVal;
return retVal;
}
return retVal;
}
private static XMLOutputFactory getOrCreateOutputFactory() throws FactoryConfigurationError { private static XMLOutputFactory getOrCreateOutputFactory() throws FactoryConfigurationError {
if (ourOutputFactory == null) { if (ourOutputFactory == null) {
ourOutputFactory = createOutputFactory();
try {
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLOutputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
} catch (ClassNotFoundException e) {
// ok
}
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
if (!ourHaveLoggedStaxImplementation) {
logStaxImplementation(outputFactory.getClass());
}
/*
* Note that these properties are Woodstox specific and they cause a crash in environments where SJSXP is
* being used (e.g. glassfish) so we don't set them there.
*/
try {
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
if (outputFactory instanceof WstxOutputFactory) {
outputFactory.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
}
} catch (ClassNotFoundException e) {
ourLog.debug("WstxOutputFactory (Woodstox) not found on classpath");
}
ourOutputFactory = outputFactory;
} }
return ourOutputFactory; return ourOutputFactory;
} }
private static XMLOutputFactory createOutputFactory() throws FactoryConfigurationError {
try {
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLOutputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
} catch (ClassNotFoundException e) {
// ok
}
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
if (!ourHaveLoggedStaxImplementation) {
logStaxImplementation(outputFactory.getClass());
}
/*
* Note that these properties are Woodstox specific and they cause a crash in environments where SJSXP is
* being used (e.g. glassfish) so we don't set them there.
*/
try {
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
if (outputFactory instanceof WstxOutputFactory) {
outputFactory.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
}
} catch (ClassNotFoundException e) {
ourLog.debug("WstxOutputFactory (Woodstox) not found on classpath");
}
return outputFactory;
}
private static URL getRootUrlForClass(Class<?> cls) { private static URL getRootUrlForClass(Class<?> cls) {
ClassLoader classLoader = cls.getClassLoader(); ClassLoader classLoader = cls.getClassLoader();
String resource = cls.getName().replace('.', '/') + ".class"; String resource = cls.getName().replace('.', '/') + ".class";

View File

@ -28,6 +28,7 @@ import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.OperationOutcomeUtil;
/** /**
@ -107,7 +108,7 @@ public class ValidationResult {
location = null; location = null;
} }
String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null; String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null;
OperationOutcomeUtil.addIssue(myCtx, oo, severity, next.getMessage(), location); OperationOutcomeUtil.addIssue(myCtx, oo, severity, next.getMessage(), location, ExceptionHandlingInterceptor.PROCESSING);
} }
return oo; return oo;

View File

@ -47,6 +47,13 @@
<!-- Don't include in standard distribution --> <!-- Don't include in standard distribution -->
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-android</artifactId>
<version>1.3-SNAPSHOT</version>
<!-- Don't include in standard distribution -->
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
@ -100,6 +107,7 @@
<descriptor>${project.basedir}/src/assembly/hapi-fhir-standard-distribution.xml</descriptor> <descriptor>${project.basedir}/src/assembly/hapi-fhir-standard-distribution.xml</descriptor>
<descriptor>${project.basedir}/src/assembly/hapi-fhir-jpaserver-example.xml</descriptor> <descriptor>${project.basedir}/src/assembly/hapi-fhir-jpaserver-example.xml</descriptor>
<descriptor>${project.basedir}/src/assembly/hapi-fhir-android-distribution.xml</descriptor> <descriptor>${project.basedir}/src/assembly/hapi-fhir-android-distribution.xml</descriptor>
<descriptor>${project.basedir}/src/assembly/hapi-fhir-cli.xml</descriptor>
</descriptors> </descriptors>
</configuration> </configuration>
</execution> </execution>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml version="1.0" encoding="ISO-8859-1"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>android-distribution</id> <id>android-distribution</id>
@ -15,9 +16,38 @@
<directory>${project.basedir}/../hapi-fhir-android/target/</directory> <directory>${project.basedir}/../hapi-fhir-android/target/</directory>
<outputDirectory>/</outputDirectory> <outputDirectory>/</outputDirectory>
<includes> <includes>
<include>hapi-fhir-android-${project.version}*.jar</include> <include>hapi-fhir-android-${project.version}.jar</include>
<include>hapi-fhir-android-${project.version}-dstu.jar</include>
<include>hapi-fhir-android-${project.version}-dstu2.jar</include>
</includes> </includes>
</fileSet> </fileSet>
</fileSets> </fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>false</unpack>
<scope>provided</scope>
<useTransitiveDependencies>true</useTransitiveDependencies>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>ca.uhn.hapi.fhir:hapi-fhir-android</include>
</includes>
<excludes>
<exclude>ca.uhn.hapi.fhir:hapi-fhir-base</exclude>
<exclude>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu</exclude>
<exclude>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2</exclude>
<exclude>ca.uhn.hapi.fhir:hapi-fhir-structures-hl7org-dstu2</exclude>
<exclude>ca.uhn.hapi.fhir:hapi-fhir-validation-resources-dstu2</exclude>
<exclude>org.glassfish:javax.json</exclude>
<exclude>org.codehaus.woodstox:woodstox-core-asl</exclude>
<exclude>javax.xml.stream:stax-api</exclude>
<exclude>org.codehaus.woodstox:stax2-api</exclude>
<exclude>org.glassfish:javax.json</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly> </assembly>

View File

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src/test/java"> <classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes> <attributes>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" path="src/test/resources"/> <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="src" path="src/main/java"> <classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes> <attributes>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" path="src/main/resources"/> <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
<classpathentry kind="src" path="target/generated-sources/tinder"/> <classpathentry kind="src" output="target/classes" path="target/generated-sources/tinder"/>
<classpathentry kind="src" path="target/generated-resources/tinder"> <classpathentry kind="src" output="target/classes" path="target/generated-resources/tinder">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>

View File

@ -16,26 +16,6 @@
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder (1).launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (2).launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature> <nature>org.eclipse.jem.workbench.JavaEMFNature</nature>

View File

@ -174,8 +174,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.REFERENCE) { if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
continue; continue;
} }
@ -193,7 +192,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
String[] nextPathsSplit = nextPathsUnsplit.split("\\|"); String[] nextPathsSplit = nextPathsUnsplit.split("\\|");
for (String nextPath : nextPathsSplit) { for (String nextPath : nextPathsSplit) {
nextPath = nextPath.trim(); nextPath = nextPath.trim();
List<Class<? extends IBaseResource>> allowedTypesInField = null; List<Class<? extends IBaseResource>> allowedTypesInField = null;
for (Object nextObject : extractValues(nextPath, theResource)) { for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null) { if (nextObject == null) {
@ -219,7 +218,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
try { try {
resourceDefinition = getContext().getResourceDefinition(typeString); resourceDefinition = getContext().getResourceDefinition(typeString);
} catch (DataFormatException e) { } catch (DataFormatException e) {
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue()); throw new InvalidRequestException(
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue());
} }
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass(); Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
@ -253,15 +253,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
String resName = targetResourceDef.getName(); String resName = targetResourceDef.getName();
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
} }
if (!typeString.equals(target.getResourceType())) { if (!typeString.equals(target.getResourceType())) {
throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart() + " is actually of type " + target.getResourceType()); throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart()
+ " is actually of type " + target.getResourceType());
} }
/* /*
* Is the target type an allowable type of resource for the path where it is referenced? * Is the target type an allowable type of resource for the path where it is referenced?
*/ */
if (allowedTypesInField == null) { if (allowedTypesInField == null) {
BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPath); BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPath);
if (childDef instanceof RuntimeChildResourceDefinition) { if (childDef instanceof RuntimeChildResourceDefinition) {
@ -272,19 +273,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
allowedTypesInField.add(IBaseResource.class); allowedTypesInField.add(IBaseResource.class);
} }
} }
boolean acceptableLink = false; boolean acceptableLink = false;
for(Class<? extends IBaseResource> next : allowedTypesInField) { for (Class<? extends IBaseResource> next : allowedTypesInField) {
if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) { if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) {
acceptableLink = true; acceptableLink = true;
break; break;
} }
} }
if (!acceptableLink) { if (!acceptableLink) {
throw new UnprocessableEntityException("Invalid reference found at path '" + nextPath + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path"); throw new UnprocessableEntityException("Invalid reference found at path '" + nextPath + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path");
} }
nextEntity = new ResourceLink(nextPath, theEntity, target); nextEntity = new ResourceLink(nextPath, theEntity, target);
} else { } else {
if (!multiType) { if (!multiType) {
@ -683,11 +684,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
/** /**
* This method is called when an update to an existing resource detects that the resource supplied for update is * This method is called when an update to an existing resource detects that the resource supplied for update is missing a tag/profile/security label that the currently persisted resource holds.
* missing a tag/profile/security label that the currently persisted resource holds.
* <p> * <p>
* The default implementation removes any profile declarations, but leaves tags and security labels in place. * The default implementation removes any profile declarations, but leaves tags and security labels in place. Subclasses may choose to override and change this behaviour.
* Subclasses may choose to override and change this behaviour.
* </p> * </p>
* *
* @param theEntity * @param theEntity
@ -695,8 +694,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* @param theTag * @param theTag
* The tag * The tag
* @return Retturns <code>true</code> if the tag should be removed * @return Retturns <code>true</code> if the tag should be removed
* @see <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security * @see <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security Labels</a> for a description of the logic that the default behaviour folows.
* Labels</a> for a description of the logic that the default behaviour folows.
*/ */
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) { protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
if (theTag.getTag().getTagType() == TagTypeEnum.PROFILE) { if (theTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
@ -709,6 +707,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType); RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef); SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
if (paramMap.isEmpty()) {
throw new InvalidRequestException("Invalid match URL[" + theMatchUrl + "] - URL has no search parameters");
}
IFhirResourceDao<R> dao = getDao(theResourceType); IFhirResourceDao<R> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap); Set<Long> ids = dao.searchForIdsWithAndOr(paramMap);
@ -719,23 +721,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
public static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) { public static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
List<NameValuePair> parameters; List<NameValuePair> parameters;
try { String matchUrl = theMatchUrl;
String matchUrl = theMatchUrl; int questionMarkIndex = matchUrl.indexOf('?');
if (matchUrl.indexOf('?') == -1) { if (questionMarkIndex != -1) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: URL does not contain any parameters ('?' not detected)"); matchUrl = matchUrl.substring(questionMarkIndex + 1);
}
matchUrl = matchUrl.replace("|", "%7C");
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
matchUrl = matchUrl.replace("=>", "=%3E");
matchUrl = matchUrl.replace("=<", "=%3C");
parameters = URLEncodedUtils.parse(new URI(matchUrl), "UTF-8");
} catch (URISyntaxException e) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString());
} }
matchUrl = matchUrl.replace("|", "%7C");
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
matchUrl = matchUrl.replace("=>", "=%3E");
matchUrl = matchUrl.replace("=<", "=%3C");
if (matchUrl.contains(" ")) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - URL is invalid (must not contain spaces)");
}
parameters = URLEncodedUtils.parse((matchUrl), Constants.CHARSET_UTF8, '&');
ArrayListMultimap<String, QualifiedParamList> nameToParamLists = ArrayListMultimap.create(); ArrayListMultimap<String, QualifiedParamList> nameToParamLists = ArrayListMultimap.create();
for (NameValuePair next : parameters) { for (NameValuePair next : parameters) {
if (isBlank(next.getValue())) {
continue;
}
String paramName = next.getName(); String paramName = next.getName();
String qualifier = null; String qualifier = null;
for (int i = 0; i < paramMap.size(); i++) { for (int i = 0; i < paramMap.size(); i++) {
@ -779,11 +786,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
continue; continue;
} }
if (nextParamName.startsWith("_")) { if (nextParamName.startsWith("_")) {
continue; continue;
} }
RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName); RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName);
if (paramDef == null) { if (paramDef == null) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName); throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
@ -1030,8 +1037,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
} else if (theForHistoryOperation) { } else if (theForHistoryOperation) {
/* /*
* If the create and update times match, this was when the resource was created * If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
* so we should mark it as a POST. Otherwise, it's a PUT.
*/ */
Date published = theEntity.getPublished().getValue(); Date published = theEntity.getPublished().getValue();
Date updated = theEntity.getUpdated().getValue(); Date updated = theEntity.getUpdated().getValue();
@ -1041,7 +1047,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT); ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
} }
} }
res.setId(theEntity.getIdDt()); res.setId(theEntity.getIdDt());
ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion())); ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion()));
@ -1125,8 +1131,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) { protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime) {
/* /*
* This should be the very first thing.. * This should be the very first thing..
*/ */
@ -1134,14 +1141,15 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
validateResourceForStorage((T) theResource, theEntity); validateResourceForStorage((T) theResource, theEntity);
String resourceType = myContext.getResourceDefinition(theResource).getName(); String resourceType = myContext.getResourceDefinition(theResource).getName();
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) { if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]"); throw new UnprocessableEntityException(
"Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
} }
} }
if (theEntity.getPublished() == null) { if (theEntity.getPublished() == null) {
theEntity.setPublished(theUpdateTime); theEntity.setPublished(theUpdateTime);
} }
if (theUpdateHistory) { if (theUpdateHistory) {
final ResourceHistoryTable historyEntry = theEntity.toHistory(); final ResourceHistoryTable historyEntry = theEntity.toHistory();
myEntityManager.persist(historyEntry); myEntityManager.persist(historyEntry);
@ -1238,7 +1246,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setResourceLinks(links); theEntity.setResourceLinks(links);
theEntity.setHasLinks(links.isEmpty() == false); theEntity.setHasLinks(links.isEmpty() == false);
theEntity.setIndexStatus(INDEX_STATUS_INDEXED); theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
} else { } else {
populateResourceIntoEntity(theResource, theEntity); populateResourceIntoEntity(theResource, theEntity);
@ -1261,7 +1269,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} else { } else {
theEntity = myEntityManager.merge(theEntity); theEntity = myEntityManager.merge(theEntity);
postUpdate(theEntity, (T) theResource); postUpdate(theEntity, (T) theResource);
} }
@ -1354,37 +1362,37 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
/** /**
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the * Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time.
* first time.
* *
* @param theEntity * @param theEntity
* The resource * The resource
* @param theResource The resource being persisted * @param theResource
* The resource being persisted
*/ */
protected void postUpdate(ResourceTable theEntity, T theResource) { protected void postUpdate(ResourceTable theEntity, T theResource) {
// nothing // nothing
} }
/** /**
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the * Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time.
* first time.
* *
* @param theEntity * @param theEntity
* The resource * The resource
* @param theResource The resource being persisted * @param theResource
* The resource being persisted
*/ */
protected void postPersist(ResourceTable theEntity, T theResource) { protected void postPersist(ResourceTable theEntity, T theResource) {
// nothing // nothing
} }
/** /**
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow * This method is invoked immediately before storing a new resource, or an update to an existing resource to allow the DAO to ensure that it is valid for persistence. By default, checks for the
* the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects * "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
* resources which have it. Subclasses should call the superclass implementation to preserve this check.
* *
* @param theResource * @param theResource
* The resource that is about to be persisted * The resource that is about to be persisted
* @param theEntityToSave TODO * @param theEntityToSave
* TODO
*/ */
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) { protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
IResource res = (IResource) theResource; IResource res = (IResource) theResource;

View File

@ -74,6 +74,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition; import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.BaseTag; import ca.uhn.fhir.jpa.entity.BaseTag;
@ -128,6 +129,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
@ -148,6 +150,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
@Autowired @Autowired
private PlatformTransactionManager myPlatformTransactionManager; private PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private DaoConfig myDaoConfig;
private String myResourceName; private String myResourceName;
private Class<T> myResourceType; private Class<T> myResourceType;
private String mySecondaryPrimaryKeyParamName; private String mySecondaryPrimaryKeyParamName;
@ -1308,7 +1313,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
throw new InvalidRequestException("Trying to delete " + theId + " but this is not the current version"); throw new InvalidRequestException("Trying to delete " + theId + " but this is not the current version");
} }
validateOkToDeleteOrThrowPreconditionFailedException(entity); validateOkToDeleteOrThrowResourceVersionConflictException(entity);
// Notify interceptors // Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType()); ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
@ -1331,26 +1336,31 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (resource.isEmpty()) { if (resource.isEmpty()) {
throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "unableToDeleteNotFound", theUrl)); throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "unableToDeleteNotFound", theUrl));
} else if (resource.size() > 1) { } else if (resource.size() > 1) {
throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "transactionOperationWithMultipleMatchFailure", "DELETE", theUrl, resource.size())); if (myDaoConfig.isAllowMultipleDelete() == false) {
throw new PreconditionFailedException(getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "transactionOperationWithMultipleMatchFailure", "DELETE", theUrl, resource.size()));
}
} }
Long pid = resource.iterator().next(); for (Long pid : resource) {
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid); ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
validateOkToDeleteOrThrowPreconditionFailedException(entity); validateOkToDeleteOrThrowResourceVersionConflictException(entity);
// Notify interceptors // Notify interceptors
IdDt idToDelete = entity.getIdDt(); IdDt idToDelete = entity.getIdDt();
ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType()); ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType());
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
// Perform delete // Perform delete
Date updateTime = new Date(); Date updateTime = new Date();
ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime); updateEntity(null, entity, true, updateTime, updateTime);
notifyWriteCompleted(); notifyWriteCompleted();
ourLog.info("Processed delete on {} in {}ms", theUrl, w.getMillisAndRestart()); }
return toMethodOutcome(savedEntity, null);
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[] {theUrl, resource.size(), w.getMillisAndRestart()});
return new DaoMethodOutcome();
} }
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime) { private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime) {
@ -1615,7 +1625,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
/** /**
* THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet()) * THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet())
*/ */
private HashSet<Long> loadReverseIncludes(Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode) { private HashSet<Long> loadReverseIncludes(Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode, EverythingModeEnum theEverythingModeEnum) {
if (theMatches.size() == 0) { if (theMatches.size() == 0) {
return new HashSet<Long>(); return new HashSet<Long>();
} }
@ -1632,6 +1642,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
boolean addedSomeThisRound; boolean addedSomeThisRound;
do { do {
HashSet<Long> pidsToInclude = new HashSet<Long>(); HashSet<Long> pidsToInclude = new HashSet<Long>();
Set<Long> nextRoundOmit = new HashSet<Long>();
for (Iterator<Include> iter = includes.iterator(); iter.hasNext();) { for (Iterator<Include> iter = includes.iterator(); iter.hasNext();) {
Include nextInclude = iter.next(); Include nextInclude = iter.next();
@ -1648,6 +1659,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
List<ResourceLink> results = q.getResultList(); List<ResourceLink> results = q.getResultList();
for (ResourceLink resourceLink : results) { for (ResourceLink resourceLink : results) {
if (theReverseMode) { if (theReverseMode) {
if (theEverythingModeEnum == EverythingModeEnum.ENCOUNTER) {
if (resourceLink.getSourcePath().equals("Encounter.subject") || resourceLink.getSourcePath().equals("Encounter.patient")) {
nextRoundOmit.add(resourceLink.getSourceResourcePid());
}
}
pidsToInclude.add(resourceLink.getSourceResourcePid()); pidsToInclude.add(resourceLink.getSourceResourcePid());
} else { } else {
pidsToInclude.add(resourceLink.getTargetResourcePid()); pidsToInclude.add(resourceLink.getTargetResourcePid());
@ -1702,6 +1718,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
theMatches.add(next); theMatches.add(next);
} }
} }
pidsToInclude.removeAll(nextRoundOmit);
addedSomeThisRound = allAdded.addAll(pidsToInclude); addedSomeThisRound = allAdded.addAll(pidsToInclude);
nextRoundMatches = pidsToInclude; nextRoundMatches = pidsToInclude;
} while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound); } while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound);
@ -2024,10 +2043,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
// Load _include and _revinclude before filter and sort in everything mode // Load _include and _revinclude before filter and sort in everything mode
if (theParams.isEverythingMode() == true) { if (theParams.getEverythingMode() != null) {
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
loadPids.addAll(loadReverseIncludes(loadPids, theParams.getRevIncludes(), true)); loadPids.addAll(loadReverseIncludes(loadPids, theParams.getRevIncludes(), true, theParams.getEverythingMode()));
loadPids.addAll(loadReverseIncludes(loadPids, theParams.getIncludes(), false)); loadPids.addAll(loadReverseIncludes(loadPids, theParams.getIncludes(), false, theParams.getEverythingMode()));
} }
} }
@ -2066,9 +2085,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// Load _revinclude resources // Load _revinclude resources
final Set<Long> revIncludedPids; final Set<Long> revIncludedPids;
if (theParams.isEverythingMode() == false) { if (theParams.getEverythingMode() == null) {
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true); revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true, null);
} else { } else {
revIncludedPids = new HashSet<Long>(); revIncludedPids = new HashSet<Long>();
} }
@ -2095,9 +2114,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
List<Long> pidsSubList = pids.subList(theFromIndex, theToIndex); List<Long> pidsSubList = pids.subList(theFromIndex, theToIndex);
// Load includes // Load includes
if (!theParams.isEverythingMode()) { if (theParams.getEverythingMode()==null) {
pidsSubList = new ArrayList<Long>(pidsSubList); pidsSubList = new ArrayList<Long>(pidsSubList);
revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false)); revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null));
} }
// Execute the query and make sure we return distinct results // Execute the query and make sure we return distinct results
@ -2502,7 +2521,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
} }
protected void validateOkToDeleteOrThrowPreconditionFailedException(ResourceTable theEntity) { protected void validateOkToDeleteOrThrowResourceVersionConflictException(ResourceTable theEntity) {
TypedQuery<ResourceLink> query = myEntityManager.createQuery("SELECT l FROM ResourceLink l WHERE l.myTargetResourcePid = :target_pid", ResourceLink.class); TypedQuery<ResourceLink> query = myEntityManager.createQuery("SELECT l FROM ResourceLink l WHERE l.myTargetResourcePid = :target_pid", ResourceLink.class);
query.setParameter("target_pid", theEntity.getId()); query.setParameter("target_pid", theEntity.getId());
query.setMaxResults(1); query.setMaxResults(1);
@ -2516,7 +2535,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
String sourceId = link.getSourceResource().getIdDt().toUnqualifiedVersionless().getValue(); String sourceId = link.getSourceResource().getIdDt().toUnqualifiedVersionless().getValue();
String sourcePath = link.getSourcePath(); String sourcePath = link.getSourcePath();
throw new PreconditionFailedException( throw new ResourceVersionConflictException(
"Unable to delete " + targetId + " because at least one resource has a reference to this resource. First reference found was resource " + sourceId + " in path " + sourcePath); "Unable to delete " + targetId + " because at least one resource has a reference to this resource. First reference found was resource " + sourceId + " in path " + sourcePath);
} }

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
public class DaoConfig { public class DaoConfig {
private boolean myAllowMultipleDelete;
private int myHardSearchLimit = 1000; private int myHardSearchLimit = 1000;
private int myHardTagListLimit = 1000; private int myHardTagListLimit = 1000;
private int myIncludeLimit = 2000; private int myIncludeLimit = 2000;
@ -74,6 +75,14 @@ public class DaoConfig {
return mySubscriptionPollDelay; return mySubscriptionPollDelay;
} }
public Long getSubscriptionPurgeInactiveAfterMillis() {
return mySubscriptionPurgeInactiveAfterMillis;
}
public boolean isAllowMultipleDelete() {
return myAllowMultipleDelete;
}
/** /**
* See {@link #setSubscriptionEnabled(boolean)} * See {@link #setSubscriptionEnabled(boolean)}
*/ */
@ -81,6 +90,10 @@ public class DaoConfig {
return mySubscriptionEnabled; return mySubscriptionEnabled;
} }
public void setAllowMultipleDelete(boolean theAllowMultipleDelete) {
myAllowMultipleDelete = theAllowMultipleDelete;
}
public void setHardSearchLimit(int theHardSearchLimit) { public void setHardSearchLimit(int theHardSearchLimit) {
myHardSearchLimit = theHardSearchLimit; myHardSearchLimit = theHardSearchLimit;
} }
@ -144,10 +157,6 @@ public class DaoConfig {
mySubscriptionPollDelay = theSubscriptionPollDelay; mySubscriptionPollDelay = theSubscriptionPollDelay;
} }
public void setSubscriptionPurgeInactiveAfterSeconds(int theSeconds) {
setSubscriptionPurgeInactiveAfterMillis(theSeconds * DateUtils.MILLIS_PER_SECOND);
}
public void setSubscriptionPurgeInactiveAfterMillis(Long theMillis) { public void setSubscriptionPurgeInactiveAfterMillis(Long theMillis) {
if (theMillis != null) { if (theMillis != null) {
Validate.exclusiveBetween(0, Long.MAX_VALUE, theMillis); Validate.exclusiveBetween(0, Long.MAX_VALUE, theMillis);
@ -155,8 +164,8 @@ public class DaoConfig {
mySubscriptionPurgeInactiveAfterMillis = theMillis; mySubscriptionPurgeInactiveAfterMillis = theMillis;
} }
public Long getSubscriptionPurgeInactiveAfterMillis() { public void setSubscriptionPurgeInactiveAfterSeconds(int theSeconds) {
return mySubscriptionPurgeInactiveAfterMillis; setSubscriptionPurgeInactiveAfterMillis(theSeconds * DateUtils.MILLIS_PER_SECOND);
} }
} }

View File

@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.DefaultProfileValidationSupport; import ca.uhn.fhir.validation.DefaultProfileValidationSupport;
@ -105,11 +106,11 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
final ResourceTable entity = readEntityLatestVersion(theId); final ResourceTable entity = readEntityLatestVersion(theId);
OperationOutcome oo = new OperationOutcome(); OperationOutcome oo = new OperationOutcome();
try { try {
validateOkToDeleteOrThrowPreconditionFailedException(entity); validateOkToDeleteOrThrowResourceVersionConflictException(entity);
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Ok to delete"); oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Ok to delete");
} catch (PreconditionFailedException e) { } catch (ResourceVersionConflictException e) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDiagnostics(e.getMessage()); oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDiagnostics(e.getMessage());
throw new PreconditionFailedException(e.getMessage(), oo); throw new ResourceVersionConflictException(e.getMessage(), oo);
} }
return new MethodOutcome(new IdDt(theId.getValue()), oo); return new MethodOutcome(new IdDt(theId.getValue()), oo);
} }

View File

@ -0,0 +1,63 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Encounter;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UnsignedIntDt;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
public class FhirResourceDaoEncounterDstu2 extends FhirResourceDaoDstu2<Encounter>implements IFhirResourceDaoEncounter<Encounter> {
@Override
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setEverythingMode(EverythingModeEnum.ENCOUNTER);
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdated);
if (theId != null) {
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
ca.uhn.fhir.rest.server.IBundleProvider retVal = search(paramMap);
return retVal;
}
@Override
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
}
}

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -36,7 +37,7 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>implements IFhirResourceDaoPatient<Patient> { public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>implements IFhirResourceDaoPatient<Patient> {
@Override @Override
public IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) { public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
@ -44,12 +45,19 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setEverythingMode(true); paramMap.setEverythingMode(EverythingModeEnum.PATIENT);
paramMap.setSort(theSort); paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdated); paramMap.setLastUpdated(theLastUpdated);
paramMap.add("_id", new StringParam(theId.getIdPart())); if (theId != null) {
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
ca.uhn.fhir.rest.server.IBundleProvider retVal = search(paramMap); ca.uhn.fhir.rest.server.IBundleProvider retVal = search(paramMap);
return retVal; return retVal;
} }
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return patientInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
}
} }

View File

@ -25,6 +25,8 @@ import javax.annotation.PostConstruct;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
@ -41,7 +43,10 @@ import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDstu2<QuestionnaireResponse> { public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDstu2<QuestionnaireResponse> {
@Autowired
@Qualifier("myFhirContextDstu2Hl7Org")
private FhirContext myRefImplCtx; private FhirContext myRefImplCtx;
private Boolean myValidateResponses; private Boolean myValidateResponses;
/** /**
@ -52,7 +57,6 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs
try { try {
Class.forName("org.hl7.fhir.instance.model.QuestionnaireResponse"); Class.forName("org.hl7.fhir.instance.model.QuestionnaireResponse");
myValidateResponses = true; myValidateResponses = true;
myRefImplCtx = FhirContext.forDstu2Hl7Org();
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
myValidateResponses = Boolean.FALSE; myValidateResponses = Boolean.FALSE;
} }

View File

@ -56,6 +56,7 @@ import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.dstu2.resource.Subscription; import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum; import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.SortSpec;
@ -129,7 +130,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
ourLog.trace("Skipping search for subscription"); ourLog.trace("Skipping search for subscription");
return; return;
} }
ourLog.info("Subscription search from {} to {}", start, end);
ourLog.debug("Subscription {} search from {} to {}", new Object[] { subscription.getId().getIdPart(), new InstantDt(new Date(start)), new InstantDt(new Date(end)) });
DateRangeParam range = new DateRangeParam(); DateRangeParam range = new DateRangeParam();
range.setLowerBound(new DateParam(QuantityCompararatorEnum.GREATERTHAN, start)); range.setLowerBound(new DateParam(QuantityCompararatorEnum.GREATERTHAN, start));
@ -176,7 +178,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
} }
@Override @Override
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) { protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime) {
ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime); ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime);
Subscription resource = (Subscription) theResource; Subscription resource = (Subscription) theResource;
@ -278,16 +281,17 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
@Override @Override
public void purgeInactiveSubscriptions() { public void purgeInactiveSubscriptions() {
Long purgeInactiveAfterMillis = getConfig().getSubscriptionPurgeInactiveAfterMillis(); Long purgeInactiveAfterMillis = getConfig().getSubscriptionPurgeInactiveAfterMillis();
if (getConfig().isSubscriptionEnabled()==false || purgeInactiveAfterMillis == null) { if (getConfig().isSubscriptionEnabled() == false || purgeInactiveAfterMillis == null) {
return; return;
} }
Date cutoff = new Date(System.currentTimeMillis() - purgeInactiveAfterMillis); Date cutoff = new Date(System.currentTimeMillis() - purgeInactiveAfterMillis);
Collection<SubscriptionTable> toPurge = mySubscriptionTableDao.findInactiveBeforeCutoff(cutoff); Collection<SubscriptionTable> toPurge = mySubscriptionTableDao.findInactiveBeforeCutoff(cutoff);
for (SubscriptionTable subscriptionTable : toPurge) { for (SubscriptionTable subscriptionTable : toPurge) {
final IdDt subscriptionId = subscriptionTable.getSubscriptionResource().getIdDt(); final IdDt subscriptionId = subscriptionTable.getSubscriptionResource().getIdDt();
ourLog.info("Deleting inactive subscription {} - Created {}, last client poll {}", new Object[] { subscriptionId.toUnqualified(), subscriptionTable.getCreated(), subscriptionTable.getLastClientPoll() }); ourLog.info("Deleting inactive subscription {} - Created {}, last client poll {}",
new Object[] { subscriptionId.toUnqualified(), subscriptionTable.getCreated(), subscriptionTable.getLastClientPoll() });
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallback<Void>() { txTemplate.execute(new TransactionCallback<Void>() {

View File

@ -28,10 +28,15 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.commons.codec.binary.StringUtils; import org.apache.commons.codec.binary.StringUtils;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt;
@ -46,32 +51,83 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.validation.DefaultProfileValidationSupport;
import ca.uhn.fhir.validation.ValidationSupportChain;
public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>implements IFhirResourceDaoValueSet<ValueSet> { public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>implements IFhirResourceDaoValueSet<ValueSet> {
@Override @Autowired
public ValueSet expand(IIdType theId, StringDt theFilter) { private IJpaValidationSupport myJpaValidationSupport;
ValueSet retVal = new ValueSet();
retVal.setDate(DateTimeDt.withCurrentTime()); private ValidationSupportChain myValidationSupport;
@Autowired
@Qualifier("myFhirContextDstu2Hl7Org")
private FhirContext myRiCtx;
private DefaultProfileValidationSupport myDefaultProfileValidationSupport;
@Override
@PostConstruct
public void postConstruct() {
super.postConstruct();
myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
myValidationSupport = new ValidationSupportChain(myDefaultProfileValidationSupport, myJpaValidationSupport);
}
@Override
public ValueSet expand(IIdType theId, String theFilter) {
BaseHasResource sourceEntity = readEntity(theId); BaseHasResource sourceEntity = readEntity(theId);
if (sourceEntity == null) { if (sourceEntity == null) {
throw new ResourceNotFoundException(theId); throw new ResourceNotFoundException(theId);
} }
ValueSet source = (ValueSet) toResource(sourceEntity, false); ValueSet source = (ValueSet) toResource(sourceEntity, false);
return expand(source, theFilter);
}
@Override
public ValueSet expandByIdentifier(String theUri, String theFilter) {
if (isBlank(theUri)) {
throw new InvalidRequestException("URI must not be blank or missing");
}
ValueSet source;
org.hl7.fhir.instance.model.ValueSet defaultValueSet = myDefaultProfileValidationSupport.fetchResource(myRiCtx, org.hl7.fhir.instance.model.ValueSet.class, theUri);
if (defaultValueSet != null) {
source = getContext().newJsonParser().parseResource(ValueSet.class, myRiCtx.newJsonParser().encodeResourceToString(defaultValueSet));
} else {
IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
if (ids.size() == 0) {
throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
}
source = (ValueSet) ids.getResources(0, 1).get(0);
}
return expand(source, theFilter);
}
@Override
public ValueSet expand(ValueSet source, String theFilter) {
ValueSet retVal = new ValueSet();
retVal.setDate(DateTimeDt.withCurrentTime());
/* /*
* Add composed concepts * Add composed concepts
*/ */
for (ComposeInclude nextInclude : source.getCompose().getInclude()) { for (ComposeInclude nextInclude : source.getCompose().getInclude()) {
for (ComposeIncludeConcept next : nextInclude.getConcept()) { for (ComposeIncludeConcept next : nextInclude.getConcept()) {
if (theFilter == null || theFilter.isEmpty()) { if (isBlank(theFilter)) {
addCompose(retVal, nextInclude.getSystem(), next.getCode(), next.getDisplay()); addCompose(retVal, nextInclude.getSystem(), next.getCode(), next.getDisplay());
} else { } else {
String filter = theFilter.getValue().toLowerCase(); String filter = theFilter.toLowerCase();
if (next.getDisplay().toLowerCase().contains(filter) || next.getCode().toLowerCase().contains(filter)) { if (next.getDisplay().toLowerCase().contains(filter) || next.getCode().toLowerCase().contains(filter)) {
addCompose(retVal, nextInclude.getSystem(), next.getCode(), next.getDisplay()); addCompose(retVal, nextInclude.getSystem(), next.getCode(), next.getDisplay());
} }
@ -88,14 +144,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
} }
return retVal; return retVal;
} }
private void addCompose(StringDt theFilter, ValueSet theValueSetToPopulate, ValueSet theSourceValueSet, CodeSystemConcept theConcept) { private void addCompose(String theFilter, ValueSet theValueSetToPopulate, ValueSet theSourceValueSet, CodeSystemConcept theConcept) {
if (theFilter == null || theFilter.isEmpty()) { if (isBlank(theFilter)) {
addCompose(theValueSetToPopulate, theSourceValueSet.getCodeSystem().getSystem(), theConcept.getCode(), theConcept.getDisplay()); addCompose(theValueSetToPopulate, theSourceValueSet.getCodeSystem().getSystem(), theConcept.getCode(), theConcept.getDisplay());
} else { } else {
String filter = theFilter.getValue().toLowerCase(); String filter = theFilter.toLowerCase();
if (theConcept.getDisplay().toLowerCase().contains(filter) || theConcept.getCode().toLowerCase().contains(filter)) { if (theConcept.getDisplay().toLowerCase().contains(filter) || theConcept.getCode().toLowerCase().contains(filter)) {
addCompose(theValueSetToPopulate, theSourceValueSet.getCodeSystem().getSystem(), theConcept.getCode(), theConcept.getDisplay()); addCompose(theValueSetToPopulate, theSourceValueSet.getCodeSystem().getSystem(), theConcept.getCode(), theConcept.getDisplay());
} }
@ -133,7 +188,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
boolean haveIdentifierParam = theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false; boolean haveIdentifierParam = theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false;
if (theId != null) { if (theId != null) {
valueSetIds = Collections.singletonList((IIdType) theId); valueSetIds = Collections.singletonList(theId);
} else if (haveIdentifierParam) { } else if (haveIdentifierParam) {
Set<Long> ids = searchForIds(ValueSet.SP_IDENTIFIER, new TokenParam(null, theValueSetIdentifier.getValue())); Set<Long> ids = searchForIds(ValueSet.SP_IDENTIFIER, new TokenParam(null, theValueSetIdentifier.getValue()));
valueSetIds = new ArrayList<IIdType>(); valueSetIds = new ArrayList<IIdType>();

View File

@ -222,6 +222,17 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
if (res != null) { if (res != null) {
nextResourceId = res.getId(); nextResourceId = res.getId();
if (nextResourceId.hasIdPart() == false) {
if (isNotBlank(nextEntry.getFullUrl())) {
nextResourceId = new IdDt(nextEntry.getFullUrl());
}
}
if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+\\:.*") && !isPlaceholder(nextResourceId)) {
throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'");
}
if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) { if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) {
nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart()); nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart());
res.setId(nextResourceId); res.setId(nextResourceId);

View File

@ -0,0 +1,39 @@
package ca.uhn.fhir.jpa.dao;
import javax.servlet.http.HttpServletRequest;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UnsignedIntDt;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
public interface IFhirResourceDaoEncounter<T extends IBaseResource> extends IFhirResourceDao<T> {
IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdate, SortSpec theSort);
IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec);
}

View File

@ -32,6 +32,8 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
public interface IFhirResourceDaoPatient<T extends IBaseResource> extends IFhirResourceDao<T> { public interface IFhirResourceDaoPatient<T extends IBaseResource> extends IFhirResourceDao<T> {
IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdate, SortSpec theSort); IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdate, SortSpec theSort);
IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec);
} }

View File

@ -32,8 +32,12 @@ import ca.uhn.fhir.model.primitive.UriDt;
public interface IFhirResourceDaoValueSet<T extends IBaseResource> extends IFhirResourceDao<T> { public interface IFhirResourceDaoValueSet<T extends IBaseResource> extends IFhirResourceDao<T> {
ValueSet expand(IIdType theId, StringDt theFilter); ValueSet expand(IIdType theId, String theFilter);
ValueSet expand(ValueSet theSource, String theFilter);
ValueSet expandByIdentifier(String theUri, String theFilter);
ValidateCodeResult validateCode(UriDt theValueSetIdentifier, IIdType theId, CodeDt theCode, UriDt theSystem, StringDt theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept); ValidateCodeResult validateCode(UriDt theValueSetIdentifier, IIdType theId, CodeDt theCode, UriDt theSystem, StringDt theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept);
public class ValidateCodeResult { public class ValidateCodeResult {

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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.validation.IValidationSupport;
public interface IJpaValidationSupport extends IValidationSupport {
}

View File

@ -30,13 +30,14 @@ import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.validation.IValidationSupport;
public class JpaValidationSupportDstu2 implements IValidationSupport { public class JpaValidationSupportDstu2 implements IJpaValidationSupport {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportDstu2.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportDstu2.class);
private FhirContext myRiCtx = FhirContext.forDstu2Hl7Org(); @Autowired
@Qualifier("myFhirContextDstu2Hl7Org")
private FhirContext myRiCtx;
@Autowired @Autowired
@Qualifier("myStructureDefinitionDaoDstu2") @Qualifier("myStructureDefinitionDaoDstu2")

View File

@ -52,6 +52,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
import ca.uhn.fhir.model.base.composite.BaseHumanNameDt; import ca.uhn.fhir.model.base.composite.BaseHumanNameDt;
import ca.uhn.fhir.model.dstu2.composite.AddressDt; import ca.uhn.fhir.model.dstu2.composite.AddressDt;
import ca.uhn.fhir.model.dstu2.composite.BoundCodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.BoundCodeableConceptDt;
@ -71,6 +72,7 @@ import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.valueset.RestfulSecurityServiceEnum; import ca.uhn.fhir.model.dstu2.valueset.RestfulSecurityServiceEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt; import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
@ -79,7 +81,7 @@ import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor { public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorDstu2.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorDstu2.class);
public SearchParamExtractorDstu2(FhirContext theContext) { public SearchParamExtractorDstu2(FhirContext theContext) {
super(theContext); super(theContext);
} }
@ -115,7 +117,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable,
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
@ -175,7 +178,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable,
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) { public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) {
@ -222,12 +226,17 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
nextValue = newValue; nextValue = newValue;
/* /*
* @SuppressWarnings("unchecked") PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>>) * @SuppressWarnings("unchecked") PhysicsUnit<? extends
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if (unit.isCompatible(UCUM.DAY)) { * org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends
* org.unitsofmeasurement.quantity.Quantity<?>>)
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if
* (unit.isCompatible(UCUM.DAY)) {
* *
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY); * @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit =
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); DurationDt newValue = new DurationDt(); newValue.setSystem(UCUM_NS); * (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY); double
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue; } * dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); DurationDt newValue =
* new DurationDt(); newValue.setSystem(UCUM_NS); newValue.setCode(UCUM.DAY.getSymbol());
* newValue.setValue(dayValue); nextValue=newValue; }
*/ */
} }
} }
@ -269,7 +278,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable,
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) {
@ -303,8 +313,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
continue; continue;
} }
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValue.getValueElement().getValue(), ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValue.getValueElement().getValue(), nextValue.getSystemElement().getValueAsString(), nextValue.getCode());
nextValue.getSystemElement().getValueAsString(), nextValue.getCode());
nextEntity.setResource(theEntity); nextEntity.setResource(theEntity);
retVal.add(nextEntity); retVal.add(nextEntity);
} else { } else {
@ -323,7 +332,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable,
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) {
@ -404,7 +414,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable,
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) { public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) {
@ -464,11 +475,11 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
systems.add(system); systems.add(system);
codes.add(value); codes.add(value);
} }
if (isNotBlank(nextValue.getType().getText())) { if (isNotBlank(nextValue.getType().getText())) {
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText()); addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
} }
} else if (nextObject instanceof ContactPointDt) { } else if (nextObject instanceof ContactPointDt) {
ContactPointDt nextValue = (ContactPointDt) nextObject; ContactPointDt nextValue = (ContactPointDt) nextObject;
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
@ -481,6 +492,14 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
} }
systems.add(nextValue.getSystemElement().getValueAsString()); systems.add(nextValue.getSystemElement().getValueAsString());
codes.add(nextValue.getValueElement().getValue()); codes.add(nextValue.getValueElement().getValue());
} else if (nextObject instanceof BoundCodeDt) {
BoundCodeDt<?> obj = (BoundCodeDt<?>) nextObject;
String system = extractSystem(obj);
String code = obj.getValue();
if (isNotBlank(code)) {
systems.add(system);
codes.add(code);
}
} else if (nextObject instanceof IPrimitiveDatatype<?>) { } else if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject; IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
@ -503,7 +522,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef); extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
} else if (nextObject instanceof RestSecurity) { } else if (nextObject instanceof RestSecurity) {
// Conformance.security search param points to something kind of useless right now - This should probably be fixed. // Conformance.security search param points to something kind of useless right now - This should probably
// be fixed.
RestSecurity sec = (RestSecurity) nextObject; RestSecurity sec = (RestSecurity) nextObject;
for (BoundCodeableConceptDt<RestfulSecurityServiceEnum> nextCC : sec.getService()) { for (BoundCodeableConceptDt<RestfulSecurityServiceEnum> nextCC : sec.getService()) {
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef); extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
@ -588,9 +608,9 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
} }
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue()); ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue()); ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
nextEntity.setResource(theEntity); nextEntity.setResource(theEntity);
retVal.add(nextEntity); retVal.add(nextEntity);
} else { } else {
@ -605,17 +625,14 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
return retVal; return retVal;
} }
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConceptDt theCodeableConcept, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConceptDt theCodeableConcept, ResourceTable theEntity,
Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
for (CodingDt nextCoding : theCodeableConcept.getCoding()) { for (CodingDt nextCoding : theCodeableConcept.getCoding()) {
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding); extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
} }
} }
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate, private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef, CodingDt nextCoding) {
RuntimeSearchParam theParameterDef, CodingDt nextCoding) {
if (nextCoding != null && !nextCoding.isEmpty()) { if (nextCoding != null && !nextCoding.isEmpty()) {
String nextSystem = nextCoding.getSystemElement().getValueAsString(); String nextSystem = nextCoding.getSystemElement().getValueAsString();
@ -632,4 +649,12 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
} }
} }
private static <T extends Enum<?>> String extractSystem(BoundCodeDt<T> theBoundCode) {
if (theBoundCode.getValueAsEnum() != null) {
IValueSetEnumBinder<T> binder = theBoundCode.getBinder();
return binder.toSystemString(theBoundCode.getValueAsEnum());
}
return null;
}
} }

View File

@ -42,7 +42,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Integer myCount; private Integer myCount;
private boolean myEverythingMode = false; private EverythingModeEnum myEverythingMode = null;
private Set<Include> myIncludes; private Set<Include> myIncludes;
private DateRangeParam myLastUpdated; private DateRangeParam myLastUpdated;
private Set<Include> myRevIncludes; private Set<Include> myRevIncludes;
@ -124,7 +124,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return mySort; return mySort;
} }
public boolean isEverythingMode() { public EverythingModeEnum getEverythingMode() {
return myEverythingMode; return myEverythingMode;
} }
@ -132,7 +132,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
myCount = theCount; myCount = theCount;
} }
public void setEverythingMode(boolean theConsolidateMatches) { public void setEverythingMode(EverythingModeEnum theConsolidateMatches) {
myEverythingMode = theConsolidateMatches; myEverythingMode = theConsolidateMatches;
} }
@ -164,4 +164,8 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return b.toString(); return b.toString();
} }
public enum EverythingModeEnum {
PATIENT, ENCOUNTER
}
} }

View File

@ -20,45 +20,79 @@ package ca.uhn.fhir.jpa.provider;
* #L% * #L%
*/ */
import java.util.Collections; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoEncounter;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu2.resource.Encounter; import ca.uhn.fhir.model.dstu2.resource.Encounter;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.Constants;
public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDstu2<Encounter> { public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDstu2<Encounter> {
/**
* Encounter/123/$everything
*/
//@formatter:off
@Operation(name = "everything", idempotent = true)
public ca.uhn.fhir.rest.server.IBundleProvider EncounterInstanceEverything(
@Operation(name="everything", idempotent=true) javax.servlet.http.HttpServletRequest theServletRequest,
public ca.uhn.fhir.rest.server.IBundleProvider everything(
javax.servlet.http.HttpServletRequest theServletRequest, @IdParam
@IdParam ca.uhn.fhir.model.primitive.IdDt theId, ca.uhn.fhir.model.primitive.IdDt theId,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name="_count") ca.uhn.fhir.model.primitive.UnsignedIntDt theCount @OperationParam(name = Constants.PARAM_COUNT)
){ ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@Sort
SortSpec theSortSpec
) {
//@formatter:on
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
SearchParameterMap paramMap = new SearchParameterMap(); return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setEverythingMode(true);
paramMap.add("_id", new StringParam(theId.getIdPart()));
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap);
return retVal;
} finally { } finally {
endRequest(theServletRequest); endRequest(theServletRequest);
} }}
/**
* /Encounter/$everything
*/
//@formatter:off
@Operation(name = "everything", idempotent = true)
public ca.uhn.fhir.rest.server.IBundleProvider EncounterTypeEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@Sort
SortSpec theSortSpec
) {
//@formatter:on
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}
} }

View File

@ -33,8 +33,13 @@ import ca.uhn.fhir.rest.server.Constants;
public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu2<Patient> { public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu2<Patient> {
/**
* Patient/123/$everything
*/
//@formatter:off
@Operation(name = "everything", idempotent = true) @Operation(name = "everything", idempotent = true)
public ca.uhn.fhir.rest.server.IBundleProvider everything( public ca.uhn.fhir.rest.server.IBundleProvider patientInstanceEverything(
javax.servlet.http.HttpServletRequest theServletRequest, javax.servlet.http.HttpServletRequest theServletRequest,
@ -49,17 +54,46 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated, DateRangeParam theLastUpdated,
// @OperationParam(name = Constants.PARAM_SORT, min=0, max=1)
@Sort @Sort
SortSpec theSortSpec SortSpec theSortSpec
) { ) {
//@formatter:on
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
return ((IFhirResourceDaoPatient<Patient>)getDao()).everything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec); return ((IFhirResourceDaoPatient<Patient>)getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
} finally { } finally {
endRequest(theServletRequest); endRequest(theServletRequest);
} }}
/**
* /Patient/$everything
*/
//@formatter:off
@Operation(name = "everything", idempotent = true)
public ca.uhn.fhir.rest.server.IBundleProvider patientTypeEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@Sort
SortSpec theSortSpec
) {
//@formatter:on
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>)getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}
} }

View File

@ -45,19 +45,63 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
@Operation(name = "$expand", idempotent = true) @Operation(name = "$expand", idempotent = true)
public ValueSet everything( public ValueSet everything(
HttpServletRequest theServletRequest, HttpServletRequest theServletRequest,
@IdParam IdDt theId,
@OperationParam(name = "filter") StringDt theFilter) { @IdParam IdDt theId,
@OperationParam(name = "filter", min=0, max=1) StringDt theFilter) {
//@formatter:on //@formatter:on
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
IFhirResourceDaoValueSet<ValueSet> dao = (IFhirResourceDaoValueSet<ValueSet>) getDao(); IFhirResourceDaoValueSet<ValueSet> dao = (IFhirResourceDaoValueSet<ValueSet>) getDao();
return dao.expand(theId, theFilter); return dao.expand(theId, toFilterString(theFilter));
} finally { } finally {
endRequest(theServletRequest); endRequest(theServletRequest);
} }
} }
//@formatter:off
@Operation(name = "$expand", idempotent = true)
public ValueSet everything(
HttpServletRequest theServletRequest,
@OperationParam(name="identifier", min=1, max=1) UriDt theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringDt theFilter) {
//@formatter:on
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet> dao = (IFhirResourceDaoValueSet<ValueSet>) getDao();
return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
} finally {
endRequest(theServletRequest);
}
}
//@formatter:off
@Operation(name = "$expand", idempotent = true)
public ValueSet everything(
HttpServletRequest theServletRequest,
@OperationParam(name="valueSet", min=1, max=1) ValueSet theValueSet,
@OperationParam(name = "filter", min=0, max=1) StringDt theFilter) {
//@formatter:on
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet> dao = (IFhirResourceDaoValueSet<ValueSet>) getDao();
return dao.expand(theValueSet, toFilterString(theFilter));
} finally {
endRequest(theServletRequest);
}
}
private String toFilterString(StringDt theFilter) {
return theFilter != null ? theFilter.getValue() : null;
}
//@formatter:off //@formatter:off
@Operation(name = "$validate-code", idempotent = true, returnParameters= { @Operation(name = "$validate-code", idempotent = true, returnParameters= {
@OperationParam(name="result", type=BooleanDt.class, min=1), @OperationParam(name="result", type=BooleanDt.class, min=1),

View File

@ -28,12 +28,14 @@ import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest;
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam;
import ca.uhn.fhir.model.dstu2.valueset.ConditionalDeleteStatusEnum;
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BoundCodeDt; import ca.uhn.fhir.model.primitive.BoundCodeDt;
@ -48,11 +50,13 @@ public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
private IFhirSystemDao<Bundle> mySystemDao; private IFhirSystemDao<Bundle> mySystemDao;
private volatile Conformance myCachedValue; private volatile Conformance myCachedValue;
private RestfulServer myRestfulServer; private RestfulServer myRestfulServer;
private DaoConfig myDaoConfig;
public JpaConformanceProviderDstu2(RestfulServer theRestfulServer, IFhirSystemDao<Bundle> theSystemDao) { public JpaConformanceProviderDstu2(RestfulServer theRestfulServer, IFhirSystemDao<Bundle> theSystemDao, DaoConfig theDaoConfig) {
super(theRestfulServer); super(theRestfulServer);
myRestfulServer = theRestfulServer; myRestfulServer = theRestfulServer;
mySystemDao = theSystemDao; mySystemDao = theSystemDao;
myDaoConfig = theDaoConfig;
super.setCache(false); super.setCache(false);
} }
@ -63,17 +67,23 @@ public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
Map<String, Long> counts = mySystemDao.getResourceCounts(); Map<String, Long> counts = mySystemDao.getResourceCounts();
FhirContext ctx = myRestfulServer.getFhirContext(); FhirContext ctx = myRestfulServer.getFhirContext();
retVal = super.getServerConformance(theRequest); retVal = super.getServerConformance(theRequest);
for (Rest nextRest : retVal.getRest()) { for (Rest nextRest : retVal.getRest()) {
for (RestResource nextResource : nextRest.getResource()) { for (RestResource nextResource : nextRest.getResource()) {
ConditionalDeleteStatusEnum conditionalDelete = nextResource.getConditionalDeleteElement().getValueAsEnum();
if (conditionalDelete == ConditionalDeleteStatusEnum.MULTIPLE_DELETES_SUPPORTED && myDaoConfig.isAllowMultipleDelete() == false) {
nextResource.setConditionalDelete(ConditionalDeleteStatusEnum.SINGLE_DELETES_SUPPORTED);
}
// Add resource counts // Add resource counts
Long count = counts.get(nextResource.getTypeElement().getValueAsString()); Long count = counts.get(nextResource.getTypeElement().getValueAsString());
if (count != null) { if (count != null) {
nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count)); nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count));
} }
// Add chained params // Add chained params
for (RestResourceSearchParam nextParam : nextResource.getSearchParam()) { for (RestResourceSearchParam nextParam : nextResource.getSearchParam()) {
if (nextParam.getTypeElement().getValueAsEnum() == SearchParamTypeEnum.REFERENCE) { if (nextParam.getTypeElement().getValueAsEnum() == SearchParamTypeEnum.REFERENCE) {
@ -86,7 +96,7 @@ public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
} }
} }
} }
} }
} }

View File

@ -69,8 +69,8 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
} }
} }
@Delete @Delete()
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource, @ConditionalUrlParam String theConditional) { public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource, @ConditionalUrlParam(supportsMultiple=true) String theConditional) {
startRequest(theRequest); startRequest(theRequest);
try { try {
if (theConditional != null) { if (theConditional != null) {

View File

@ -103,6 +103,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@PersistenceContext() @PersistenceContext()
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
@Autowired @Autowired
@Qualifier("myFhirContextDstu2")
protected FhirContext myFhirCtx; protected FhirContext myFhirCtx;
@Autowired @Autowired
@Qualifier("myImmunizationDaoDstu2") @Qualifier("myImmunizationDaoDstu2")
@ -151,7 +152,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
protected PlatformTransactionManager myTxManager; protected PlatformTransactionManager myTxManager;
@Autowired @Autowired
@Qualifier("myValueSetDaoDstu2") @Qualifier("myValueSetDaoDstu2")
protected IFhirResourceDao<ValueSet> myValueSetDao; protected IFhirResourceDaoValueSet<ValueSet> myValueSetDao;
@Before @Before
public void beforeCreateInterceptor() { public void beforeCreateInterceptor() {

View File

@ -1,17 +1,32 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass; import org.junit.AfterClass;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
public class BaseJpaTest { public class BaseJpaTest {
public static String loadClasspath(String resource) throws IOException {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream(resource);
if (bundleRes == null) {
throw new NullPointerException("Can not load " + resource);
}
String bundleStr = IOUtils.toString(bundleRes);
return bundleStr;
}
@AfterClass @AfterClass
public static void afterClassShutdownDerby() throws SQLException { public static void afterClassShutdownDerby() throws SQLException {
// try { // try {
@ -35,6 +50,16 @@ public class BaseJpaTest {
return retVal; return retVal;
} }
protected List<IIdType> toUnqualifiedVersionlessIds(Bundle theFound) {
List<IIdType> retVal = new ArrayList<IIdType>();
for (Entry next : theFound.getEntry()) {
// if (next.getResource()!= null) {
retVal.add(next.getResource().getId().toUnqualifiedVersionless());
// }
}
return retVal;
}
protected List<IIdType> toUnqualifiedVersionlessIds(List<IBaseResource> theFound) { protected List<IIdType> toUnqualifiedVersionlessIds(List<IBaseResource> theFound) {
List<IIdType> retVal = new ArrayList<IIdType>(); List<IIdType> retVal = new ArrayList<IIdType>();
for (IBaseResource next : theFound) { for (IBaseResource next : theFound) {

View File

@ -56,9 +56,12 @@ import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Practitioner; import ca.uhn.fhir.model.dstu2.resource.Practitioner;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.resource.Substance; import ca.uhn.fhir.model.dstu2.resource.Substance;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum; import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
@ -100,6 +103,19 @@ public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test {
assertEquals(0, results.size()); assertEquals(0, results.size());
} }
@Test
public void testCodeSearch() {
Subscription subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setCriteria("Observation?");
IIdType id = mySubscriptionDao.create(subs).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(Subscription.SP_TYPE, new TokenParam(null, SubscriptionChannelTypeEnum.WEBSOCKET.getCode()));
map.add(Subscription.SP_STATUS, new TokenParam(null, SubscriptionStatusEnum.ACTIVE.getCode()));
assertThat(toUnqualifiedVersionlessIds(mySubscriptionDao.search(map)), contains(id));
}
@Test @Test
public void testIndexNoDuplicatesString() { public void testIndexNoDuplicatesString() {

View File

@ -97,6 +97,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
@ -683,7 +684,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
try { try {
myOrganizationDao.delete(orgId); myOrganizationDao.delete(orgId);
fail(); fail();
} catch (PreconditionFailedException e) { } catch (ResourceVersionConflictException e) {
assertThat(e.getMessage(), containsString("Unable to delete Organization/" + orgId.getIdPart() assertThat(e.getMessage(), containsString("Unable to delete Organization/" + orgId.getIdPart()
+ " because at least one resource has a reference to this resource. First reference found was resource Patient/" + patId.getIdPart() + " in path Patient.managingOrganization")); + " because at least one resource has a reference to this resource. First reference found was resource Patient/" + patId.getIdPart() + " in path Patient.managingOrganization"));
} }
@ -839,6 +840,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
} }
@Test @Test
public void testHistoryByForcedId() { public void testHistoryByForcedId() {
IIdType idv1; IIdType idv1;
@ -2088,21 +2091,21 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
pm = new SearchParameterMap(); pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
pm.setSort(new SortSpec(Patient.SP_RES_ID)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(5, actual.size()); assertEquals(5, actual.size());
assertThat(actual, contains(idMethodName, id1, id2, id3, id4)); assertThat(actual, contains(idMethodName, id1, id2, id3, id4));
pm = new SearchParameterMap(); pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
pm.setSort(new SortSpec(Patient.SP_RES_ID).setOrder(SortOrderEnum.ASC)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.ASC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(5, actual.size()); assertEquals(5, actual.size());
assertThat(actual, contains(idMethodName, id1, id2, id3, id4)); assertThat(actual, contains(idMethodName, id1, id2, id3, id4));
pm = new SearchParameterMap(); pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
pm.setSort(new SortSpec(Patient.SP_RES_ID).setOrder(SortOrderEnum.DESC)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(5, actual.size()); assertEquals(5, actual.size());
assertThat(actual, contains(id4, id3, id2, id1, idMethodName)); assertThat(actual, contains(id4, id3, id2, id1, idMethodName));

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2ValidateTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2ValidateTest.class);
@ -141,9 +142,9 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
OperationOutcome outcome=null; OperationOutcome outcome=null;
try { try {
myOrganizationDao.validate(null, orgId, null, null, ValidationModeEnum.DELETE, null); myOrganizationDao.validate(null, orgId, null, null, ValidationModeEnum.DELETE, null);
fail(); fail();
} catch (PreconditionFailedException e) { } catch (ResourceVersionConflictException e) {
outcome= (OperationOutcome) e.getOperationOutcome(); outcome= (OperationOutcome) e.getOperationOutcome();
} }

View File

@ -1,68 +1,42 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder; import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.jpa.provider.ResourceProviderDstu2Test;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
@RunWith(SpringJUnit4ClassRunner.class) public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
@ContextConfiguration({ "/hapi-fhir-server-resourceproviders-dstu2.xml", "/fhir-jpabase-spring-test-config.xml" })
public class FhirResourceDaoValueSetDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoValueSetDstu2Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoValueSetDstu2Test.class);
private static IIdType vsid; private IIdType myExtensionalVsId;
@Autowired
private FhirContext myCtx;
@Autowired
@Qualifier("mySystemDaoDstu2")
private IFhirSystemDao<Bundle> mySystemDao;
@Autowired
@Qualifier("myValueSetDaoDstu2")
private IFhirResourceDaoValueSet<ValueSet> myValueSetDao;
@Before @Before
@Transactional @Transactional
public void before01() { public void before02() throws IOException {
if (vsid == null) { ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml");
FhirSystemDaoDstu2Test.doDeleteEverything(mySystemDao); upload.setId("");
} myExtensionalVsId = myValueSetDao.create(upload).getId().toUnqualifiedVersionless();
}
@Before
@Transactional
public void before02() {
if (vsid == null) {
ValueSet upload = myCtx.newXmlParser().parseResource(ValueSet.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/extensional-case-2.xml")));
upload.setId("");
vsid = myValueSetDao.create(upload).getId().toUnqualifiedVersionless();
}
} }
@Test @Test
@ -137,7 +111,7 @@ public class FhirResourceDaoValueSetDstu2Test {
@Test @Test
public void testValidateCodeOperationByResourceIdAndCodeableConcept() { public void testValidateCodeOperationByResourceIdAndCodeableConcept() {
UriDt valueSetIdentifier = null; UriDt valueSetIdentifier = null;
IIdType id = vsid; IIdType id = myExtensionalVsId;
CodeDt code = null; CodeDt code = null;
UriDt system = null; UriDt system = null;
StringDt display = null; StringDt display = null;
@ -151,7 +125,7 @@ public class FhirResourceDaoValueSetDstu2Test {
@Test @Test
public void testValidateCodeOperationByResourceIdAndCodeAndSystem() { public void testValidateCodeOperationByResourceIdAndCodeAndSystem() {
UriDt valueSetIdentifier = null; UriDt valueSetIdentifier = null;
IIdType id = vsid; IIdType id = myExtensionalVsId;
CodeDt code = new CodeDt("11378-7"); CodeDt code = new CodeDt("11378-7");
UriDt system = new UriDt("http://loinc.org"); UriDt system = new UriDt("http://loinc.org");
StringDt display = null; StringDt display = null;
@ -163,11 +137,11 @@ public class FhirResourceDaoValueSetDstu2Test {
} }
@Test @Test
public void testValueSetExpandOperation() throws IOException { public void testExpandById() throws IOException {
String resp; String resp;
ValueSet expanded = myValueSetDao.expand(vsid, null); ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null);
resp = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
// @formatter:off // @formatter:off
assertThat(resp, assertThat(resp,
@ -191,8 +165,8 @@ public class FhirResourceDaoValueSetDstu2Test {
* Filter with display name * Filter with display name
*/ */
expanded = myValueSetDao.expand(vsid, new StringDt("systolic")); expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"));
resp = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
//@formatter:off //@formatter:off
assertThat(resp, stringContainsInOrder( assertThat(resp, stringContainsInOrder(
@ -204,8 +178,8 @@ public class FhirResourceDaoValueSetDstu2Test {
* Filter with code * Filter with code
*/ */
expanded = myValueSetDao.expand(vsid, new StringDt("11378")); expanded = myValueSetDao.expand(myExtensionalVsId, ("11378"));
resp = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
//@formatter:off //@formatter:off
assertThat(resp, stringContainsInOrder( assertThat(resp, stringContainsInOrder(
@ -213,5 +187,34 @@ public class FhirResourceDaoValueSetDstu2Test {
"<display value=\"Systolic blood pressure at First encounter\"/>")); "<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on //@formatter:on
} }
@Test
public void testExpandByIdentifier() {
ValueSet expanded = myValueSetDao.expandByIdentifier("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "11378");
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
}
@Test
public void testExpandByValueSet() throws IOException {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml");
ValueSet expanded = myValueSetDao.expand(toExpand, "11378");
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
}
} }

View File

@ -18,7 +18,6 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass; import org.junit.AfterClass;
@ -28,7 +27,6 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
@ -346,8 +344,8 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
@Test @Test
public void testTransactionWithCidIds2() throws Exception { public void testTransactionWithCidIds2() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/dstu1_bundle.xml"); String resource = "/dstu1_bundle.xml";
String bundleStr = IOUtils.toString(bundleRes); String bundleStr = loadClasspath(resource);
Bundle bundle = ourFhirContext.newXmlParser().parseBundle(bundleStr); Bundle bundle = ourFhirContext.newXmlParser().parseBundle(bundleStr);
List<IResource> res = new ArrayList<IResource>(); List<IResource> res = new ArrayList<IResource>();
@ -363,6 +361,7 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
assertThat(encodeResourceToString, not(containsString("smsp"))); assertThat(encodeResourceToString, not(containsString("smsp")));
} }
/** /**
* This is the correct way to do this, not {@link #testTransactionWithCidIds()} * This is the correct way to do this, not {@link #testTransactionWithCidIds()}
*/ */

View File

@ -31,6 +31,7 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum; import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.entity.TagTypeEnum;
@ -44,7 +45,11 @@ import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest;
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryResponse; import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryResponse;
import ca.uhn.fhir.model.dstu2.resource.Communication;
import ca.uhn.fhir.model.dstu2.resource.Medication;
import ca.uhn.fhir.model.dstu2.resource.MedicationOrder;
import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
@ -53,8 +58,11 @@ import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum; import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -354,6 +362,42 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
} }
} }
@Test
public void testTransactionCreateWithInvalidMatchUrl() {
String methodName = "testTransactionCreateWithInvalidMatchUrl";
Bundle request = new Bundle();
Patient p;
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
EntryRequest entry = request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
try {
entry.setIfNoneExist("Patient?identifier identifier" + methodName);
mySystemDao.transaction(request);
fail();
} catch (InvalidRequestException e) {
assertEquals("Failed to parse match URL[Patient?identifier identifiertestTransactionCreateWithInvalidMatchUrl] - URL is invalid (must not contain spaces)", e.getMessage());
}
try {
entry.setIfNoneExist("Patient?identifier=");
mySystemDao.transaction(request);
fail();
} catch (InvalidRequestException e) {
assertEquals("Invalid match URL[Patient?identifier=] - URL has no search parameters", e.getMessage());
}
try {
entry.setIfNoneExist("Patient?foo=bar");
mySystemDao.transaction(request);
fail();
} catch (InvalidRequestException e) {
assertEquals("Failed to parse match URL[Patient?foo=bar] - Resource type Patient does not have a parameter with name: foo", e.getMessage());
}
}
public void testTransactionCreateWithDuplicateMatchUrl02() { public void testTransactionCreateWithDuplicateMatchUrl02() {
String methodName = "testTransactionCreateWithDuplicateMatchUrl02"; String methodName = "testTransactionCreateWithDuplicateMatchUrl02";
Bundle request = new Bundle(); Bundle request = new Bundle();
@ -634,7 +678,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
try { try {
mySystemDao.transaction(request); mySystemDao.transaction(request);
fail(); fail();
} catch (ResourceNotFoundException e) { } catch (PreconditionFailedException e) {
assertThat(e.getMessage(), containsString("resource with match URL \"Patient?")); assertThat(e.getMessage(), containsString("resource with match URL \"Patient?"));
} }
} }
@ -731,6 +775,68 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals("201 Created", resp.getEntry().get(1).getResponse().getStatus()); assertEquals("201 Created", resp.getEntry().get(1).getResponse().getStatus());
} }
public static void main(String[] args) {
Communication com = new Communication();
com.getSender().setReference("Patient/james");
com.addRecipient().setReference("Group/everyone");
com.addMedium().setText("Skype");
com.addPayload().setContent(new StringDt("Welcome to Connectathon 10! Any HAPI users feel free to grab me if you want to chat or need help!"));
System.out.println(FhirContext.forDstu2().newJsonParser().setPrettyPrint(true).encodeResourceToString(com));
}
@Test
public void testTransactionWithReferenceToCreateIfNoneExist() {
Bundle bundle = new Bundle();
bundle.setType(BundleTypeEnum.TRANSACTION);
Medication med = new Medication();
IdDt medId = IdDt.newRandomUuid();
med.setId(medId);
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Medication?code=billscodes|theCode");
MedicationOrder mo = new MedicationOrder();
mo.setMedication(new ResourceReferenceDt(medId));
bundle.addEntry().setResource(mo).setFullUrl(mo.getId().getValue()).getRequest().setMethod(HTTPVerbEnum.POST);
ourLog.info("Request:\n"+myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
Bundle outcome = mySystemDao.transaction(bundle);
ourLog.info("Response:\n"+myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
IdDt medId1 = new IdDt(outcome.getEntry().get(0).getResponse().getLocation());
IdDt medOrderId1 = new IdDt( outcome.getEntry().get(1).getResponse().getLocation());
/*
* Again!
*/
bundle = new Bundle();
bundle.setType(BundleTypeEnum.TRANSACTION);
med = new Medication();
medId = IdDt.newRandomUuid();
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Medication?code=billscodes|theCode");
mo = new MedicationOrder();
mo.setMedication(new ResourceReferenceDt(medId));
bundle.addEntry().setResource(mo).setFullUrl(mo.getId().getValue()).getRequest().setMethod(HTTPVerbEnum.POST);
outcome = mySystemDao.transaction(bundle);
IdDt medId2 = new IdDt(outcome.getEntry().get(0).getResponse().getLocation());
IdDt medOrderId2 = new IdDt(outcome.getEntry().get(1).getResponse().getLocation());
assertTrue(medId1.isIdPartValidLong());
assertTrue(medId2.isIdPartValidLong());
assertTrue(medOrderId1.isIdPartValidLong());
assertTrue(medOrderId2.isIdPartValidLong());
assertEquals(medId1, medId2);
assertNotEquals(medOrderId1, medOrderId2);
}
@Test @Test
public void testTransactionReadAndSearch() { public void testTransactionReadAndSearch() {
String methodName = "testTransactionReadAndSearch"; String methodName = "testTransactionReadAndSearch";

View File

@ -101,7 +101,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
restServer.setPlainProviders(mySystemProvider); restServer.setPlainProviders(mySystemProvider);
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(restServer, mySystemDao); JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(restServer, mySystemDao, myDaoConfig);
confProvider.setImplementationDescription("THIS IS THE DESC"); confProvider.setImplementationDescription("THIS IS THE DESC");
restServer.setServerConformanceProvider(confProvider); restServer.setServerConformanceProvider(confProvider);

View File

@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsInRelativeOrder; import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
@ -25,9 +26,7 @@ import java.net.Socket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -69,12 +68,15 @@ import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire; import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse; import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.valueset.AnswerFormatEnum; import ca.uhn.fhir.model.dstu2.valueset.AnswerFormatEnum;
import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum; import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum; import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum; import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -89,6 +91,8 @@ import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test { public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
@ -97,6 +101,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
// private static JpaConformanceProvider ourConfProvider; // private static JpaConformanceProvider ourConfProvider;
@Override
public void before() throws Exception {
super.before();
myDaoConfig.setAllowMultipleDelete(true);
}
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException { private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false"); HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
CloseableHttpResponse resp = ourHttpClient.execute(get); CloseableHttpResponse resp = ourHttpClient.execute(get);
@ -104,58 +115,6 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertEquals(200, resp.getStatusLine().getStatusCode()); assertEquals(200, resp.getStatusLine().getStatusCode());
} }
// private void delete(String theResourceType, String theParamName, String theParamValue) {
// Bundle resources;
// do {
// IQuery<Bundle> forResource = ourClient.search().forResource(theResourceType);
// if (theParamName != null) {
// forResource = forResource.where(new StringClientParam(theParamName).matches().value(theParamValue));
// }
// resources = forResource.execute();
// for (IResource next : resources.toListOfResources()) {
// ourLog.info("Deleting resource: {}", next.getId());
// ourClient.delete().resource(next).execute();
// }
// } while (resources.size() > 0);
// }
//
// private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue)
// {
// Bundle resources = ourClient.search().forResource(theResourceType).where(new
// TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute();
// for (IResource next : resources.toListOfResources()) {
// ourLog.info("Deleting resource: {}", next.getId());
// ourClient.delete().resource(next).execute();
// }
// }
@Test
public void testSearchByIdOr() {
IIdType id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Patient.class)
.where(Patient.RES_ID.matches().values(id1.getIdPart(), id2.getIdPart()))
.and(Patient.RES_ID.matches().value(id1.getIdPart()))
.execute();
//@formatter:on
assertThat(toIdListUnqualifiedVersionless(found), containsInAnyOrder(id1));
}
@Test @Test
public void testBundleCreate() throws Exception { public void testBundleCreate() throws Exception {
IGenericClient client = ourClient; IGenericClient client = ourClient;
@ -184,6 +143,78 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
// private void delete(String theResourceType, String theParamName, String theParamValue) {
// Bundle resources;
// do {
// IQuery<Bundle> forResource = ourClient.search().forResource(theResourceType);
// if (theParamName != null) {
// forResource = forResource.where(new StringClientParam(theParamName).matches().value(theParamValue));
// }
// resources = forResource.execute();
// for (IResource next : resources.toListOfResources()) {
// ourLog.info("Deleting resource: {}", next.getId());
// ourClient.delete().resource(next).execute();
// }
// } while (resources.size() > 0);
// }
//
// private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue)
// {
// Bundle resources = ourClient.search().forResource(theResourceType).where(new
// TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute();
// for (IResource next : resources.toListOfResources()) {
// ourLog.info("Deleting resource: {}", next.getId());
// ourClient.delete().resource(next).execute();
// }
// }
@Test
public void testCodeSearch() {
Subscription subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setCriteria("Observation?");
IIdType id = ourClient.create().resource(subs).execute().getId().toUnqualifiedVersionless();
//@formatter:off
ca.uhn.fhir.model.dstu2.resource.Bundle resp = ourClient
.search()
.forResource(Subscription.class)
.where(Subscription.TYPE.exactly().code(SubscriptionChannelTypeEnum.WEBSOCKET.getCode()))
.and(Subscription.STATUS.exactly().code(SubscriptionStatusEnum.ACTIVE.getCode()))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
//@formatter:off
assertThat(toUnqualifiedVersionlessIds(resp), contains(id));
//@formatter:off
resp = ourClient
.search()
.forResource(Subscription.class)
.where(Subscription.TYPE.exactly().systemAndCode(SubscriptionChannelTypeEnum.WEBSOCKET.getSystem(), SubscriptionChannelTypeEnum.WEBSOCKET.getCode()))
.and(Subscription.STATUS.exactly().systemAndCode(SubscriptionStatusEnum.ACTIVE.getSystem(), SubscriptionStatusEnum.ACTIVE.getCode()))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
//@formatter:off
assertThat(toUnqualifiedVersionlessIds(resp), contains(id));
//@formatter:off
resp = ourClient
.search()
.forResource(Subscription.class)
.where(Subscription.TYPE.exactly().systemAndCode(SubscriptionChannelTypeEnum.WEBSOCKET.getSystem(), SubscriptionChannelTypeEnum.WEBSOCKET.getCode()))
.and(Subscription.STATUS.exactly().systemAndCode("foo", SubscriptionStatusEnum.ACTIVE.getCode()))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
//@formatter:off
assertThat(toUnqualifiedVersionlessIds(resp), empty());
}
@Test @Test
public void testCountParam() throws Exception { public void testCountParam() throws Exception {
// NB this does not get used- The paging provider has its own limits built in // NB this does not get used- The paging provider has its own limits built in
@ -207,19 +238,6 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
@Test
public void testCreateWithForcedId() throws IOException {
String methodName = "testCreateWithForcedId";
Patient p = new Patient();
p.addName().addFamily(methodName);
p.setId(methodName);
IIdType optId = ourClient.update().resource(p).execute().getId();
assertEquals(methodName, optId.getIdPart());
assertEquals("1", optId.getVersionIdPart());
}
@Test @Test
public void testCreateQuestionnaireResponseWithValidation() throws IOException { public void testCreateQuestionnaireResponseWithValidation() throws IOException {
ValueSet options = new ValueSet(); ValueSet options = new ValueSet();
@ -328,6 +346,19 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
@Test
public void testCreateWithForcedId() throws IOException {
String methodName = "testCreateWithForcedId";
Patient p = new Patient();
p.addName().addFamily(methodName);
p.setId(methodName);
IIdType optId = ourClient.update().resource(p).execute().getId();
assertEquals(methodName, optId.getIdPart());
assertEquals("1", optId.getVersionIdPart());
}
@Test @Test
public void testDeepChaining() { public void testDeepChaining() {
Location l1 = new Location(); Location l1 = new Location();
@ -363,6 +394,63 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
@Test
public void testDeleteConditionalMultiple() {
String methodName = "testDeleteConditionalMultiple";
myDaoConfig.setAllowMultipleDelete(false);
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("FAM1");
IIdType id1 = myPatientDao.create(p).getId().toUnqualifiedVersionless();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("FAM2");
IIdType id2 = myPatientDao.create(p).getId().toUnqualifiedVersionless();
try {
//@formatter:off
ourClient
.delete()
.resourceConditionalByType(Patient.class)
.where(Patient.IDENTIFIER.exactly().code(methodName))
.execute();
//@formatter:on
fail();
} catch (PreconditionFailedException e) {
assertEquals("HTTP 412 Precondition Failed: Failed to DELETE resource with match URL \"Patient?identifier=testDeleteConditionalMultiple\" because this search matched 2 resources", e.getMessage());
}
// Not deleted yet..
ourClient.read().resource("Patient").withId(id1).execute();
ourClient.read().resource("Patient").withId(id2).execute();
myDaoConfig.setAllowMultipleDelete(true);
//@formatter:off
ourClient
.delete()
.resourceConditionalByType(Patient.class)
.where(Patient.IDENTIFIER.exactly().code(methodName))
.execute();
//@formatter:on
try {
ourClient.read().resource("Patient").withId(id1).execute();
fail();
} catch (ResourceGoneException e) {
// good
}
try {
ourClient.read().resource("Patient").withId(id2).execute();
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Test @Test
public void testDeleteInvalidReference() throws IOException { public void testDeleteInvalidReference() throws IOException {
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient"); HttpDelete delete = new HttpDelete(ourServerBase + "/Patient");
@ -376,7 +464,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
response.close(); response.close();
} }
} }
@Test @Test
public void testDeleteResourceConditional1() throws IOException { public void testDeleteResourceConditional1() throws IOException {
String methodName = "testDeleteResourceConditional1"; String methodName = "testDeleteResourceConditional1";
@ -416,7 +504,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
/** /**
* Based on email from Rene Spronk * Based on email from Rene Spronk
*/ */
@ -543,11 +631,133 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
@Test
public void testEverythingEncounterInstance() throws Exception {
String methodName = "testEverythingEncounterInstance";
Organization org1parent = new Organization();
org1parent.setId("org1parent");
org1parent.setName(methodName + "1parent");
IIdType orgId1parent = ourClient.update().resource(org1parent).execute().getId().toUnqualifiedVersionless();
Organization org1 = new Organization();
org1.setName(methodName + "1");
org1.getPartOf().setReference(orgId1parent);
IIdType orgId1 = ourClient.create().resource(org1).execute().getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addName().addFamily(methodName);
p.getManagingOrganization().setReference(orgId1);
IIdType patientId = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Organization org2 = new Organization();
org2.setName(methodName + "1");
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.getOwner().setReference(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
Location locParent = new Location();
locParent.setName(methodName+"Parent");
IIdType locPId = ourClient.create().resource(locParent).execute().getId().toUnqualifiedVersionless();
Location locChild = new Location();
locChild.setName(methodName);
locChild.getPartOf().setReference(locPId);
IIdType locCId = ourClient.create().resource(locChild).execute().getId().toUnqualifiedVersionless();
Encounter encU = new Encounter();
encU.getPatient().setReference(patientId);
encU.addLocation().getLocation().setReference(locCId);
IIdType encUId = ourClient.create().resource(encU).execute().getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.getPatient().setReference(patientId);
enc.addLocation().getLocation().setReference(locCId);
IIdType encId = ourClient.create().resource(enc).execute().getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(patientId);
obs.getDevice().setReference(devId);
obs.getEncounter().setReference(encId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onInstance(encId).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IIdType> ids = toUnqualifiedVersionlessIds(b);
assertThat(ids, containsInAnyOrder(patientId, encId, orgId1, orgId2, orgId1parent, locPId, locCId, obsId, devId));
assertThat(ids, not(containsInRelativeOrder(encUId)));
ourLog.info(ids.toString());
}
@Test
public void testEverythingEncounterType() throws Exception {
String methodName = "testEverythingEncounterInstance";
Organization org1parent = new Organization();
org1parent.setId("org1parent");
org1parent.setName(methodName + "1parent");
IIdType orgId1parent = ourClient.update().resource(org1parent).execute().getId().toUnqualifiedVersionless();
Organization org1 = new Organization();
org1.setName(methodName + "1");
org1.getPartOf().setReference(orgId1parent);
IIdType orgId1 = ourClient.create().resource(org1).execute().getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addName().addFamily(methodName);
p.getManagingOrganization().setReference(orgId1);
IIdType patientId = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Organization org2 = new Organization();
org2.setName(methodName + "1");
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.getOwner().setReference(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
Location locParent = new Location();
locParent.setName(methodName+"Parent");
IIdType locPId = ourClient.create().resource(locParent).execute().getId().toUnqualifiedVersionless();
Location locChild = new Location();
locChild.setName(methodName);
locChild.getPartOf().setReference(locPId);
IIdType locCId = ourClient.create().resource(locChild).execute().getId().toUnqualifiedVersionless();
Encounter encU = new Encounter();
encU.addIdentifier().setValue(methodName);
IIdType encUId = ourClient.create().resource(encU).execute().getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.getPatient().setReference(patientId);
enc.addLocation().getLocation().setReference(locCId);
IIdType encId = ourClient.create().resource(enc).execute().getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(patientId);
obs.getDevice().setReference(devId);
obs.getEncounter().setReference(encId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onType(Encounter.class).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IIdType> ids = toUnqualifiedVersionlessIds(b);
assertThat(ids, containsInAnyOrder(patientId, encUId, encId, orgId1, orgId2, orgId1parent, locPId, locCId, obsId, devId));
ourLog.info(ids.toString());
}
/** /**
* See #147 * See #147
*/ */
@Test @Test
public void testEverythingDoesntRepeatPatient() throws Exception { public void testEverythingPatientDoesntRepeatPatient() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle b; ca.uhn.fhir.model.dstu2.resource.Bundle b;
b = myFhirCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/bug147-bundle.json"))); b = myFhirCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/bug147-bundle.json")));
@ -601,14 +811,159 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertThat(ids.size(), greaterThan(10)); assertThat(ids.size(), greaterThan(10));
} }
} }
/**
* Test for #226
*/
@Test
public void testEverythingPatientIncludesBackReferences() throws Exception {
String methodName = "testEverythingIncludesBackReferences";
Medication med = new Medication();
med.getCode().setText(methodName);
IIdType medId = myMedicationDao.create(med).getId().toUnqualifiedVersionless();
Patient pat = new Patient();
pat.addAddress().addLine(methodName);
IIdType patId = myPatientDao.create(pat).getId().toUnqualifiedVersionless();
MedicationOrder mo = new MedicationOrder();
mo.getPatient().setReference(patId);
mo.setMedication(new ResourceReferenceDt(medId));
IIdType moId = myMedicationOrderDao.create(mo).getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onInstance(patId).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IIdType> ids = toUnqualifiedVersionlessIds(b);
ourLog.info(ids.toString());
assertThat(ids, containsInAnyOrder(patId, medId, moId));
}
/**
* See #148
*/
@Test
public void testEverythingPatientIncludesCondition() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
Patient p = new Patient();
p.setId("1");
b.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
Condition c = new Condition();
c.getPatient().setReference("Patient/1");
b.addEntry().setResource(c).getRequest().setMethod(HTTPVerbEnum.POST);
ca.uhn.fhir.model.dstu2.resource.Bundle resp = ourClient.transaction().withBundle(b).execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
IdDt patientId = new IdDt(resp.getEntry().get(0).getResponse().getLocation());
assertEquals("Patient", patientId.getResourceType());
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withNoParameters(Parameters.class).execute();
b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IdDt> ids = new ArrayList<IdDt>();
for (Entry next : b.getEntry()) {
IdDt toAdd = next.getResource().getId().toUnqualifiedVersionless();
ids.add(toAdd);
}
assertThat(ids.toString(), containsString("Patient/"));
assertThat(ids.toString(), containsString("Condition/"));
}
@Test
public void testEverythingPatientOperation() throws Exception {
String methodName = "testEverythingOperation";
Organization org1parent = new Organization();
org1parent.setId("org1parent");
org1parent.setName(methodName + "1parent");
IIdType orgId1parent = ourClient.update().resource(org1parent).execute().getId().toUnqualifiedVersionless();
Organization org1 = new Organization();
org1.setName(methodName + "1");
org1.getPartOf().setReference(orgId1parent);
IIdType orgId1 = ourClient.create().resource(org1).execute().getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addName().addFamily(methodName);
p.getManagingOrganization().setReference(orgId1);
IIdType patientId = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Organization org2 = new Organization();
org2.setName(methodName + "1");
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.getOwner().setReference(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(patientId);
obs.getDevice().setReference(devId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.getPatient().setReference(patientId);
IIdType encId = ourClient.create().resource(enc).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IIdType> ids = toUnqualifiedVersionlessIds(b);
assertThat(ids, containsInAnyOrder(patientId, devId, obsId, encId, orgId1, orgId2, orgId1parent));
ourLog.info(ids.toString());
}
@Test
public void testEverythingPatientType() throws Exception {
String methodName = "testEverythingPatientType";
Organization o1 = new Organization();
o1.setName(methodName+"1");
IIdType o1Id = ourClient.create().resource(o1).execute().getId().toUnqualifiedVersionless();
Organization o2 = new Organization();
o2.setName(methodName+"2");
IIdType o2Id = ourClient.create().resource(o2).execute().getId().toUnqualifiedVersionless();
Patient p1 = new Patient();
p1.addName().addFamily(methodName+"1");
p1.getManagingOrganization().setReference(o1Id);
IIdType p1Id = ourClient.create().resource(p1).execute().getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().addFamily(methodName+"2");
p2.getManagingOrganization().setReference(o2Id);
IIdType p2Id = ourClient.create().resource(p2).execute().getId().toUnqualifiedVersionless();
Condition c1 = new Condition();
c1.getPatient().setReference(p1Id);
IIdType c1Id = ourClient.create().resource(c1).execute().getId().toUnqualifiedVersionless();
Condition c2 = new Condition();
c2.getPatient().setReference(p2Id);
IIdType c2Id = ourClient.create().resource(c2).execute().getId().toUnqualifiedVersionless();
Condition c3 = new Condition();
c3.addIdentifier().setValue(methodName+"3");
IIdType c3Id = ourClient.create().resource(c3).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onType(Patient.class).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IIdType> ids = toUnqualifiedVersionlessIds(b);
assertThat(ids, containsInAnyOrder(o1Id, o2Id, p1Id, p2Id, c1Id, c2Id));
assertThat(ids, not(containsInRelativeOrder(c3Id)));
}
@Test @Test
public void testEverythingWithLastUpdatedAndSort() throws Exception { public void testEverythingPatientWithLastUpdatedAndSort() throws Exception {
String methodName = "testEverythingWithLastUpdatedAndSort"; String methodName = "testEverythingWithLastUpdatedAndSort";
long time0 = System.currentTimeMillis();
Thread.sleep(10);
Organization org = new Organization(); Organization org = new Organization();
org.setName(methodName); org.setName(methodName);
IIdType oId = ourClient.create().resource(org).execute().getId().toUnqualifiedVersionless(); IIdType oId = ourClient.create().resource(org).execute().getId().toUnqualifiedVersionless();
@ -691,122 +1046,6 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
/**
* See #148
*/
@Test
public void testEverythingIncludesCondition() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
Patient p = new Patient();
p.setId("1");
b.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
Condition c = new Condition();
c.getPatient().setReference("Patient/1");
b.addEntry().setResource(c).getRequest().setMethod(HTTPVerbEnum.POST);
ca.uhn.fhir.model.dstu2.resource.Bundle resp = ourClient.transaction().withBundle(b).execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
IdDt patientId = new IdDt(resp.getEntry().get(0).getResponse().getLocation());
assertEquals("Patient", patientId.getResourceType());
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withNoParameters(Parameters.class).execute();
b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
List<IdDt> ids = new ArrayList<IdDt>();
for (Entry next : b.getEntry()) {
IdDt toAdd = next.getResource().getId().toUnqualifiedVersionless();
ids.add(toAdd);
}
assertThat(ids.toString(), containsString("Patient/"));
assertThat(ids.toString(), containsString("Condition/"));
}
/**
* Test for #226
*/
@Test
public void testEverythingIncludesBackReferences() throws Exception {
String methodName = "testEverythingIncludesBackReferences";
Medication med = new Medication();
med.getCode().setText(methodName);
IIdType medId = myMedicationDao.create(med).getId().toUnqualifiedVersionless();
Patient pat = new Patient();
pat.addAddress().addLine(methodName);
IIdType patId = myPatientDao.create(pat).getId().toUnqualifiedVersionless();
MedicationOrder mo = new MedicationOrder();
mo.getPatient().setReference(patId);
mo.setMedication(new ResourceReferenceDt(medId));
IIdType moId = myMedicationOrderDao.create(mo).getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onInstance(patId).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
Set<IdDt> ids = toIdList(b);
ourLog.info(ids.toString());
assertThat(ids, containsInAnyOrder(patId, medId, moId));
}
@Test
public void testEverythingOperation() throws Exception {
String methodName = "testEverythingOperation";
Organization org1parent = new Organization();
org1parent.setId("org1parent");
org1parent.setName(methodName + "1parent");
IIdType orgId1parent = ourClient.update().resource(org1parent).execute().getId().toUnqualifiedVersionless();
Organization org1 = new Organization();
org1.setName(methodName + "1");
org1.getPartOf().setReference(orgId1parent);
IIdType orgId1 = ourClient.create().resource(org1).execute().getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addName().addFamily(methodName);
p.getManagingOrganization().setReference(orgId1);
IIdType patientId = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Organization org2 = new Organization();
org2.setName(methodName + "1");
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.getOwner().setReference(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(patientId);
obs.getDevice().setReference(devId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.getPatient().setReference(patientId);
IIdType encId = ourClient.create().resource(enc).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withNoParameters(Parameters.class).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
Set<IdDt> ids = toIdList(b);
assertThat(ids, containsInAnyOrder(patientId, devId, obsId, encId, orgId1, orgId2, orgId1parent));
ourLog.info(ids.toString());
}
private Set<IdDt> toIdList(ca.uhn.fhir.model.dstu2.resource.Bundle b) {
Set<IdDt> ids = new HashSet<IdDt>();
for (Entry next : b.getEntry()) {
ids.add(next.getResource().getId().toUnqualifiedVersionless());
}
return ids;
}
@Test @Test
public void testGetResourceCountsOperation() throws Exception { public void testGetResourceCountsOperation() throws Exception {
@ -886,6 +1125,29 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
@Test
public void testMetaOperations() throws Exception {
String methodName = "testMetaOperations";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
MetaDt meta = ourClient.meta().get(MetaDt.class).fromResource(id).execute();
assertEquals(0, meta.getTag().size());
MetaDt inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().add().onResource(id).meta(inMeta).execute();
assertEquals(1, meta.getTag().size());
inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().delete().onResource(id).meta(inMeta).execute();
assertEquals(0, meta.getTag().size());
}
@Test @Test
public void testMetaOperationWithNoMetaParameter() throws Exception { public void testMetaOperationWithNoMetaParameter() throws Exception {
Patient p = new Patient(); Patient p = new Patient();
@ -930,29 +1192,6 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
@Test
public void testMetaOperations() throws Exception {
String methodName = "testMetaOperations";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
MetaDt meta = ourClient.meta().get(MetaDt.class).fromResource(id).execute();
assertEquals(0, meta.getTag().size());
MetaDt inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().add().onResource(id).meta(inMeta).execute();
assertEquals(1, meta.getTag().size());
inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().delete().onResource(id).meta(inMeta).execute();
assertEquals(0, meta.getTag().size());
}
/** /**
* Test for issue #60 * Test for issue #60
*/ */
@ -1093,6 +1332,33 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
@Test
public void testSearchByIdOr() {
IIdType id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Patient.class)
.where(Patient.RES_ID.matches().values(id1.getIdPart(), id2.getIdPart()))
.and(Patient.RES_ID.matches().value(id1.getIdPart()))
.execute();
//@formatter:on
assertThat(toIdListUnqualifiedVersionless(found), containsInAnyOrder(id1));
}
@Test @Test
public void testSearchByResourceChain() { public void testSearchByResourceChain() {
@ -1297,7 +1563,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
o.getCode().setText("testSearchWithInvalidSort"); o.getCode().setText("testSearchWithInvalidSort");
myObservationDao.create(o); myObservationDao.create(o);
//@formatter:off //@formatter:off
Bundle found = ourClient ourClient
.search() .search()
.forResource(Observation.class) .forResource(Observation.class)
.sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet .sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet
@ -1552,6 +1818,21 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
@Test
public void testTransaction() throws Exception {
String contents = loadClasspath("/update.xml");
HttpPost post = new HttpPost(ourServerBase);
post.setEntity(new StringEntity(contents, ContentType.create("application/xml+fhir", "UTF-8")));
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
assertEquals(200, resp.getStatusLine().getStatusCode());
String output= IOUtils.toString(resp.getEntity().getContent());
ourLog.info(output);
} finally {
resp.close();
}
}
@Test @Test
public void testTryToCreateResourceWithReferenceThatDoesntExist() { public void testTryToCreateResourceWithReferenceThatDoesntExist() {
Patient p1 = new Patient(); Patient p1 = new Patient();
@ -1568,6 +1849,27 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
@Test
public void testUpdateInvalidReference() throws IOException, Exception {
String methodName = "testUpdateInvalidReference";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPut post = new HttpPut(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent());
ourLog.info(responseString);
assertThat(responseString, containsString("<pre>Can not update a resource with no ID</pre>"));
assertThat(responseString, containsString("<OperationOutcome"));
assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
response.close();
}
}
@Test @Test
public void testUpdateRejectsInvalidTypes() throws InterruptedException { public void testUpdateRejectsInvalidTypes() throws InterruptedException {
@ -1629,28 +1931,6 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} }
} }
@Test
public void testUpdateInvalidReference() throws IOException, Exception {
String methodName = "testUpdateInvalidReference";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPut post = new HttpPut(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent());
ourLog.info(responseString);
assertThat(responseString, containsString("<pre>Can not update a resource with no ID</pre>"));
assertThat(responseString, containsString("<OperationOutcome"));
assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
response.close();
}
}
@Test @Test
public void testUpdateResourceWithPrefer() throws IOException, Exception { public void testUpdateResourceWithPrefer() throws IOException, Exception {

View File

@ -0,0 +1,161 @@
package ca.uhn.fhir.jpa.provider;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2Test {
private IIdType myExtensionalVsId;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu2ValueSetTest.class);
@Before
@Transactional
public void before02() throws IOException {
ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml");
upload.setId("");
myExtensionalVsId = myValueSetDao.create(upload).getId().toUnqualifiedVersionless();
}
@Test
public void testExpandById() throws IOException {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
// @formatter:off
assertThat(resp,
stringContainsInOrder("<ValueSet xmlns=\"http://hl7.org/fhir\">",
"<expansion>",
"<contains>",
"<system value=\"http://loinc.org\"/>",
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>",
"</contains>",
"<contains>",
"<system value=\"http://loinc.org\"/>",
"<code value=\"8450-9\"/>",
"<display value=\"Systolic blood pressure--expiration\"/>",
"</contains>",
"</expansion>"
));
//@formatter:on
/*
* Filter with display name
*/
//@formatter:off
respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "filter", new StringDt("systolic"))
.execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"));
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
/*
* Filter with code
*/
//@formatter:off
respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "filter", new StringDt("11378"))
.execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
}
@Test
public void testExpandByIdentifier() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriDt("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.andParameter("filter", new StringDt("11378"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
}
@Test
public void testExpandByValueSet() throws IOException {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml");
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringDt("11378"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
}
}

View File

@ -8,6 +8,7 @@ import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -26,9 +27,11 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Questionnaire; import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.OperationDefinition; import ca.uhn.fhir.model.dstu2.resource.OperationDefinition;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class SystemProviderDstu2Test extends BaseJpaTest { public class SystemProviderDstu2Test extends BaseJpaTest {
@ -38,6 +41,36 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
private static FhirContext ourCtx; private static FhirContext ourCtx;
private static IGenericClient ourClient; private static IGenericClient ourClient;
@Test
public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml");
String bundle = IOUtils.toString(bundleRes);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
IdDt id = new IdDt(bundleResp.getEntry().get(0).getResponse().getLocation());
assertEquals("Patient", id.getResourceType());
assertTrue(id.hasIdPart());
assertTrue(id.isIdPartValidLong());
assertTrue(id.hasVersionIdPart());
assertTrue(id.isVersionIdPartValidLong());
}
@Test
public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
} catch (InvalidRequestException e) {
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
assertEquals("processing", oo.getIssue().get(0).getCode());
}
}
@Test @Test
public void testTransactionFromBundle() throws Exception { public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml"); InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml");

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
<status value="generated" /> <status value="generated" />
<div xmlns="http://www.w3.org/1999/xhtml">A selection of codes from http://loinc.org</div> <div xmlns="http://www.w3.org/1999/xhtml">A selection of codes from http://loinc.org</div>
</text> </text>
<url value="http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"/>
<identifier> <identifier>
<value <value
value="http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2" /> value="http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2" />

View File

@ -0,0 +1,107 @@
<Bundle xmlns="http://hl7.org/fhir">
<id value="20151003220331"/>
<type value="transaction"/>
<entry>
<fullUrl value="urn:uuid:abd7a623-699e-46b5-5f5f-8d1d79fa33e6"/>
<resource>
<Patient>
<identifier>
<use value="usual"/>
<type>
<coding>
<system value="http://hl7.org/fhir/identifier-type"/>
<code value="NH"/>
</coding>
</type>
<system value="urn:oid:2.16.840.1.113883.2.1.4.1"/>
<value value="23455457476474754545"/>
<assigner>
<display value="NHS"/>
</assigner>
</identifier>
<identifier>
<use value="usual"/>
<type>
<coding>
<system value="http://hl7.org/fhir/identifier-type"/>
<code value="MR"/>
</coding>
</type>
<system value="http://www.ghh.org/identifiers/"/>
<value value="4567567567"/>
<assigner>
<display value="TCPAS"/>
</assigner>
</identifier>
<name>
<use value="official"/>
<family value="Connectathon"/>
<given value="Ann"/>
</name>
<telecom>
<system value="phone"/>
<value value="277543"/>
<use value="home"/>
</telecom>
<gender value="female"/>
<birthDate value="1928-05-24"/>
<address>
<line value="22 Stable Road"/>
<city value="Whitstable"/>
<state value="Kent"/>
<country value="CR5 1EL"/>
</address>
<contact>
<relationship>
<coding>
<system value="http://hl7.org/fhir/patient-contact-relationship"/>
<code value="parent"/>
</coding>
</relationship>
<name>
<family value="Connectathon"/>
<given value="Joe"/>
</name>
</contact>
</Patient>
</resource>
<request>
<method value="PUT"/>
<url value="/Patient?identifier=http://www.ghh.org/identifiers/|4567567567"/>
</request>
</entry>
<entry>
<fullUrl value="urn:uuid:491aabbf-fcff-4697-5bb2-84e856d5786b"/>
<resource>
<Encounter>
<identifier>
<use value="usual"/>
<type>
<coding>
<system value="http://hl7.org/fhir/identifier-type"/>
<code value="MR"/>
</coding>
</type>
<system value="http://general-hospital.co.uk/Identifiers/"/>
<value value="123447878787866666"/>
<assigner>
<display value="GENHOS"/>
</assigner>
</identifier>
<status value="in-progress"/>
<class value="inpatient"/>
<patient>
<reference value="urn:uuid:abd7a623-699e-46b5-5f5f-8d1d79fa33e6"/>
<display value="Connectathon, Ann(*24.05.1928)"/>
</patient>
<period>
<start value="2015-05-02T09:00:00+01:00"/>
</period>
</Encounter>
</resource>
<request>
<method value="PUT"/>
<url value="/Encounter?identifier=http://general-hospital.co.uk/Identifiers/|123447878787866666"/>
</request>
</entry>
</Bundle>

View File

@ -0,0 +1,73 @@
<Bundle xmlns="http://hl7.org/fhir" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<type value="transaction"/>
<entry>
<fullUrl value="uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d"/>
<resource>
<Patient>
<text>
<status value="generated"/>
<xhtml:div>
<xhtml:img src="http://pbs.twimg.com/profile_images/544507893991485440/r_vo3uj2_bigger.png" alt="Twitter Avatar"/>
@fhirabend
</xhtml:div>
</text>
<identifier>
<use value="secondary"/>
<system value="http://twitter.com"/>
<value value="2922960201"/>
</identifier>
<name>
<family value="fhirabend"/>
</name>
<photo>
<url value="http://pbs.twimg.com/profile_images/544507893991485440/r_vo3uj2_bigger.png"/>
</photo>
</Patient>
</resource>
<request>
<method value="PUT"/>
<url value="Patient?identifier=http://twitter.com|2922960201"/>
</request>
</entry>
<entry>
<resource>
<Communication>
<meta>
<profile value="http://foo/Profile1"/>
<tag>
<system value="http://twitter.com"/>
<code value="http://twitter.com/hashtag/FHIR"/>
<display value="#FHIR"/>
</tag>
</meta>
<text>
<status value="generated"/>
<xhtml:div>
<xhtml:b> @fhirabend:</xhtml:b>
RT @kainamti: Motto of the FHIR calendar 2016: the #FHIR is strong in you @HL7 http://t.co/E03Y3NIjME
</xhtml:div>
</text>
<identifier>
<system value="http://twitter.com"/>
<value value="650408456562823168"/>
</identifier>
<sender>
<reference value="uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d"/>
</sender>
<payload>
<contentString value="RT @kainamti: Motto of the FHIR calendar 2016: the #FHIR is strong in you @HL7 http://t.co/E03Y3NIjME"/>
</payload>
<medium>
<coding>
<code value="Twitter"/>
</coding>
</medium>
<sent value="2015-10-03T22:33:54+02:00"/>
</Communication>
</resource>
<request>
<method value="PUT"/>
<url value="Communication?identifier=http://twitter.com|650408456562823168"/>
</request>
</entry>
</Bundle>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bundle xmlns="http://hl7.org/fhir">
<id value="bundle-transaction"/>
<meta>
<lastUpdated value="2014-08-18T01:43:30Z"/>
</meta>
<type value="transaction"/>
<entry>
<fullUrl value="urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0b"/>
<resource>
<Medication>
<code>
<coding>
<system value="http://snomed.info/sct"/>
<code value="408036003"/>
<display value="Rosuvastatin 10mg tablet"/>
</coding>
</code>
</Medication>
</resource>
<request>
<method value="POST"/>
<url value="Medication"/>
<ifNoneExist value="code=408036003"/>
</request>o
</entry>
</Bundle>

View File

@ -98,7 +98,6 @@
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<!-- You may not need this if you are deploying to an application server which provides database connection pools itself. --> <!-- You may not need this if you are deploying to an application server which provides database connection pools itself. -->
@ -148,6 +147,11 @@
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-schematron</artifactId>
<version>${phloc_schematron_version}</version>
</dependency>
</dependencies> </dependencies>
@ -166,6 +170,9 @@
<webApp> <webApp>
<contextPath>/hapi-fhir-jpaserver-example</contextPath> <contextPath>/hapi-fhir-jpaserver-example</contextPath>
</webApp> </webApp>
<webAppConfig>
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
</webAppConfig>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@ -9,6 +9,7 @@ import org.springframework.web.context.WebApplicationContext;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
@ -80,7 +81,7 @@ public class JpaServerDemo extends RestfulServer {
setServerConformanceProvider(confProvider); setServerConformanceProvider(confProvider);
} else { } else {
IFhirSystemDao<Bundle> systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); IFhirSystemDao<Bundle> systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class);
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao); JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class));
confProvider.setImplementationDescription("Example Server"); confProvider.setImplementationDescription("Example Server");
setServerConformanceProvider(confProvider); setServerConformanceProvider(confProvider);
} }

View File

@ -14,6 +14,7 @@ import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
@ -83,7 +84,7 @@ public class TestRestfulServer extends RestfulServer {
systemProviderDstu2 = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class); systemProviderDstu2 = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class);
etagSupport = ETagSupportEnum.ENABLED; etagSupport = ETagSupportEnum.ENABLED;
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao); JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class));
confProvider.setImplementationDescription(implDesc); confProvider.setImplementationDescription(implDesc);
setServerConformanceProvider(confProvider); setServerConformanceProvider(confProvider);
baseUrlProperty = "fhir.baseurl.dstu2"; baseUrlProperty = "fhir.baseurl.dstu2";

View File

@ -1,10 +1,19 @@
package ca.uhn.fhirtest.mvc; package ca.uhn.fhirtest.mvc;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.to.BaseController; import ca.uhn.fhir.to.BaseController;
import ca.uhn.fhir.to.model.HomeRequest; import ca.uhn.fhir.to.model.HomeRequest;
@ -21,6 +30,30 @@ public class SubscriptionPlaygroundController extends BaseController {
ourLog.info(logPrefix(theModel) + "Displayed subscriptions playground page"); ourLog.info(logPrefix(theModel) + "Displayed subscriptions playground page");
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
//@formatter:off
Bundle resp = client
.search()
.forResource(Subscription.class)
// .where(Subscription.TYPE.exactly().code(SubscriptionChannelTypeEnum.WEBSOCKET.getCode()))
// .and(Subscription.STATUS.exactly().code(SubscriptionStatusEnum.ACTIVE.getCode()))
.returnBundle(Bundle.class)
.sort().descending(Subscription.TYPE)
.sort().ascending(Subscription.STATUS)
.execute();
//@formatter:off
List<Subscription> subscriptions = new ArrayList<Subscription>();
for (Entry next : resp.getEntry()) {
if (next.getResource() instanceof Subscription) {
subscriptions.add((Subscription) next.getResource());
}
}
theModel.put("subscriptions", subscriptions);
return "subscriptions"; return "subscriptions";
} }

View File

@ -9,6 +9,24 @@
</encoder> </encoder>
</appender> </appender>
<appender name="SUBSCRIPTION_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<file>${fhir.logdir}/subscription.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${fhir.logdir}/subscription.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{req.remoteAddr}] [%X{req.userAgent}] %-5level %logger{36} %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level> <level>DEBUG</level>
@ -39,19 +57,25 @@
</encoder> </encoder>
</appender> </appender>
<logger name="fhirtest.access" level="INFO" additivity="false"> <logger name="fhirtest.access" level="INFO" additivity="false">
<appender-ref ref="ACCESS" /> <appender-ref ref="ACCESS"/>
</logger> </logger>
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="true" level="debug">
<appender-ref ref="SUBSCRIPTION_FILE"/>
</logger>
<logger name="ca.uhn.fhir.jpa.subscription" additivity="true" level="debug">
<appender-ref ref="SUBSCRIPTION_FILE"/>
</logger>
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug"> <logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug">
<appender-ref ref="STDOUT" /> <appender-ref ref="FILE"/>
<appender-ref ref="FILE" /> <appender-ref ref="SUBSCRIPTION_FILE"/>
</logger> </logger>
</appender>
<root level="INFO"> <root level="INFO">
<appender-ref ref="FILE" /> <appender-ref ref="FILE"/>
</root> </root>
</configuration> </configuration>

View File

@ -17,6 +17,7 @@
<property name="subscriptionEnabled" value="true"></property> <property name="subscriptionEnabled" value="true"></property>
<property name="subscriptionPurgeInactiveAfterSeconds" value="3600" /> <!-- 1 hour --> <property name="subscriptionPurgeInactiveAfterSeconds" value="3600" /> <!-- 1 hour -->
<property name="subscriptionPollDelay" value="5000"></property> <property name="subscriptionPollDelay" value="5000"></property>
<property name="allowMultipleDelete" value="true"/>
</bean> </bean>
<util:list id="myServerInterceptors"> <util:list id="myServerInterceptors">
@ -54,6 +55,8 @@
<property name="loggerName" value="fhirtest.access"/> <property name="loggerName" value="fhirtest.access"/>
<property name="messageFormat" <property name="messageFormat"
value="Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]"/> value="Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]"/>
<property name="logExceptions" value="true"/>
<property name="errorMessageFormat" value="ERROR - ${requestVerb} ${requestUrl}"/>
</bean> </bean>
</beans> </beans>

View File

@ -30,99 +30,153 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="container-fluid"> <div class="container-fluid">
<p> <p>This page is a test playground for WebSocket
This page is a test playground for WebSocket Subscriptions Subscriptions</p>
</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Nav tabs --> <!-- Subscription Creation -->
<ul class="nav nav-tabs" role="tablist"> <div id="subscription_creation">
<li role="presentation" class="active"><a href="#wss"
aria-controls="wss" role="tab" data-toggle="tab">Static
Websocket</a></li>
<li role="presentation"><a href="#wsd" aria-controls="wsd"
role="tab" data-toggle="tab">Dynamic Websocket</a></li>
</ul>
<!-- Tab panes --> <!-- Nav tabs -->
<div class="tab-content"> <ul class="nav nav-tabs" role="tablist">
<div role="tabpanel" class="tab-pane active" id="wss">...1</div> <li role="presentation" class="active"><a href="#wss"
aria-controls="wss" role="tab" data-toggle="tab">Static
<!-- Dynamic WebSocket --> Websocket</a></li>
<div role="tabpanel" class="tab-pane" id="wsd"> <li role="presentation"><a href="#wsd" aria-controls="wsd"
<script type="text/javascript"> role="tab" data-toggle="tab">Dynamic Websocket</a></li>
function dwsCreate() { </ul>
try {
var socket = new WebSocket("ws://localhost:8888/websocket/dstu2"); <!-- Tab panes -->
alert('Socket Status: '+socket.readyState); <div class="tab-content">
socket.onopen = function() { <!-- Static Websocket -->
alert("Opening Socket: " + socket.readyState); <div role="tabpanel" class="tab-pane active" id="wss">
socket.send("bind Observation?");
} <table class="table table-striped table-condensed">
socket.onmessage = function(msg) { <tr>
alert("New message: " + msg); <td>ID</td>
} <td>Criteria</td>
socket.onerror = function(error) { <td>Type</td>
alert("error: " + error); <td>Status</td>
} <td></td>
} catch (exception) { </tr>
alert('Error'+exception); <tr th:each="subscription : ${subscriptions}">
} <td><a th:href="${subscription.id}" th:text="${subscription.id.toUnqualifiedVersionless().getIdPart()}"/>
return false; </td>
} <td th:text="${subscription.criteria}" style="max-width: 200px;"/>
</script> <td th:text="${subscription.channel.type}" />
<td th:text="${subscription.status}" />
<div class="row"> <td>
<div class="col-sm-12"> <button th:if="${subscription.channel.type} == 'websocket' AND ${subscription.status} == 'active'" type="button" class="btn btn-default btn-primary btn-block" id="dws_create" th:onclick="'dwsCreate(\'' + ${subscription.id.getIdPart()} + '\');'"> <i class="glyphicon glyphicon-cog"></i> Subscribe</button>
Enter a criteria in the text box below and then click subscribe to </td>
create a dynamic subscription and then display the results as they </tr>
arrive. </table>
</div>
<p th:if="${subscriptions.isEmpty()}">There are
currently no subscriptions on this server.</p>
</div>
<!-- Dynamic WebSocket -->
<div role="tabpanel" class="tab-pane" id="wsd">
<div class="row">
<div class="col-sm-12">Enter a criteria in the text box
below and then click subscribe to create a dynamic
subscription and then display the results as they arrive.</div>
</div>
<div class="row" style="boorder-top: 10px;">
<div class="col-sm-7">
<div class="input-group">
<div class="input-group-addon">Criteria</div>
<input class="form-control"
placeholder="e.g: Observation?patient=123" id="dws_criteria" />
</div> </div>
</div>
<div class="row" style="boorder-top: 10px;"> <div class="col-sm-3">
<div class="col-sm-7"> <button type="button"
<div class="input-group"> class="btn btn-default btn-primary btn-block" id="dws_create"
<div class="input-group-addon"> onclick="dwsCreate(document.getElementById('dws_criteria').value, true);">
Criteria <i class="glyphicon glyphicon-cog"></i> Subscribe
</div> </button>
<input class="form-control" placeholder="e.g: Observation?patient=123" id="dws_criteria"/>
</div>
</div>
<div class="col-sm-3">
<button type="button" class="btn btn-default btn-primary btn-block" id="dws_create" onclick="dwsCreate();"><i class="glyphicon glyphicon-cog"></i> Subscribe</button>
</div>
</div>
</div> </div>
</div> </div>
<!-- Dynamic Subscription Results -->
<div class="panel panel-default">
<div class="panel-heading">Subscription Results</div>
<div class="panel-body">
<p>
This will be status...
</p>
</div> </div>
<!-- List group -->
<ul class="list-group">
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
</ul>
</div> </div>
</div> </div>
<!-- Subscription Results -->
<div class="panel panel-default" style="display: none;"
id="resultsDiv">
<div class="panel-heading">Subscription Results</div>
<ul class="list-group" id="subscription_status_list">
</ul>
</div>
</div> </div>
<script type="text/javascript">
function messageStatus(message) {
var listItem = $('<li />', {
'class' : 'list-group-item'
});
listItem.append($('<div class="glyphicon glyphicon-info-sign" style="width: 20px;"/>'));
listItem.append($('<span>' + message + '</span>'));
$('#subscription_status_list').append(listItem);
}
function messageIn(message, remainder) {
var listItem = $('<li />', {
'class' : 'list-group-item'
});
listItem.append($('<div class="glyphicon glyphicon-download" style="width: 20px;"/>'));
listItem.append($('<span>' + message + '</span>'));
if (remainder) {
listItem.append($('<p>(' + remainder + ' bytes)</p>'));
listItem.append($('<pre style="display:none;"/>').text(remainder));
}
$('#subscription_status_list').append(listItem);
}
function dwsCreate(bindArgument, dynamic) {
$('#resultsDiv').css("display", "");
$('#subscription_creation').hide();
try {
var url = "ws://fhirtest.uhn.ca/websocket/dstu2";
messageStatus("Connecting to: " + url);
var socket = new WebSocket(url);
socket.onopen = function() {
messageStatus("Connected to: " + url);
$('#subscription_status_list').append($('<li />', {
'class' : 'list-group-item'
}).append($('<div class="glyphicon glyphicon-upload" style="width: 20px;"/>')).append($('<span>bind ' + bindArgument + '</span>')));
socket.send("bind " + bindArgument);
}
// This function is called when a new message comes from the server
socket.onmessage = function(msg) {
var data = msg.data;
var remainder = null;
if (data.indexOf('\n') != -1) {
data = data.substring(0, data.indexOf('\n'));
remainder = data.substring(data.indexOf('\n') + 1);
}
messageIn(data, remainder);
}
socket.onerror = function(error) {
messageStatus("Error: " + error);
}
} catch (exception) {
messageStatus("Error: " + exception);
}
return false;
}
</script>
</div> </div>
</div> </div>

View File

@ -12,6 +12,9 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.resource.Organization; import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.IGenericClient;
@ -21,7 +24,7 @@ public class UhnFhirTestApp {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
int myPort = 8888; int myPort = 8888;
String base = "http://localhost:" + myPort + "/base"; String base = "http://localhost:" + myPort + "/baseDstu2";
// new File("target/testdb").mkdirs(); // new File("target/testdb").mkdirs();
System.setProperty("fhir.db.location", "./target/testdb"); System.setProperty("fhir.db.location", "./target/testdb");
@ -47,13 +50,13 @@ public class UhnFhirTestApp {
// base = "http://spark.furore.com/fhir"; // base = "http://spark.furore.com/fhir";
if (true) { if (true) {
FhirContext ctx = new FhirContext(); FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient(base); IGenericClient client = ctx.newRestfulGenericClient(base);
// client.setLogRequestAndResponse(true); // client.setLogRequestAndResponse(true);
Organization o1 = new Organization(); Organization o1 = new Organization();
o1.getName().setValue("Some Org"); o1.getName().setValue("Some Org");
MethodOutcome create = client.create(o1); MethodOutcome create = client.create().resource(o1).execute();
IdDt orgId = (IdDt) create.getId(); IdDt orgId = (IdDt) create.getId();
Patient p1 = new Patient(); Patient p1 = new Patient();
@ -64,11 +67,14 @@ public class UhnFhirTestApp {
TagList list = new TagList(); TagList list = new TagList();
list.addTag("http://hl7.org/fhir/tag", "urn:happytag", "This is a happy resource"); list.addTag("http://hl7.org/fhir/tag", "urn:happytag", "This is a happy resource");
ResourceMetadataKeyEnum.TAG_LIST.put(p1, list); ResourceMetadataKeyEnum.TAG_LIST.put(p1, list);
client.create(p1); client.create().resource(p1).execute();
List<IResource> resources = ctx.newJsonParser().parseBundle(IOUtils.toString(UhnFhirTestApp.class.getResourceAsStream("/bundle.json"))).toListOfResources();
// client.transaction().withResources(resources).execute();
Subscription subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setCriteria("Observation?");
client.create().resource(subs).execute();
// for (int i = 0; i < 1000; i++) { // for (int i = 0; i < 1000; i++) {
// //
// Patient p = (Patient) resources.get(0); // Patient p = (Patient) resources.get(0);
@ -78,22 +84,22 @@ public class UhnFhirTestApp {
// client.transaction(resources); // client.transaction(resources);
// } // }
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
client.create(p1); client.create().resource(p1).execute();
} }

View File

@ -57,7 +57,7 @@
<Embed-Dependency>*;scope=!provided|test</Embed-Dependency> <Embed-Dependency>*;scope=!provided|test</Embed-Dependency>
<Embed-Directory>lib</Embed-Directory> <Embed-Directory>lib</Embed-Directory>
<Embed-Transitive>true</Embed-Transitive> <Embed-Transitive>true</Embed-Transitive>
<_removeheaders>Built-By</_removeheaders> <_removeheaders>Built-By, Include-Resource, Private-Package</_removeheaders>
<!-- <Private-Package>org.foo.myproject.*</Private-Package> <Bundle-Activator>org.foo.myproject.impl1.Activator</Bundle-Activator> --> <!-- <Private-Package>org.foo.myproject.*</Private-Package> <Bundle-Activator>org.foo.myproject.impl1.Activator</Bundle-Activator> -->
</instructions> </instructions>
</configuration> </configuration>
@ -70,6 +70,24 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<phase>integration-test</phase>
</execution>
</executions>
</plugin>
</plugins> </plugins>
<resources> <resources>
<resource> <resource>

View File

@ -0,0 +1,26 @@
package ca.uhn.fhir.osgi;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.io.FileReader;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
public class ManifestIT {
/**
* See #234
*/
@Test
public void testValidateManifest() throws Exception {
FileReader r = new FileReader("./target/classes/META-INF/MANIFEST.MF");
String file = IOUtils.toString(r);
assertThat(file, not(containsString(".jar=/")));
}
}

View File

@ -178,7 +178,12 @@ public class GenericClientTest {
assertThat(extractBody(capt, count), containsString("value=\"John\"")); assertThat(extractBody(capt, count), containsString("value=\"John\""));
count++; count++;
client.create().resource(ourCtx.newJsonParser().encodeResourceToString(p1)).execute(); String resourceAsString = ourCtx.newJsonParser().encodeResourceToString(p1);
client
.create()
.resource(resourceAsString)
.execute();
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length); assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.JSON.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertEquals(EncodingEnum.JSON.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, count), containsString("[\"John\"]")); assertThat(extractBody(capt, count), containsString("[\"John\"]"));

View File

@ -69,9 +69,6 @@ import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class SearchSearchServerDstu1Test { public class SearchSearchServerDstu1Test {
private static CloseableHttpClient ourClient; private static CloseableHttpClient ourClient;
@ -559,9 +556,6 @@ public class SearchSearchServerDstu1Test {
} }
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider { public static class DummyPatientResourceProvider implements IResourceProvider {
/** /**

View File

@ -54,6 +54,39 @@ public class JsonParserDstu2Test {
private static final FhirContext ourCtx = FhirContext.forDstu2(); private static final FhirContext ourCtx = FhirContext.forDstu2();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu2Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu2Test.class);
@Test
public void testNamespacePreservationEncode() throws Exception {
//@formatter:off
String input = "<Patient xmlns=\"http://hl7.org/fhir\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">" +
"<text>" +
"<xhtml:div>" +
"<xhtml:img src=\"foo\"/>" +
"@fhirabend" +
"</xhtml:div>" +
"</text>" +
"</Patient>";
//@formatter:on
Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, input);
String expected = "<xhtml:div xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"><xhtml:img src=\"foo\"/>@fhirabend</xhtml:div>";
assertEquals(expected, parsed.getText().getDiv().getValueAsString());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
ourLog.info(encoded);
assertThat(encoded, containsString("\"div\":\"" + expected.replace("\"", "\\\"") + "\""));
}
@Test
public void testNamespacePreservationParse() throws Exception {
String input = "{\"resourceType\":\"Patient\",\"text\":{\"div\":\"<xhtml:div xmlns:xhtml=\\\"http://www.w3.org/1999/xhtml\\\"><xhtml:img src=\\\"foo\\\"/>@fhirabend</xhtml:div>\"}}";
Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, input);
assertEquals("<xhtml:div xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"><xhtml:img src=\"foo\"/>@fhirabend</xhtml:div>", parsed.getText().getDiv().getValueAsString());
String encoded = ourCtx.newXmlParser().encodeResourceToString(parsed);
assertEquals("<Patient xmlns=\"http://hl7.org/fhir\"><text><xhtml:div xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"><xhtml:img src=\"foo\"/>@fhirabend</xhtml:div></text></Patient>",encoded);
}
@Test @Test
public void testEncodeAndParseExtensions() throws Exception { public void testEncodeAndParseExtensions() throws Exception {
@ -98,8 +131,10 @@ public class JsonParserDstu2Test {
assertThat(enc, Matchers.stringContainsInOrder("{\"resourceType\":\"Patient\",", "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}", assertThat(enc, Matchers.stringContainsInOrder("{\"resourceType\":\"Patient\",", "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}",
"{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}")); "{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}"));
assertThat(enc, Matchers.stringContainsInOrder("\"modifierExtension\":[" + "{" + "\"url\":\"http://example.com/extensions#modext\"," + "\"valueDate\":\"1995-01-02\"" + "}" + "],")); assertThat(enc, Matchers.stringContainsInOrder("\"modifierExtension\":[" + "{" + "\"url\":\"http://example.com/extensions#modext\"," + "\"valueDate\":\"1995-01-02\"" + "}" + "],"));
assertThat(enc, containsString("\"_given\":[" + "{" + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext\"," + "\"valueString\":\"given\"" + "}" + "]" + "}," + "{" + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext_parent\"," + "\"extension\":[" + "{" assertThat(enc,
+ "\"url\":\"http://examples.com#givenext_child\"," + "\"valueString\":\"CHILD\"" + "}" + "]" + "}" + "]" + "}")); containsString("\"_given\":[" + "{" + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext\"," + "\"valueString\":\"given\"" + "}" + "]" + "}," + "{" + "\"extension\":[" + "{"
+ "\"url\":\"http://examples.com#givenext_parent\"," + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext_child\"," + "\"valueString\":\"CHILD\"" + "}" + "]" + "}"
+ "]" + "}"));
/* /*
* Now parse this back * Now parse this back
@ -402,7 +437,8 @@ public class JsonParserDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\":\"foo\",", "\"code\":\"bar\"", "\"system\":\"" + Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\":\"" + Constants.TAG_SUBSETTED_CODE + "\",")); assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\":\"foo\",", "\"code\":\"bar\"", "\"system\":\"" + Constants.TAG_SUBSETTED_SYSTEM + "\",",
"\"code\":\"" + Constants.TAG_SUBSETTED_CODE + "\","));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus"))); assertThat(encoded, not(containsString("maritalStatus")));
@ -423,7 +459,8 @@ public class JsonParserDstu2Test {
String enc = ourCtx.newJsonParser().encodeResourceToString(pt); String enc = ourCtx.newJsonParser().encodeResourceToString(pt);
ourLog.info(enc); ourLog.info(enc);
assertEquals("{\"resourceType\":\"Patient\",\"meta\":{\"tag\":[{\"system\":\"scheme\",\"code\":\"term\",\"display\":\"display\"}]},\"identifier\":[{\"system\":\"sys\",\"value\":\"val\"}]}", enc); assertEquals("{\"resourceType\":\"Patient\",\"meta\":{\"tag\":[{\"system\":\"scheme\",\"code\":\"term\",\"display\":\"display\"}]},\"identifier\":[{\"system\":\"sys\",\"value\":\"val\"}]}",
enc);
} }
@ -517,7 +554,7 @@ public class JsonParserDstu2Test {
assertEquals(exp, act); assertEquals(exp, act);
} }
/** /**
* Test for #146 * Test for #146
*/ */
@ -636,9 +673,9 @@ public class JsonParserDstu2Test {
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-transaction2.json")); String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-transaction2.json"));
ourCtx.newJsonParser().parseBundle(content); ourCtx.newJsonParser().parseBundle(content);
ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content); ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content);
// TODO: preserve comments // TODO: preserve comments
} }
@ -857,7 +894,7 @@ public class JsonParserDstu2Test {
assertEquals(exp, act); assertEquals(exp, act);
} }
@Test @Test
public void testParsePatientInBundle() { public void testParsePatientInBundle() {

View File

@ -27,6 +27,7 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine; import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -42,6 +43,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; import ca.uhn.fhir.model.dstu2.resource.Bundle.Link;
@ -69,8 +71,11 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
public class GenericClientDstu2Test { public class GenericClientDstu2Test {
private static FhirContext ourCtx; private static FhirContext ourCtx;
private HttpClient myHttpClient; private HttpClient myHttpClient;
private HttpResponse myHttpResponse; private HttpResponse myHttpResponse;
private int myResponseCount = 0;
@Before @Before
public void before() { public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
@ -106,6 +111,133 @@ public class GenericClientDstu2Test {
return msg; return msg;
} }
@Test
public void testAcceptHeaderFetchConformance() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
idx++;
client.fetchConformance().ofType(Conformance.class).encodedJson().execute();
assertEquals("http://example.com/fhir/metadata?_format=json", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
idx++;
client.fetchConformance().ofType(Conformance.class).encodedXml().execute();
assertEquals("http://example.com/fhir/metadata?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
idx++;
}
@Test
public void testAcceptHeaderPreflightConformance() throws Exception {
String methodName = "testAcceptHeaderPreflightConformance";
final IParser p = ourCtx.newXmlParser();
final Conformance conf = new Conformance();
conf.setCopyright("COPY");
final Patient patient = new Patient();
patient.addName().addFamily("FAMILY");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myResponseCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(conf)), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(patient)), Charset.forName("UTF-8"));
}
}
});
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = ourCtx.newRestfulGenericClient("http://" + methodName + ".example.com/fhir");
Patient resp = client.read(Patient.class, new IdDt("123"));
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://" + methodName + ".example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertEquals("http://" + methodName + ".example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
}
@Test
public void testAcceptHeaderPreflightConformancePreferJson() throws Exception {
String methodName = "testAcceptHeaderPreflightConformancePreferJson";
final IParser p = ourCtx.newXmlParser();
final Conformance conf = new Conformance();
conf.setCopyright("COPY");
final Patient patient = new Patient();
patient.addName().addFamily("FAMILY");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myResponseCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(conf)), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(patient)), Charset.forName("UTF-8"));
}
}
});
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = ourCtx.newRestfulGenericClient("http://" + methodName + ".example.com/fhir");
client.setEncoding(EncodingEnum.JSON);
Patient resp = client.read(Patient.class, new IdDt("123"));
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://" + methodName + ".example.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), not(containsString(Constants.CT_FHIR_XML)));
assertEquals("http://" + methodName + ".example.com/fhir/Patient/123?_format=json", capt.getAllValues().get(1).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), not(containsString(Constants.CT_FHIR_XML)));
}
@Test @Test
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public void testConformance() throws Exception { public void testConformance() throws Exception {
@ -141,136 +273,6 @@ public class GenericClientDstu2Test {
} }
@Test
public void testAcceptHeaderFetchConformance() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
Conformance resp = (Conformance)client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
idx++;
resp = (Conformance)client.fetchConformance().ofType(Conformance.class).encodedJson().execute();
assertEquals("http://example.com/fhir/metadata?_format=json", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
idx++;
resp = (Conformance)client.fetchConformance().ofType(Conformance.class).encodedXml().execute();
assertEquals("http://example.com/fhir/metadata?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
idx++;
}
private int myResponseCount = 0;
@Test
public void testAcceptHeaderPreflightConformancePreferJson() throws Exception {
String methodName = "testAcceptHeaderPreflightConformancePreferJson";
final IParser p = ourCtx.newXmlParser();
final Conformance conf = new Conformance();
conf.setCopyright("COPY");
final Patient patient = new Patient();
patient.addName().addFamily("FAMILY");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myResponseCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(conf)), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(patient)), Charset.forName("UTF-8"));
}
}
});
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = ourCtx.newRestfulGenericClient("http://"+methodName+".example.com/fhir");
client.setEncoding(EncodingEnum.JSON);
Patient resp = client.read(Patient.class, new IdDt("123"));
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://"+methodName+".example.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), not(containsString(Constants.CT_FHIR_XML)));
assertEquals("http://"+methodName+".example.com/fhir/Patient/123?_format=json", capt.getAllValues().get(1).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), not(containsString(Constants.CT_FHIR_XML)));
}
@Test
public void testAcceptHeaderPreflightConformance() throws Exception {
String methodName = "testAcceptHeaderPreflightConformance";
final IParser p = ourCtx.newXmlParser();
final Conformance conf = new Conformance();
conf.setCopyright("COPY");
final Patient patient = new Patient();
patient.addName().addFamily("FAMILY");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myResponseCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(conf)), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(patient)), Charset.forName("UTF-8"));
}
}
});
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = ourCtx.newRestfulGenericClient("http://"+methodName+".example.com/fhir");
Patient resp = client.read(Patient.class, new IdDt("123"));
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://"+methodName+".example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
assertEquals("http://"+methodName+".example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toASCIIString());
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
}
@Test @Test
public void testCreate() throws Exception { public void testCreate() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -382,44 +384,6 @@ public class GenericClientDstu2Test {
idx++; idx++;
} }
@SuppressWarnings("deprecation")
@Test
public void testUpdateNonFluent() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
Patient p = new Patient();
p.addName().addFamily("FOOFAMILY");
client.update(new IdDt("Patient/123"), p);
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
idx++;
client.update("123", p);
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
idx++;
}
@Test @Test
public void testCreatePrefer() throws Exception { public void testCreatePrefer() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -450,7 +414,7 @@ public class GenericClientDstu2Test {
idx++; idx++;
} }
@Test @Test
public void testCreateReturningResourceBody() throws Exception { public void testCreateReturningResourceBody() throws Exception {
Patient p = new Patient(); Patient p = new Patient();
@ -535,7 +499,7 @@ public class GenericClientDstu2Test {
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod()); assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString()); assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
idx++; idx++;
client.delete(Patient.class, "123"); client.delete(Patient.class, "123");
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod()); assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString()); assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
@ -631,7 +595,8 @@ public class GenericClientDstu2Test {
.since(new InstantDt("2001-01-02T11:22:33Z")) .since(new InstantDt("2001-01-02T11:22:33Z"))
.execute(); .execute();
//@formatter:on //@formatter:on
assertThat(capt.getAllValues().get(idx).getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://example.com/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertThat(capt.getAllValues().get(idx).getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123"))
.or(equalTo("http://example.com/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z")));
assertEquals(1, response.getEntry().size()); assertEquals(1, response.getEntry().size());
idx++; idx++;
@ -646,7 +611,7 @@ public class GenericClientDstu2Test {
assertThat(capt.getAllValues().get(idx).getURI().toString(), containsString("_since=2001-01")); assertThat(capt.getAllValues().get(idx).getURI().toString(), containsString("_since=2001-01"));
assertEquals(1, response.getEntry().size()); assertEquals(1, response.getEntry().size());
idx++; idx++;
} }
@Test @Test
public void testMetaAdd() throws Exception { public void testMetaAdd() throws Exception {
@ -684,12 +649,12 @@ public class GenericClientDstu2Test {
assertEquals("http://example.com/fhir/Patient/123/$meta-add", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("http://example.com/fhir/Patient/123/$meta-add", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"meta\"/><valueMeta><profile value=\"urn:profile:in\"/></valueMeta></parameter></Parameters>", extractBody(capt, idx)); assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"meta\"/><valueMeta><profile value=\"urn:profile:in\"/></valueMeta></parameter></Parameters>",
extractBody(capt, idx));
idx++; idx++;
} }
@Test @Test
public void testMetaGet() throws Exception { public void testMetaGet() throws Exception {
IParser p = ourCtx.newXmlParser(); IParser p = ourCtx.newXmlParser();
@ -946,9 +911,8 @@ public class GenericClientDstu2Test {
Parameters outParams = client.operation().onInstance(new IdDt("Patient", "18066")).named("$everything").withParameters(inParams).execute(); Parameters outParams = client.operation().onInstance(new IdDt("Patient", "18066")).named("$everything").withParameters(inParams).execute();
/* /*
* Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation * Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation methods return a Parameters instance however, so HAPI creates a Parameters object
* methods return a Parameters instance however, so HAPI creates a Parameters object with a single parameter * with a single parameter containing the value.
* containing the value.
*/ */
ca.uhn.fhir.model.dstu2.resource.Bundle responseBundle = (ca.uhn.fhir.model.dstu2.resource.Bundle) outParams.getParameter().get(0).getResource(); ca.uhn.fhir.model.dstu2.resource.Bundle responseBundle = (ca.uhn.fhir.model.dstu2.resource.Bundle) outParams.getParameter().get(0).getResource();
@ -1001,6 +965,121 @@ public class GenericClientDstu2Test {
idx++; idx++;
} }
@Test
public void testOperationWithInlineParams() throws Exception {
IParser p = ourCtx.newXmlParser();
Parameters outParams = new Parameters();
outParams.addParameter().setValue(new StringDt("STRINGVALOUT1"));
outParams.addParameter().setValue(new StringDt("STRINGVALOUT2"));
final String respString = p.encodeResourceToString(outParams);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
//@formatter:off
Parameters resp = client
.operation()
.onServer()
.named("$SOMEOPERATION")
.withParameter(Parameters.class, "name1", new StringDt("value1"))
.andParameter("name2", new StringDt("value1"))
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(respString, p.encodeResourceToString(resp));
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"name1\"/><valueString value=\"value1\"/></parameter><parameter><name value=\"name2\"/><valueString value=\"value1\"/></parameter></Parameters>",
(extractBody(capt, idx)));
idx++;
/*
* Composite type
*/
//@formatter:off
resp = client
.operation()
.onServer()
.named("$SOMEOPERATION")
.withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1"))
.andParameter("name2", new StringDt("value1"))
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(respString, p.encodeResourceToString(resp));
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"name1\"/><valueIdentifier><system value=\"system1\"/><value value=\"value1\"/></valueIdentifier></parameter><parameter><name value=\"name2\"/><valueString value=\"value1\"/></parameter></Parameters>",
(extractBody(capt, idx)));
idx++;
/*
* Resource
*/
//@formatter:off
resp = client
.operation()
.onServer()
.named("$SOMEOPERATION")
.withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1"))
.andParameter("name2", new Patient().setActive(true))
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals(respString, p.encodeResourceToString(resp));
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"name1\"/><valueIdentifier><system value=\"system1\"/><value value=\"value1\"/></valueIdentifier></parameter><parameter><name value=\"name2\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><active value=\"true\"/></Patient></resource></parameter></Parameters>",
(extractBody(capt, idx)));
idx++;
}
@Test(expected = IllegalArgumentException.class)
public void testOperationWithInvalidParam() {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
// Who knows what the heck this is!
IBase weirdBase = new IBase() {
private static final long serialVersionUID = 1L;
@Override
public boolean isEmpty() {
return false;
}
};
//@formatter:off
client
.operation()
.onServer()
.named("$SOMEOPERATION")
.withParameter(Parameters.class, "name1", weirdBase)
.execute();
//@formatter:on
}
@Test @Test
public void testOperationWithListOfParameterResponse() throws Exception { public void testOperationWithListOfParameterResponse() throws Exception {
IParser p = ourCtx.newXmlParser(); IParser p = ourCtx.newXmlParser();
@ -1315,11 +1394,7 @@ public class GenericClientDstu2Test {
Patient response; Patient response;
int idx = 0; int idx = 0;
response = (Patient)client response = (Patient) client.read().resource(Patient.class).withUrl(new IdDt("http://domain2.example.com/base/Patient/123")).execute();
.read()
.resource(Patient.class)
.withUrl(new IdDt("http://domain2.example.com/base/Patient/123"))
.execute();
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
} }
@ -1369,7 +1444,7 @@ public class GenericClientDstu2Test {
assertEquals("2015-06-22T15:48:57.554-04:00", ResourceMetadataKeyEnum.UPDATED.get(response).getValueAsString()); assertEquals("2015-06-22T15:48:57.554-04:00", ResourceMetadataKeyEnum.UPDATED.get(response).getValueAsString());
} }
@Test @Test
public void testReadWithElementsParam() throws Exception { public void testReadWithElementsParam() throws Exception {
String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}"; String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}";
@ -1390,7 +1465,8 @@ public class GenericClientDstu2Test {
.execute(); .execute();
//@formatter:on //@formatter:on
assertThat(capt.getValue().getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient/123?_elements=identifier%2Cname"))); assertThat(capt.getValue().getURI().toString(),
either(equalTo("http://example.com/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient/123?_elements=identifier%2Cname")));
assertEquals(Patient.class, response.getClass()); assertEquals(Patient.class, response.getClass());
} }
@ -1420,8 +1496,7 @@ public class GenericClientDstu2Test {
} }
//@formatter:on //@formatter:on
} }
@Test @Test
public void testReadWithSummaryParamHtml() throws Exception { public void testReadWithSummaryParamHtml() throws Exception {
String msg = "<div>HELP IM A DIV</div>"; String msg = "<div>HELP IM A DIV</div>";
@ -1524,7 +1599,8 @@ public class GenericClientDstu2Test {
.execute(); .execute();
//@formatter:on //@formatter:on
assertThat(capt.getValue().getURI().toString(), either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertThat(capt.getValue().getURI().toString(),
either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname")));
assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass());
} }
@ -1835,6 +1911,43 @@ public class GenericClientDstu2Test {
} }
@SuppressWarnings("deprecation")
@Test
public void testUpdateNonFluent() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
Patient p = new Patient();
p.addName().addFamily("FOOFAMILY");
client.update(new IdDt("Patient/123"), p);
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
idx++;
client.update("123", p);
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
idx++;
}
@Test @Test
public void testUpdatePrefer() throws Exception { public void testUpdatePrefer() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -1924,7 +2037,9 @@ public class GenericClientDstu2Test {
response = client.validate().resource(p).execute(); response = client.validate().resource(p).execute();
assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx)); assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>",
extractBody(capt, idx));
assertNotNull(response.getOperationOutcome()); assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue());
idx++; idx++;
@ -1932,7 +2047,9 @@ public class GenericClientDstu2Test {
response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute();
assertEquals("http://example.com/fhir/Patient/$validate?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("http://example.com/fhir/Patient/$validate?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx)); assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>",
extractBody(capt, idx));
assertNotNull(response.getOperationOutcome()); assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue());
idx++; idx++;
@ -1986,7 +2103,9 @@ public class GenericClientDstu2Test {
assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx)); assertEquals(
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>",
extractBody(capt, idx));
assertNotNull(response.getOperationOutcome()); assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue());
idx++; idx++;

View File

@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
/** /**
@ -78,6 +79,30 @@ public class LoggingInterceptorDstu2Test {
assertThat(captor.getValue(), StringContains.containsString("read - Patient/1")); assertThat(captor.getValue(), StringContains.containsString("read - Patient/1"));
} }
@Test
public void testException() throws Exception {
LoggingInterceptor interceptor = new LoggingInterceptor();
interceptor.setLogExceptions(true);
assertTrue(interceptor.isLogExceptions());
interceptor.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}");
assertEquals("ERROR - ${requestVerb} ${requestUrl}", interceptor.getErrorMessageFormat());
servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor));
Logger logger = mock(Logger.class);
interceptor.setLogger(logger);
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/EX");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.closeQuietly(status.getEntity().getContent());
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(logger, times(2)).info(captor.capture());
assertThat(captor.getAllValues().get(0), StringContains.containsString("read - Patient/EX"));
assertThat(captor.getAllValues().get(1), StringContains.containsString("ERROR - GET http://localhost:"+ourPort+"/Patient/EX"));
}
@Test @Test
public void testSearch() throws Exception { public void testSearch() throws Exception {
@ -242,6 +267,9 @@ public class LoggingInterceptorDstu2Test {
*/ */
@Read() @Read()
public Patient getResourceById(@IdParam IdDt theId) { public Patient getResourceById(@IdParam IdDt theId) {
if (theId.getIdPart().equals("EX")) {
throw new InvalidRequestException("FOO");
}
String key = theId.getIdPart(); String key = theId.getIdPart();
Patient retVal = getIdToPatient().get(key); Patient retVal = getIdToPatient().get(key);
return retVal; return retVal;

View File

@ -14,22 +14,8 @@ import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.formats.IParser;
import org.hl7.fhir.instance.formats.ParserType;
import org.hl7.fhir.instance.model.ConceptMap;
import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity; import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.instance.model.Resource;
import org.hl7.fhir.instance.model.StructureDefinition; import org.hl7.fhir.instance.model.StructureDefinition;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.terminologies.ValueSetExpander;
import org.hl7.fhir.instance.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.instance.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.instance.utils.EOperationOutcome;
import org.hl7.fhir.instance.utils.INarrativeGenerator;
import org.hl7.fhir.instance.utils.IWorkerContext;
import org.hl7.fhir.instance.validation.IResourceValidator;
import org.hl7.fhir.instance.validation.IResourceValidator.BestPracticeWarningLevel; import org.hl7.fhir.instance.validation.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.instance.validation.ValidationMessage; import org.hl7.fhir.instance.validation.ValidationMessage;
import org.w3c.dom.Document; import org.w3c.dom.Document;
@ -46,7 +32,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.IValidationSupport.CodeValidationResult;
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule { public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
@ -151,7 +136,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
} }
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) { protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx); HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
org.hl7.fhir.instance.validation.InstanceValidator v; org.hl7.fhir.instance.validation.InstanceValidator v;
try { try {
@ -260,125 +245,4 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
return profile; return profile;
} }
private final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory, ValueSetExpander {
private final FhirContext myCtx;
private HapiWorkerContext(FhirContext theCtx) {
myCtx = theCtx;
}
@Override
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
return myValidationSupport.expandValueSet(myCtx, theInc);
}
@Override
public ValueSetExpansionOutcome expandVS(ValueSet theSource) {
throw new UnsupportedOperationException();
}
@Override
public ValueSet fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) {
return null;
} else {
return myValidationSupport.fetchCodeSystem(myCtx, theSystem);
}
}
@Override
public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) throws EOperationOutcome, Exception {
if (myValidationSupport == null) {
return null;
} else {
return myValidationSupport.fetchResource(myCtx, theClass, theUri);
}
}
@Override
public INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(ParserType theType) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(String theType) {
throw new UnsupportedOperationException();
}
@Override
public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) {
throw new UnsupportedOperationException();
}
@Override
public IParser newJsonParser() {
throw new UnsupportedOperationException();
}
@Override
public IResourceValidator newValidator() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public IParser newXmlParser() {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsSystem(String theSystem) {
if (myValidationSupport == null) {
return false;
} else {
return myValidationSupport.isCodeSystemSupported(myCtx, theSystem);
}
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
if (result == null) {
return null;
}
return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition());
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay,
ConceptSetComponent theVsi) {
throw new UnsupportedOperationException();
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
throw new UnsupportedOperationException();
}
@Override
public ValueSetExpander getExpander() {
return this;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet theSource) throws ETooCostly, Exception {
ValueSetExpander vse = new ValueSetExpanderSimple(this, this);
ValueSetExpansionOutcome vso = vse.expand(theSource);
if (vso.getError() != null) {
return null;
} else {
return vso;
}
}
@Override
public List<ConceptMap> findMapsForSource(String theUrl) {
throw new UnsupportedOperationException();
}
}
} }

View File

@ -0,0 +1,144 @@
package ca.uhn.fhir.validation;
import java.util.List;
import org.hl7.fhir.instance.formats.IParser;
import org.hl7.fhir.instance.formats.ParserType;
import org.hl7.fhir.instance.model.ConceptMap;
import org.hl7.fhir.instance.model.Resource;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.terminologies.ValueSetExpander;
import org.hl7.fhir.instance.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.instance.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.instance.utils.EOperationOutcome;
import org.hl7.fhir.instance.utils.INarrativeGenerator;
import org.hl7.fhir.instance.utils.IWorkerContext;
import org.hl7.fhir.instance.validation.IResourceValidator;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.IValidationSupport.CodeValidationResult;
public final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory, ValueSetExpander {
private final FhirContext myCtx;
private IValidationSupport myValidationSupport;
public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
myCtx = theCtx;
myValidationSupport = theValidationSupport;
}
@Override
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
return myValidationSupport.expandValueSet(myCtx, theInc);
}
@Override
public ValueSetExpansionOutcome expandVS(ValueSet theSource) {
throw new UnsupportedOperationException();
}
@Override
public ValueSet fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) {
return null;
} else {
return myValidationSupport.fetchCodeSystem(myCtx, theSystem);
}
}
@Override
public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) throws EOperationOutcome, Exception {
if (myValidationSupport == null) {
return null;
} else {
return myValidationSupport.fetchResource(myCtx, theClass, theUri);
}
}
@Override
public INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(ParserType theType) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(String theType) {
throw new UnsupportedOperationException();
}
@Override
public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) {
throw new UnsupportedOperationException();
}
@Override
public IParser newJsonParser() {
throw new UnsupportedOperationException();
}
@Override
public IResourceValidator newValidator() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public IParser newXmlParser() {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsSystem(String theSystem) {
if (myValidationSupport == null) {
return false;
} else {
return myValidationSupport.isCodeSystemSupported(myCtx, theSystem);
}
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
if (result == null) {
return null;
}
return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition());
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay,
ConceptSetComponent theVsi) {
throw new UnsupportedOperationException();
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
throw new UnsupportedOperationException();
}
@Override
public ValueSetExpander getExpander() {
return this;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet theSource) throws ETooCostly, Exception {
ValueSetExpander vse = new ValueSetExpanderSimple(this, this);
ValueSetExpansionOutcome vso = vse.expand(theSource);
if (vso.getError() != null) {
return null;
} else {
return vso;
}
}
@Override
public List<ConceptMap> findMapsForSource(String theUrl) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.tinder;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import ca.uhn.fhir.tinder.parser.CompartmentParser;
public class CompartmentGeneratorMojo extends AbstractMojo {
@Parameter(required = true)
private String fhirVersion;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
CompartmentParser p = new CompartmentParser(fhirVersion);
try {
p.parse();
} catch (MojoExecutionException e) {
throw e;
} catch (MojoFailureException e) {
throw e;
} catch (Exception e) {
throw new MojoFailureException("Failure during parse", e);
}
}
public static void main(String[] args) throws MojoExecutionException, MojoFailureException {
CompartmentGeneratorMojo mojo = new CompartmentGeneratorMojo();
mojo.fhirVersion = "dstu2";
mojo.execute();
}
}

View File

@ -0,0 +1,55 @@
package ca.uhn.fhir.tinder.model;
import java.util.ArrayList;
import java.util.List;
public class CompartmentDef {
private String myOwnerResourceName;
private List<Target> myTargets;
public String getOwnerResourceName() {
return myOwnerResourceName;
}
public List<Target> getTargets() {
if (myTargets == null) {
myTargets = new ArrayList<Target>();
}
return myTargets;
}
public void setOwnerResourceName(String theOwnerResourceName) {
myOwnerResourceName = theOwnerResourceName;
}
public void setTargets(List<Target> theTargets) {
myTargets = theTargets;
}
public class Target {
private List<String> myPaths;
private String myResourceName;
public List<String> getPaths() {
if (myPaths == null) {
myPaths = new ArrayList<String>();
}
return myPaths;
}
public String getResourceName() {
return myResourceName;
}
public void setPaths(List<String> thePaths) {
myPaths = thePaths;
}
public void setResourceName(String theResourceName) {
myResourceName = theResourceName;
}
}
}

View File

@ -0,0 +1,50 @@
package ca.uhn.fhir.tinder.parser;
import java.io.InputStream;
import org.apache.maven.plugin.MojoFailureException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import ca.uhn.fhir.tinder.util.XMLUtils;
public class CompartmentParser {
private String myVersion;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompartmentParser.class);
public CompartmentParser(String theVersion) {
myVersion = theVersion;
}
public void parse() throws Exception {
String resName = "/compartment/" + myVersion + "/compartments.xml";
InputStream nextRes = getClass().getResourceAsStream(resName);
if (nextRes == null) {
throw new MojoFailureException("Unknown base resource name: " + resName);
}
ourLog.info("Reading spreadsheet file {}", resName);
Document file;
try {
file = XMLUtils.parse(nextRes, false);
} catch (Exception e) {
throw new Exception("Failed during reading: " + resName, e);
}
Element resourcesSheet = null;
for (int i = 0; i < file.getElementsByTagName("Worksheet").getLength() && resourcesSheet == null; i++) {
resourcesSheet = (Element) file.getElementsByTagName("Worksheet").item(i);
if (!"resources".equals(resourcesSheet.getAttributeNS("urn:schemas-microsoft-com:office:spreadsheet", "Name"))) {
resourcesSheet = null;
}
}
if (resourcesSheet == null) {
throw new Exception("Failed to find worksheet with name 'Data Elements' in spreadsheet: " + resName);
}
}
}

View File

@ -0,0 +1,636 @@
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author>Grahame</Author>
<LastAuthor>Eric Haas</LastAuthor>
<Created>2013-07-04T21:40:46Z</Created>
<LastSaved>2015-04-10T01:54:05Z</LastSaved>
<Version>15.00</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>8400</WindowHeight>
<WindowWidth>19170</WindowWidth>
<WindowTopX>0</WindowTopX>
<WindowTopY>0</WindowTopY>
<ActiveSheet>1</ActiveSheet>
<RefModeR1C1/>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62">
<Alignment ss:Horizontal="Left" ss:Vertical="Top"/>
</Style>
<Style ss:ID="s63">
<Alignment ss:Horizontal="Left" ss:Vertical="Top"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"
ss:Bold="1"/>
<Interior ss:Color="#FFFFFF" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s64">
<Alignment ss:Horizontal="Left" ss:Vertical="Top" ss:WrapText="1"/>
</Style>
<Style ss:ID="s65">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"
ss:Bold="1"/>
<Interior ss:Color="#FFFFFF" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s66">
<Font ss:FontName="Verdana" x:Family="Swiss" ss:Size="9" ss:Color="#333333"/>
</Style>
</Styles>
<Worksheet ss:Name="compartments">
<Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="6" x:FullColumns="1"
x:FullRows="1" ss:StyleID="s62" ss:DefaultColumnWidth="65.25"
ss:DefaultRowHeight="15">
<Column ss:StyleID="s62" ss:AutoFitWidth="0" ss:Width="108.75" ss:Span="1"/>
<Column ss:Index="3" ss:StyleID="s62" ss:AutoFitWidth="0" ss:Width="129.75"/>
<Column ss:StyleID="s62" ss:AutoFitWidth="0" ss:Width="207.75"/>
<Column ss:StyleID="s62" ss:AutoFitWidth="0" ss:Width="254.25"/>
<Row ss:AutoFitHeight="0" ss:StyleID="s63">
<Cell><Data ss:Type="String">Name</Data></Cell>
<Cell><Data ss:Type="String">Title</Data></Cell>
<Cell><Data ss:Type="String">Description</Data></Cell>
<Cell><Data ss:Type="String">Identification</Data></Cell>
<Cell><Data ss:Type="String">Inclusion</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="75">
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell><Data ss:Type="String">Patient</Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="String">The set of resources associated with a particular patient</Data></Cell>
<Cell><Data ss:Type="String">There is an instance of the patient compartment for each patient resource, and the identity of the compartment is the same as the patient. When a patient is linked to another patient, all the records associated with the linked patient are in the compartment associated with the target of the link. </Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="String">The patient compartment includes any resources where the subject of the resource is the patient, and some other resources that are directly linked to resources in the patient compartment</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">Encounter</Data></Cell>
<Cell><Data ss:Type="String">The set of resources associated with a particular encounter</Data></Cell>
<Cell><Data ss:Type="String">There is an instance of the encounter compartment for each encounter resource, and the identity of the compartment is the same as the encounter</Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="String">The encounter compartment includes any resources where the resource has an explicitly nominated encounter, and some other resources that them selves link to resources in the encounter compartment. Note that for many resources, the exact nature of the link to encounter can be ambiguous (e.g. for a DiagnosticReport, is it the encounter when it was initiated, or when it was reported?)</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">relatedPerson</Data></Cell>
<Cell><Data ss:Type="String">RelatedPerson</Data></Cell>
<Cell><Data ss:Type="String">The set of resources associated with a particular 'related person'</Data></Cell>
<Cell><Data ss:Type="String">There is an instance of the relatedPerson compartment for each relatedPerson resource, and the identity of the compartment is the same as the relatedPerson</Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="String">The relatedPerson compartment includes any resources where the resource is explicitly linked to relatedPerson (usually as author)</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">practitioner</Data></Cell>
<Cell><Data ss:Type="String">Practitioner</Data></Cell>
<Cell><Data ss:Type="String">The set of resources associated with a particular practitioner</Data></Cell>
<Cell><Data ss:Type="String">There is an instance of the practitioner compartment for each Practitioner resource, and the identity of the compartment is the same as the Practitioner</Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="String">The practitioner compartment includes any resources where the resource is explicitly linked to a Practitioner (usually as author, but other kinds of linkage exist)</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">device</Data></Cell>
<Cell><Data ss:Type="String">Device</Data></Cell>
<Cell><Data ss:Type="String">The set of resources associated with a particular device</Data></Cell>
<Cell><Data ss:Type="String">There is an instance of the practitioner compartment for each Device resource, and the identity of the compartment is the same as the Device</Data></Cell>
<Cell ss:StyleID="s64"><Data ss:Type="String">The device compartment includes any resources where the resource is explicitly linked to a Device (mostly subject or performer)</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Unsynced/>
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>4</ActiveRow>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
<x:PageLayoutZoom>0</x:PageLayoutZoom>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="resources">
<Table ss:ExpandedColumnCount="6" ss:ExpandedRowCount="94" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="65.25" ss:DefaultRowHeight="15">
<Column ss:AutoFitWidth="0" ss:Width="156.75"/>
<Column ss:AutoFitWidth="0" ss:Width="198"/>
<Column ss:Index="5" ss:AutoFitWidth="0" ss:Width="96"/>
<Row ss:AutoFitHeight="0" ss:StyleID="s65">
<Cell><Data ss:Type="String">Resource</Data></Cell>
<Cell><Data ss:Type="String">Patient</Data></Cell>
<Cell><Data ss:Type="String">Encounter</Data></Cell>
<Cell><Data ss:Type="String">RelatedPerson</Data></Cell>
<Cell><Data ss:Type="String">Practitioner</Data></Cell>
<Cell><Data ss:Type="String">Device</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">AllergyIntolerance</Data></Cell>
<Cell><Data ss:Type="String">patient | recorder | reporter</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">reporter</Data></Cell>
<Cell><Data ss:Type="String">recorder | reporter</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Account</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">subject</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Appointment</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">actor</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">AppointmentResponse</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">actor</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">AuditEvent</Data></Cell>
<Cell><Data ss:Type="String">patient | participant.patient | reference.patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">participant</Data></Cell>
<Cell><Data ss:Type="String">participant</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Basic</Data></Cell>
<Cell><Data ss:Type="String">patient | author</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Binary</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">BodySite</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Bundle</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">CarePlan</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">patient | participant | performer</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">participant | performer</Data></Cell>
<Cell><Data ss:Type="String">participant | performer</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Claim</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">provider</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ClaimResponse</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ClinicalImpression</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">assessor</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Communication</Data></Cell>
<Cell><Data ss:Type="String">subject | sender | recipient</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">sender | recipient</Data></Cell>
<Cell><Data ss:Type="String">sender | recipient</Data></Cell>
<Cell><Data ss:Type="String">sender | recipient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">CommunicationRequest</Data></Cell>
<Cell><Data ss:Type="String">subject | sender | recipient | requester</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">sender | recipient | requester</Data></Cell>
<Cell><Data ss:Type="String">sender | recipient | requester</Data></Cell>
<Cell><Data ss:Type="String">sender | recipient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Composition</Data></Cell>
<Cell><Data ss:Type="String">subject | author | attester</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">subject | author | attester</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ConceptMap</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Condition</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">asserter</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Conformance</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DetectedIssue</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Contract</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Coverage</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DataElement</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Device</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">{def}</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DeviceComponent</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">source</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DeviceMetric</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">source</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DeviceUseRequest</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">device</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DeviceUseStatement</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="6"><Data ss:Type="String">device</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DiagnosticOrder</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">actor | orderer</Data></Cell>
<Cell><Data ss:Type="String">actor | subject</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DiagnosticReport</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">performer</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DocumentManifest</Data></Cell>
<Cell><Data ss:Type="String">subject | author | recipient</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">subject | author | recipient</Data></Cell>
<Cell><Data ss:Type="String">subject | author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">DocumentReference</Data></Cell>
<Cell><Data ss:Type="String">subject | author</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">subject | author | authenticator</Data></Cell>
<Cell><Data ss:Type="String">subject | author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">EligibilityRequest</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">EligibilityResponse</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Encounter</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell><Data ss:Type="String">{def}</Data></Cell>
<Cell><Data ss:Type="String">participant</Data></Cell>
<Cell><Data ss:Type="String">practitioner | participant</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">EnrollmentRequest</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">EnrollmentResponse</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">EpisodeOfCare</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5" ss:StyleID="s66"><Data ss:Type="String">care-manager | team-member</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ExplanationOfBenefit</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">FamilyMemberHistory</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Flag</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Goal</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Group</Data></Cell>
<Cell><Data ss:Type="String">member</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">member</Data></Cell>
<Cell><Data ss:Type="String">member</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">HealthcareService</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ImagingObjectSelection</Data></Cell>
<Cell><Data ss:Type="String">patient | author</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ImagingStudy</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Immunization</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">performer | requester</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ImmunizationRecommendation</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ImplementationGuide</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">List</Data></Cell>
<Cell><Data ss:Type="String">subject | source</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">source</Data></Cell>
<Cell><Data ss:Type="String">subject | source</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Location</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Media</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">subject | operator</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Medication</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">MedicationAdministration</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">practitioner</Data></Cell>
<Cell><Data ss:Type="String">device</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">MedicationDispense</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">dispenser | receiver | responsibleparty</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">MedicationOrder</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">prescriber</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">MedicationStatement</Data></Cell>
<Cell><Data ss:Type="String">patient | source</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">source</Data></Cell>
<Cell><Data ss:Type="String">source</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">MessageHeader</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">receiver | author | responsible | enterer</Data></Cell>
<Cell><Data ss:Type="String">target</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">NamingSystem</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">NutritionOrder</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">provider</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Observation</Data></Cell>
<Cell><Data ss:Type="String">subject | performer</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">performer</Data></Cell>
<Cell><Data ss:Type="String">performer</Data></Cell>
<Cell><Data ss:Type="String">subject | device</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">OperationDefinition</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">OperationOutcome</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Order</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">source | target</Data></Cell>
<Cell><Data ss:Type="String">target</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">OrderResponse</Data></Cell>
<Cell><Data ss:Type="String">request.patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">who</Data></Cell>
<Cell><Data ss:Type="String">who</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Organization</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Patient</Data></Cell>
<Cell><Data ss:Type="String">link</Data></Cell>
<Cell ss:Index="5" ss:StyleID="s66"><Data ss:Type="String">careprovider</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">PaymentNotice</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">PaymentReconciliation</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Person</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">link</Data></Cell>
<Cell><Data ss:Type="String">practitioner</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Practitioner</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">{def}</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Procedure</Data></Cell>
<Cell><Data ss:Type="String">patient | performer</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">performer</Data></Cell>
<Cell><Data ss:Type="String">performer</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ProcedureRequest</Data></Cell>
<Cell><Data ss:Type="String">subject | orderer | performer</Data></Cell>
<Cell><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">performer | orderer</Data></Cell>
<Cell><Data ss:Type="String">performer | orderer</Data></Cell>
<Cell><Data ss:Type="String">orderer</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ProcessRequest</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">provider</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ProcessResponse</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">requestprovider</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Provenance</Data></Cell>
<Cell><Data ss:Type="String">target.subject | target.patient | patient</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">agent</Data></Cell>
<Cell><Data ss:Type="String">agent</Data></Cell>
<Cell><Data ss:Type="String">agent</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Questionnaire</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">QuestionnaireResponse</Data></Cell>
<Cell><Data ss:Type="String">subject | author</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">encounter</Data></Cell>
<Cell><Data ss:Type="String">author | source</Data></Cell>
<Cell><Data ss:Type="String">author | source</Data></Cell>
<Cell><Data ss:Type="String">author</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ReferralRequest</Data></Cell>
<Cell><Data ss:Type="String">patient | requester</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">requester | recipient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">RelatedPerson</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">{def}</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">RiskAssessment</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">performer</Data></Cell>
<Cell><Data ss:Type="String">performer</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Schedule</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">actor</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
<Cell><Data ss:Type="String">actor</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">SearchParameter</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Slot</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Specimen</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">collector</Data></Cell>
<Cell><Data ss:Type="String">subject</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">StructureDefinition</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Subscription</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">Substance</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15.75">
<Cell><Data ss:Type="String">SupplyRequest</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0" ss:Height="15.75">
<Cell><Data ss:Type="String">SupplyDelivery</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">supplier | receiver</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">TestScript</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">ValueSet</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="String">VisionPrescription</Data></Cell>
<Cell><Data ss:Type="String">patient</Data></Cell>
<Cell ss:StyleID="s66"><Data ss:Type="String">encounter</Data></Cell>
<Cell ss:Index="5"><Data ss:Type="String">prescriber</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Unsynced/>
<Print>
<ValidPrinterInfo/>
<PaperSizeIndex>9</PaperSizeIndex>
<HorizontalResolution>600</HorizontalResolution>
<VerticalResolution>600</VerticalResolution>
</Print>
<Selected/>
<FreezePanes/>
<FrozenNoSplit/>
<SplitHorizontal>1</SplitHorizontal>
<TopRowBottomPane>10</TopRowBottomPane>
<ActivePane>2</ActivePane>
<Panes>
<Pane>
<Number>3</Number>
</Pane>
<Pane>
<Number>2</Number>
<ActiveRow>20</ActiveRow>
<RangeSelection>R21</RangeSelection>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
<x:PageLayoutZoom>0</x:PageLayoutZoom>
</WorksheetOptions>
</Worksheet>
</Workbook>

View File

@ -15,6 +15,7 @@
<bean id="myFhirContext${versionCapitalized}" class="ca.uhn.fhir.context.FhirContext" factory-method="for${versionCapitalized}"/> <bean id="myFhirContext${versionCapitalized}" class="ca.uhn.fhir.context.FhirContext" factory-method="for${versionCapitalized}"/>
<bean id="mySystemDao${versionCapitalized}" class="ca.uhn.fhir.jpa.dao.FhirSystemDao${versionCapitalized}"> <bean id="mySystemDao${versionCapitalized}" class="ca.uhn.fhir.jpa.dao.FhirSystemDao${versionCapitalized}">
<property name="context" ref="myFhirContext${versionCapitalized}"/> <property name="context" ref="myFhirContext${versionCapitalized}"/>
</bean> </bean>
@ -23,6 +24,7 @@
</bean> </bean>
#if ( ${versionCapitalized} == 'Dstu2' ) #if ( ${versionCapitalized} == 'Dstu2' )
<bean id="myFhirContextDstu2Hl7Org" class="ca.uhn.fhir.context.FhirContext" factory-method="forDstu2Hl7Org"/>
<jpa:repositories base-package="ca.uhn.fhir.jpa.dao.data" /> <jpa:repositories base-package="ca.uhn.fhir.jpa.dao.data" />
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/> <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
@ -41,7 +43,7 @@
#foreach ( $res in $resources ) #foreach ( $res in $resources )
<bean id="my${res.name}Dao${versionCapitalized}" <bean id="my${res.name}Dao${versionCapitalized}"
## Some resource types have customized DAOs for resource specific functionality ## Some resource types have customized DAOs for resource specific functionality
#if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'ValueSet')) #if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'ValueSet'))
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}"> class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#else #else
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}"> class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">

View File

@ -186,6 +186,10 @@
<id>SingingTree</id> <id>SingingTree</id>
<name>Bryce Van Dyk</name> <name>Bryce Van Dyk</name>
</developer> </developer>
<developer>
<id>botunge</id>
<name>Thomas Andersen</name>
</developer>
</developers> </developers>
<licenses> <licenses>

View File

@ -106,6 +106,39 @@
did not have latest valueset definitions applied. Thanks did not have latest valueset definitions applied. Thanks
to Bill de Beaubien for reporting! to Bill de Beaubien for reporting!
</action> </action>
<action type="fix">
JPA server can now successfully search for tokens pointing at code values
(values with no explicit system but an implied one, such as Patient.gender)
even if the system is supplied in the query.
</action>
<action type="fix" issue="235">
Correct issues with Android library. Thanks to
Thomas Andersen for the submission!
</action>
<action type="fix">
JPA server incorrectly rejected match URLs
if they did not contain a question mark. Thanks
to Bill de Beaubien for reporting!
</action>
<action type="fix" issue="234">
Remove invalid entries in OSGi Manifest. Thanks
to Alexander Kley for the fix!
</action>
<action type="add">
JPA server now supports $everything on Patient and Encounter types (patient and encounter instance was already supported)
</action>
<action type="add">
Generic client operation invocations now
have an additional inline method for generating the input
Parameters using chained method calls instead
of by passing a Parameters resource in
</action>
<action type="fix">
Parsing an XML resource where the XHTML
namespace was declared before the beginning
of the narrative section caused an invalid
re-encoding when encoding to JSON.
</action>
</release> </release>
<release version="1.2" date="2015-09-18"> <release version="1.2" date="2015-09-18">
<action type="add"> <action type="add">

View File

@ -8,6 +8,8 @@ for i in $(find $FHIRTRUNK/build/source -name *-spreadsheet.xml | egrep "/[a-z0-
rm hapi-tinder-plugin/src/main/resources/dt/dstu2/* rm hapi-tinder-plugin/src/main/resources/dt/dstu2/*
for i in $(find $FHIRTRUNK/build/source/datatypes | grep xml | grep -v spreadsheet | grep -v -); do cp -v $i hapi-tinder-plugin/src/main/resources/dt/dstu2/; done for i in $(find $FHIRTRUNK/build/source/datatypes | grep xml | grep -v spreadsheet | grep -v -); do cp -v $i hapi-tinder-plugin/src/main/resources/dt/dstu2/; done
cp ~/workspace/fhir/trunk/build/source/compartments.xml hapi-tinder-plugin/src/main/resources/compartment/
cp $FHIRTRUNK/build/publish/valuesets.xml hapi-fhir-validation-resources/src/main/resources/org/hl7/fhir/instance/model/valueset/ cp $FHIRTRUNK/build/publish/valuesets.xml hapi-fhir-validation-resources/src/main/resources/org/hl7/fhir/instance/model/valueset/
cp $FHIRTRUNK/build/publish/v3-codesystems.xml hapi-fhir-validation-resources/src/main/resources/org/hl7/fhir/instance/model/valueset/ cp $FHIRTRUNK/build/publish/v3-codesystems.xml hapi-fhir-validation-resources/src/main/resources/org/hl7/fhir/instance/model/valueset/
cp $FHIRTRUNK/build/publish/v2-codesystems.xml hapi-fhir-validation-resources/src/main/resources/org/hl7/fhir/instance/model/valueset/ cp $FHIRTRUNK/build/publish/v2-codesystems.xml hapi-fhir-validation-resources/src/main/resources/org/hl7/fhir/instance/model/valueset/