Credit for #120
This commit is contained in:
parent
18f1c579ab
commit
1a4a23bdc5
|
@ -0,0 +1,58 @@
|
||||||
|
package example;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.dstu2.Dstu2BundleFactory;
|
||||||
|
|
||||||
|
public class IncludesExamples {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
testSearchForPatients();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testSearchForPatients() {
|
||||||
|
List<IResource> resources = new IncludesExamples().searchForPatients();
|
||||||
|
|
||||||
|
// Create a bundle with both
|
||||||
|
FhirContext ctx = FhirContext.forDstu2();
|
||||||
|
|
||||||
|
Dstu2BundleFactory bf = new Dstu2BundleFactory(ctx);
|
||||||
|
bf.initializeBundleFromResourceList(null, resources, "http://example.com/base", "http://example.com/base/Patient", 1, BundleTypeEnum.SEARCHSET);
|
||||||
|
IBaseResource b = bf.getResourceBundle();
|
||||||
|
|
||||||
|
// Encode the bundle
|
||||||
|
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
|
||||||
|
System.out.println(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// START SNIPPET: addIncludes
|
||||||
|
@Search
|
||||||
|
private List<IResource> searchForPatients() {
|
||||||
|
// Create an organization
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("Organization/65546");
|
||||||
|
org.setName("Test Organization");
|
||||||
|
|
||||||
|
// Create a patient
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("Patient/1333");
|
||||||
|
patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
|
||||||
|
patient.getManagingOrganization().setResource(org);
|
||||||
|
|
||||||
|
// Here we return only the patient object, which has links to other resources
|
||||||
|
List<IResource> retVal = new ArrayList<IResource>();
|
||||||
|
retVal.add(patient);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
// END SNIPPET: addIncludes
|
||||||
|
|
||||||
|
}
|
|
@ -20,18 +20,15 @@ package ca.uhn.fhir.context;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -68,7 +65,6 @@ import ca.uhn.fhir.model.api.IBoundCodeableConcept;
|
||||||
import ca.uhn.fhir.model.api.ICodeEnum;
|
import ca.uhn.fhir.model.api.ICodeEnum;
|
||||||
import ca.uhn.fhir.model.api.IDatatype;
|
import ca.uhn.fhir.model.api.IDatatype;
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.IResourceBlock;
|
import ca.uhn.fhir.model.api.IResourceBlock;
|
||||||
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||||
|
@ -227,45 +223,34 @@ class ModelScanner {
|
||||||
* annotations if the HL7.org ones are found instead.
|
* annotations if the HL7.org ones are found instead.
|
||||||
*/
|
*/
|
||||||
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
|
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
|
||||||
|
|
||||||
T retVal = theTarget.getAnnotation(theAnnotationType);
|
T retVal = theTarget.getAnnotation(theAnnotationType);
|
||||||
if (true) {
|
if (true) {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Below disabled for now due to performance issues
|
// Below disabled for now due to performance issues
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (retVal == null) {
|
* if (retVal == null) { String sourceClassName = theAnnotationType.getName(); String candidateAltClassName =
|
||||||
String sourceClassName = theAnnotationType.getName();
|
* sourceClassName.replace("ca.uhn.fhir.model.api.annotation", "org.hl7.fhir.instance.model.annotations");
|
||||||
String candidateAltClassName = sourceClassName.replace("ca.uhn.fhir.model.api.annotation", "org.hl7.fhir.instance.model.annotations");
|
*
|
||||||
|
* if (!sourceClassName.equals(candidateAltClassName)) { try { final Class<? extends Annotation>
|
||||||
|
* altAnnotationClass = (Class<? extends Annotation>) Class.forName(candidateAltClassName); final Annotation
|
||||||
|
* altAnnotation = theTarget.getAnnotation(altAnnotationClass); if (altAnnotation == null) { return null; }
|
||||||
|
*
|
||||||
|
* ourLog.debug("Forwarding annotation request for [{}] to class [{}]", sourceClassName, candidateAltClassName);
|
||||||
|
*
|
||||||
|
* InvocationHandler h = new InvocationHandler() {
|
||||||
|
*
|
||||||
|
* @Override public Object invoke(Object theProxy, Method theMethod, Object[] theArgs) throws Throwable { Method
|
||||||
|
* altMethod = altAnnotationClass.getMethod(theMethod.getName(), theMethod.getParameterTypes()); return
|
||||||
|
* altMethod.invoke(altAnnotation, theArgs); } }; retVal = (T)
|
||||||
|
* Proxy.newProxyInstance(theAnnotationType.getClassLoader(), new Class<?>[] { theAnnotationType }, h);
|
||||||
|
*
|
||||||
|
* } catch (ClassNotFoundException e) { return null; } } }
|
||||||
|
*/
|
||||||
|
|
||||||
if (!sourceClassName.equals(candidateAltClassName)) {
|
|
||||||
try {
|
|
||||||
final Class<? extends Annotation> altAnnotationClass = (Class<? extends Annotation>) Class.forName(candidateAltClassName);
|
|
||||||
final Annotation altAnnotation = theTarget.getAnnotation(altAnnotationClass);
|
|
||||||
if (altAnnotation == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ourLog.debug("Forwarding annotation request for [{}] to class [{}]", sourceClassName, candidateAltClassName);
|
|
||||||
|
|
||||||
InvocationHandler h = new InvocationHandler() {
|
|
||||||
@Override
|
|
||||||
public Object invoke(Object theProxy, Method theMethod, Object[] theArgs) throws Throwable {
|
|
||||||
Method altMethod = altAnnotationClass.getMethod(theMethod.getName(), theMethod.getParameterTypes());
|
|
||||||
return altMethod.invoke(altAnnotation, theArgs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
retVal = (T) Proxy.newProxyInstance(theAnnotationType.getClassLoader(), new Class<?>[] { theAnnotationType }, h);
|
|
||||||
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,7 +519,13 @@ class ModelScanner {
|
||||||
* Child is an extension
|
* Child is an extension
|
||||||
*/
|
*/
|
||||||
Class<? extends IBase> et = (Class<? extends IBase>) nextElementType;
|
Class<? extends IBase> et = (Class<? extends IBase>) nextElementType;
|
||||||
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et);
|
|
||||||
|
IValueSetEnumBinder<Enum<?>> binder = null;
|
||||||
|
if (BoundCodeDt.class.isAssignableFrom(nextElementType) || IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
|
||||||
|
binder = getBoundCodeBinder(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et, binder);
|
||||||
orderMap.put(order, def);
|
orderMap.put(order, def);
|
||||||
if (IElement.class.isAssignableFrom(nextElementType)) {
|
if (IElement.class.isAssignableFrom(nextElementType)) {
|
||||||
addScanAlso((Class<? extends IElement>) nextElementType);
|
addScanAlso((Class<? extends IElement>) nextElementType);
|
||||||
|
@ -570,7 +561,8 @@ class ModelScanner {
|
||||||
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
||||||
orderMap.put(order, def);
|
orderMap.put(order, def);
|
||||||
|
|
||||||
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType) || IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType) || IBaseDatatype.class.isAssignableFrom(nextElementType)
|
||||||
|
|| IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||||
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
|
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
|
||||||
|
|
||||||
addScanAlso(nextDatatype);
|
addScanAlso(nextDatatype);
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.hl7.fhir.instance.model.IBase;
|
||||||
import org.hl7.fhir.instance.model.IBaseResource;
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.api.annotation.Extension;
|
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||||
|
@ -47,17 +48,29 @@ public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclared
|
||||||
private String myExtensionUrl;
|
private String myExtensionUrl;
|
||||||
private boolean myModifier;
|
private boolean myModifier;
|
||||||
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToChildExtension;
|
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToChildExtension;
|
||||||
|
private Object myInstanceConstructorArguments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param theDefinedLocally See {@link Extension#definedLocally()}
|
* @param theBoundTypeBinder
|
||||||
|
* If the child is of a type that requires a constructor argument to instantiate, this is the argument to
|
||||||
|
* use
|
||||||
|
* @param theDefinedLocally
|
||||||
|
* See {@link Extension#definedLocally()}
|
||||||
*/
|
*/
|
||||||
RuntimeChildDeclaredExtensionDefinition(Field theField, Child theChild, Description theDescriptionAnnotation, Extension theExtension, String theElementName, String theExtensionUrl, Class<? extends IBase> theChildType) throws ConfigurationException {
|
RuntimeChildDeclaredExtensionDefinition(Field theField, Child theChild, Description theDescriptionAnnotation, Extension theExtension, String theElementName, String theExtensionUrl, Class<? extends IBase> theChildType, IValueSetEnumBinder<Enum<?>> theBoundTypeBinder)
|
||||||
|
throws ConfigurationException {
|
||||||
super(theField, theChild, theDescriptionAnnotation, theElementName);
|
super(theField, theChild, theDescriptionAnnotation, theElementName);
|
||||||
assert isNotBlank(theExtensionUrl);
|
assert isNotBlank(theExtensionUrl);
|
||||||
myExtensionUrl = theExtensionUrl;
|
myExtensionUrl = theExtensionUrl;
|
||||||
myChildType = theChildType;
|
myChildType = theChildType;
|
||||||
myDefinedLocally=theExtension.definedLocally();
|
myDefinedLocally = theExtension.definedLocally();
|
||||||
myModifier = theExtension.isModifier();
|
myModifier = theExtension.isModifier();
|
||||||
|
myInstanceConstructorArguments = theBoundTypeBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getInstanceConstructorArguments() {
|
||||||
|
return myInstanceConstructorArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,7 +157,7 @@ public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclared
|
||||||
List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
|
List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
|
||||||
types.add(IResource.class);
|
types.add(IResource.class);
|
||||||
myChildDef = new RuntimeResourceReferenceDefinition("valueResource", types);
|
myChildDef = new RuntimeResourceReferenceDefinition("valueResource", types);
|
||||||
}else {
|
} else {
|
||||||
myChildDef = elementDef;
|
myChildDef = elementDef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.lang.reflect.Field;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.IBase;
|
import org.hl7.fhir.instance.model.IBase;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IDatatype;
|
|
||||||
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ class ParserState<T> {
|
||||||
switch (target.getChildType()) {
|
switch (target.getChildType()) {
|
||||||
case COMPOSITE_DATATYPE: {
|
case COMPOSITE_DATATYPE: {
|
||||||
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
|
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
|
||||||
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
|
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(myDefinition.getInstanceConstructorArguments());
|
||||||
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
|
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
|
||||||
ElementCompositeState newState = new ElementCompositeState(myPreResourceState, compositeTarget, newChildInstance);
|
ElementCompositeState newState = new ElementCompositeState(myPreResourceState, compositeTarget, newChildInstance);
|
||||||
push(newState);
|
push(newState);
|
||||||
|
@ -1403,7 +1403,7 @@ class ParserState<T> {
|
||||||
}
|
}
|
||||||
case PRIMITIVE_DATATYPE: {
|
case PRIMITIVE_DATATYPE: {
|
||||||
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
|
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
|
||||||
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance();
|
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments());
|
||||||
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
|
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
|
||||||
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
|
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
|
||||||
push(newState);
|
push(newState);
|
||||||
|
@ -1458,12 +1458,12 @@ class ParserState<T> {
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ElementCompositeState<T2 extends IBase> extends BaseState {
|
private class ElementCompositeState extends BaseState {
|
||||||
|
|
||||||
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
|
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
|
||||||
private T2 myInstance;
|
private IBase myInstance;
|
||||||
|
|
||||||
public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, T2 theInstance) {
|
public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBase theInstance) {
|
||||||
super(thePreResourceState);
|
super(thePreResourceState);
|
||||||
myDefinition = theDef;
|
myDefinition = theDef;
|
||||||
myInstance = theInstance;
|
myInstance = theInstance;
|
||||||
|
@ -1516,7 +1516,7 @@ class ParserState<T> {
|
||||||
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
|
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
|
||||||
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(child.getInstanceConstructorArguments());
|
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(child.getInstanceConstructorArguments());
|
||||||
child.getMutator().addValue(myInstance, newChildInstance);
|
child.getMutator().addValue(myInstance, newChildInstance);
|
||||||
ParserState<T>.ElementCompositeState<ICompositeType> newState = new ElementCompositeState<ICompositeType>(getPreResourceState(), compositeTarget, newChildInstance);
|
ParserState<T>.ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance);
|
||||||
push(newState);
|
push(newState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1540,7 +1540,7 @@ class ParserState<T> {
|
||||||
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
|
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
|
||||||
IBase newBlockInstance = blockTarget.newInstance();
|
IBase newBlockInstance = blockTarget.newInstance();
|
||||||
child.getMutator().addValue(myInstance, newBlockInstance);
|
child.getMutator().addValue(myInstance, newBlockInstance);
|
||||||
ElementCompositeState<IBase> newState = new ElementCompositeState<IBase>(getPreResourceState(), blockTarget, newBlockInstance);
|
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), blockTarget, newBlockInstance);
|
||||||
push(newState);
|
push(newState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1592,7 +1592,7 @@ class ParserState<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T2 getCurrentElement() {
|
protected IBase getCurrentElement() {
|
||||||
return myInstance;
|
return myInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1627,7 +1627,7 @@ class ParserState<T> {
|
||||||
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
|
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
|
||||||
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
|
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
|
||||||
myExtension.setValue(newChildInstance);
|
myExtension.setValue(newChildInstance);
|
||||||
ElementCompositeState<IBase> newState = new ElementCompositeState<IBase>(getPreResourceState(), compositeTarget, newChildInstance);
|
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance);
|
||||||
push(newState);
|
push(newState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1664,7 +1664,7 @@ class ParserState<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class SecurityLabelElementStateHapi extends ElementCompositeState<BaseCodingDt> {
|
private class SecurityLabelElementStateHapi extends ElementCompositeState {
|
||||||
|
|
||||||
public SecurityLabelElementStateHapi(ParserState<T>.PreResourceState thePreResourceState,BaseRuntimeElementCompositeDefinition<?> theDef, BaseCodingDt codingDt) {
|
public SecurityLabelElementStateHapi(ParserState<T>.PreResourceState thePreResourceState,BaseRuntimeElementCompositeDefinition<?> theDef, BaseCodingDt codingDt) {
|
||||||
super(thePreResourceState, theDef, codingDt);
|
super(thePreResourceState, theDef, codingDt);
|
||||||
|
@ -2224,18 +2224,21 @@ class ParserState<T> {
|
||||||
DISPLAY, INITIAL, REFERENCE
|
DISPLAY, INITIAL, REFERENCE
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResourceStateHapi extends ElementCompositeState<IResource> {
|
private class ResourceStateHapi extends ElementCompositeState {
|
||||||
|
|
||||||
|
private IResource myInstance;
|
||||||
|
|
||||||
public ResourceStateHapi(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IResource theInstance) {
|
public ResourceStateHapi(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IResource theInstance) {
|
||||||
super(thePreResourceState, theDef, theInstance);
|
super(thePreResourceState, theDef, theInstance);
|
||||||
|
myInstance = theInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
|
public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
|
||||||
if ("id".equals(theChildName)) {
|
if ("id".equals(theChildName)) {
|
||||||
push(new PrimitiveState(getPreResourceState(), getCurrentElement().getId()));
|
push(new PrimitiveState(getPreResourceState(), myInstance.getId()));
|
||||||
} else if ("meta".equals(theChildName)) {
|
} else if ("meta".equals(theChildName)) {
|
||||||
push(new MetaElementState(getPreResourceState(), getCurrentElement().getResourceMetadata()));
|
push(new MetaElementState(getPreResourceState(), myInstance.getResourceMetadata()));
|
||||||
} else {
|
} else {
|
||||||
super.enteringNewElement(theNamespace, theChildName);
|
super.enteringNewElement(theNamespace, theChildName);
|
||||||
}
|
}
|
||||||
|
@ -2243,7 +2246,7 @@ class ParserState<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResourceStateHl7Org extends ElementCompositeState<IBaseResource> {
|
private class ResourceStateHl7Org extends ElementCompositeState {
|
||||||
|
|
||||||
public ResourceStateHl7Org(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBaseResource theInstance) {
|
public ResourceStateHl7Org(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBaseResource theInstance) {
|
||||||
super(thePreResourceState, theDef, theInstance);
|
super(thePreResourceState, theDef, theInstance);
|
||||||
|
|
|
@ -77,6 +77,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private AddProfileTagEnum myAddProfileTag;
|
private AddProfileTagEnum myAddProfileTag;
|
||||||
|
private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES;
|
||||||
private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML;
|
private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML;
|
||||||
private ETagSupportEnum myETagSupport = DEFAULT_ETAG_SUPPORT;
|
private ETagSupportEnum myETagSupport = DEFAULT_ETAG_SUPPORT;
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
|
@ -87,18 +88,19 @@ public class RestfulServer extends HttpServlet {
|
||||||
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
||||||
private Collection<IResourceProvider> myResourceProviders;
|
private Collection<IResourceProvider> myResourceProviders;
|
||||||
private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy();
|
private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy();
|
||||||
|
private ResourceBinding myServerBinding = new ResourceBinding();
|
||||||
private BaseMethodBinding<?> myServerConformanceMethod;
|
private BaseMethodBinding<?> myServerConformanceMethod;
|
||||||
private Object myServerConformanceProvider;
|
private Object myServerConformanceProvider;
|
||||||
private String myServerName = "HAPI FHIR Server";
|
private String myServerName = "HAPI FHIR Server";
|
||||||
|
|
||||||
/** This is configurable but by default we just use HAPI version */
|
/** This is configurable but by default we just use HAPI version */
|
||||||
private String myServerVersion = VersionUtil.getVersion();
|
private String myServerVersion = VersionUtil.getVersion();
|
||||||
private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES;
|
|
||||||
|
|
||||||
private boolean myStarted;
|
private boolean myStarted;
|
||||||
|
|
||||||
private boolean myUseBrowserFriendlyContentTypes;
|
private boolean myUseBrowserFriendlyContentTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public RestfulServer() {
|
public RestfulServer() {
|
||||||
|
@ -257,8 +259,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourceBinding myServerBinding = new ResourceBinding();
|
|
||||||
|
|
||||||
private void findSystemMethods(Object theSystemProvider, Class<?> clazz) {
|
private void findSystemMethods(Object theSystemProvider, Class<?> clazz) {
|
||||||
Class<?> supertype = clazz.getSuperclass();
|
Class<?> supertype = clazz.getSuperclass();
|
||||||
if (!Object.class.equals(supertype)) {
|
if (!Object.class.equals(supertype)) {
|
||||||
|
@ -293,6 +293,10 @@ public class RestfulServer extends HttpServlet {
|
||||||
return myAddProfileTag;
|
return myAddProfileTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BundleInclusionRule getBundleInclusionRule() {
|
||||||
|
return myBundleInclusionRule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either
|
* Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either
|
||||||
* with the <code>_format</code> URL parameter, or with an <code>Accept</code> header in the request. The default is
|
* with the <code>_format</code> URL parameter, or with an <code>Accept</code> header in the request. The default is
|
||||||
|
@ -900,6 +904,15 @@ public class RestfulServer extends HttpServlet {
|
||||||
myAddProfileTag = theAddProfileTag;
|
myAddProfileTag = theAddProfileTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set how bundle factory should decide whether referenced resources should be included in bundles
|
||||||
|
*
|
||||||
|
* @param theBundleInclusionRule - inclusion rule (@see BundleInclusionRule for behaviors)
|
||||||
|
*/
|
||||||
|
public void setBundleInclusionRule(BundleInclusionRule theBundleInclusionRule) {
|
||||||
|
myBundleInclusionRule = theBundleInclusionRule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with
|
* Sets the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with
|
||||||
* the <code>_format</code> URL parameter, or with an <code>Accept</code> header in the request. The default is
|
* the <code>_format</code> URL parameter, or with an <code>Accept</code> header in the request. The default is
|
||||||
|
@ -1073,28 +1086,15 @@ public class RestfulServer extends HttpServlet {
|
||||||
theResponse.getWriter().write(theException.getMessage());
|
theResponse.getWriter().write(theException.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean partIsOperation(String nextString) {
|
private static boolean partIsOperation(String nextString) {
|
||||||
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$');
|
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$');
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NarrativeModeEnum {
|
public enum NarrativeModeEnum {
|
||||||
NORMAL, ONLY, SUPPRESS;
|
NORMAL, ONLY, SUPPRESS;
|
||||||
|
|
||||||
public static NarrativeModeEnum valueOfCaseInsensitive(String theCode) {
|
public static NarrativeModeEnum valueOfCaseInsensitive(String theCode) {
|
||||||
return valueOf(NarrativeModeEnum.class, theCode.toUpperCase());
|
return valueOf(NarrativeModeEnum.class, theCode.toUpperCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BundleInclusionRule getBundleInclusionRule() {
|
|
||||||
return myBundleInclusionRule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set how bundle factory should decide whether referenced resources should be included in bundles
|
|
||||||
*
|
|
||||||
* @param theBundleInclusionRule - inclusion rule (@see BundleInclusionRule for behaviors)
|
|
||||||
*/
|
|
||||||
public void setBundleInclusionRule(BundleInclusionRule theBundleInclusionRule) {
|
|
||||||
myBundleInclusionRule = theBundleInclusionRule;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ca.uhn.fhir.parser;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -138,4 +138,22 @@ public class BaseParserTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #120
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseExtensionWithBoundCodeType() {
|
||||||
|
//@formatter:off
|
||||||
|
String resText =
|
||||||
|
"<DiagnosticReport xmlns=\"http://hl7.org/fhir\">" +
|
||||||
|
" <extension url=\"http://agfa.com/extensions#workflowAction\">" +
|
||||||
|
" <valueCode value=\"sign-off\"/>" +
|
||||||
|
" </extension>" +
|
||||||
|
"</DiagnosticReport>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
MyDiagnosticReportWithBoundCodeExtension res = ourCtx.newXmlParser().parseResource(MyDiagnosticReportWithBoundCodeExtension.class, resText);
|
||||||
|
assertEquals(WorkflowActionEnum.SIGNOFF, res.getWorkflowAction().getValueAsEnum());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||||
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.CodeDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #120
|
||||||
|
*/
|
||||||
|
@ResourceDef(name = "DiagnosticReport")
|
||||||
|
public class MyDiagnosticReportWithBoundCodeExtension extends DiagnosticReport {
|
||||||
|
|
||||||
|
public static final String SP_IMAGING_STUDY = "ImagingStudy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each extension is defined in a field. Any valid HAPI Data Type can be used for the field type. Note that the
|
||||||
|
* [name=""] attribute in the @Child annotation needs to match the name for the bean accessor and mutator methods.
|
||||||
|
*/
|
||||||
|
// @Child(name = "taskId", order = 16)
|
||||||
|
// @Extension(url = "http://agfa.com/extensions#taskId", definedLocally = true, isModifier = false)
|
||||||
|
// @Description(shortDefinition = "The task id")
|
||||||
|
private IntegerDt taskId;
|
||||||
|
|
||||||
|
@Child(name = "workflowAction", type = CodeDt.class, order = 17)
|
||||||
|
@Extension(url = "http://agfa.com/extensions#workflowAction", definedLocally = true, isModifier = false)
|
||||||
|
@Description(shortDefinition = "sign-off |sign-off-later |correctionist +", formalDefinition = "The work-flow action")
|
||||||
|
private BoundCodeDt<WorkflowActionEnum> workflowAction;
|
||||||
|
|
||||||
|
// @Child(name = "workflowComments", order = 18)
|
||||||
|
// @Extension(url = "http://agfa.com/extensions#workflowComments", definedLocally = true, isModifier = false)
|
||||||
|
// @Description(shortDefinition = "The work-flow comments")
|
||||||
|
private StringDt workflowComments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is important to override the isEmpty() method, adding a check for any newly added fields.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return super.isEmpty() && ElementUtil.isEmpty(taskId, workflowAction, workflowComments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********
|
||||||
|
* Accessors and mutators follow
|
||||||
|
*
|
||||||
|
* IMPORTANT: Each extension is required to have an getter/accessor and a stter/mutator. You are highly recommended
|
||||||
|
* to create getters which create instances if they do not already exist, since this is how the rest of the HAPI
|
||||||
|
* FHIR API works.
|
||||||
|
********/
|
||||||
|
|
||||||
|
/** Getter for mandatory */
|
||||||
|
|
||||||
|
public IntegerDt getTaskId() {
|
||||||
|
if (taskId == null) {
|
||||||
|
taskId = new IntegerDt();
|
||||||
|
}
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyDiagnosticReportWithBoundCodeExtension setTaskId(int taskId) {
|
||||||
|
this.taskId = new IntegerDt(taskId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyDiagnosticReportWithBoundCodeExtension setTaskId(IntegerDt taskId) {
|
||||||
|
this.taskId = taskId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundCodeDt<WorkflowActionEnum> getWorkflowAction() {
|
||||||
|
if (workflowAction == null) {
|
||||||
|
workflowAction = new BoundCodeDt<WorkflowActionEnum>(WorkflowActionEnum.VALUESET_BINDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return workflowAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyDiagnosticReportWithBoundCodeExtension setWorkflowAction(BoundCodeDt<WorkflowActionEnum> workflowAction) {
|
||||||
|
this.workflowAction = workflowAction;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyDiagnosticReportWithBoundCodeExtension setWorkflowAction(WorkflowActionEnum workflowAction) {
|
||||||
|
getWorkflowAction().setValueAsEnum(workflowAction);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringDt getWorkflowComments() {
|
||||||
|
if (workflowComments == null) {
|
||||||
|
workflowComments = new StringDt();
|
||||||
|
}
|
||||||
|
return workflowComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyDiagnosticReportWithBoundCodeExtension setWorkflowComments(StringDt workflowComments) {
|
||||||
|
this.workflowComments = workflowComments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyDiagnosticReportWithBoundCodeExtension setWorkflowComments(String workflowComments) {
|
||||||
|
this.workflowComments = new StringDt(workflowComments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #120
|
||||||
|
*/
|
||||||
|
public enum WorkflowActionEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code Value: <b>sign-off</b>
|
||||||
|
*
|
||||||
|
* Sign off workflow action.
|
||||||
|
*/
|
||||||
|
SIGNOFF("sign-off","#workflow-action"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code Value: <b>sign-off-later</b>
|
||||||
|
*
|
||||||
|
* Sign off later workflow action.
|
||||||
|
*/
|
||||||
|
SIGNOFFLATER("sign-off-later","#workflow-action"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code Value: <b>correctionist</b>
|
||||||
|
*
|
||||||
|
* Correctionist workflow action.
|
||||||
|
*/
|
||||||
|
CORRECTIONIST("correctionist","#workflow-action");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier for this Value Set:
|
||||||
|
* local/workflow-action
|
||||||
|
*/
|
||||||
|
public static final String VALUESET_IDENTIFIER = "#workflow-action";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name for this Value Set:
|
||||||
|
* WorkflowAction
|
||||||
|
*/
|
||||||
|
public static final String VALUESET_NAME = "WorkflowAction";
|
||||||
|
|
||||||
|
private static Map<String, WorkflowActionEnum> CODE_TO_ENUM = new HashMap<String, WorkflowActionEnum>();
|
||||||
|
private static Map<String, Map<String, WorkflowActionEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, WorkflowActionEnum>>();
|
||||||
|
|
||||||
|
private final String myCode;
|
||||||
|
private final String mySystem;
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (WorkflowActionEnum next : WorkflowActionEnum.values()) {
|
||||||
|
CODE_TO_ENUM.put(next.getCode(), next);
|
||||||
|
|
||||||
|
if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) {
|
||||||
|
SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap<String, WorkflowActionEnum>());
|
||||||
|
}
|
||||||
|
SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the code associated with this enumerated value
|
||||||
|
*/
|
||||||
|
public String getCode() {
|
||||||
|
return myCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the code system associated with this enumerated value
|
||||||
|
*/
|
||||||
|
public String getSystem() {
|
||||||
|
return mySystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the enumerated value associated with this code
|
||||||
|
*/
|
||||||
|
public WorkflowActionEnum forCode(String theCode) {
|
||||||
|
WorkflowActionEnum retVal = CODE_TO_ENUM.get(theCode);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts codes to their respective enumerated values
|
||||||
|
*/
|
||||||
|
public static final IValueSetEnumBinder<WorkflowActionEnum> VALUESET_BINDER = new IValueSetEnumBinder<WorkflowActionEnum>() {
|
||||||
|
@Override
|
||||||
|
public String toCodeString(WorkflowActionEnum theEnum) {
|
||||||
|
return theEnum.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toSystemString(WorkflowActionEnum theEnum) {
|
||||||
|
return theEnum.getSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorkflowActionEnum fromCodeString(String theCodeString) {
|
||||||
|
return CODE_TO_ENUM.get(theCodeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorkflowActionEnum fromCodeString(String theCodeString, String theSystemString) {
|
||||||
|
Map<String, WorkflowActionEnum> map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString);
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return map.get(theCodeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
WorkflowActionEnum(String theCode, String theSystem){
|
||||||
|
myCode = theCode;
|
||||||
|
mySystem = theSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,8 +8,8 @@
|
||||||
<body>
|
<body>
|
||||||
<release version="0.9" date="TBA">
|
<release version="0.9" date="TBA">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
Support for DSTU2 features introduced: New Bundle encoding style, as well as new
|
Support for DSTU2 features introduced: New resource definitions, Bundle resource,
|
||||||
extension encoding in JSON.
|
encoding changes (ID in resource bodt, meta tag)
|
||||||
</action>
|
</action>
|
||||||
<action type="fix" issue="65">
|
<action type="fix" issue="65">
|
||||||
Fix an issue encoding extensions on primitive types in JSON. Previously the "_value" object
|
Fix an issue encoding extensions on primitive types in JSON. Previously the "_value" object
|
||||||
|
@ -143,6 +143,16 @@
|
||||||
DateClientParam.before() incorrectly placed "<=" instead of
|
DateClientParam.before() incorrectly placed "<=" instead of
|
||||||
"<" in the request URL. Thanks to Ryan for reporting!
|
"<" in the request URL. Thanks to Ryan for reporting!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add" issue="77" dev="wdebeau1">
|
||||||
|
Server now only automatically adds _include resources which are provided
|
||||||
|
as references if the client request actually requested that specific include.
|
||||||
|
See RestfulServer
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="120">
|
||||||
|
User defined resource types which contain extensions that use a bound code type
|
||||||
|
(e.g. an BoundCodeDt with a custom Enum) failed to parse correctly. Thanks
|
||||||
|
to baopingle for reporting and providing a test case!
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.8" date="2014-Dec-17">
|
<release version="0.8" date="2014-Dec-17">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
|
@ -1,656 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<document xmlns="http://maven.apache.org/changes/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 ./changes.xsd">
|
|
||||||
<properties>
|
|
||||||
<author>James Agnew</author>
|
|
||||||
<title>HAPI FHIR Changelog</title>
|
|
||||||
</properties>
|
|
||||||
<body>
|
|
||||||
<release version="0.9" date="TBA">
|
|
||||||
<action type="add">
|
|
||||||
Support for DSTU2 features introduced: New Bundle encoding style, as well as new
|
|
||||||
extension encoding in JSON.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Library now checks if custom resource types can be instantiated on startup
|
|
||||||
(e.g. because they don't have a no-argument constructor) in order to
|
|
||||||
avoid failing later
|
|
||||||
</action>
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<action type="add">
|
|
||||||
Bump a few dependency JARs to the latest versions in Maven POM:
|
|
||||||
<![CDATA[
|
|
||||||
<ul>
|
|
||||||
<li>SLF4j (in base module) - Bumped to 1.7.9</li>
|
|
||||||
<li>Apache HTTPClient (in base module) - Bumped to 4.3.6</li>
|
|
||||||
<li>Hibernate (in JPA module) - Bumped to 4.3.7</li>
|
|
||||||
</ul>
|
|
||||||
]]>
|
|
||||||
=======
|
|
||||||
<action type="fix" issue="67">
|
|
||||||
IdDt failed to recognize local identifiers containing fragments that look like
|
|
||||||
real identifiers as being local identifiers even though they started with '#'.
|
|
||||||
For example, a local resource reference of "#aa/_history/aa" would be incorrectly
|
|
||||||
parsed as a non-local reference.
|
|
||||||
Thanks to Mohammad Jafari for reporting!
|
|
||||||
>>>>>>> 31d61100db41590b9760daf9e0942ad553ff69e2
|
|
||||||
</action>
|
|
||||||
</release>
|
|
||||||
<release version="0.8" date="2014-Dec-17">
|
|
||||||
<action type="add">
|
|
||||||
<![CDATA[<b>API CHANGE:</b>]]> The "FHIR structures" for DSTU1 (the classes which model the
|
|
||||||
resources and composite datatypes) have been moved out of the core JAR into their
|
|
||||||
own JAR, in order to allow support for DEV resources, and DSTU2 resources when thast
|
|
||||||
version is finalized. See
|
|
||||||
<![CDATA[<a href="./doc_upgrading.html">upgrading</a>]]>
|
|
||||||
for more information.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
<![CDATA[
|
|
||||||
<b>Deprocated API Removal</b>: The following classes (which were deprocated previously)
|
|
||||||
have now been removed:
|
|
||||||
<ul>
|
|
||||||
<li><b>ISecurityManager</b>: If you are using this class, the same functionality
|
|
||||||
is available through the more general purpose
|
|
||||||
<a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_server_interceptor.html">server interceptor</a>
|
|
||||||
capabilities.
|
|
||||||
<li><b>CodingListParam</b>: This class was made redundant by the
|
|
||||||
<a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/rest/param/TokenOrListParam.html">TokenOrListParam</a>
|
|
||||||
class, which can be used in its place.
|
|
||||||
</ul>
|
|
||||||
]]>
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
<![CDATA[
|
|
||||||
<b>API Change</b>: The IResource#getResourceMetadata() method has been changed
|
|
||||||
from returning
|
|
||||||
<code>Map<ResourceMetadataKeyEnum<?>, Object><code>
|
|
||||||
to returning a new type called
|
|
||||||
<code>ResourceMetadataMap</code>. This new type implements
|
|
||||||
<code>Map<ResourceMetadataKeyEnum<?>, Object><code>
|
|
||||||
itself, so this change should not break existing code, but may
|
|
||||||
require a clean build in order to run correctly.
|
|
||||||
]]>
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="38" dev="wdebeau1">
|
|
||||||
Profile generation on the server was not working due to IdDt being
|
|
||||||
incorrectly used. Thanks to Bill de Beaubien for the pull request!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="42" dev="wdebeau1">
|
|
||||||
Profiles did not generate correctly if a resource definition class had a
|
|
||||||
defined extension which was of a composite type. Thanks to Bill de Beaubien for the pull request!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="44" dev="petromykhailysyn">
|
|
||||||
Remove unnecessary IOException from narrative generator API. Thanks to
|
|
||||||
Petro Mykhailysyn for the pull request!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="48" dev="wdebeau1">
|
|
||||||
Introduced a new
|
|
||||||
<![CDATA[<code>@ProvidesResources</code>]]> annotation which can be added to
|
|
||||||
resource provider and servers to allow them to declare additional resource
|
|
||||||
classes they are able to serve. This is useful if you have a server which can
|
|
||||||
serve up multiple classes for the same resource type (e.g. a server that sometimes
|
|
||||||
returns a default Patient, but sometimes uses a custom subclass).
|
|
||||||
Thanks to Bill de Beaubien for the pull request!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="49" dev="wdebeau1">
|
|
||||||
Introduced a new
|
|
||||||
<![CDATA[<code>@Destroy</code>]]> annotation which can be added to
|
|
||||||
a resource provider method. This method will be called by the server when it
|
|
||||||
is being closed/destroyed (e.g. when the application is being undeployed, the
|
|
||||||
container is being shut down, etc.)
|
|
||||||
Thanks to Bill de Beaubien for the pull request!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add a new method <![CDATA[handleException]]> to the server interceptor
|
|
||||||
framework which allows interceptors to be notified of any exceptions and
|
|
||||||
runtime errors within server methods. Interceptors may optionally also
|
|
||||||
override the default error handling behaviour of the RestfulServer.
|
|
||||||
</action>
|
|
||||||
<action dev="wdebeau1" type="add">
|
|
||||||
Add constants to BaseResource for the "_id" search parameter which all resources
|
|
||||||
should support.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
DateRangeParam parameters on the server now return correct
|
|
||||||
<![CDATA[<code>getLowerBoundAsInstant()</code>]]>
|
|
||||||
and
|
|
||||||
<![CDATA[<code>getUpperBoundAsInstant()</code>]]>
|
|
||||||
values if a single unqualified value is passed in. For example, if
|
|
||||||
a query containing
|
|
||||||
<![CDATA[<code>&birthdate=2012-10-01</code>]]>
|
|
||||||
is received, previously these two methods would both return the same
|
|
||||||
value, but with this fix
|
|
||||||
<![CDATA[<code>getUpperBoundAsInstant()</code>]]>
|
|
||||||
now returns the instant at 23:59:59.9999.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Resource fields with a type of "*" (or Any) sometimes failed to parse if a
|
|
||||||
value type of "code" was used. Thanks to Bill de Beaubien for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="add" dev="lmds">
|
|
||||||
Remove dependency on JAXB libraries, which were used to parse and encode
|
|
||||||
dates and times (even in the JSON parser). JAXB is built in to most JDKs
|
|
||||||
but the version bundled with IBM's JDK is flaky and resulted in a number
|
|
||||||
of problems when deploying to Websphere.
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="50" dev="jjathman">
|
|
||||||
Primitive datatypes now preserve their original string value when parsing resources,
|
|
||||||
as well as containing the "parsed value". For instance, a DecimalDt field value of
|
|
||||||
<![CDATA[<code>1.0000</code>]]> will be parsed into the corresponding
|
|
||||||
decimal value, but will also retain the original value with the corresponding
|
|
||||||
level of precision. This allows vadliator rules to be applied to
|
|
||||||
original values as received "over the wire", such as well formatted but
|
|
||||||
invalid dates, e.g. "2001-15-01". Thanks to Joe Athman for reporting and
|
|
||||||
helping to come up with a fix!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
When using Generic Client, if performing a
|
|
||||||
<![CDATA[create]]> or <![CDATA[update]]> operation using a String as the resource body,
|
|
||||||
the client will auto-detect the FHIR encoding style and send an appropriate
|
|
||||||
<![CDATA[Content-Type]]> header.
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="52">
|
|
||||||
JPA module (and public HAPI-FHIR test server) were unable to process resource types
|
|
||||||
where at least one search parameter has no path specified. These now correctly save
|
|
||||||
(although the server does not yet process these params, and it should). Thanks to
|
|
||||||
GitHub user shvoidlee for reporting and help with analysis!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Generic/Fluent Client "create" and "update" method requests were not setting a content type header
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="53" dev="petromykhailysyn">
|
|
||||||
DateDt left precision value as <![CDATA[null]]> in the constructor
|
|
||||||
<![CDATA[DateDt(Date)]]>.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
RESTful server now doesn't overwrite resource IDs if they are absolute. In other words, if
|
|
||||||
a server's Resource Provider returns a resource with ID "Patient/123" it will be translated to
|
|
||||||
"[base url]/Patient/123" but if the RP returns ID "http://foo/Patient/123" the ID will be
|
|
||||||
returned exactly as is. Thanks to Bill de Beaubien for the suggestion!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="55">
|
|
||||||
JPA module Transaction operation was not correctly replacing logical IDs
|
|
||||||
beginning with "cid:" with server assigned IDs, as required by the
|
|
||||||
specification.
|
|
||||||
</action>
|
|
||||||
<action type="fix" dev="tahurac">
|
|
||||||
<![CDATA[FhirTerser]]> did not visit or find children in contained resources when
|
|
||||||
searching a resource. This caused server implementations to not always return contained
|
|
||||||
resources when they are included with a resource being returned.
|
|
||||||
</action>
|
|
||||||
<action type="add" dev="lmds">
|
|
||||||
Add a method <![CDATA[String IResource#getResourceName()]]> which returns the name of the
|
|
||||||
resource in question (e.g. "Patient", or "Observation"). This is intended as a
|
|
||||||
convenience to users.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Do not strip version from resource references in resources returned
|
|
||||||
from server search methods. Thanks to Bill de Beaubien for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix" dev="jjathman" issue="54">
|
|
||||||
Correct an issue with the validator where changes to the underlying
|
|
||||||
OperationOutcome produced by a validation cycle cause the validation
|
|
||||||
results to be incorrect.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Client interceptors registered to an interface based client instance
|
|
||||||
were applied to other client instances for the same client interface as well. (Issue
|
|
||||||
did not affect generic/fluent clients)
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="57">
|
|
||||||
DateDt, DateTimeDt and types InstantDt types now do not throw an exception
|
|
||||||
if they are used to parse a value with the wrong level of precision for
|
|
||||||
the given type but do throw an exception if the wrong level of precision
|
|
||||||
is passed into their constructors.<![CDATA[<br/><br/>]]>
|
|
||||||
This means that HAPI FHIR can now successfully parse resources from external
|
|
||||||
sources that have the wrong level of precision, but will generate a validation
|
|
||||||
error if the resource is validated. Thanks to Alexander Kley for the suggestion!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Encoding a Binary resource without a content type set should not result in a NullPointerException. Thanks
|
|
||||||
to Alexander Kley for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Server gives a more helpful error message if multiple IResourceProvider implementations
|
|
||||||
are provided for the same resource type. Thanks to wanghaisheng for the idea!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="61">
|
|
||||||
Bring DSTU1 resource definitions up to version 0.0.82-2929<![CDATA[<br/>]]>
|
|
||||||
Bring DEV resource definitions up to 0.4.0-3775<![CDATA[<br/>]]>
|
|
||||||
Thanks to crinacimpian for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="62">
|
|
||||||
JPA server did not correctly process _include requests if included
|
|
||||||
resources were present with a non-numeric identifier. Thanks to
|
|
||||||
Bill de Beaubien for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="60">
|
|
||||||
Client requests which include a resource/bundle body (e.g. create,
|
|
||||||
update, transaction) were not including a charset in the content type
|
|
||||||
header, leading to servers incorrectly assuming ISO-8859/1. Thanks to
|
|
||||||
shvoidlee for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="59" dev="wdebeau1">
|
|
||||||
Clean up the way that Profile resources are automatically exported
|
|
||||||
by the server for custom resource profile classes. See the
|
|
||||||
<![CDATA[<a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/model/api/annotation/ResourceDef.html">@ResourceDef</a>]]>
|
|
||||||
JavaDoc for information on how this works.
|
|
||||||
</action>
|
|
||||||
</release>
|
|
||||||
<release version="0.7" date="2014-Oct-23">
|
|
||||||
<action type="add" issue="30">
|
|
||||||
<![CDATA[<b>API CHANGE:</b>]]> The TagList class previously implemented ArrayList semantics,
|
|
||||||
but this has been replaced with LinkedHashMap semantics. This means that the list of
|
|
||||||
tags will no longer accept duplicate tags, but that tag order will still be
|
|
||||||
preserved. Thanks to Bill de Beaubien for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="33">
|
|
||||||
Server was incorrectly including contained resources being returned as both contained resources, and as
|
|
||||||
top-level resources in the returned bundle for search operations.
|
|
||||||
Thanks to Bill de Beaubien for reporting! This also fixes Issue #20, thanks to
|
|
||||||
lephty for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="add" dev="suranga">
|
|
||||||
Documentation fixes
|
|
||||||
</action>
|
|
||||||
<action type="add" dev="dougmartin">
|
|
||||||
Add a collection of new methods on the generic client which support the
|
|
||||||
<![CDATA[
|
|
||||||
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#read(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
|
|
||||||
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#vread(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
|
|
||||||
and <b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#search(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">search</a></b>
|
|
||||||
]]>
|
|
||||||
operations using an absolute URL. This allows developers to perform these operations using
|
|
||||||
URLs they obtained from other sources (or external resource references within resources). In
|
|
||||||
addition, the existing read/vread operations will now access absolute URL references if
|
|
||||||
they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics
|
|
||||||
for contributing this implementation!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server implementation was not correctly figuring out its own FHIR Base URL when deployed
|
|
||||||
on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of
|
|
||||||
Systems Made Simple for their help in figuring out this issue!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
XML Parser failed to encode fields with both a resource reference child and
|
|
||||||
a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of
|
|
||||||
Systems Made Simple for their help in figuring out this issue!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to
|
|
||||||
Bernard Gitaadji for reporting and diagnosing the issue!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of
|
|
||||||
Orion Health for reporting this!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="24">
|
|
||||||
Conformance profiles which are automatically generated by the server were missing a few mandatory elements,
|
|
||||||
which meant that the profile did not correctly validate. Thanks to Bill de Beaubien of Systems Made Simple
|
|
||||||
for reporting this!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
XHTML (in narratives) containing escapable characters (e.g. < or ") will now always have those characters
|
|
||||||
escaped properly in encoded messages.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Resources containing entities which are not valid in basic XML (e.g. &sect;) will have those
|
|
||||||
entities converted to their equivalent unicode characters when resources are encoded, since FHIR does
|
|
||||||
not allow extended entities in resource instances.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add a new client interceptor which adds HTTP Authorization Bearer Tokens (for use with OAUTH2 servers)
|
|
||||||
to client requests.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Add phloc-commons dependency explicitly, which resolves an issue building HAPI from source on
|
|
||||||
some platforms. Thanks to Odysseas Pentakalos for the patch!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
HAPI now logs a single line indicating the StAX implementation being used upon the
|
|
||||||
first time an XML parser is created.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Update methods on the server did not return a "content-location" header, but
|
|
||||||
only a "location" header. Both are required according to the FHIR specification.
|
|
||||||
Thanks to Bill de Beaubien of Systems Made Simple for reporting this!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="26" dev="akley">
|
|
||||||
Parser failed to correctly read contained Binary resources. Thanks to Alexander Kley for
|
|
||||||
the patch!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="29" dev="akley">
|
|
||||||
Calling encode multiple times on a resource with contained resources caused the contained
|
|
||||||
resources to be re-added (and the actual message to grow) with each encode pass. Thanks to
|
|
||||||
Alexander Kley for the test case!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
JSON-encoded contained resources with the incorrect "_id" element (which should be "id", but some
|
|
||||||
incorrect examples exist on the FHIR specification) now parse correctly. In other words, HAPI
|
|
||||||
previously only accepted the correct "id" element, but now it also accepts the incorrect
|
|
||||||
"_id" element just to be more lenient.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Several unit tests failed on Windows (or any platform with non UTF-8 default encoding). This may
|
|
||||||
have also caused resource validation to fail occasionally on these platforms as well.
|
|
||||||
Thanks to Bill de Beaubien for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
toString() method on TokenParam was incorrectly showing the system as the value.
|
|
||||||
Thanks to Bill de Beaubien for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="update">
|
|
||||||
Documentation on contained resources contained a typo and did not actually produce contained resources. Thanks
|
|
||||||
to David Hay of Orion Health for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="31" dev="preston">
|
|
||||||
Add a
|
|
||||||
<![CDATA[<a href="https://www.vagrantup.com/">Vagrant</a>]]>
|
|
||||||
based environment (basically a fully built, self contained development environment) for
|
|
||||||
trying out the HAPI server modules. Thanks to Preston Lee for the pull request, and for
|
|
||||||
offering to maintain this!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="32" dev="jjathman">
|
|
||||||
Change validation API so that it uses a return type instead of exceptions to communicate
|
|
||||||
validation failures. Thanks to Joe Athman for the pull request!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="35" dev="petromykhailysyn">
|
|
||||||
Add a client interceptor which adds an HTTP cookie to each client request. Thanks to
|
|
||||||
Petro Mykhailysyn for the pull request!
|
|
||||||
</action>
|
|
||||||
</release>
|
|
||||||
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
|
|
||||||
<!--
|
|
||||||
<action type="add">
|
|
||||||
Allow generic client ... OAUTH
|
|
||||||
</action>
|
|
||||||
-->
|
|
||||||
<action type="add">
|
|
||||||
Add server interceptor framework, and new interceptor for logging incoming
|
|
||||||
requests.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add server validation framework for validating resources against the FHIR schemas and schematrons
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University
|
|
||||||
Health Network for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="4">
|
|
||||||
Create method was incorrectly returning an HTTP 204 on sucessful completion, but
|
|
||||||
should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng
|
|
||||||
for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing
|
|
||||||
non US-ASCII characters will correctly display in the browser
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
JSON parser was incorrectly encoding extensions on composite elements outside the element itself
|
|
||||||
(as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of
|
|
||||||
Orion for reporting this!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Contained/included resource instances received by a client are now automatically
|
|
||||||
added to any ResourceReferenceDt instancea in other resources which reference them.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add documentation on how to use eBay CORS Filter to support Cross Origin Resource
|
|
||||||
Sharing (CORS) to server. CORS support that was built in to the server itself has
|
|
||||||
been removed, as it did not work correctly (and was reinventing a wheel that others
|
|
||||||
have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance
|
|
||||||
in testing this!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
IResource interface did not expose the getLanguage/setLanguage methods from BaseResource,
|
|
||||||
so the resource language was difficult to access.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use
|
|
||||||
of single quotes
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Transaction server method is now allowed to return an OperationOutcome in addition to the
|
|
||||||
incoming resources. The public test server now does this in order to return status information
|
|
||||||
about the transaction processing.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Update method in the server can now flag (via a field on the MethodOutcome object being returned)
|
|
||||||
that the result was actually a creation, and Create method can indicate that it was actually an
|
|
||||||
update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the
|
|
||||||
response, but this may be useful in some circumstances.
|
|
||||||
</action>
|
|
||||||
<action type="fix" dev="tahurac">
|
|
||||||
Annotation client search methods with a specific resource type (e.g. List<Patient> search())
|
|
||||||
won't return any resources that aren't of the correct type that are received in a response
|
|
||||||
bundle (generally these are referenced resources, so they are populated in the reference fields instead).
|
|
||||||
Thanks to Tahura Chaudhry of University Health Network for the unit test!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Added narrative generator template for OperationOutcome resource
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format
|
|
||||||
is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple
|
|
||||||
for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server search method for an unnamed query gets called if the client requests a named query
|
|
||||||
with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Category header (for tags) is correctly read in client for "read" operation
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Transaction method in server can now have parameter type Bundle instead of
|
|
||||||
List<IResource>
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
HAPI parsers now use field access to get/set values instead of method accessors and mutators.
|
|
||||||
This should give a small performance boost.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
JSON parser encodes resource references incorrectly, using the name "resource" instead
|
|
||||||
of the name "reference" for the actual reference. Thanks to
|
|
||||||
Ricky Nguyen for reporting and tracking down the issue!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Rename NotImpementedException to NotImplementedException (to correct typo)
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status)
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Fix performance issue in date/time datatypes where pattern matchers were not static
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but
|
|
||||||
previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health
|
|
||||||
for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
QuantityParam correctly encodes approximate (~) prefix to values
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="14">
|
|
||||||
If a server defines a method with parameter "_id", incoming search requests for that method may
|
|
||||||
get delegated to the wrong method. Thanks to Neal Acharya for reporting!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
SecurityEvent.Object structural element has been renamed to
|
|
||||||
SecurityEvent.ObjectElement to avoid conflicting names with the
|
|
||||||
java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for
|
|
||||||
reporting!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Text/narrative blocks that were created with a non-empty
|
|
||||||
namespace prefix (e.g. <xhtml:div xmlns:xhtml="...">...</xhtml:div>)
|
|
||||||
failed to encode correctly (prefix was missing in encoded resource)
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Resource references previously encoded their children (display and reference)
|
|
||||||
in the wrong order so references with both would fail schema validation.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
SecurityEvent resource's enums now use friendly enum names instead of the unfriendly
|
|
||||||
numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the
|
|
||||||
suggestion!
|
|
||||||
</action>
|
|
||||||
</release>
|
|
||||||
<release version="0.5" date="2014-Jul-30">
|
|
||||||
<action type="add">
|
|
||||||
HAPI has a number of RESTful method parameter types that have similar but not identical
|
|
||||||
purposes and confusing names. A cleanup has been undertaken to clean this up.
|
|
||||||
This means that a number of existing classes
|
|
||||||
have been deprocated in favour of new naming schemes.
|
|
||||||
<![CDATA[<br/><br/>]]>
|
|
||||||
All annotation-based clients and all server search method parameters are now named
|
|
||||||
(type)Param, for example: StringParam, TokenParam, etc.
|
|
||||||
<![CDATA[<br/><br/>]]>
|
|
||||||
All generic/fluent client method parameters are now named
|
|
||||||
(type)ClientParam, for example: StringClientParam, TokenClientParam, etc.
|
|
||||||
<![CDATA[<br/><br/>]]>
|
|
||||||
All renamed classes have been retained and deprocated, so this change should not cause any issues
|
|
||||||
for existing applications but those applications should be refactored to use the
|
|
||||||
new parameters when possible.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Allow server methods to return wildcard generic types (e.g. List<? extends IResource>)
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
|
|
||||||
"&identifier=system|codepart1\|codepart2"
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add support for OPTIONS verb (which returns the server conformance statement)
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add support for CORS headers in server
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Bump SLF4j dependency to latest version (1.7.7)
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add interceptor framework for clients (annotation based and generic), and add interceptors
|
|
||||||
for configurable logging, capturing requests and responses, and HTTP basic auth.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead
|
|
||||||
of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="1">
|
|
||||||
If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or
|
|
||||||
an OperationOutcome resource, include the message in the exception message so that it will be
|
|
||||||
more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion!
|
|
||||||
</action>
|
|
||||||
<action type="add" issue="2">
|
|
||||||
Read invocations in the client now process the "Content-Location" header and use it to
|
|
||||||
populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion!
|
|
||||||
</action>
|
|
||||||
<action type="fix" issue="3">
|
|
||||||
Fix issue where vread invocations on server incorrectly get routed to instance history method if one is
|
|
||||||
defined. Thanks to Neal Acharya from UHN for surfacing this one!
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary
|
|
||||||
blobs from being used for nefarious purposes. See
|
|
||||||
<![CDATA[<a href="http://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_id=677&tracker_item_id=3298">FHIR Tracker Bug 3298</a>]]>
|
|
||||||
for more information.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Support has been added for using an HTTP proxy for outgoing requests.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Fix: Primitive extensions declared against custom resource types
|
|
||||||
are encoded even if they have no value. Thanks to David Hay of Orion for
|
|
||||||
reporting this!
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Fix: RESTful server deployed to a location where the URL to access it contained a
|
|
||||||
space (e.g. a WAR file with a space in the name) failed to work correctly.
|
|
||||||
Thanks to David Hay of Orion for reporting this!
|
|
||||||
</action>
|
|
||||||
</release>
|
|
||||||
<release version="0.4" date="2014-Jul-13">
|
|
||||||
<action type="add">
|
|
||||||
<![CDATA[<b>BREAKING CHANGE:</b>]]>: IdDt has been modified so that it
|
|
||||||
contains a partial or complete resource identity. Previously it contained
|
|
||||||
only the simple alphanumeric id of the resource (the part at the end of the "read" URL for
|
|
||||||
that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123")
|
|
||||||
and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have
|
|
||||||
been added to this datatype which provide just the numeric portion. See the JavaDoc
|
|
||||||
for more information.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
<![CDATA[<b>API CHANGE:</b>]]>: Most elements in the HAPI FHIR model contain
|
|
||||||
a getId() and setId() method. This method is confusing because it is only actually used
|
|
||||||
for IDREF elements (which are rare) but its name makes it easy to confuse with more
|
|
||||||
important identifiers. For this reason, these methods have been deprocated and replaced with
|
|
||||||
get/setElementSpecificId() methods. The old methods will be removed at some point. Resource
|
|
||||||
types are unchanged and retain their get/setId methods.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously
|
|
||||||
QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to
|
|
||||||
support quantity search parameters on the server (e.g. Observation.value-quantity)
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Introduce StringParameter type which can be used as a RESTful operation search parameter
|
|
||||||
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Parsers (XML/JSON) now support deleted entries in bundles
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Transaction method now supported in servers
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Support for Binary resources added (in servers, clients, parsers, etc.)
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Support for Query resources fixed (in parser)
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource)
|
|
||||||
now parse and encode correctly, meaning that all contained resources are placed in the "contained" element
|
|
||||||
of the root resource, and the parser looks in the root resource for all container levels when stitching
|
|
||||||
contained resources back together.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server methods with @Include parameter would sometimes fail when no _include was actually
|
|
||||||
specified in query strings.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct
|
|
||||||
query string if the system is null.
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Add support for paging responses from RESTful servers.
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are
|
|
||||||
produced by the Health Intersections server)
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server now automatically compresses responses if the client indicates support
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Server failed to support optional parameters when type is String and :exact qualifier is used
|
|
||||||
</action>
|
|
||||||
<action type="fix">
|
|
||||||
Read method in client correctly populated resource ID in returned object
|
|
||||||
</action>
|
|
||||||
<action type="add">
|
|
||||||
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
|
|
||||||
</action>
|
|
||||||
</release>
|
|
||||||
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode">
|
|
||||||
</release>
|
|
||||||
</body>
|
|
||||||
</document>
|
|
|
@ -103,52 +103,30 @@ patient.getManagingOrganization().setReference("Organization/124362");]]></sourc
|
||||||
bundles. Both resources are added to a bundle, which will then have
|
bundles. Both resources are added to a bundle, which will then have
|
||||||
two entries:
|
two entries:
|
||||||
</p>
|
</p>
|
||||||
<source><![CDATA[// Create an organization
|
<macro name="snippet">
|
||||||
Organization org = new Organization();
|
<param name="id" value="addIncludes" />
|
||||||
org.setId("Organization/65546");
|
<param name="file" value="examples/src/main/java/example/IncludesExamples.java" />
|
||||||
org.getName().setValue("Contained Test Organization");
|
</macro>
|
||||||
|
|
||||||
// Create a patient
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.setId("Patient/1333");
|
|
||||||
patient.addIdentifier("urn:mrns", "253345");
|
|
||||||
patient.getManagingOrganization().setResource(org);
|
|
||||||
|
|
||||||
// Create a list containing both resources. In a server method, you might just
|
|
||||||
// return this list, but here we will create a bundle to encode.
|
|
||||||
List<IResource> resources = new ArrayList<IResource>();
|
|
||||||
resources.add(org);
|
|
||||||
resources.add(patient);
|
|
||||||
|
|
||||||
// Create a bundle with both
|
|
||||||
Bundle b = Bundle.withResources(resources, ourCtx, "http://example.com/base");
|
|
||||||
|
|
||||||
// Encode the buntdle
|
|
||||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
|
|
||||||
System.out.println(encoded);]]></source>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This will give the following output:
|
This will give the following output:
|
||||||
</p>
|
</p>
|
||||||
<source><![CDATA[<feed xmlns="http://www.w3.org/2005/Atom">
|
<source><![CDATA[<Bundle xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="4e151274-2b19-4930-97f2-8427167a176c"/>
|
||||||
|
<type value="searchset"/>
|
||||||
|
<total value="1"/>
|
||||||
|
<link>
|
||||||
|
<relation value="fhir-base"/>
|
||||||
|
<url value="http://example.com/base"/>
|
||||||
|
</link>
|
||||||
|
<link>
|
||||||
|
<relation value="self"/>
|
||||||
|
<url value="http://example.com/base/Patient"/>
|
||||||
|
</link>
|
||||||
<entry>
|
<entry>
|
||||||
<title>Organization Organization/65546</title>
|
<resource>
|
||||||
<id>http://example.com/base/Organization/65546</id>
|
|
||||||
<published>2014-10-14T09:22:54-04:00</published>
|
|
||||||
<link rel="self" href="http://example.com/base/Organization/65546"/>
|
|
||||||
<content type="text/xml">
|
|
||||||
<Organization xmlns="http://hl7.org/fhir">
|
|
||||||
<name value="Contained Test Organization"/>
|
|
||||||
</Organization>
|
|
||||||
</content>
|
|
||||||
</entry>
|
|
||||||
<entry>
|
|
||||||
<title>Patient Patient/1333</title>
|
|
||||||
<id>http://example.com/base/Patient/1333</id>
|
|
||||||
<published>2014-10-14T09:22:54-04:00</published>
|
|
||||||
<link rel="self" href="http://example.com/base/Patient/1333"/>
|
|
||||||
<content type="text/xml">
|
|
||||||
<Patient xmlns="http://hl7.org/fhir">
|
<Patient xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="1333"/>
|
||||||
<identifier>
|
<identifier>
|
||||||
<system value="urn:mrns"/>
|
<system value="urn:mrns"/>
|
||||||
<value value="253345"/>
|
<value value="253345"/>
|
||||||
|
@ -157,9 +135,21 @@ System.out.println(encoded);]]></source>
|
||||||
<reference value="Organization/65546"/>
|
<reference value="Organization/65546"/>
|
||||||
</managingOrganization>
|
</managingOrganization>
|
||||||
</Patient>
|
</Patient>
|
||||||
</content>
|
</resource>
|
||||||
</entry>
|
</entry>
|
||||||
</feed>]]></source>
|
<entry>
|
||||||
|
<resource>
|
||||||
|
<Organization xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="65546"/>
|
||||||
|
<name value="Test Organization"/>
|
||||||
|
</Organization>
|
||||||
|
</resource>
|
||||||
|
<search>
|
||||||
|
<mode value="include"/>
|
||||||
|
</search>
|
||||||
|
</entry>
|
||||||
|
</Bundle>
|
||||||
|
]]></source>
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue