Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
4b2a3e6882
7
.project
7
.project
|
@ -11,8 +11,13 @@
|
|||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
|
|
|
@ -176,13 +176,13 @@ public class ValidatorExamples {
|
|||
IValidationSupport valSupport = new IValidationSupport() {
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
// TODO: Implement
|
||||
return false;
|
||||
}
|
||||
|
@ -194,13 +194,13 @@ public class ValidatorExamples {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -16,13 +16,23 @@
|
|||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (1).launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
|
|
|
@ -27,7 +27,7 @@ public interface IRuntimeDatatypeDefinition {
|
|||
|
||||
boolean isSpecialization();
|
||||
|
||||
public BaseRuntimeElementDefinition<?> getProfileOf();
|
||||
public Class<? extends IBaseDatatype> getProfileOf();
|
||||
|
||||
boolean isProfileOf(Class<? extends IBaseDatatype> theType);
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
|
@ -41,6 +43,7 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
|||
private Map<String, BaseRuntimeElementDefinition<?>> myNameToChildDefinition;
|
||||
private Map<Class<? extends IBase>, String> myDatatypeToElementName;
|
||||
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myDatatypeToElementDefinition;
|
||||
private String myReferenceSuffix;
|
||||
|
||||
public RuntimeChildChoiceDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, List<Class<? extends IBase>> theChoiceTypes) {
|
||||
super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName);
|
||||
|
@ -82,9 +85,15 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
|||
myDatatypeToElementName = new HashMap<Class<? extends IBase>, String>();
|
||||
myDatatypeToElementDefinition = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
|
||||
|
||||
if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
|
||||
myReferenceSuffix = "Resource";
|
||||
} else {
|
||||
myReferenceSuffix = "Reference";
|
||||
}
|
||||
|
||||
for (Class<? extends IBase> next : myChoiceTypes) {
|
||||
|
||||
String elementName;
|
||||
String elementName = null;
|
||||
BaseRuntimeElementDefinition<?> nextDef;
|
||||
boolean nonPreferred = false;
|
||||
if (IBaseResource.class.isAssignableFrom(next)) {
|
||||
|
@ -109,32 +118,43 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
|||
* element fooString when encoded, because markdown is a profile of string. This is according to the
|
||||
* FHIR spec
|
||||
*/
|
||||
nextDefForChoice = nextDefDatatype.getProfileOf();
|
||||
nextDefForChoice = null;
|
||||
nonPreferred = true;
|
||||
Class<? extends IBaseDatatype> profileType = nextDefDatatype.getProfileOf();
|
||||
BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(profileType);
|
||||
elementName = getElementName() + StringUtils.capitalize(elementDef.getName());
|
||||
}
|
||||
}
|
||||
elementName = getElementName() + StringUtils.capitalize(nextDefForChoice.getName());
|
||||
}
|
||||
|
||||
if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) {
|
||||
myNameToChildDefinition.put(elementName, nextDef);
|
||||
}
|
||||
|
||||
if (IBaseResource.class.isAssignableFrom(next)) {
|
||||
Class<? extends IBase> refType = theContext.getVersion().getResourceReferenceType();
|
||||
myDatatypeToElementDefinition.put(refType, nextDef);
|
||||
|
||||
String alternateElementName;
|
||||
if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
|
||||
alternateElementName = getElementName() + "Resource";
|
||||
} else {
|
||||
alternateElementName = getElementName() + "Reference";
|
||||
if (nextDefForChoice != null) {
|
||||
elementName = getElementName() + StringUtils.capitalize(nextDefForChoice.getName());
|
||||
}
|
||||
myDatatypeToElementName.put(refType, alternateElementName);
|
||||
}
|
||||
|
||||
// I don't see how elementName could be null here, but eclipse complains..
|
||||
if (elementName != null) {
|
||||
if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) {
|
||||
myNameToChildDefinition.put(elementName, nextDef);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a resource reference, the element name is "fooNameReference"
|
||||
*/
|
||||
if (IBaseResource.class.isAssignableFrom(next) || IBaseReference.class.isAssignableFrom(next)) {
|
||||
next = theContext.getVersion().getResourceReferenceType();
|
||||
elementName = getElementName() + myReferenceSuffix;
|
||||
}
|
||||
|
||||
myDatatypeToElementDefinition.put(next, nextDef);
|
||||
myDatatypeToElementName.put(next, elementName);
|
||||
|
||||
if (myDatatypeToElementName.containsKey(next)) {
|
||||
String existing = myDatatypeToElementName.get(next);
|
||||
if (!existing.equals(elementName)) {
|
||||
throw new ConfigurationException("Already have element name " + existing + " for datatype " + next.getClass().getSimpleName() + " in " + getElementName() + ", cannot add " + elementName);
|
||||
}
|
||||
} else {
|
||||
myDatatypeToElementName.put(next, elementName);
|
||||
}
|
||||
}
|
||||
|
||||
myNameToChildDefinition = Collections.unmodifiableMap(myNameToChildDefinition);
|
||||
|
@ -145,7 +165,8 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
|||
|
||||
@Override
|
||||
public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
|
||||
return myDatatypeToElementName.get(theDatatype);
|
||||
String retVal = myDatatypeToElementName.get(theDatatype);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -65,8 +65,8 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos
|
|||
}
|
||||
|
||||
@Override
|
||||
public BaseRuntimeElementDefinition<?> getProfileOf() {
|
||||
return myProfileOf;
|
||||
public Class<? extends IBaseDatatype> getProfileOf() {
|
||||
return myProfileOfType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -58,8 +58,8 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini
|
|||
}
|
||||
|
||||
@Override
|
||||
public BaseRuntimeElementDefinition<?> getProfileOf() {
|
||||
return myProfileOf;
|
||||
public Class<? extends IBaseDatatype> getProfileOf() {
|
||||
return myProfileOfType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.lang.reflect.Method;
|
|||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -47,6 +48,7 @@ import ca.uhn.fhir.rest.annotation.OperationParam;
|
|||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.param.CollectionBinder;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -66,6 +68,7 @@ public class OperationParameter implements IParameter {
|
|||
private Class<?> myParameterType;
|
||||
private String myParamType;
|
||||
private FhirContext myContext;
|
||||
private boolean myAllowGet;
|
||||
|
||||
public OperationParameter(FhirContext theCtx, String theOperationName, OperationParam theOperationParam) {
|
||||
this(theCtx, theOperationName, theOperationParam.name(), theOperationParam.min(), theOperationParam.max());
|
||||
|
@ -107,6 +110,8 @@ public class OperationParameter implements IParameter {
|
|||
myMax = 1;
|
||||
}
|
||||
|
||||
myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType);
|
||||
|
||||
/*
|
||||
* The parameter can be of type string for validation methods - This is a bit
|
||||
* weird. See ValidateDstu2Test. We should probably clean this up..
|
||||
|
@ -114,6 +119,10 @@ public class OperationParameter implements IParameter {
|
|||
if (!myParameterType.equals(IBase.class) && !myParameterType.equals(String.class)) {
|
||||
if (IBaseResource.class.isAssignableFrom(myParameterType) && myParameterType.isInterface()) {
|
||||
myParamType = "Resource";
|
||||
} else if (DateRangeParam.class.isAssignableFrom(myParameterType)) {
|
||||
myParamType = "date";
|
||||
myMax = 2;
|
||||
myAllowGet = true;
|
||||
} else if (!IBase.class.isAssignableFrom(myParameterType) || myParameterType.isInterface() || Modifier.isAbstract(myParameterType.getModifiers())) {
|
||||
throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName());
|
||||
} else if (myParameterType.equals(ValidationModeEnum.class)) {
|
||||
|
@ -153,13 +162,25 @@ public class OperationParameter implements IParameter {
|
|||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
String[] paramValues = theRequest.getParameters().get(myName);
|
||||
if (paramValues != null && paramValues.length > 0) {
|
||||
if (IPrimitiveType.class.isAssignableFrom(myParameterType)) {
|
||||
for (String nextValue : paramValues) {
|
||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||
RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition((Class<? extends IBase>) myParameterType);
|
||||
IPrimitiveType<?> instance = def.newInstance();
|
||||
instance.setValueAsString(nextValue);
|
||||
matchingParamValues.add(instance);
|
||||
if (myAllowGet) {
|
||||
|
||||
if (DateRangeParam.class.isAssignableFrom(myParameterType)) {
|
||||
List<QualifiedParamList> parameters = new ArrayList<QualifiedParamList>();
|
||||
parameters.add(QualifiedParamList.singleton(paramValues[0]));
|
||||
if (paramValues.length > 1) {
|
||||
parameters.add(QualifiedParamList.singleton(paramValues[1]));
|
||||
}
|
||||
DateRangeParam dateRangeParam = new DateRangeParam();
|
||||
dateRangeParam.setValuesAsQueryTokens(parameters);
|
||||
matchingParamValues.add(dateRangeParam);
|
||||
} else {
|
||||
for (String nextValue : paramValues) {
|
||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||
RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition((Class<? extends IBase>) myParameterType);
|
||||
IPrimitiveType<?> instance = def.newInstance();
|
||||
instance.setValueAsString(nextValue);
|
||||
matchingParamValues.add(instance);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HapiLocalizer localizer = theRequest.getServer().getFhirContext().getLocalizer();
|
||||
|
|
|
@ -11,7 +11,14 @@ a browser.
|
|||
<!--*/-->
|
||||
|
||||
<div>
|
||||
<div class="hapiHeaderText" th:if="${not resource.nameElement.empty}" th:narrative="${resource.name}"></div>
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU1'">
|
||||
<div class="hapiHeaderText" th:if="${not resource.nameElement.empty}" th:narrative="${resource.name}"></div>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<div class="hapiHeaderText" th:if="${not resource.code.empty}" th:narrative="${resource.code}"></div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<!--/*-->
|
||||
|
|
|
@ -16,12 +16,12 @@ public class LoadingValidationSupport implements IValidationSupport {
|
|||
private static FhirContext myCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -46,12 +46,12 @@ public class LoadingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<classpathentry kind="src" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**/*.java" including="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<classpathentry kind="src" path="src/test/resources"/>
|
||||
<classpathentry kind="src" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**/*.java" including="**/*.java" kind="src" path="src/main/resources"/>
|
||||
<classpathentry including="**/*.java" kind="src" path="target/generated-sources/tinder"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="target/generated-resources/tinder">
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" path="target/generated-sources/tinder"/>
|
||||
<classpathentry kind="src" path="target/generated-resources/tinder">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
@ -32,5 +32,5 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
|
@ -17,13 +17,23 @@
|
|||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder (1).launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (2).launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
|
|
|
@ -111,6 +111,7 @@ import ca.uhn.fhir.rest.method.QualifiedParamList;
|
|||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -715,7 +716,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return ids;
|
||||
}
|
||||
|
||||
static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
|
||||
public static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
List<NameValuePair> parameters;
|
||||
try {
|
||||
|
@ -778,7 +779,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (nextParamName.startsWith("_")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName);
|
||||
if (paramDef == null) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
|
||||
|
|
|
@ -1227,11 +1227,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
} else {
|
||||
theOrders.add(theBuilder.desc(theFrom.get("myUpdated")));
|
||||
}
|
||||
|
||||
|
||||
createSort(theBuilder, theFrom, theSort.getChain(), theOrders, thePredicates);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceType);
|
||||
RuntimeSearchParam param = resourceDef.getSearchParam(theSort.getParamName());
|
||||
if (param == null) {
|
||||
|
@ -1615,7 +1615,7 @@ public abstract class BaseHapiFhirResourceDao<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())
|
||||
*/
|
||||
private HashSet<Long> loadReverseIncludes(List<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode) {
|
||||
private HashSet<Long> loadReverseIncludes(Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode) {
|
||||
if (theMatches.size() == 0) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
|
@ -1902,7 +1902,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
Long pid = translateForcedIdToPid(theId);
|
||||
BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid);
|
||||
|
||||
|
||||
if (entity == null) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
@ -2013,6 +2013,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
for (Tuple next : query.getResultList()) {
|
||||
loadPids.add(next.get(0, Long.class));
|
||||
}
|
||||
if (loadPids.isEmpty()) {
|
||||
return new SimpleBundleProvider();
|
||||
}
|
||||
} else {
|
||||
loadPids = searchForIdsWithAndOr(theParams);
|
||||
if (loadPids.isEmpty()) {
|
||||
|
@ -2020,6 +2023,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
}
|
||||
}
|
||||
|
||||
// Load _include and _revinclude before filter and sort in everything mode
|
||||
if (theParams.isEverythingMode() == true) {
|
||||
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
|
||||
loadPids.addAll(loadReverseIncludes(loadPids, theParams.getRevIncludes(), true));
|
||||
loadPids.addAll(loadReverseIncludes(loadPids, theParams.getIncludes(), false));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle _lastUpdated
|
||||
DateRangeParam lu = theParams.getLastUpdated();
|
||||
if (lu != null && (lu.getLowerBoundAsInstant() != null || lu.getUpperBoundAsInstant() != null)) {
|
||||
|
@ -2028,7 +2039,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
|
||||
Predicate predicateIds = (from.get("myId").in(loadPids));
|
||||
Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), lu.getLowerBoundAsInstant()) : null;
|
||||
Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from.<Date> get("myUpdated"), lu.getUpperBoundAsInstant()) : null;
|
||||
|
@ -2044,59 +2055,22 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
for (Long next : query.getResultList()) {
|
||||
loadPids.add(next);
|
||||
}
|
||||
|
||||
|
||||
if (loadPids.isEmpty()) {
|
||||
return new SimpleBundleProvider();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle sorting if any was provided
|
||||
final List<Long> pids;
|
||||
if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) {
|
||||
List<Order> orders = new ArrayList<Order>();
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
predicates.add(from.get("myId").in(loadPids));
|
||||
createSort(builder, from, theParams.getSort(), orders, predicates);
|
||||
if (orders.size() > 0) {
|
||||
Set<Long> originalPids = loadPids;
|
||||
loadPids = new LinkedHashSet<Long>();
|
||||
cq.multiselect(from.get("myId").as(Long.class));
|
||||
cq.where(predicates.toArray(new Predicate[0]));
|
||||
cq.orderBy(orders);
|
||||
|
||||
TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
|
||||
|
||||
for (Tuple next : query.getResultList()) {
|
||||
loadPids.add(next.get(0, Long.class));
|
||||
}
|
||||
|
||||
ourLog.debug("Sort PID order is now: {}", loadPids);
|
||||
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
|
||||
// Any ressources which weren't matched by the sort get added to the bottom
|
||||
for (Long next : originalPids) {
|
||||
if (loadPids.contains(next) == false) {
|
||||
pids.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
}
|
||||
} else {
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
}
|
||||
final List<Long> pids = processSort(theParams, loadPids);
|
||||
|
||||
// Load _revinclude resources
|
||||
final Set<Long> revIncludedPids;
|
||||
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
|
||||
revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true);
|
||||
if (theParams.isEverythingMode()) {
|
||||
revIncludedPids.addAll(loadReverseIncludes(pids, theParams.getIncludes(), false));
|
||||
if (theParams.isEverythingMode() == false) {
|
||||
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
|
||||
revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true);
|
||||
} else {
|
||||
revIncludedPids = new HashSet<Long>();
|
||||
}
|
||||
} else {
|
||||
revIncludedPids = new HashSet<Long>();
|
||||
|
@ -2152,6 +2126,49 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private List<Long> processSort(final SearchParameterMap theParams, Set<Long> loadPids) {
|
||||
final List<Long> pids;
|
||||
if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) {
|
||||
List<Order> orders = new ArrayList<Order>();
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
predicates.add(from.get("myId").in(loadPids));
|
||||
createSort(builder, from, theParams.getSort(), orders, predicates);
|
||||
if (orders.size() > 0) {
|
||||
Set<Long> originalPids = loadPids;
|
||||
loadPids = new LinkedHashSet<Long>();
|
||||
cq.multiselect(from.get("myId").as(Long.class));
|
||||
cq.where(predicates.toArray(new Predicate[0]));
|
||||
cq.orderBy(orders);
|
||||
|
||||
TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
|
||||
|
||||
for (Tuple next : query.getResultList()) {
|
||||
loadPids.add(next.get(0, Long.class));
|
||||
}
|
||||
|
||||
ourLog.debug("Sort PID order is now: {}", loadPids);
|
||||
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
|
||||
// Any ressources which weren't matched by the sort get added to the bottom
|
||||
for (Long next : originalPids) {
|
||||
if (loadPids.contains(next) == false) {
|
||||
pids.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
}
|
||||
} else {
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
}
|
||||
return pids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider search(String theParameterName, IQueryParameterType theValue) {
|
||||
return search(Collections.singletonMap(theParameterName, theValue));
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
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;
|
||||
|
@ -8,13 +28,15 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.UnsignedIntDt;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>implements IFhirResourceDaoPatient<Patient> {
|
||||
|
||||
@Override
|
||||
public IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount) {
|
||||
public IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
if (theCount != null) {
|
||||
paramMap.setCount(theCount.getValue());
|
||||
|
@ -23,6 +45,8 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
|
|||
paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
|
||||
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
|
||||
paramMap.setEverythingMode(true);
|
||||
paramMap.setSort(theSort);
|
||||
paramMap.setLastUpdated(theLastUpdated);
|
||||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
||||
ca.uhn.fhir.rest.server.IBundleProvider retVal = search(paramMap);
|
||||
return retVal;
|
||||
|
|
|
@ -26,10 +26,12 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.UnsignedIntDt;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public interface IFhirResourceDaoPatient<T extends IBaseResource> extends IFhirResourceDao<T> {
|
||||
|
||||
IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount);
|
||||
IBundleProvider everything(HttpServletRequest theServletRequest, IdDt theId, UnsignedIntDt theCount, DateRangeParam theLastUpdate, SortSpec theSort);
|
||||
|
||||
}
|
||||
|
|
|
@ -51,12 +51,12 @@ public class JpaValidationSupportDstu2 implements IValidationSupport {
|
|||
private FhirContext myDstu2Ctx;
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
public ValueSet fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -85,20 +85,19 @@ public class JpaValidationSupportDstu2 implements IValidationSupport {
|
|||
/*
|
||||
* Validator wants RI structures and not HAPI ones, so convert
|
||||
*
|
||||
* TODO: we really need a more efficient way of converting.. Or maybe this will
|
||||
* just go away when we move to RI structures
|
||||
* TODO: we really need a more efficient way of converting.. Or maybe this will just go away when we move to RI structures
|
||||
*/
|
||||
String encoded = myDstu2Ctx.newJsonParser().encodeResourceToString(res);
|
||||
return myRiCtx.newJsonParser().parseResource(theClass, encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
private Set<Include> myIncludes;
|
||||
private DateRangeParam myLastUpdated;
|
||||
private Set<Include> myRevIncludes;
|
||||
|
||||
private SortSpec mySort;
|
||||
|
||||
public void add(String theName, IQueryParameterAnd<?> theAnd) {
|
||||
|
@ -76,8 +77,8 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
}
|
||||
|
||||
public void add(String theName, IQueryParameterType theParam) {
|
||||
assert !Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map
|
||||
|
||||
assert!Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map
|
||||
|
||||
if (theParam == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,35 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*
|
||||
* #%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.jpa.dao.IFhirResourceDaoPatient;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Sort;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu2<Patient> {
|
||||
|
||||
|
@ -18,12 +42,21 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
|
|||
ca.uhn.fhir.model.primitive.IdDt theId,
|
||||
|
||||
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
|
||||
@OperationParam(name = "_count")
|
||||
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount) {
|
||||
@OperationParam(name = Constants.PARAM_COUNT)
|
||||
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
|
||||
|
||||
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
|
||||
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
|
||||
DateRangeParam theLastUpdated,
|
||||
|
||||
// @OperationParam(name = Constants.PARAM_SORT, min=0, max=1)
|
||||
@Sort
|
||||
SortSpec theSortSpec
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>)getDao()).everything(theServletRequest, theId, theCount);
|
||||
return ((IFhirResourceDaoPatient<Patient>)getDao()).everything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
|
|
@ -27,36 +27,49 @@ import java.util.concurrent.ScheduledFuture;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
|
||||
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.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
||||
public class SubscriptionWebsocketHandler extends TextWebSocketHandler implements ISubscriptionWebsocketHandler, Runnable {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandler.class);
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myFhirContextDstu2")
|
||||
private FhirContext myCtx;
|
||||
|
||||
private ScheduledFuture<?> myScheduleFuture;
|
||||
|
||||
private IState myState = new InitialState();
|
||||
|
||||
@Autowired
|
||||
private IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
|
||||
|
||||
|
||||
private IIdType mySubscriptionId;
|
||||
private Long mySubscriptionPid;
|
||||
|
||||
|
||||
@Autowired
|
||||
private TaskScheduler myTaskScheduler;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession theSession, CloseStatus theStatus) throws Exception {
|
||||
super.afterConnectionClosed(theSession, theStatus);
|
||||
|
@ -68,53 +81,113 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
super.afterConnectionEstablished(theSession);
|
||||
ourLog.info("Incoming WebSocket connection from {}", theSession.getRemoteAddress());
|
||||
}
|
||||
|
||||
|
||||
protected void handleFailure(Exception theE) {
|
||||
ourLog.error("Failure during communication", theE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) throws Exception {
|
||||
ourLog.info("Textmessage: " + theMessage.getPayload());
|
||||
|
||||
|
||||
myState.handleTextMessage(theSession, theMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession theSession, Throwable theException) throws Exception {
|
||||
super.handleTransportError(theSession, theException);
|
||||
ourLog.error("Transport error", theException);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
ourLog.info("Creating scheduled task for subscription websocket connection");
|
||||
myScheduleFuture = myTaskScheduler.scheduleWithFixedDelay(this, 1000);
|
||||
}
|
||||
|
||||
|
||||
@PreDestroy
|
||||
public void preDescroy() {
|
||||
ourLog.info("Cancelling scheduled task for subscription websocket connection");
|
||||
myScheduleFuture.cancel(true);
|
||||
IState state = myState;
|
||||
if (state != null) {
|
||||
state.closing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Long subscriptionPid = mySubscriptionPid;
|
||||
if (subscriptionPid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ourLog.debug("Subscription {} websocket handler polling", subscriptionPid);
|
||||
|
||||
|
||||
List<IBaseResource> results = mySubscriptionDao.getUndeliveredResourcesAndPurge(subscriptionPid);
|
||||
if (results.isEmpty() == false) {
|
||||
myState.deliver(results);
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleBoundState implements IState {
|
||||
private class BoundDynamicSubscriptionState implements IState {
|
||||
|
||||
private EncodingEnum myEncoding;
|
||||
private WebSocketSession mySession;
|
||||
|
||||
public BoundDynamicSubscriptionState(WebSocketSession theSession, EncodingEnum theEncoding) {
|
||||
mySession = theSession;
|
||||
myEncoding = theEncoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
ourLog.info("Deleting subscription {}", mySubscriptionId);
|
||||
try {
|
||||
mySubscriptionDao.delete(mySubscriptionId);
|
||||
} catch (Exception e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
try {
|
||||
for (IBaseResource nextResource : theResults) {
|
||||
ourLog.info("Sending WebSocket message for resource: {}", nextResource.getIdElement());
|
||||
String encoded = myEncoding.newParser(myCtx).encodeResourceToString(nextResource);
|
||||
String payload = "add " + mySubscriptionId.getIdPart() + '\n' + encoded;
|
||||
mySession.sendMessage(new TextMessage(payload));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
|
||||
try {
|
||||
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BoundStaticSubscipriptionState implements IState {
|
||||
|
||||
private WebSocketSession mySession;
|
||||
|
||||
public SimpleBoundState(WebSocketSession theSession) {
|
||||
public BoundStaticSubscipriptionState(WebSocketSession theSession) {
|
||||
mySession = theSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
try {
|
||||
|
@ -134,11 +207,93 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class InitialState implements IState {
|
||||
|
||||
private IIdType bindSimple(WebSocketSession theSession, String theBindString) {
|
||||
IdDt id = new IdDt(theBindString);
|
||||
|
||||
if (!id.hasIdPart() || !id.isIdPartValid()) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.hasResourceType() == false) {
|
||||
id = id.withResourceType("Subscription");
|
||||
}
|
||||
|
||||
try {
|
||||
Subscription subscription = mySubscriptionDao.read(id);
|
||||
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
|
||||
mySubscriptionId = subscription.getIdElement();
|
||||
myState = new BoundStaticSubscipriptionState(theSession);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - Unknown subscription: " + id.getValue()));
|
||||
} catch (IOException e1) {
|
||||
handleFailure(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private IIdType bingSearch(WebSocketSession theSession, String theRemaining) {
|
||||
Subscription subscription = new Subscription();
|
||||
subscription.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||
subscription.setStatus(SubscriptionStatusEnum.ACTIVE);
|
||||
subscription.setCriteria(theRemaining);
|
||||
|
||||
try {
|
||||
String params = theRemaining.substring(theRemaining.indexOf('?')+1);
|
||||
List<NameValuePair> paramValues = URLEncodedUtils.parse(params, Constants.CHARSET_UTF8, '&');
|
||||
EncodingEnum encoding = EncodingEnum.JSON;
|
||||
for (NameValuePair nameValuePair : paramValues) {
|
||||
if (Constants.PARAM_FORMAT.equals(nameValuePair.getName())) {
|
||||
EncodingEnum nextEncoding = Constants.FORMAT_VAL_TO_ENCODING.get(nameValuePair.getValue());
|
||||
if (nextEncoding != null) {
|
||||
encoding = nextEncoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = mySubscriptionDao.create(subscription).getId();
|
||||
|
||||
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
|
||||
mySubscriptionId = subscription.getIdElement();
|
||||
myState = new BoundDynamicSubscriptionState(theSession, encoding);
|
||||
|
||||
return id;
|
||||
} catch (UnprocessableEntityException e) {
|
||||
ourLog.warn("Failed to bind subscription: " + e.getMessage());
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - " + e.getMessage()));
|
||||
} catch (IOException e2) {
|
||||
handleFailure(e2);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleFailure(e);
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
|
||||
} catch (IOException e2) {
|
||||
handleFailure(e2);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
throw new IllegalStateException();
|
||||
|
@ -148,52 +303,37 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
|
||||
String message = theMessage.getPayload();
|
||||
if (message.startsWith("bind ")) {
|
||||
IdDt id = new IdDt(message.substring("bind ".length()));
|
||||
|
||||
if (!id.hasIdPart() || !id.isIdPartValid()) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
String remaining = message.substring("bind ".length());
|
||||
|
||||
IIdType subscriptionId;
|
||||
if (remaining.contains("?")) {
|
||||
subscriptionId = bingSearch(theSession, remaining);
|
||||
} else {
|
||||
subscriptionId = bindSimple(theSession, remaining);
|
||||
if (subscriptionId == null) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (id.hasResourceType()==false) {
|
||||
id = id.withResourceType("Subscription");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Subscription subscription = mySubscriptionDao.read(id);
|
||||
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
|
||||
mySubscriptionId = subscription.getIdElement();
|
||||
myState = new SimpleBoundState(theSession);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - Unknown subscription: " + id.getValue()));
|
||||
} catch (IOException e1) {
|
||||
handleFailure(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
theSession.sendMessage(new TextMessage("bound " + id.getIdPart()));
|
||||
theSession.sendMessage(new TextMessage("bound " + subscriptionId.getIdPart()));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private interface IState{
|
||||
private interface IState {
|
||||
|
||||
void closing();
|
||||
|
||||
void deliver(List<IBaseResource> theResults);
|
||||
|
||||
void handleTextMessage(WebSocketSession theSession, TextMessage theMessage);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,16 +4,18 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:websocket="http://www.springframework.org/schema/websocket"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
|
||||
|
||||
<context:annotation-config />
|
||||
|
||||
<websocket:handlers>
|
||||
<websocket:mapping path="/baseDstu2/websocket" handler="mySubscriptionWebsocketHandler" />
|
||||
<websocket:mapping path="/websocket/dstu2" handler="mySubscriptionWebsocketHandler" />
|
||||
</websocket:handlers>
|
||||
|
||||
<!--
|
||||
|
@ -26,4 +28,6 @@
|
|||
|
||||
<bean id="mySubscriptionSecurityInterceptor" class="ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptor"/>
|
||||
|
||||
<tx:annotation-driven transaction-manager="myTxManagerDstu2" />
|
||||
|
||||
</beans>
|
|
@ -65,6 +65,8 @@ import ca.uhn.fhir.model.primitive.DateTimeDt;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.CompositeParam;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
@ -78,6 +80,7 @@ import ca.uhn.fhir.rest.param.TokenAndListParam;
|
|||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
||||
|
@ -85,6 +88,19 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|||
public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SearchTest.class);
|
||||
|
||||
@Test
|
||||
public void testSearchWithEmptySort() {
|
||||
SearchParameterMap criteriaUrl = new SearchParameterMap();
|
||||
DateRangeParam range = new DateRangeParam();
|
||||
range.setLowerBound(new DateParam(QuantityCompararatorEnum.GREATERTHAN, 1000000));
|
||||
range.setUpperBound(new DateParam(QuantityCompararatorEnum.LESSTHAN, 2000000));
|
||||
criteriaUrl.setLastUpdated(range);
|
||||
criteriaUrl.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
|
||||
IBundleProvider results = myObservationDao.search(criteriaUrl);
|
||||
assertEquals(0, results.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIndexNoDuplicatesString() {
|
||||
Patient p = new Patient();
|
||||
|
|
|
@ -1919,6 +1919,10 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
@Test()
|
||||
public void testSortByComposite() {
|
||||
Observation o = new Observation();
|
||||
o.getCode().setText("testSortByComposite");
|
||||
myObservationDao.create(o);
|
||||
|
||||
SearchParameterMap pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT));
|
||||
try {
|
||||
|
|
|
@ -602,6 +602,96 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingWithLastUpdatedAndSort() throws Exception {
|
||||
String methodName = "testEverythingWithLastUpdatedAndSort";
|
||||
|
||||
long time0 = System.currentTimeMillis();
|
||||
Thread.sleep(10);
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName(methodName);
|
||||
IIdType oId = ourClient.create().resource(org).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
long time1 = System.currentTimeMillis();
|
||||
Thread.sleep(10);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily(methodName);
|
||||
p.getManagingOrganization().setReference(oId);
|
||||
IIdType pId = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
long time2 = System.currentTimeMillis();
|
||||
Thread.sleep(10);
|
||||
|
||||
Condition c = new Condition();
|
||||
c.getCode().setText(methodName);
|
||||
c.getPatient().setReference(pId);
|
||||
IIdType cId = ourClient.create().resource(c).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
Thread.sleep(10);
|
||||
long time3 = System.currentTimeMillis();
|
||||
|
||||
// %3E=> %3C=<
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantDt(new Date(time1)).getValueAsString());
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent());
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, containsInAnyOrder(pId, cId));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantDt(new Date(time2)).getValueAsString() + "&_lastUpdated=%3C" + new InstantDt(new Date(time3)).getValueAsString());
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent());
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, containsInAnyOrder(cId));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantDt(new Date(time1)).getValueAsString() + "&_sort=_lastUpdated");
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent());
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, contains(pId, cId));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_sort:desc=_lastUpdated");
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent());
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, contains(cId, pId, oId));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #148
|
||||
*/
|
||||
|
@ -1203,6 +1293,9 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
|
||||
@Test(expected = InvalidRequestException.class)
|
||||
public void testSearchWithInvalidSort() throws Exception {
|
||||
Observation o = new Observation();
|
||||
o.getCode().setText("testSearchWithInvalidSort");
|
||||
myObservationDao.create(o);
|
||||
//@formatter:off
|
||||
Bundle found = ourClient
|
||||
.search()
|
||||
|
|
|
@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
|
@ -12,6 +14,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
|||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -22,10 +25,20 @@ import ca.uhn.fhir.model.dstu2.resource.Subscription;
|
|||
import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum;
|
||||
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.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
||||
public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
||||
|
||||
public class BaseSocket {
|
||||
protected String myError;
|
||||
protected boolean myGotBound;
|
||||
protected int myPingCount;
|
||||
protected String mySubsId;
|
||||
|
||||
}
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionsDstu2Test.class);
|
||||
|
||||
@Override
|
||||
|
@ -37,7 +50,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
myDaoConfig.getInterceptors().add(interceptor);
|
||||
}
|
||||
|
||||
private void sleepUntilPingCount(SimpleEchoSocket socket, int wantPingCount) throws InterruptedException {
|
||||
private void sleepUntilPingCount(BaseSocket socket, int wantPingCount) throws InterruptedException {
|
||||
ourLog.info("Entering loop");
|
||||
for (long start = System.currentTimeMillis(), now = System.currentTimeMillis(); now - start <= 20000; now = System.currentTimeMillis()) {
|
||||
ourLog.debug("Starting");
|
||||
|
@ -90,7 +103,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
|
||||
IIdType id = ourClient.create().resource(subs).execute().getId();
|
||||
subs.setId(id);
|
||||
|
||||
|
||||
try {
|
||||
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
|
||||
ourClient.update().resource(subs).execute();
|
||||
|
@ -100,7 +113,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
}
|
||||
|
||||
try {
|
||||
subs.setStatus((SubscriptionStatusEnum)null);
|
||||
subs.setStatus((SubscriptionStatusEnum) null);
|
||||
ourClient.update().resource(subs).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
|
@ -111,14 +124,13 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
ourClient.update().resource(subs).execute();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateWithPopulatedButInvalidStatue() {
|
||||
Subscription subs = new Subscription();
|
||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||
subs.setCriteria("Observation?identifier=123");
|
||||
subs.getStatusElement().setValue("aaaaa");
|
||||
|
||||
|
||||
try {
|
||||
ourClient.create().resource(subs).execute();
|
||||
fail();
|
||||
|
@ -149,6 +161,135 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriptionDynamic() throws Exception {
|
||||
myDaoConfig.setSubscriptionEnabled(true);
|
||||
myDaoConfig.setSubscriptionPollDelay(0);
|
||||
|
||||
String methodName = "testSubscriptionDynamic";
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType pId = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
String criteria = "Observation?subject=Patient/" + pId.getIdPart();
|
||||
DynamicEchoSocket socket = new DynamicEchoSocket(criteria, EncodingEnum.JSON);
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
try {
|
||||
client.start();
|
||||
URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2");
|
||||
client.connect(socket, echoUri, new ClientUpgradeRequest());
|
||||
ourLog.info("Connecting to : {}", echoUri);
|
||||
|
||||
sleepUntilPingCount(socket, 1);
|
||||
|
||||
mySubscriptionDao.read(new IdDt("Subscription", socket.mySubsId));
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(pId);
|
||||
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||
IIdType afterId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
obs = new Observation();
|
||||
obs.getSubject().setReference(pId);
|
||||
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||
IIdType afterId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
sleepUntilPingCount(socket, 3);
|
||||
|
||||
obs = (Observation) socket.myReceived.get(0);
|
||||
assertEquals(afterId1.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
||||
obs = (Observation) socket.myReceived.get(1);
|
||||
assertEquals(afterId2.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
||||
obs = new Observation();
|
||||
obs.getSubject().setReference(pId);
|
||||
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||
IIdType afterId3 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
sleepUntilPingCount(socket, 4);
|
||||
|
||||
obs = (Observation) socket.myReceived.get(2);
|
||||
assertEquals(afterId3.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
||||
} finally {
|
||||
try {
|
||||
client.stop();
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure", e);
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSubscriptionDynamicXml() throws Exception {
|
||||
myDaoConfig.setSubscriptionEnabled(true);
|
||||
myDaoConfig.setSubscriptionPollDelay(0);
|
||||
|
||||
String methodName = "testSubscriptionDynamic";
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType pId = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
String criteria = "Observation?subject=Patient/" + pId.getIdPart() + "&_format=xml";
|
||||
DynamicEchoSocket socket = new DynamicEchoSocket(criteria, EncodingEnum.XML);
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
try {
|
||||
client.start();
|
||||
URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2");
|
||||
client.connect(socket, echoUri, new ClientUpgradeRequest());
|
||||
ourLog.info("Connecting to : {}", echoUri);
|
||||
|
||||
sleepUntilPingCount(socket, 1);
|
||||
|
||||
mySubscriptionDao.read(new IdDt("Subscription", socket.mySubsId));
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(pId);
|
||||
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||
IIdType afterId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
obs = new Observation();
|
||||
obs.getSubject().setReference(pId);
|
||||
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||
IIdType afterId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
sleepUntilPingCount(socket, 3);
|
||||
|
||||
obs = (Observation) socket.myReceived.get(0);
|
||||
assertEquals(afterId1.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
||||
obs = (Observation) socket.myReceived.get(1);
|
||||
assertEquals(afterId2.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
||||
obs = new Observation();
|
||||
obs.getSubject().setReference(pId);
|
||||
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||
IIdType afterId3 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
sleepUntilPingCount(socket, 4);
|
||||
|
||||
obs = (Observation) socket.myReceived.get(2);
|
||||
assertEquals(afterId3.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
||||
} finally {
|
||||
try {
|
||||
client.stop();
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure", e);
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriptionSimple() throws Exception {
|
||||
myDaoConfig.setSubscriptionEnabled(true);
|
||||
|
@ -183,7 +324,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
SimpleEchoSocket socket = new SimpleEchoSocket(subsId);
|
||||
try {
|
||||
client.start();
|
||||
URI echoUri = new URI("ws://localhost:" + ourPort + "/baseDstu2/websocket");
|
||||
URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2");
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
client.connect(socket, echoUri, request);
|
||||
ourLog.info("Connecting to : {}", echoUri);
|
||||
|
@ -196,7 +337,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
IIdType afterId3 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
sleepUntilPingCount(socket, 2);
|
||||
|
||||
|
||||
} finally {
|
||||
try {
|
||||
client.stop();
|
||||
|
@ -241,28 +382,15 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
* Basic Echo Client Socket
|
||||
*/
|
||||
@WebSocket(maxTextMessageSize = 64 * 1024)
|
||||
public static class SimpleEchoSocket {
|
||||
|
||||
private String myError;
|
||||
|
||||
private boolean myGotBound;
|
||||
|
||||
private int myPingCount;
|
||||
|
||||
// @OnWebSocketClose
|
||||
// public void onClose(int statusCode, String reason) {
|
||||
// ourLog.info("Connection closed: {} - {}", statusCode, reason);
|
||||
// this.session = null;
|
||||
// this.closeLatch.countDown();
|
||||
// }
|
||||
|
||||
private String mySubsId;
|
||||
public class SimpleEchoSocket extends BaseSocket {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Session session;
|
||||
|
||||
public SimpleEchoSocket(String theSubsId) {
|
||||
mySubsId = theSubsId;
|
||||
}
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session session) {
|
||||
ourLog.info("Got connect: {}", session);
|
||||
|
@ -275,13 +403,13 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
ourLog.error("Failure", t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String theMsg) {
|
||||
ourLog.info("Got msg: {}", theMsg);
|
||||
if (theMsg.equals("bound " + mySubsId)) {
|
||||
myGotBound = true;
|
||||
} else if (myGotBound && theMsg.startsWith("ping " + mySubsId)){
|
||||
} else if (myGotBound && theMsg.startsWith("ping " + mySubsId)) {
|
||||
myPingCount++;
|
||||
} else {
|
||||
myError = "Unexpected message: " + theMsg;
|
||||
|
@ -289,4 +417,51 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Echo Client Socket
|
||||
*/
|
||||
@WebSocket(maxTextMessageSize = 64 * 1024)
|
||||
public class DynamicEchoSocket extends BaseSocket {
|
||||
|
||||
private List<IBaseResource> myReceived = new ArrayList<IBaseResource>();
|
||||
@SuppressWarnings("unused")
|
||||
private Session session;
|
||||
private String myCriteria;
|
||||
private EncodingEnum myEncoding;
|
||||
|
||||
public DynamicEchoSocket(String theCriteria, EncodingEnum theEncoding) {
|
||||
myCriteria = theCriteria;
|
||||
myEncoding = theEncoding;
|
||||
}
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session session) {
|
||||
ourLog.info("Got connect: {}", session);
|
||||
this.session = session;
|
||||
try {
|
||||
String sending = "bind " + myCriteria;
|
||||
ourLog.info("Sending: {}", sending);
|
||||
session.getRemote().sendString(sending);
|
||||
} catch (Throwable t) {
|
||||
ourLog.error("Failure", t);
|
||||
}
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String theMsg) {
|
||||
ourLog.info("Got msg: {}", theMsg);
|
||||
if (theMsg.startsWith("bound ")) {
|
||||
myGotBound = true;
|
||||
mySubsId = (theMsg.substring("bound ".length()));
|
||||
myPingCount++;
|
||||
} else if (myGotBound && theMsg.startsWith("add " + mySubsId + "\n")) {
|
||||
String text = theMsg.substring(("add " + mySubsId + "\n").length());
|
||||
IBaseResource res = myEncoding.newParser(myFhirCtx).parseResource(text);
|
||||
myReceived.add(res);
|
||||
myPingCount++;
|
||||
} else {
|
||||
myError = "Unexpected message: " + theMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,13 @@
|
|||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>1.3-SNAPSHOT</version>
|
||||
<classifier>classes</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.phloc</groupId>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.apache.derby.drda.NetworkServerControl;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
@ -22,7 +26,30 @@ public class DerbyNetworkServer implements InitializingBean, DisposableBean {
|
|||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
server = new NetworkServerControl();
|
||||
server.start (null);
|
||||
Writer w = new Writer() {
|
||||
|
||||
@Override
|
||||
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
|
||||
ourLog.info("[DERBY] " + new String(theCbuf, theOff, theLen));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// nothing
|
||||
}};
|
||||
server.start (new PrintWriter(w));
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
DerbyNetworkServer s = new DerbyNetworkServer();
|
||||
s.afterPropertiesSet();
|
||||
s.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
@ -35,7 +36,7 @@ public class TestRestfulServer extends RestfulServer {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestRestfulServer.class);
|
||||
|
||||
private ClassPathXmlApplicationContext myAppCtx;
|
||||
private ApplicationContext myAppCtx;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
|
@ -44,6 +45,7 @@ public class TestRestfulServer extends RestfulServer {
|
|||
|
||||
// Get the spring context from the web container (it's declared in web.xml)
|
||||
WebApplicationContext parentAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
myAppCtx = (parentAppCtx);
|
||||
|
||||
// These two parmeters are also declared in web.xml
|
||||
String implDesc = getInitParameter("ImplementationDescription");
|
||||
|
@ -64,9 +66,6 @@ public class TestRestfulServer extends RestfulServer {
|
|||
String baseUrlProperty;
|
||||
switch (fhirVersionParam.trim().toUpperCase()) {
|
||||
case "DSTU1": {
|
||||
myAppCtx = new ClassPathXmlApplicationContext(new String[] {
|
||||
"hapi-fhir-server-database-config-dstu1.xml",
|
||||
"hapi-fhir-server-resourceproviders-dstu1.xml"}, parentAppCtx);
|
||||
setFhirContext(FhirContext.forDstu1());
|
||||
beans = myAppCtx.getBean("myResourceProvidersDstu1", List.class);
|
||||
systemProviderDstu1 = myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class);
|
||||
|
@ -79,11 +78,6 @@ public class TestRestfulServer extends RestfulServer {
|
|||
break;
|
||||
}
|
||||
case "DSTU2": {
|
||||
myAppCtx = new ClassPathXmlApplicationContext(new String[] {
|
||||
"hapi-fhir-server-database-config-dstu2.xml",
|
||||
"hapi-fhir-server-resourceproviders-dstu2.xml",
|
||||
"fhir-spring-subscription-config-dstu2.xml"
|
||||
}, parentAppCtx);
|
||||
setFhirContext(FhirContext.forDstu2());
|
||||
beans = myAppCtx.getBean("myResourceProvidersDstu2", List.class);
|
||||
systemProviderDstu2 = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package ca.uhn.fhirtest.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import ca.uhn.fhir.to.BaseController;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
|
||||
@org.springframework.stereotype.Controller()
|
||||
public class SubscriptionPlaygroundController extends BaseController {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionPlaygroundController.class);
|
||||
|
||||
@RequestMapping(value = { "/subscriptions" })
|
||||
public String subscriptionsHome(final HttpServletRequest theServletRequest, HomeRequest theRequest, final ModelMap theModel) {
|
||||
addCommonParams(theServletRequest, theRequest, theModel);
|
||||
|
||||
theModel.put("notHome", true);
|
||||
theModel.put("extraBreadcrumb", "Subscriptions");
|
||||
|
||||
ourLog.info(logPrefix(theModel) + "Displayed subscriptions playground page");
|
||||
|
||||
return "subscriptions";
|
||||
}
|
||||
|
||||
}
|
|
@ -51,9 +51,9 @@
|
|||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="myTxManager" />
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -38,9 +38,9 @@
|
|||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="myTxManager" />
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
<appender-ref ref="ACCESS" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</logger>
|
||||
|
||||
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
|
||||
|
||||
<context:component-scan base-package="ca.uhn.fhirtest.mvc" />
|
||||
|
||||
<mvc:annotation-driven />
|
||||
|
||||
</beans>
|
|
@ -34,6 +34,8 @@
|
|||
<property name="socketTimeout" value="10000"/>
|
||||
</bean>
|
||||
|
||||
<context:component-scan base-package="ca.uhn.fhirtest.mvc" />
|
||||
|
||||
<mvc:interceptors>
|
||||
<mvc:interceptor>
|
||||
<mvc:mapping path="/**"/>
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head th:include="tmpl-head :: head">
|
||||
<title>RESTful Tester</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form action="" method="get" id="outerForm">
|
||||
<input type="hidden" id="serverId" name="serverId"
|
||||
th:value="${serverId}" />
|
||||
|
||||
<div th:replace="tmpl-navbar-top :: top"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<div th:replace="tmpl-navbar-left :: left"></div>
|
||||
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-9 col-md-offset-3 main">
|
||||
|
||||
<div th:replace="tmpl-banner :: banner"></div>
|
||||
|
||||
<!-- ********************************************************** -->
|
||||
<!-- ** Subscriptions Playground ** -->
|
||||
<!-- ********************************************************** -->
|
||||
|
||||
<div class="panel panel-default" th:if="${resourceName.empty}">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Subscriptions Playground</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="container-fluid">
|
||||
<p>
|
||||
This page is a test playground for WebSocket Subscriptions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<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 -->
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="wss">...1</div>
|
||||
|
||||
<!-- Dynamic WebSocket -->
|
||||
<div role="tabpanel" class="tab-pane" id="wsd">
|
||||
<script type="text/javascript">
|
||||
function dwsCreate() {
|
||||
try {
|
||||
var socket = new WebSocket("ws://localhost:8888/websocket/dstu2");
|
||||
alert('Socket Status: '+socket.readyState);
|
||||
|
||||
socket.onopen = function() {
|
||||
alert("Opening Socket: " + socket.readyState);
|
||||
socket.send("bind Observation?");
|
||||
}
|
||||
socket.onmessage = function(msg) {
|
||||
alert("New message: " + msg);
|
||||
}
|
||||
socket.onerror = function(error) {
|
||||
alert("error: " + error);
|
||||
}
|
||||
} catch (exception) {
|
||||
alert('Error'+exception);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<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 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>
|
||||
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div th:replace="tmpl-footer :: footer"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -12,6 +12,11 @@
|
|||
/WEB-INF/hapi-fhir-server-config.xml
|
||||
/WEB-INF/hapi-fhir-tester-application-context.xml
|
||||
/WEB-INF/hapi-fhir-tester-config.xml
|
||||
classpath:/hapi-fhir-server-database-config-dstu1.xml
|
||||
classpath:/hapi-fhir-server-database-config-dstu2.xml
|
||||
classpath:hapi-fhir-server-resourceproviders-dstu1.xml
|
||||
classpath:hapi-fhir-server-resourceproviders-dstu2.xml
|
||||
classpath:fhir-spring-subscription-config-dstu2.xml
|
||||
</param-value>
|
||||
</context-param>
|
||||
|
||||
|
@ -25,6 +30,7 @@
|
|||
<param-value>
|
||||
/WEB-INF/hapi-fhir-tester-application-context.xml
|
||||
/WEB-INF/hapi-fhir-tester-config.xml
|
||||
classpath:fhir-spring-subscription-config-dstu2.xml
|
||||
</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>2</load-on-startup>
|
||||
|
|
|
@ -67,7 +67,7 @@ public class UhnFhirTestApp {
|
|||
client.create(p1);
|
||||
|
||||
List<IResource> resources = ctx.newJsonParser().parseBundle(IOUtils.toString(UhnFhirTestApp.class.getResourceAsStream("/bundle.json"))).toListOfResources();
|
||||
client.transaction().withResources(resources).execute();
|
||||
// client.transaction().withResources(resources).execute();
|
||||
|
||||
// for (int i = 0; i < 1000; i++) {
|
||||
//
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.narrative;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -53,7 +54,7 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
Patient value = new Patient();
|
||||
|
||||
value.addIdentifier().setSystem("urn:names").setValue("123456");
|
||||
value.addName().addFamily("blow").addGiven("joe").addGiven((String)null).addGiven("john");
|
||||
value.addName().addFamily("blow").addGiven("joe").addGiven((String) null).addGiven("john");
|
||||
value.getAddressFirstRep().addLine("123 Fake Street").addLine("Unit 1");
|
||||
value.getAddressFirstRep().setCity("Toronto").setState("ON").setCountry("Canada");
|
||||
|
||||
|
@ -67,12 +68,12 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
|
||||
String title = myGen.generateTitle(value);
|
||||
assertEquals("joe john BLOW (123456)", title);
|
||||
// ourLog.info(title);
|
||||
// ourLog.info(title);
|
||||
|
||||
value.getIdentifierFirstRep().setValue("FOO MRN 123");
|
||||
title = myGen.generateTitle(value);
|
||||
assertEquals("joe john BLOW (FOO MRN 123)", title);
|
||||
// ourLog.info(title);
|
||||
// ourLog.info(title);
|
||||
|
||||
}
|
||||
|
||||
|
@ -80,7 +81,7 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
public void testGenerateEncounter() throws DataFormatException {
|
||||
Encounter enc = new Encounter();
|
||||
|
||||
enc.addIdentifier().setSystem("urn:visits").setValue( "1234567");
|
||||
enc.addIdentifier().setSystem("urn:visits").setValue("1234567");
|
||||
enc.setClassElement(EncounterClassEnum.AMBULATORY);
|
||||
enc.setPeriod(new PeriodDt().setStart(new DateTimeDt("2001-01-02T11:11:00")));
|
||||
enc.setType(ca.uhn.fhir.model.dstu2.valueset.EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
|
||||
|
@ -92,7 +93,6 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGenerateDiagnosticReport() throws DataFormatException {
|
||||
DiagnosticReport value = new DiagnosticReport();
|
||||
|
@ -127,9 +127,9 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
|
||||
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, parse);
|
||||
|
||||
// String output = gen.generateTitle(oo);
|
||||
// ourLog.info(output);
|
||||
// assertEquals("Operation Outcome (2 issues)", output);
|
||||
// String output = gen.generateTitle(oo);
|
||||
// ourLog.info(output);
|
||||
// assertEquals("Operation Outcome (2 issues)", output);
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(null, oo, narrative);
|
||||
|
@ -137,11 +137,11 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
|
||||
ourLog.info(output);
|
||||
|
||||
// oo = new OperationOutcome();
|
||||
// oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("AA");
|
||||
// output = gen.generateTitle(oo);
|
||||
// ourLog.info(output);
|
||||
// assertEquals("Operation Outcome (fatal)", output);
|
||||
// oo = new OperationOutcome();
|
||||
// oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("AA");
|
||||
// output = gen.generateTitle(oo);
|
||||
// ourLog.info(output);
|
||||
// assertEquals("Operation Outcome (fatal)", output);
|
||||
|
||||
}
|
||||
|
||||
|
@ -193,27 +193,43 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some & Diagnostic Report </div>"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGenerateMedicationPrescription(){
|
||||
public void testGenerateMedicationPrescription() {
|
||||
MedicationOrder mp = new MedicationOrder();
|
||||
mp.setId("12345");
|
||||
Medication med = new Medication();
|
||||
med.getCode().setText("ciproflaxin");
|
||||
ResourceReferenceDt medRef = new ResourceReferenceDt(med);
|
||||
mp.setMedication(medRef );
|
||||
mp.setMedication(medRef);
|
||||
mp.setStatus(MedicationOrderStatusEnum.ACTIVE);
|
||||
mp.setDateWritten(new DateTimeDt("2014-09-01"));
|
||||
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(mp, narrative);
|
||||
|
||||
assertTrue("Expected medication name of ciprofloaxin within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ciprofloaxin")>-1);
|
||||
assertTrue("Expected string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE")>-1);
|
||||
|
||||
|
||||
assertTrue("Expected medication name of ciprofloaxin within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ciprofloaxin") > -1);
|
||||
assertTrue("Expected string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE") > -1);
|
||||
|
||||
String title = myGen.generateTitle(mp);
|
||||
assertEquals("ciprofloaxin", title);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateMedication() {
|
||||
Medication med = new Medication();
|
||||
med.getCode().setText("ciproflaxin");
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(med, narrative);
|
||||
|
||||
String string = narrative.getDiv().toString();
|
||||
assertThat(string, containsString("ciproflaxin"));
|
||||
|
||||
String title = myGen.generateTitle(med);
|
||||
assertEquals("ciproflaxin", title);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -92,63 +92,6 @@ public class XmlParserDstu2Test {
|
|||
private static final FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserDstu2Test.class);
|
||||
|
||||
@Test
|
||||
public void testParseBundleWithResourceId() {
|
||||
//@formatter:off
|
||||
String input = "<Bundle xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<entry><fullUrl value=\"http://localhost:58402/fhir/context/Patient/1\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1\"/><meta><versionId value=\"3\"/><lastUpdated value=\"2015-09-11T23:35:43.273-04:00\"/></meta><name><family value=\"testHistoryWithDeletedResource\"/></name></Patient></resource></entry>"
|
||||
+ "<entry><fullUrl value=\"http://localhost:58402/fhir/context/Patient/1\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1\"/><meta><versionId value=\"2\"/><lastUpdated value=\"2015-09-11T23:35:42.849-04:00\"/></meta><name><family value=\"testHistoryWithDeletedResource\"/></name></Patient></resource></entry>"
|
||||
+ "<entry><fullUrl value=\"http://localhost:58402/fhir/context/Patient/1\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1\"/><meta><versionId value=\"1\"/><lastUpdated value=\"2015-09-11T23:35:42.295-04:00\"/></meta><name><family value=\"testHistoryWithDeletedResource\"/></name></Patient></resource></entry>"
|
||||
+ "</Bundle>\n";
|
||||
//@formatter:on
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle bundle = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, input);
|
||||
assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/3", bundle.getEntry().get(0).getResource().getId().getValue());
|
||||
assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
|
||||
assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/1", bundle.getEntry().get(2).getResource().getId().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeReferenceUsingUnqualifiedResourceWorksCorrectly() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("phitcc_pat_normal");
|
||||
patient.addName().addGiven("Patty").setUse(NameUseEnum.NICKNAME);
|
||||
patient.addTelecom().setSystem(ContactPointSystemEnum.EMAIL).setValue("patpain@ehealthinnovation.org");
|
||||
patient.setGender(AdministrativeGenderEnum.FEMALE);
|
||||
patient.setBirthDate(new DateDt("2001-10-13"));
|
||||
|
||||
DateTimeDt obsEffectiveTime = new DateTimeDt("2015-04-11T12:22:01-04:00");
|
||||
|
||||
Observation obsParent = new Observation();
|
||||
obsParent.setId("phitcc_obs_bp_parent");
|
||||
obsParent.getSubject().setResource(patient);
|
||||
obsParent.setStatus(ObservationStatusEnum.FINAL);
|
||||
obsParent.setEffective(obsEffectiveTime);
|
||||
|
||||
Observation obsSystolic = new Observation();
|
||||
obsSystolic.setId("phitcc_obs_bp_dia");
|
||||
obsSystolic.getSubject().setResource(patient);
|
||||
obsSystolic.setEffective(obsEffectiveTime);
|
||||
obsParent.addRelated().setType(ObservationRelationshipTypeEnum.HAS_MEMBER).setTarget(new ResourceReferenceDt(obsSystolic));
|
||||
|
||||
Observation obsDiastolic = new Observation();
|
||||
obsDiastolic.setId("phitcc_obs_bp_dia");
|
||||
obsDiastolic.getSubject().setResource(patient);
|
||||
obsDiastolic.setEffective(obsEffectiveTime);
|
||||
obsParent.addRelated().setType(ObservationRelationshipTypeEnum.HAS_MEMBER).setTarget(new ResourceReferenceDt(obsDiastolic));
|
||||
|
||||
String str = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(obsParent);
|
||||
ourLog.info(str);
|
||||
|
||||
assertThat(str, containsString("<reference value=\"Patient/phitcc_pat_normal\"/>"));
|
||||
assertThat(str, containsString("<reference value=\"Observation/phitcc_obs_bp_dia\"/>"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testBundleWithBinary() {
|
||||
//@formatter:off
|
||||
|
@ -231,86 +174,6 @@ public class XmlParserDstu2Test {
|
|||
assertTrue(parsed.getEntries().get(0).getResource().getId().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
|
||||
*/
|
||||
@Test
|
||||
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
|
||||
IParser xmlParser = ourCtx.newXmlParser();
|
||||
|
||||
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
|
||||
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
|
||||
SimpleQuantityDt q = (SimpleQuantityDt) ms.getDosage().get(0).getQuantity();
|
||||
assertEquals("1", q.getValueElement().getValueAsString());
|
||||
|
||||
String output = xmlParser.encodeResourceToString(ms);
|
||||
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* See #216
|
||||
*/
|
||||
@Test
|
||||
public void testEncodeAndParseIdentifierDstu2() {
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("SYS").setValue("VAL").setType(IdentifierTypeCodesEnum.MR);
|
||||
|
||||
String out = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(out);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(out, stringContainsInOrder("<identifier>",
|
||||
"<type>",
|
||||
"<coding>",
|
||||
"<system value=\"http://hl7.org/fhir/v2/0203\"/>",
|
||||
"<code value=\"MR\"/>",
|
||||
"</coding>",
|
||||
"</type>",
|
||||
"<system value=\"SYS\"/>",
|
||||
"<value value=\"VAL\"/>",
|
||||
"</identifier>"));
|
||||
//@formatter:on
|
||||
|
||||
patient = ourCtx.newXmlParser().parseResource(Patient.class, out);
|
||||
assertThat(patient.getIdentifier().get(0).getType().getValueAsEnum(), contains(IdentifierTypeCodesEnum.MR));
|
||||
assertEquals("http://hl7.org/fhir/v2/0203", patient.getIdentifier().get(0).getType().getCoding().get(0).getSystem());
|
||||
assertEquals("MR", patient.getIdentifier().get(0).getType().getCoding().get(0).getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #216
|
||||
*/
|
||||
@Test
|
||||
public void testParseMalformedIdentifierDstu2() {
|
||||
|
||||
// This was changed from 0.5 to 1.0.0
|
||||
|
||||
//@formatter:off
|
||||
String out = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <identifier>\n" +
|
||||
" <type value=\"MRN\"/>\n" +
|
||||
" <system value=\"SYS\"/>\n" +
|
||||
" <value value=\"VAL\"/>\n" +
|
||||
" </identifier>\n" +
|
||||
"</Patient>";
|
||||
//@formatter:on
|
||||
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setParserErrorHandler(errorHandler);
|
||||
|
||||
Patient patient = p.parseResource(Patient.class, out);
|
||||
assertThat(patient.getIdentifier().get(0).getType().getValueAsEnum(), IsEmptyCollection.empty());
|
||||
|
||||
ArgumentCaptor<String> capt = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).unknownAttribute(any(IParseLocation.class), capt.capture());
|
||||
|
||||
assertEquals("value", capt.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseContained() {
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -448,9 +311,11 @@ public class XmlParserDstu2Test {
|
|||
String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
|
||||
assertThat(enc, containsString("<Patient xmlns=\"http://hl7.org/fhir\"><extension url=\"http://example.com/extensions#someext\"><valueDateTime value=\"2011-01-02T11:13:15\"/></extension>"));
|
||||
assertThat(enc, containsString("<modifierExtension url=\"http://example.com/extensions#modext\"><valueDate value=\"1995-01-02\"/></modifierExtension>"));
|
||||
assertThat(enc, containsString("<extension url=\"http://example.com#parent\"><extension url=\"http://example.com#child\"><valueString value=\"value1\"/></extension><extension url=\"http://example.com#child\"><valueString value=\"value2\"/></extension></extension>"));
|
||||
assertThat(enc, containsString(
|
||||
"<extension url=\"http://example.com#parent\"><extension url=\"http://example.com#child\"><valueString value=\"value1\"/></extension><extension url=\"http://example.com#child\"><valueString value=\"value2\"/></extension></extension>"));
|
||||
assertThat(enc, containsString("<given value=\"Joe\"><extension url=\"http://examples.com#givenext\"><valueString value=\"given\"/></extension></given>"));
|
||||
assertThat(enc, containsString("<given value=\"Shmoe\"><extension url=\"http://examples.com#givenext_parent\"><extension url=\"http://examples.com#givenext_child\"><valueString value=\"CHILD\"/></extension></extension></given>"));
|
||||
assertThat(enc, containsString(
|
||||
"<given value=\"Shmoe\"><extension url=\"http://examples.com#givenext_parent\"><extension url=\"http://examples.com#givenext_child\"><valueString value=\"CHILD\"/></extension></extension></given>"));
|
||||
|
||||
/*
|
||||
* Now parse this back
|
||||
|
@ -490,6 +355,38 @@ public class XmlParserDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #216
|
||||
*/
|
||||
@Test
|
||||
public void testEncodeAndParseIdentifierDstu2() {
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("SYS").setValue("VAL").setType(IdentifierTypeCodesEnum.MR);
|
||||
|
||||
String out = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(out);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(out, stringContainsInOrder("<identifier>",
|
||||
"<type>",
|
||||
"<coding>",
|
||||
"<system value=\"http://hl7.org/fhir/v2/0203\"/>",
|
||||
"<code value=\"MR\"/>",
|
||||
"</coding>",
|
||||
"</type>",
|
||||
"<system value=\"SYS\"/>",
|
||||
"<value value=\"VAL\"/>",
|
||||
"</identifier>"));
|
||||
//@formatter:on
|
||||
|
||||
patient = ourCtx.newXmlParser().parseResource(Patient.class, out);
|
||||
assertThat(patient.getIdentifier().get(0).getType().getValueAsEnum(), contains(IdentifierTypeCodesEnum.MR));
|
||||
assertEquals("http://hl7.org/fhir/v2/0203", patient.getIdentifier().get(0).getType().getCoding().get(0).getSystem());
|
||||
assertEquals("MR", patient.getIdentifier().get(0).getType().getCoding().get(0).getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseMetaProfileAndTags() {
|
||||
Patient p = new Patient();
|
||||
|
@ -595,6 +492,38 @@ public class XmlParserDstu2Test {
|
|||
assertEquals(new Tag("scheme2", "term2", "label2"), tagList.get(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for #233
|
||||
*/
|
||||
@Test
|
||||
public void testEncodeAndParseProfiledDatatype() {
|
||||
MedicationOrder mo = new MedicationOrder();
|
||||
mo.addDosageInstruction().getTiming().getRepeat().setBounds(new DurationDt().setCode("code"));
|
||||
String out = ourCtx.newXmlParser().encodeResourceToString(mo);
|
||||
ourLog.info(out);
|
||||
assertThat(out, containsString("</boundsQuantity>"));
|
||||
|
||||
mo = ourCtx.newXmlParser().parseResource(MedicationOrder.class, out);
|
||||
DurationDt duration = (DurationDt) mo.getDosageInstruction().get(0).getTiming().getRepeat().getBounds();
|
||||
assertEquals("code", duration.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
|
||||
*/
|
||||
@Test
|
||||
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
|
||||
IParser xmlParser = ourCtx.newXmlParser();
|
||||
|
||||
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
|
||||
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
|
||||
SimpleQuantityDt q = (SimpleQuantityDt) ms.getDosage().get(0).getQuantity();
|
||||
assertEquals("1", q.getValueElement().getValueAsString());
|
||||
|
||||
String output = xmlParser.encodeResourceToString(ms);
|
||||
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseSecurityLabels() {
|
||||
Patient p = new Patient();
|
||||
|
@ -791,12 +720,14 @@ public class XmlParserDstu2Test {
|
|||
ourLog.info(encoded);
|
||||
|
||||
// @formatter:on
|
||||
assertThat(encoded, stringContainsInOrder("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"123\"/>", "<code>", "<coding>", "<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>",
|
||||
"</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#123\"/>", "<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>"));
|
||||
assertThat(encoded,
|
||||
stringContainsInOrder("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"123\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#123\"/>",
|
||||
"<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>"));
|
||||
//@formatter:off
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #113
|
||||
*/
|
||||
|
@ -824,12 +755,13 @@ public class XmlParserDstu2Test {
|
|||
ourLog.info(encoded);
|
||||
|
||||
//@formatter:on
|
||||
assertThat(encoded, stringContainsInOrder("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"1\"/>", "<code>", "<coding>", "<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>",
|
||||
"</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#1\"/>", "<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>"));
|
||||
assertThat(encoded,
|
||||
stringContainsInOrder("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"1\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#1\"/>",
|
||||
"<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>"));
|
||||
//@formatter:off
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #113
|
||||
*/
|
||||
|
@ -861,8 +793,10 @@ public class XmlParserDstu2Test {
|
|||
ourLog.info(encoded);
|
||||
|
||||
//@formatter:on
|
||||
assertThat(encoded, stringContainsInOrder("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"123\"/>", "<code>", "<coding>", "<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>",
|
||||
"</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#123\"/>", "<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>"));
|
||||
assertThat(encoded,
|
||||
stringContainsInOrder("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"123\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#123\"/>",
|
||||
"<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>"));
|
||||
//@formatter:off
|
||||
|
||||
}
|
||||
|
@ -893,7 +827,6 @@ public class XmlParserDstu2Test {
|
|||
assertThat(encoded, (containsString("BARFOO")));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* #158
|
||||
|
@ -911,6 +844,7 @@ public class XmlParserDstu2Test {
|
|||
assertThat(encoded, not(containsString("tag")));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* #158
|
||||
*/
|
||||
|
@ -929,7 +863,6 @@ public class XmlParserDstu2Test {
|
|||
assertThat(encoded, not(containsString("Label")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeExtensionWithResourceContent() {
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
|
@ -951,6 +884,7 @@ public class XmlParserDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeNarrativeSuppressed() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -1010,7 +944,44 @@ public class XmlParserDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeReferenceUsingUnqualifiedResourceWorksCorrectly() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("phitcc_pat_normal");
|
||||
patient.addName().addGiven("Patty").setUse(NameUseEnum.NICKNAME);
|
||||
patient.addTelecom().setSystem(ContactPointSystemEnum.EMAIL).setValue("patpain@ehealthinnovation.org");
|
||||
patient.setGender(AdministrativeGenderEnum.FEMALE);
|
||||
patient.setBirthDate(new DateDt("2001-10-13"));
|
||||
|
||||
DateTimeDt obsEffectiveTime = new DateTimeDt("2015-04-11T12:22:01-04:00");
|
||||
|
||||
Observation obsParent = new Observation();
|
||||
obsParent.setId("phitcc_obs_bp_parent");
|
||||
obsParent.getSubject().setResource(patient);
|
||||
obsParent.setStatus(ObservationStatusEnum.FINAL);
|
||||
obsParent.setEffective(obsEffectiveTime);
|
||||
|
||||
Observation obsSystolic = new Observation();
|
||||
obsSystolic.setId("phitcc_obs_bp_dia");
|
||||
obsSystolic.getSubject().setResource(patient);
|
||||
obsSystolic.setEffective(obsEffectiveTime);
|
||||
obsParent.addRelated().setType(ObservationRelationshipTypeEnum.HAS_MEMBER).setTarget(new ResourceReferenceDt(obsSystolic));
|
||||
|
||||
Observation obsDiastolic = new Observation();
|
||||
obsDiastolic.setId("phitcc_obs_bp_dia");
|
||||
obsDiastolic.getSubject().setResource(patient);
|
||||
obsDiastolic.setEffective(obsEffectiveTime);
|
||||
obsParent.addRelated().setType(ObservationRelationshipTypeEnum.HAS_MEMBER).setTarget(new ResourceReferenceDt(obsDiastolic));
|
||||
|
||||
String str = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(obsParent);
|
||||
ourLog.info(str);
|
||||
|
||||
assertThat(str, containsString("<reference value=\"Patient/phitcc_pat_normal\"/>"));
|
||||
assertThat(str, containsString("<reference value=\"Observation/phitcc_obs_bp_dia\"/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSummary() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -1052,6 +1023,57 @@ public class XmlParserDstu2Test {
|
|||
assertThat(encoded, not(containsString("maritalStatus")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeWithEncodeElements() throws Exception {
|
||||
String content = IOUtils.toString(XmlParserDstu2Test.class.getResourceAsStream("/bundle-example.xml"));
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAMILY");
|
||||
patient.addAddress().addLine("LINE1");
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle();
|
||||
bundle.setTotal(100);
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient.name", "Bundle.entry")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
assertThat(out, not(containsString("total")));
|
||||
assertThat(out, (containsString("Patient")));
|
||||
assertThat(out, (containsString("name")));
|
||||
assertThat(out, not(containsString("address")));
|
||||
}
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient.name")));
|
||||
p.setEncodeElementsAppliesToResourceTypes(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
assertThat(out, (containsString("total")));
|
||||
assertThat(out, (containsString("Patient")));
|
||||
assertThat(out, (containsString("name")));
|
||||
assertThat(out, not(containsString("address")));
|
||||
}
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setEncodeElementsAppliesToResourceTypes(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
assertThat(out, (containsString("total")));
|
||||
assertThat(out, (containsString("Patient")));
|
||||
assertThat(out, (containsString("name")));
|
||||
assertThat(out, (containsString("address")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoreExtensions() throws Exception {
|
||||
|
||||
|
@ -1105,7 +1127,7 @@ public class XmlParserDstu2Test {
|
|||
assertThat(enc, containsString("<given value=\"Joe\"><extension url=\"http://examples.com#givenext\"><valueString value=\"given\"/></extension></given>"));
|
||||
assertThat(enc, containsString("<given value=\"Shmoe\"><extension url=\"http://examples.com#givenext_parent\"><extension url=\"http://examples.com#givenext_child\"><valueString value=\"CHILD\"/></extension></extension></given>"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOmitResourceId() {
|
||||
Patient p = new Patient();
|
||||
|
@ -1116,57 +1138,6 @@ public class XmlParserDstu2Test {
|
|||
assertThat(ourCtx.newXmlParser().setOmitResourceId(true).encodeResourceToString(p), containsString("ABC"));
|
||||
assertThat(ourCtx.newXmlParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithEncodeElements() throws Exception {
|
||||
String content = IOUtils.toString(XmlParserDstu2Test.class.getResourceAsStream("/bundle-example.xml"));
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAMILY");
|
||||
patient.addAddress().addLine("LINE1");
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle();
|
||||
bundle.setTotal(100);
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient.name", "Bundle.entry")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
assertThat(out, not(containsString("total")));
|
||||
assertThat(out, (containsString("Patient")));
|
||||
assertThat(out, (containsString("name")));
|
||||
assertThat(out, not(containsString("address")));
|
||||
}
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient.name")));
|
||||
p.setEncodeElementsAppliesToResourceTypes(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
assertThat(out, (containsString("total")));
|
||||
assertThat(out, (containsString("Patient")));
|
||||
assertThat(out, (containsString("name")));
|
||||
assertThat(out, not(containsString("address")));
|
||||
}
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setEncodeElementsAppliesToResourceTypes(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
assertThat(out, (containsString("total")));
|
||||
assertThat(out, (containsString("Patient")));
|
||||
assertThat(out, (containsString("name")));
|
||||
assertThat(out, (containsString("address")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseAndEncodeBundle() throws Exception {
|
||||
|
@ -1202,7 +1173,7 @@ public class XmlParserDstu2Test {
|
|||
assertTrue(d.toString(), d.identical());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseAndEncodeBundleNewStyle() throws Exception {
|
||||
String content = IOUtils.toString(XmlParserDstu2Test.class.getResourceAsStream("/bundle-example.xml"));
|
||||
|
@ -1239,7 +1210,8 @@ public class XmlParserDstu2Test {
|
|||
assertTrue(d.toString(), d.identical());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseAndEncodeExtensionOnResourceReference() {
|
||||
//@formatter:off
|
||||
|
@ -1642,6 +1614,22 @@ public class XmlParserDstu2Test {
|
|||
assertEquals("Observation", link.getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBundleWithResourceId() {
|
||||
//@formatter:off
|
||||
String input = "<Bundle xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<entry><fullUrl value=\"http://localhost:58402/fhir/context/Patient/1\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1\"/><meta><versionId value=\"3\"/><lastUpdated value=\"2015-09-11T23:35:43.273-04:00\"/></meta><name><family value=\"testHistoryWithDeletedResource\"/></name></Patient></resource></entry>"
|
||||
+ "<entry><fullUrl value=\"http://localhost:58402/fhir/context/Patient/1\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1\"/><meta><versionId value=\"2\"/><lastUpdated value=\"2015-09-11T23:35:42.849-04:00\"/></meta><name><family value=\"testHistoryWithDeletedResource\"/></name></Patient></resource></entry>"
|
||||
+ "<entry><fullUrl value=\"http://localhost:58402/fhir/context/Patient/1\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1\"/><meta><versionId value=\"1\"/><lastUpdated value=\"2015-09-11T23:35:42.295-04:00\"/></meta><name><family value=\"testHistoryWithDeletedResource\"/></name></Patient></resource></entry>"
|
||||
+ "</Bundle>\n";
|
||||
//@formatter:on
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle bundle = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, input);
|
||||
assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/3", bundle.getEntry().get(0).getResource().getId().getValue());
|
||||
assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
|
||||
assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/1", bundle.getEntry().get(2).getResource().getId().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* see #144 and #146
|
||||
*/
|
||||
|
@ -1710,6 +1698,38 @@ public class XmlParserDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #216
|
||||
*/
|
||||
@Test
|
||||
public void testParseMalformedIdentifierDstu2() {
|
||||
|
||||
// This was changed from 0.5 to 1.0.0
|
||||
|
||||
//@formatter:off
|
||||
String out = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <identifier>\n" +
|
||||
" <type value=\"MRN\"/>\n" +
|
||||
" <system value=\"SYS\"/>\n" +
|
||||
" <value value=\"VAL\"/>\n" +
|
||||
" </identifier>\n" +
|
||||
"</Patient>";
|
||||
//@formatter:on
|
||||
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setParserErrorHandler(errorHandler);
|
||||
|
||||
Patient patient = p.parseResource(Patient.class, out);
|
||||
assertThat(patient.getIdentifier().get(0).getType().getValueAsEnum(), IsEmptyCollection.empty());
|
||||
|
||||
ArgumentCaptor<String> capt = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).unknownAttribute(any(IParseLocation.class), capt.capture());
|
||||
|
||||
assertEquals("value", capt.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMetadata() throws Exception {
|
||||
//@formatter:off
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
@ -20,67 +22,102 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
|
||||
public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||
|
||||
private Map<String, ValueSet> myDefaultValueSets;
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
return (T) FhirInstanceValidator.loadProfileOrReturnNull(null, FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext), theUri.substring("http://hl7.org/fhir/StructureDefinition/".length()));
|
||||
}
|
||||
if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
Map<String, ValueSet> defaultValueSets = myDefaultValueSets;
|
||||
if (defaultValueSets == null) {
|
||||
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/valuesets.xml");
|
||||
if (valuesetText == null) {
|
||||
return null;
|
||||
}
|
||||
InputStreamReader reader;
|
||||
try {
|
||||
reader = new InputStreamReader(valuesetText, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Shouldn't happen!
|
||||
throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
|
||||
}
|
||||
|
||||
defaultValueSets = new HashMap<String, ValueSet>();
|
||||
|
||||
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
|
||||
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader);
|
||||
for (BundleEntryComponent next : bundle.getEntry()) {
|
||||
IdType nextId = new IdType(next.getFullUrl());
|
||||
if (nextId.isEmpty() || !nextId.getValue().startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
continue;
|
||||
}
|
||||
defaultValueSets.put(nextId.toVersionless().getValue(), (ValueSet) next.getResource());
|
||||
}
|
||||
|
||||
myDefaultValueSets = defaultValueSets;
|
||||
}
|
||||
|
||||
return (T) defaultValueSets.get(theUri);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private Map<String, ValueSet> myDefaultValueSets;
|
||||
private Map<String, ValueSet> myCodeSystems;
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
return (T) FhirInstanceValidator.loadProfileOrReturnNull(null, FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext), theUri.substring("http://hl7.org/fhir/StructureDefinition/".length()));
|
||||
}
|
||||
if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
Map<String, ValueSet> defaultValueSets = myDefaultValueSets;
|
||||
if (defaultValueSets == null) {
|
||||
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/valuesets.xml");
|
||||
if (valuesetText == null) {
|
||||
return null;
|
||||
}
|
||||
InputStreamReader reader;
|
||||
try {
|
||||
reader = new InputStreamReader(valuesetText, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Shouldn't happen!
|
||||
throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
|
||||
}
|
||||
|
||||
defaultValueSets = new HashMap<String, ValueSet>();
|
||||
|
||||
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
|
||||
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader);
|
||||
for (BundleEntryComponent next : bundle.getEntry()) {
|
||||
IdType nextId = new IdType(next.getFullUrl());
|
||||
if (nextId.isEmpty() || !nextId.getValue().startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
continue;
|
||||
}
|
||||
defaultValueSets.put(nextId.toVersionless().getValue(), (ValueSet) next.getResource());
|
||||
}
|
||||
|
||||
myDefaultValueSets = defaultValueSets;
|
||||
}
|
||||
|
||||
return (T) defaultValueSets.get(theUri);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return new CodeValidationResult(IssueSeverity.INFORMATION, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
return null;
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
Map<String, ValueSet> codeSystems = myCodeSystems;
|
||||
if (codeSystems == null) {
|
||||
codeSystems = new HashMap<String, ValueSet>();
|
||||
|
||||
loadCodeSystems(theContext, codeSystems, "/org/hl7/fhir/instance/model/valueset/valuesets.xml");
|
||||
loadCodeSystems(theContext, codeSystems, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
|
||||
loadCodeSystems(theContext, codeSystems, "/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml");
|
||||
|
||||
myCodeSystems = codeSystems;
|
||||
}
|
||||
|
||||
return codeSystems.get(theSystem);
|
||||
}
|
||||
|
||||
private void loadCodeSystems(FhirContext theContext, Map<String, ValueSet> codeSystems, String file) {
|
||||
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(file);
|
||||
if (valuesetText != null) {
|
||||
InputStreamReader reader;
|
||||
try {
|
||||
reader = new InputStreamReader(valuesetText, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Shouldn't happen!
|
||||
throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
|
||||
}
|
||||
|
||||
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
|
||||
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader);
|
||||
for (BundleEntryComponent next : bundle.getEntry()) {
|
||||
ValueSet nextValueSet = (ValueSet) next.getResource();
|
||||
String system = nextValueSet.getCodeSystem().getSystem();
|
||||
if (isNotBlank(system)) {
|
||||
codeSystems.put(system, nextValueSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
|
||||
return myValidationSupport.expandValueSet(theInc);
|
||||
return myValidationSupport.expandValueSet(myCtx, theInc);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -282,7 +282,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
if (myValidationSupport == null) {
|
||||
return null;
|
||||
} else {
|
||||
return myValidationSupport.fetchCodeSystem(theSystem);
|
||||
return myValidationSupport.fetchCodeSystem(myCtx, theSystem);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,13 +335,13 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
if (myValidationSupport == null) {
|
||||
return false;
|
||||
} else {
|
||||
return myValidationSupport.isCodeSystemSupported(theSystem);
|
||||
return myValidationSupport.isCodeSystemSupported(myCtx, theSystem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
|
||||
CodeValidationResult result = myValidationSupport.validateCode(theSystem, theCode, theDisplay);
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public interface IValidationSupport {
|
|||
* The portion to include
|
||||
* @return The expansion
|
||||
*/
|
||||
ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude);
|
||||
ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
|
||||
|
||||
/**
|
||||
* Fetch a code system by ID
|
||||
|
@ -27,7 +27,7 @@ public interface IValidationSupport {
|
|||
* The code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
ValueSet fetchCodeSystem(String theSystem);
|
||||
ValueSet fetchCodeSystem(FhirContext theContext, String theSystem);
|
||||
|
||||
/**
|
||||
* Loads a resource needed by the validation (a StructureDefinition, or a
|
||||
|
@ -53,7 +53,7 @@ public interface IValidationSupport {
|
|||
* @return Returns <code>true</code> if codes in the given code system can be
|
||||
* validated
|
||||
*/
|
||||
boolean isCodeSystemSupported(String theSystem);
|
||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
|
@ -68,7 +68,7 @@ public interface IValidationSupport {
|
|||
* The display name, if it should also be validated
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay);
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
|
||||
public class CodeValidationResult {
|
||||
private ConceptDefinitionComponent definition;
|
||||
|
|
|
@ -24,19 +24,19 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theInclude.getSystem())) {
|
||||
return next.expandValueSet(theInclude);
|
||||
if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) {
|
||||
return next.expandValueSet(theCtx, theInclude);
|
||||
}
|
||||
}
|
||||
return myChain.get(0).expandValueSet(theInclude);
|
||||
return myChain.get(0).expandValueSet(theCtx, theInclude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
public ValueSet fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
ValueSet retVal = next.fetchCodeSystem(theSystem);
|
||||
ValueSet retVal = next.fetchCodeSystem(theCtx, theSystem);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
|
@ -56,9 +56,9 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theSystem)) {
|
||||
if (next.isCodeSystemSupported(theCtx, theSystem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -66,13 +66,13 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCodeSystem)) {
|
||||
return next.validateCode(theCodeSystem, theCode, theDisplay);
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
return next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
}
|
||||
}
|
||||
return myChain.get(0).validateCode(theCodeSystem, theCode, theDisplay);
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ package ca.uhn.fhir.validation;
|
|||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
@ -16,6 +18,7 @@ import org.apache.commons.io.IOUtils;
|
|||
import org.hl7.fhir.instance.model.CodeType;
|
||||
import org.hl7.fhir.instance.model.Observation;
|
||||
import org.hl7.fhir.instance.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.instance.model.Patient;
|
||||
import org.hl7.fhir.instance.model.StringType;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
|
||||
|
@ -61,19 +64,19 @@ public class FhirInstanceValidatorTest {
|
|||
myValidConcepts = new ArrayList<String>();
|
||||
|
||||
myMockSupport = mock(IValidationSupport.class);
|
||||
when(myMockSupport.expandValueSet(any(ConceptSetComponent.class))).thenAnswer(new Answer<ValueSetExpansionComponent>() {
|
||||
when(myMockSupport.expandValueSet(any(FhirContext.class), any(ConceptSetComponent.class))).thenAnswer(new Answer<ValueSetExpansionComponent>() {
|
||||
@Override
|
||||
public ValueSetExpansionComponent answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
ConceptSetComponent arg = (ConceptSetComponent)theInvocation.getArguments()[0];
|
||||
ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getSystem());
|
||||
if (retVal == null) {
|
||||
retVal = myDefaultValidationSupport.expandValueSet(arg);
|
||||
retVal = myDefaultValidationSupport.expandValueSet(any(FhirContext.class), arg);
|
||||
}
|
||||
ourLog.info("expandValueSet({}) : {}", new Object[] { theInvocation.getArguments()[0], retVal });
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
when(myMockSupport.isCodeSystemSupported(any(String.class))).thenAnswer(new Answer<Boolean>() {
|
||||
when(myMockSupport.isCodeSystemSupported(any(FhirContext.class), any(String.class))).thenAnswer(new Answer<Boolean>() {
|
||||
@Override
|
||||
public Boolean answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
boolean retVal = mySupportedCodeSystemsForExpansion.containsKey(theInvocation.getArguments()[0]);
|
||||
|
@ -93,28 +96,29 @@ public class FhirInstanceValidatorTest {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
when(myMockSupport.validateCode(any(String.class), any(String.class), any(String.class)))
|
||||
when(myMockSupport.validateCode(any(FhirContext.class), any(String.class), any(String.class), any(String.class)))
|
||||
.thenAnswer(new Answer<CodeValidationResult>() {
|
||||
@Override
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
String system = (String) theInvocation.getArguments()[0];
|
||||
String code = (String) theInvocation.getArguments()[1];
|
||||
FhirContext ctx = (FhirContext) theInvocation.getArguments()[0];
|
||||
String system = (String) theInvocation.getArguments()[1];
|
||||
String code = (String) theInvocation.getArguments()[2];
|
||||
CodeValidationResult retVal;
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(code)));
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(system, code, (String) theInvocation.getArguments()[2]);
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, (String) theInvocation.getArguments()[2]);
|
||||
}
|
||||
ourLog.info("validateCode({}, {}, {}) : {}",
|
||||
new Object[] { system, code, (String) theInvocation.getArguments()[2], retVal });
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
when(myMockSupport.fetchCodeSystem(any(String.class))).thenAnswer(new Answer<ValueSet>() {
|
||||
when(myMockSupport.fetchCodeSystem(any(FhirContext.class), any(String.class))).thenAnswer(new Answer<ValueSet>() {
|
||||
@Override
|
||||
public ValueSet answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
ValueSet retVal = myDefaultValidationSupport.fetchCodeSystem((String) theInvocation.getArguments()[0]);
|
||||
ourLog.info("fetchCodeSystem({}) : {}", new Object[] { (String) theInvocation.getArguments()[0], retVal });
|
||||
ValueSet retVal = myDefaultValidationSupport.fetchCodeSystem((FhirContext) theInvocation.getArguments()[0],(String) theInvocation.getArguments()[1]);
|
||||
ourLog.info("fetchCodeSystem({}) : {}", new Object[] { (String) theInvocation.getArguments()[1], retVal });
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
|
@ -306,6 +310,29 @@ public class FhirInstanceValidatorTest {
|
|||
output.getMessages().get(0).getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateResourceWithValuesetExpansion() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://system").setValue("12345").getType().addCoding().setSystem("foo").setCode("bar");
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(patient);
|
||||
List<SingleValidationMessage> all = logResultsAndReturnAll(output);
|
||||
assertEquals(1, all.size());
|
||||
assertEquals("/f:Patient/f:identifier/f:type", all.get(0).getLocationString());
|
||||
assertEquals("None of the codes are in the expected value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type)", all.get(0).getMessage());
|
||||
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://system").setValue("12345").getType().addCoding().setSystem("http://hl7.org/fhir/v2/0203").setCode("MR");
|
||||
|
||||
output = myVal.validateWithResult(patient);
|
||||
all = logResultsAndReturnAll(output);
|
||||
assertEquals(0, all.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateResourceWithExampleBindingCodeValidationFailing() {
|
||||
Observation input = new Observation();
|
||||
|
@ -353,7 +380,11 @@ public class FhirInstanceValidatorTest {
|
|||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
assertEquals(errors.toString(), 1, errors.size());
|
||||
assertEquals("Unable to validate code \"1234\" in code system \"http://loinc.org\"", errors.get(0).getMessage());
|
||||
assertEquals(ResultSeverityEnum.WARNING, errors.get(0).getSeverity());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -44,11 +44,7 @@
|
|||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
<version>1.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dev</artifactId>
|
||||
<version>0.9</version>
|
||||
</dependency>-->
|
||||
<!--<dependency> <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-structures-dev</artifactId> <version>0.9</version> </dependency> -->
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
|
@ -71,7 +67,7 @@
|
|||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>commons-dbcp</groupId>
|
||||
|
@ -202,6 +198,13 @@
|
|||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<attachClasses>true</attachClasses>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -0,0 +1,637 @@
|
|||
package ca.uhn.fhir.to;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.HttpEntityWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
public class BaseController {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseController.class);
|
||||
static final String PARAM_RESOURCE = "resource";
|
||||
static final String RESOURCE_COUNT_EXT_URL = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount";
|
||||
|
||||
@Autowired
|
||||
protected TesterConfig myConfig;
|
||||
private Map<FhirVersionEnum, FhirContext> myContexts = new HashMap<FhirVersionEnum, FhirContext>();
|
||||
private List<String> myFilterHeaders;
|
||||
@Autowired
|
||||
private TemplateEngine myTemplateEngine;
|
||||
|
||||
public BaseController() {
|
||||
super();
|
||||
}
|
||||
|
||||
protected IResource addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
if (myConfig.getDebugTemplatesMode()) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
final String serverId = theRequest.getServerIdWithDefault(myConfig);
|
||||
final String serverBase = theRequest.getServerBase(theServletRequest, myConfig);
|
||||
final String serverName = theRequest.getServerName(myConfig);
|
||||
theModel.put("serverId", serverId);
|
||||
theModel.put("base", serverBase);
|
||||
theModel.put("baseName", serverName);
|
||||
theModel.put("resourceName", defaultString(theRequest.getResource()));
|
||||
theModel.put("encoding", theRequest.getEncoding());
|
||||
theModel.put("pretty", theRequest.getPretty());
|
||||
theModel.put("_summary", theRequest.get_summary());
|
||||
theModel.put("serverEntries", myConfig.getIdToServerName());
|
||||
|
||||
return loadAndAddConf(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
|
||||
return theAllHeaders;
|
||||
}
|
||||
ArrayList<Header> retVal = new ArrayList<Header>();
|
||||
for (Header next : theAllHeaders) {
|
||||
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal.toArray(new Header[retVal.size()]);
|
||||
}
|
||||
|
||||
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
|
||||
String str = StringEscapeUtils.escapeHtml4(theResultBody);
|
||||
if (str == null || theEncodingEnum == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
if (theEncodingEnum == EncodingEnum.JSON) {
|
||||
|
||||
boolean inValue = false;
|
||||
boolean inQuote = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char prevChar = (i > 0) ? str.charAt(i - 1) : ' ';
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
if (prevChar != '\\' && nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
} else if (nextChar == '\\' && nextChar2 == '"') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else {
|
||||
if (nextChar == ':') {
|
||||
inValue = true;
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '[' || nextChar == '{') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
if (inValue) {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
} else {
|
||||
b.append("<span class='hlTagName'>"");
|
||||
}
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else if (nextChar == ':') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = true;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
boolean inQuote = false;
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else if (inTag) {
|
||||
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("</span><span class='hlControl'>></span>");
|
||||
inTag = false;
|
||||
i += 3;
|
||||
} else if (nextChar == ' ') {
|
||||
b.append("</span><span class='hlAttr'>");
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
inTag = true;
|
||||
i += 3;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private String formatUrl(String theUrlBase, String theResultBody) {
|
||||
String str = theResultBody;
|
||||
if (str == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
try {
|
||||
str = URLDecoder.decode(str, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
ourLog.error("Should not happen", e);
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("<span class='hlUrlBase'>");
|
||||
|
||||
boolean inParams = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
// char nextChar2 = i < str.length()-2 ? str.charAt(i+1):' ';
|
||||
// char nextChar3 = i < str.length()-2 ? str.charAt(i+2):' ';
|
||||
if (!inParams) {
|
||||
if (nextChar == '?') {
|
||||
inParams = true;
|
||||
b.append("</span><wbr /><span class='hlControl'>?</span><span class='hlTagName'>");
|
||||
} else {
|
||||
if (i == theUrlBase.length()) {
|
||||
b.append("</span><wbr /><span class='hlText'>");
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&') {
|
||||
b.append("</span><wbr /><span class='hlControl'>&</span><span class='hlTagName'>");
|
||||
} else if (nextChar == '=') {
|
||||
b.append("</span><span class='hlControl'>=</span><span class='hlAttr'>");
|
||||
// }else if (nextChar=='%' && Character.isLetterOrDigit(nextChar2)&& Character.isLetterOrDigit(nextChar3)) {
|
||||
// URLDecoder.decode(s, enc)
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams) {
|
||||
b.append("</span>");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
protected FhirContext getContext(HomeRequest theRequest) {
|
||||
FhirVersionEnum version = theRequest.getFhirVersion(myConfig);
|
||||
FhirContext retVal = myContexts.get(version);
|
||||
if (retVal == null) {
|
||||
retVal = new FhirContext(version);
|
||||
myContexts.put(version, retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException {
|
||||
String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE));
|
||||
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName);
|
||||
if (def == null) {
|
||||
throw new ServletException("Invalid resourceName: " + resourceName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
protected ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
|
||||
ResultType returnsResource;
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
|
||||
if (theClient.getLastResponse() == null) {
|
||||
theModel.put("errorMsg", "Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return returnsResource;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
switch (theRequest.getFhirVersion(myConfig)) {
|
||||
case DEV:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
case DSTU1:
|
||||
return loadAndAddConfDstu1(theServletRequest, theRequest, theModel);
|
||||
case DSTU2:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
throw new IllegalStateException("Unknown version: " + theRequest.getFhirVersion(myConfig));
|
||||
}
|
||||
|
||||
private Conformance loadAndAddConfDstu1(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
Conformance conformance;
|
||||
try {
|
||||
conformance = (Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (ca.uhn.fhir.model.dstu.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getType().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu.resource.Conformance.RestResource>() {
|
||||
@Override
|
||||
public int compare(ca.uhn.fhir.model.dstu.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu.resource.Conformance.RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count2exts != null && count2exts.size() > 0) {
|
||||
count2 = (DecimalDt) count2exts.get(0).getValue();
|
||||
}
|
||||
int retVal = count2.compareTo(count1);
|
||||
if (retVal == 0) {
|
||||
retVal = theO1.getType().getValue().compareTo(theO2.getType().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Conformance conformance;
|
||||
try {
|
||||
conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource>() {
|
||||
@Override
|
||||
public int compare(ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count2exts != null && count2exts.size() > 0) {
|
||||
count2 = (DecimalDt) count2exts.get(0).getValue();
|
||||
}
|
||||
int retVal = count2.compareTo(count1);
|
||||
if (retVal == 0) {
|
||||
retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
protected String logPrefix(ModelMap theModel) {
|
||||
return "[server=" + theModel.get("serverId") + "] - ";
|
||||
}
|
||||
|
||||
private String parseNarrative(HomeRequest theRequest, EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = (IResource) theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected String preProcessMessageBody(String theBody) {
|
||||
if (theBody == null) {
|
||||
return "";
|
||||
}
|
||||
String retVal = theBody.trim();
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < retVal.length(); i++) {
|
||||
char nextChar = retVal.charAt(i);
|
||||
int nextCharI = nextChar;
|
||||
if (nextCharI == 65533) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 160) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 194) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
retVal = b.toString();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription, CaptureInterceptor theInterceptor, HomeRequest theRequest) {
|
||||
try {
|
||||
HttpRequestBase lastRequest = theInterceptor.getLastRequest();
|
||||
HttpResponse lastResponse = theInterceptor.getLastResponse();
|
||||
String requestBody = null;
|
||||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = lastRequest != null ? lastRequest.getMethod() : null;
|
||||
String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null;
|
||||
String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody());
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
|
||||
if (entity.isRepeatable()) {
|
||||
requestBody = IOUtils.toString(entity.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null;
|
||||
String mimeType = ct != null ? ct.getMimeType() : null;
|
||||
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
|
||||
String narrativeString = "";
|
||||
|
||||
StringBuilder resultDescription = new StringBuilder();
|
||||
Bundle bundle = null;
|
||||
|
||||
if (ctEnum == null) {
|
||||
resultDescription.append("Non-FHIR response");
|
||||
} else {
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
if (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("JSON resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("JSON bundle");
|
||||
bundle = getContext(theRequest).newJsonParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
if (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("XML resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("XML bundle");
|
||||
bundle = getContext(theRequest).newXmlParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DSTU2 no longer has a title in the bundle format, but it's still useful here..
|
||||
*/
|
||||
if (bundle != null) {
|
||||
INarrativeGenerator gen = getContext(theRequest).getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
for (BundleEntry next : bundle.getEntries()) {
|
||||
if (next.getTitle().isEmpty() && next.getResource() != null) {
|
||||
String title = gen.generateTitle(next.getResource());
|
||||
next.getTitle().setValue(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultDescription.append(" (").append(resultBody.length() + " bytes)");
|
||||
|
||||
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
|
||||
Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
|
||||
|
||||
theModelMap.put("outcomeDescription", outcomeDescription);
|
||||
theModelMap.put("resultDescription", resultDescription.toString());
|
||||
theModelMap.put("action", action);
|
||||
theModelMap.put("bundle", bundle);
|
||||
theModelMap.put("resultStatus", resultStatus);
|
||||
|
||||
theModelMap.put("requestUrl", requestUrl);
|
||||
theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl));
|
||||
|
||||
String requestBodyText = format(requestBody, ctEnum);
|
||||
theModelMap.put("requestBody", requestBodyText);
|
||||
|
||||
String resultBodyText = format(resultBody, ctEnum);
|
||||
theModelMap.put("resultBody", resultBodyText);
|
||||
|
||||
theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000);
|
||||
theModelMap.put("requestHeaders", requestHeaders);
|
||||
theModelMap.put("responseHeaders", responseHeaders);
|
||||
theModelMap.put("narrative", narrativeString);
|
||||
theModelMap.put("latencyMs", theLatency);
|
||||
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theModelMap.put("errorMsg", "Error during processing: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CaptureInterceptor implements IClientInterceptor {
|
||||
|
||||
private HttpRequestBase myLastRequest;
|
||||
private HttpResponse myLastResponse;
|
||||
private String myResponseBody;
|
||||
|
||||
public HttpRequestBase getLastRequest() {
|
||||
return myLastRequest;
|
||||
}
|
||||
|
||||
public HttpResponse getLastResponse() {
|
||||
return myLastResponse;
|
||||
}
|
||||
|
||||
public String getLastResponseBody() {
|
||||
return myResponseBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptRequest(HttpRequestBase theRequest) {
|
||||
assert myLastRequest == null;
|
||||
myLastRequest = theRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptResponse(HttpResponse theResponse) throws IOException {
|
||||
assert myLastResponse == null;
|
||||
myLastResponse = theResponse;
|
||||
|
||||
HttpEntity respEntity = theResponse.getEntity();
|
||||
if (respEntity != null) {
|
||||
final byte[] bytes;
|
||||
try {
|
||||
bytes = IOUtils.toByteArray(respEntity.getContent());
|
||||
} catch (IllegalStateException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
myResponseBody = new String(bytes, "UTF-8");
|
||||
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyEntityWrapper extends HttpEntityWrapper {
|
||||
|
||||
private byte[] myBytes;
|
||||
|
||||
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
|
||||
super(theWrappedEntity);
|
||||
myBytes = theBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException {
|
||||
return new ByteArrayInputStream(myBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream theOutstream) throws IOException {
|
||||
theOutstream.write(myBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected enum ResultType {
|
||||
BUNDLE, NONE, RESOURCE, TAGLIST
|
||||
}
|
||||
|
||||
}
|
|
@ -2,19 +2,10 @@ package ca.uhn.fhir.to;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.json.Json;
|
||||
|
@ -22,27 +13,13 @@ import javax.json.stream.JsonGenerator;
|
|||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.HttpEntityWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
|
@ -55,13 +32,10 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
|||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.gclient.ICreateTyped;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
|
@ -73,27 +47,14 @@ import ca.uhn.fhir.rest.gclient.StringClientParam;
|
|||
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
import ca.uhn.fhir.to.model.ResourceRequest;
|
||||
import ca.uhn.fhir.to.model.TransactionRequest;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
@org.springframework.stereotype.Controller()
|
||||
public class Controller {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Controller.class);
|
||||
private static final String PARAM_RESOURCE = "resource";
|
||||
private static final String RESOURCE_COUNT_EXT_URL = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount";
|
||||
|
||||
@Autowired
|
||||
private TesterConfig myConfig;
|
||||
|
||||
private Map<FhirVersionEnum, FhirContext> myContexts = new HashMap<FhirVersionEnum, FhirContext>();
|
||||
|
||||
private List<String> myFilterHeaders;
|
||||
|
||||
@Autowired
|
||||
private TemplateEngine myTemplateEngine;
|
||||
public class Controller extends BaseController {
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Controller.class);
|
||||
|
||||
@RequestMapping(value = { "/about" })
|
||||
public String actionAbout(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
|
@ -558,39 +519,6 @@ public class Controller {
|
|||
return "result";
|
||||
}
|
||||
|
||||
private IResource addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
if (myConfig.getDebugTemplatesMode()) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
final String serverId = theRequest.getServerIdWithDefault(myConfig);
|
||||
final String serverBase = theRequest.getServerBase(theServletRequest, myConfig);
|
||||
final String serverName = theRequest.getServerName(myConfig);
|
||||
theModel.put("serverId", serverId);
|
||||
theModel.put("base", serverBase);
|
||||
theModel.put("baseName", serverName);
|
||||
theModel.put("resourceName", defaultString(theRequest.getResource()));
|
||||
theModel.put("encoding", theRequest.getEncoding());
|
||||
theModel.put("pretty", theRequest.getPretty());
|
||||
theModel.put("_summary", theRequest.get_summary());
|
||||
theModel.put("serverEntries", myConfig.getIdToServerName());
|
||||
|
||||
return loadAndAddConf(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
|
||||
return theAllHeaders;
|
||||
}
|
||||
ArrayList<Header> retVal = new ArrayList<Header>();
|
||||
for (Header next : theAllHeaders) {
|
||||
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal.toArray(new Header[retVal.size()]);
|
||||
}
|
||||
|
||||
private void doActionCreateOrValidate(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod) {
|
||||
boolean validate = "validate".equals(theMethod);
|
||||
|
||||
|
@ -805,197 +733,6 @@ public class Controller {
|
|||
return haveSearchParams;
|
||||
}
|
||||
|
||||
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
|
||||
String str = StringEscapeUtils.escapeHtml4(theResultBody);
|
||||
if (str == null || theEncodingEnum == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
if (theEncodingEnum == EncodingEnum.JSON) {
|
||||
|
||||
boolean inValue = false;
|
||||
boolean inQuote = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char prevChar = (i > 0) ? str.charAt(i - 1) : ' ';
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
if (prevChar != '\\' && nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
} else if (nextChar == '\\' && nextChar2 == '"') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else {
|
||||
if (nextChar == ':') {
|
||||
inValue = true;
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '[' || nextChar == '{') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
if (inValue) {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
} else {
|
||||
b.append("<span class='hlTagName'>"");
|
||||
}
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else if (nextChar == ':') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = true;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
boolean inQuote = false;
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else if (inTag) {
|
||||
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("</span><span class='hlControl'>></span>");
|
||||
inTag = false;
|
||||
i += 3;
|
||||
} else if (nextChar == ' ') {
|
||||
b.append("</span><span class='hlAttr'>");
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
inTag = true;
|
||||
i += 3;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private String formatUrl(String theUrlBase, String theResultBody) {
|
||||
String str = theResultBody;
|
||||
if (str == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
try {
|
||||
str = URLDecoder.decode(str, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
ourLog.error("Should not happen", e);
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("<span class='hlUrlBase'>");
|
||||
|
||||
boolean inParams = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
// char nextChar2 = i < str.length()-2 ? str.charAt(i+1):' ';
|
||||
// char nextChar3 = i < str.length()-2 ? str.charAt(i+2):' ';
|
||||
if (!inParams) {
|
||||
if (nextChar == '?') {
|
||||
inParams = true;
|
||||
b.append("</span><wbr /><span class='hlControl'>?</span><span class='hlTagName'>");
|
||||
} else {
|
||||
if (i == theUrlBase.length()) {
|
||||
b.append("</span><wbr /><span class='hlText'>");
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&') {
|
||||
b.append("</span><wbr /><span class='hlControl'>&</span><span class='hlTagName'>");
|
||||
} else if (nextChar == '=') {
|
||||
b.append("</span><span class='hlControl'>=</span><span class='hlAttr'>");
|
||||
// }else if (nextChar=='%' && Character.isLetterOrDigit(nextChar2)&& Character.isLetterOrDigit(nextChar3)) {
|
||||
// URLDecoder.decode(s, enc)
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams) {
|
||||
b.append("</span>");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private FhirContext getContext(HomeRequest theRequest) {
|
||||
FhirVersionEnum version = theRequest.getFhirVersion(myConfig);
|
||||
FhirContext retVal = myContexts.get(version);
|
||||
if (retVal == null) {
|
||||
retVal = new FhirContext(version);
|
||||
myContexts.put(version, retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException {
|
||||
String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE));
|
||||
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName);
|
||||
if (def == null) {
|
||||
throw new ServletException("Invalid resourceName: " + resourceName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
private ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
|
||||
ResultType returnsResource;
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
|
||||
if (theClient.getLastResponse() == null) {
|
||||
theModel.put("errorMsg", "Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return returnsResource;
|
||||
}
|
||||
|
||||
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonGenerator theClientCodeJsonWriter) {
|
||||
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
|
||||
if (isBlank(nextName)) {
|
||||
|
@ -1099,349 +836,4 @@ public class Controller {
|
|||
return true;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
switch (theRequest.getFhirVersion(myConfig)) {
|
||||
case DEV:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
case DSTU1:
|
||||
return loadAndAddConfDstu1(theServletRequest, theRequest, theModel);
|
||||
case DSTU2:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
throw new IllegalStateException("Unknown version: " + theRequest.getFhirVersion(myConfig));
|
||||
}
|
||||
|
||||
private Conformance loadAndAddConfDstu1(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
Conformance conformance;
|
||||
try {
|
||||
conformance = (Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getType().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<RestResource>() {
|
||||
@Override
|
||||
public int compare(RestResource theO1, RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count2exts != null && count2exts.size() > 0) {
|
||||
count2 = (DecimalDt) count2exts.get(0).getValue();
|
||||
}
|
||||
int retVal = count2.compareTo(count1);
|
||||
if (retVal == 0) {
|
||||
retVal = theO1.getType().getValue().compareTo(theO2.getType().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Conformance conformance;
|
||||
try {
|
||||
conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource>() {
|
||||
@Override
|
||||
public int compare(ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count2exts != null && count2exts.size() > 0) {
|
||||
count2 = (DecimalDt) count2exts.get(0).getValue();
|
||||
}
|
||||
int retVal = count2.compareTo(count1);
|
||||
if (retVal == 0) {
|
||||
retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
private String logPrefix(ModelMap theModel) {
|
||||
return "[server=" + theModel.get("serverId") + "] - ";
|
||||
}
|
||||
|
||||
private String parseNarrative(HomeRequest theRequest, EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = (IResource) theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String preProcessMessageBody(String theBody) {
|
||||
if (theBody == null) {
|
||||
return "";
|
||||
}
|
||||
String retVal = theBody.trim();
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < retVal.length(); i++) {
|
||||
char nextChar = retVal.charAt(i);
|
||||
int nextCharI = nextChar;
|
||||
if (nextCharI == 65533) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 160) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 194) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
retVal = b.toString();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription,
|
||||
CaptureInterceptor theInterceptor, HomeRequest theRequest) {
|
||||
try {
|
||||
HttpRequestBase lastRequest = theInterceptor.getLastRequest();
|
||||
HttpResponse lastResponse = theInterceptor.getLastResponse();
|
||||
String requestBody = null;
|
||||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = lastRequest != null ? lastRequest.getMethod() : null;
|
||||
String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null;
|
||||
String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody());
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
|
||||
if (entity.isRepeatable()) {
|
||||
requestBody = IOUtils.toString(entity.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null;
|
||||
String mimeType = ct != null ? ct.getMimeType() : null;
|
||||
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
|
||||
String narrativeString = "";
|
||||
|
||||
StringBuilder resultDescription = new StringBuilder();
|
||||
Bundle bundle = null;
|
||||
|
||||
if (ctEnum == null) {
|
||||
resultDescription.append("Non-FHIR response");
|
||||
} else {
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
if (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("JSON resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("JSON bundle");
|
||||
bundle = getContext(theRequest).newJsonParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
if (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("XML resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("XML bundle");
|
||||
bundle = getContext(theRequest).newXmlParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DSTU2 no longer has a title in the bundle format, but it's still useful here..
|
||||
*/
|
||||
if (bundle != null) {
|
||||
INarrativeGenerator gen = getContext(theRequest).getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
for (BundleEntry next : bundle.getEntries()) {
|
||||
if (next.getTitle().isEmpty() && next.getResource() != null) {
|
||||
String title = gen.generateTitle(next.getResource());
|
||||
next.getTitle().setValue(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultDescription.append(" (").append(resultBody.length() + " bytes)");
|
||||
|
||||
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
|
||||
Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
|
||||
|
||||
theModelMap.put("outcomeDescription", outcomeDescription);
|
||||
theModelMap.put("resultDescription", resultDescription.toString());
|
||||
theModelMap.put("action", action);
|
||||
theModelMap.put("bundle", bundle);
|
||||
theModelMap.put("resultStatus", resultStatus);
|
||||
|
||||
theModelMap.put("requestUrl", requestUrl);
|
||||
theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl));
|
||||
|
||||
String requestBodyText = format(requestBody, ctEnum);
|
||||
theModelMap.put("requestBody", requestBodyText);
|
||||
|
||||
String resultBodyText = format(resultBody, ctEnum);
|
||||
theModelMap.put("resultBody", resultBodyText);
|
||||
|
||||
theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000);
|
||||
theModelMap.put("requestHeaders", requestHeaders);
|
||||
theModelMap.put("responseHeaders", responseHeaders);
|
||||
theModelMap.put("narrative", narrativeString);
|
||||
theModelMap.put("latencyMs", theLatency);
|
||||
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theModelMap.put("errorMsg", "Error during processing: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CaptureInterceptor implements IClientInterceptor {
|
||||
|
||||
private HttpRequestBase myLastRequest;
|
||||
private HttpResponse myLastResponse;
|
||||
private String myResponseBody;
|
||||
|
||||
public HttpRequestBase getLastRequest() {
|
||||
return myLastRequest;
|
||||
}
|
||||
|
||||
public HttpResponse getLastResponse() {
|
||||
return myLastResponse;
|
||||
}
|
||||
|
||||
public String getLastResponseBody() {
|
||||
return myResponseBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptRequest(HttpRequestBase theRequest) {
|
||||
assert myLastRequest == null;
|
||||
myLastRequest = theRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptResponse(HttpResponse theResponse) throws IOException {
|
||||
assert myLastResponse == null;
|
||||
myLastResponse = theResponse;
|
||||
|
||||
HttpEntity respEntity = theResponse.getEntity();
|
||||
if (respEntity != null) {
|
||||
final byte[] bytes;
|
||||
try {
|
||||
bytes = IOUtils.toByteArray(respEntity.getContent());
|
||||
} catch (IllegalStateException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
myResponseBody = new String(bytes, "UTF-8");
|
||||
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyEntityWrapper extends HttpEntityWrapper {
|
||||
|
||||
private byte[] myBytes;
|
||||
|
||||
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
|
||||
super(theWrappedEntity);
|
||||
myBytes = theBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException {
|
||||
return new ByteArrayInputStream(myBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream theOutstream) throws IOException {
|
||||
theOutstream.write(myBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum ResultType {
|
||||
BUNDLE, NONE, RESOURCE, TAGLIST
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,17 +31,22 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -228,7 +228,7 @@ public class TinderStructuresMojo extends AbstractMojo {
|
|||
String dtOutputDir = "target/generated-sources/tinder/ca/uhn/fhir/model/dev/composite";
|
||||
|
||||
ResourceGeneratorUsingSpreadsheet rp = new ResourceGeneratorUsingSpreadsheet("dstu2", ".");
|
||||
rp.setBaseResourceNames(Arrays.asList( "bundle"
|
||||
rp.setBaseResourceNames(Arrays.asList( "careplan"
|
||||
// //, "contract"
|
||||
// "valueset", "organization", "location"
|
||||
// , "observation", "conformance"
|
||||
|
@ -252,11 +252,12 @@ public class TinderStructuresMojo extends AbstractMojo {
|
|||
rp.getLocalImports().putAll(datatypeLocalImports);
|
||||
datatypeLocalImports.putAll(rp.getLocalImports());
|
||||
|
||||
String vsOutputDir = "target/generated-sources/tinder/ca/uhn/fhir/model/dev/valueset";
|
||||
vsp.writeMarkedValueSets(new File(vsOutputDir), "ca.uhn.fhir.model.dev");
|
||||
|
||||
dtp.writeAll(new File(dtOutputDir), null, "ca.uhn.fhir.model.dev");
|
||||
rp.writeAll(new File(rpOutputDir), new File(rpSOutputDir), "ca.uhn.fhir.model.dev");
|
||||
|
||||
String vsOutputDir = "target/generated-sources/tinder/ca/uhn/fhir/model/dev/valueset";
|
||||
vsp.writeMarkedValueSets(new File(vsOutputDir), "ca.uhn.fhir.model.dev");
|
||||
}
|
||||
|
||||
public static class ProfileFileDefinition {
|
||||
|
|
|
@ -79,7 +79,11 @@ public class ValueSetGenerator {
|
|||
version = "dstu2";
|
||||
}
|
||||
|
||||
String vs = IOUtils.toString(ValueSetGenerator.class.getResourceAsStream("/vs/" + version + "/all-valuesets-bundle.xml"));
|
||||
String name = "/vs/" + version + "/all-valuesets-bundle.xml";
|
||||
if (version.equals("dstu2")) {
|
||||
name = "/org/hl7/fhir/instance/model/valueset/valuesets.xml";
|
||||
}
|
||||
String vs = IOUtils.toString(ValueSetGenerator.class.getResourceAsStream(name));
|
||||
if ("dstu".equals(myVersion)) {
|
||||
Bundle bundle = newXmlParser.parseBundle(vs);
|
||||
for (BundleEntry next : bundle.getEntries()) {
|
||||
|
|
|
@ -84,6 +84,28 @@
|
|||
a more appropriate 400/404 in the JPA server (vread on invalid version,
|
||||
delete with no ID, etc.)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Fix narrative generation for DSTU2 Medication resource
|
||||
</action>
|
||||
<action type="fix">
|
||||
Profile validator now works for valuesets which use
|
||||
v2 tables
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA server Patient/[id]/$everything operation now supports
|
||||
_lastUpdated filtering and _sort'ing of results.
|
||||
</action>
|
||||
<action type="fix" issue="233">
|
||||
Fix parser issue where profiled choice element datatypes (e.g. value[x] where one allowable
|
||||
type is Duration, which is a profile of Quantity) get incorrectly encoded using the
|
||||
profiled datatype name instead of the base datatype name as required by the FHIR
|
||||
spec. Thanks to Nehashri Puttu Lokesh for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Some generated Enum types in DSTU2 HAPI structures
|
||||
did not have latest valueset definitions applied. Thanks
|
||||
to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.2" date="2015-09-18">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue