diff --git a/examples/src/main/java/example/IncludesExamples.java b/examples/src/main/java/example/IncludesExamples.java new file mode 100644 index 00000000000..fc1d20a4fee --- /dev/null +++ b/examples/src/main/java/example/IncludesExamples.java @@ -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 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 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 retVal = new ArrayList(); + retVal.add(patient); + return retVal; + } + // END SNIPPET: addIncludes + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 0b5cd320ee0..82c11d7b264 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -20,18 +20,15 @@ package ca.uhn.fhir.context; * #L% */ -import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.*; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; 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.IDatatype; 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.IResourceBlock; import ca.uhn.fhir.model.api.IValueSetEnumBinder; @@ -227,45 +223,34 @@ class ModelScanner { * annotations if the HL7.org ones are found instead. */ private T pullAnnotation(AnnotatedElement theTarget, Class theAnnotationType) { - + T retVal = theTarget.getAnnotation(theAnnotationType); if (true) { return retVal; } // Below disabled for now due to performance issues - + /* - if (retVal == null) { - String sourceClassName = theAnnotationType.getName(); - String candidateAltClassName = sourceClassName.replace("ca.uhn.fhir.model.api.annotation", "org.hl7.fhir.instance.model.annotations"); + * if (retVal == null) { String sourceClassName = theAnnotationType.getName(); String candidateAltClassName = + * sourceClassName.replace("ca.uhn.fhir.model.api.annotation", "org.hl7.fhir.instance.model.annotations"); + * + * if (!sourceClassName.equals(candidateAltClassName)) { try { final Class + * altAnnotationClass = (Class) 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 altAnnotationClass = (Class) 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; } @@ -534,7 +519,13 @@ class ModelScanner { * Child is an extension */ Class et = (Class) nextElementType; - RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et); + + IValueSetEnumBinder> 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); if (IElement.class.isAssignableFrom(nextElementType)) { addScanAlso((Class) nextElementType); @@ -570,7 +561,8 @@ class ModelScanner { RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation); 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 nextDatatype = (Class) nextElementType; addScanAlso(nextDatatype); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java index ea6c24b089f..d1ba0bb72cf 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java @@ -34,6 +34,7 @@ import org.hl7.fhir.instance.model.IBase; import org.hl7.fhir.instance.model.IBaseResource; 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.Description; import ca.uhn.fhir.model.api.annotation.Extension; @@ -47,17 +48,29 @@ public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclared private String myExtensionUrl; private boolean myModifier; private Map 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 theChildType) throws ConfigurationException { + RuntimeChildDeclaredExtensionDefinition(Field theField, Child theChild, Description theDescriptionAnnotation, Extension theExtension, String theElementName, String theExtensionUrl, Class theChildType, IValueSetEnumBinder> theBoundTypeBinder) + throws ConfigurationException { super(theField, theChild, theDescriptionAnnotation, theElementName); assert isNotBlank(theExtensionUrl); myExtensionUrl = theExtensionUrl; myChildType = theChildType; - myDefinedLocally=theExtension.definedLocally(); + myDefinedLocally = theExtension.definedLocally(); myModifier = theExtension.isModifier(); + myInstanceConstructorArguments = theBoundTypeBinder; + } + + @Override + public Object getInstanceConstructorArguments() { + return myInstanceConstructorArguments; } @Override @@ -144,7 +157,7 @@ public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclared List> types = new ArrayList>(); types.add(IResource.class); myChildDef = new RuntimeResourceReferenceDefinition("valueResource", types); - }else { + } else { myChildDef = elementDef; } } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveBoundCodeDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveBoundCodeDatatypeDefinition.java index 3d686f01fc9..41c00fe88b9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveBoundCodeDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveBoundCodeDatatypeDefinition.java @@ -24,7 +24,6 @@ import java.lang.reflect.Field; 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.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index d8141f62bbe..2fca573c619 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -1395,7 +1395,7 @@ class ParserState { switch (target.getChildType()) { case COMPOSITE_DATATYPE: { BaseRuntimeElementCompositeDefinition compositeTarget = (BaseRuntimeElementCompositeDefinition) target; - ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(); + ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(myDefinition.getInstanceConstructorArguments()); myDefinition.getMutator().addValue(myParentInstance, newChildInstance); ElementCompositeState newState = new ElementCompositeState(myPreResourceState, compositeTarget, newChildInstance); push(newState); @@ -1403,7 +1403,7 @@ class ParserState { } case PRIMITIVE_DATATYPE: { RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target; - IPrimitiveType newChildInstance = primitiveTarget.newInstance(); + IPrimitiveType newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments()); myDefinition.getMutator().addValue(myParentInstance, newChildInstance); PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance); push(newState); @@ -1458,12 +1458,12 @@ class ParserState { return newState; } - private class ElementCompositeState extends BaseState { + private class ElementCompositeState extends BaseState { 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); myDefinition = theDef; myInstance = theInstance; @@ -1516,7 +1516,7 @@ class ParserState { BaseRuntimeElementCompositeDefinition compositeTarget = (BaseRuntimeElementCompositeDefinition) target; ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(child.getInstanceConstructorArguments()); child.getMutator().addValue(myInstance, newChildInstance); - ParserState.ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); + ParserState.ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); push(newState); return; } @@ -1540,7 +1540,7 @@ class ParserState { RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target; IBase newBlockInstance = blockTarget.newInstance(); child.getMutator().addValue(myInstance, newBlockInstance); - ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), blockTarget, newBlockInstance); + ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), blockTarget, newBlockInstance); push(newState); return; } @@ -1592,7 +1592,7 @@ class ParserState { } @Override - protected T2 getCurrentElement() { + protected IBase getCurrentElement() { return myInstance; } @@ -1627,7 +1627,7 @@ class ParserState { BaseRuntimeElementCompositeDefinition compositeTarget = (BaseRuntimeElementCompositeDefinition) target; ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(); myExtension.setValue(newChildInstance); - ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); + ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); push(newState); return; } @@ -1664,7 +1664,7 @@ class ParserState { } - private class SecurityLabelElementStateHapi extends ElementCompositeState { + private class SecurityLabelElementStateHapi extends ElementCompositeState { public SecurityLabelElementStateHapi(ParserState.PreResourceState thePreResourceState,BaseRuntimeElementCompositeDefinition theDef, BaseCodingDt codingDt) { super(thePreResourceState, theDef, codingDt); @@ -2224,18 +2224,21 @@ class ParserState { DISPLAY, INITIAL, REFERENCE } - private class ResourceStateHapi extends ElementCompositeState { + private class ResourceStateHapi extends ElementCompositeState { + + private IResource myInstance; public ResourceStateHapi(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition theDef, IResource theInstance) { super(thePreResourceState, theDef, theInstance); + myInstance = theInstance; } @Override public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException { if ("id".equals(theChildName)) { - push(new PrimitiveState(getPreResourceState(), getCurrentElement().getId())); + push(new PrimitiveState(getPreResourceState(), myInstance.getId())); } else if ("meta".equals(theChildName)) { - push(new MetaElementState(getPreResourceState(), getCurrentElement().getResourceMetadata())); + push(new MetaElementState(getPreResourceState(), myInstance.getResourceMetadata())); } else { super.enteringNewElement(theNamespace, theChildName); } @@ -2243,7 +2246,7 @@ class ParserState { } - private class ResourceStateHl7Org extends ElementCompositeState { + private class ResourceStateHl7Org extends ElementCompositeState { public ResourceStateHl7Org(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition theDef, IBaseResource theInstance) { super(thePreResourceState, theDef, theInstance); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 19aeea19dff..f83102c3724 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -77,6 +77,7 @@ public class RestfulServer extends HttpServlet { private static final long serialVersionUID = 1L; private AddProfileTagEnum myAddProfileTag; + private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES; private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML; private ETagSupportEnum myETagSupport = DEFAULT_ETAG_SUPPORT; private FhirContext myFhirContext; @@ -87,18 +88,19 @@ public class RestfulServer extends HttpServlet { private Map myResourceNameToProvider = new HashMap(); private Collection myResourceProviders; private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy(); + private ResourceBinding myServerBinding = new ResourceBinding(); private BaseMethodBinding myServerConformanceMethod; 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 */ private String myServerVersion = VersionUtil.getVersion(); - private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES; private boolean myStarted; - private boolean myUseBrowserFriendlyContentTypes; + private boolean myUseBrowserFriendlyContentTypes; - /** + /** * Constructor */ public RestfulServer() { @@ -257,8 +259,6 @@ public class RestfulServer extends HttpServlet { } - private ResourceBinding myServerBinding = new ResourceBinding(); - private void findSystemMethods(Object theSystemProvider, Class clazz) { Class supertype = clazz.getSuperclass(); if (!Object.class.equals(supertype)) { @@ -293,6 +293,10 @@ public class RestfulServer extends HttpServlet { 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 * with the _format URL parameter, or with an Accept header in the request. The default is @@ -900,6 +904,15 @@ public class RestfulServer extends HttpServlet { 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 * the _format URL parameter, or with an Accept header in the request. The default is @@ -1073,28 +1086,15 @@ public class RestfulServer extends HttpServlet { 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) == '$'); } - public enum NarrativeModeEnum { + public enum NarrativeModeEnum { NORMAL, ONLY, SUPPRESS; public static NarrativeModeEnum valueOfCaseInsensitive(String theCode) { 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; - } } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/BaseParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/BaseParserTest.java index f17fefeb1fb..8682c7e40f3 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/BaseParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/BaseParserTest.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.parser; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import org.junit.Test; @@ -138,4 +138,22 @@ public class BaseParserTest { } + /** + * See #120 + */ + @Test + public void testParseExtensionWithBoundCodeType() { + //@formatter:off + String resText = + "" + + " " + + " " + + " " + + ""; + //@formatter:on + + MyDiagnosticReportWithBoundCodeExtension res = ourCtx.newXmlParser().parseResource(MyDiagnosticReportWithBoundCodeExtension.class, resText); + assertEquals(WorkflowActionEnum.SIGNOFF, res.getWorkflowAction().getValueAsEnum()); + } + } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java new file mode 100644 index 00000000000..7f9a526b8a4 --- /dev/null +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java @@ -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 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 getWorkflowAction() { + if (workflowAction == null) { + workflowAction = new BoundCodeDt(WorkflowActionEnum.VALUESET_BINDER); + } + + return workflowAction; + } + + public MyDiagnosticReportWithBoundCodeExtension setWorkflowAction(BoundCodeDt 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; + } + +} \ No newline at end of file diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/WorkflowActionEnum.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/WorkflowActionEnum.java new file mode 100644 index 00000000000..ec6ad3bcb28 --- /dev/null +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/WorkflowActionEnum.java @@ -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: sign-off + * + * Sign off workflow action. + */ +SIGNOFF("sign-off","#workflow-action"), + +/** + * Code Value: sign-off-later + * + * Sign off later workflow action. + */ +SIGNOFFLATER("sign-off-later","#workflow-action"), + +/** + * Code Value: correctionist + * + * 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 CODE_TO_ENUM = new HashMap(); +private static Map> SYSTEM_TO_CODE_TO_ENUM = new HashMap>(); + +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()); + } + 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 VALUESET_BINDER = new IValueSetEnumBinder() { + @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 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; +} + +} \ No newline at end of file diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 46cddfd6b4d..14f4acc68e2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -8,8 +8,8 @@ - Support for DSTU2 features introduced: New Bundle encoding style, as well as new - extension encoding in JSON. + Support for DSTU2 features introduced: New resource definitions, Bundle resource, + encoding changes (ID in resource bodt, meta tag) Fix an issue encoding extensions on primitive types in JSON. Previously the "_value" object @@ -143,6 +143,16 @@ DateClientParam.before() incorrectly placed "<=" instead of "<" in the request URL. Thanks to Ryan for reporting! + + Server now only automatically adds _include resources which are provided + as references if the client request actually requested that specific include. + See RestfulServer + + + 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! + diff --git a/src/changes/changes.xml.orig b/src/changes/changes.xml.orig deleted file mode 100644 index d6c5eefd9e4..00000000000 --- a/src/changes/changes.xml.orig +++ /dev/null @@ -1,656 +0,0 @@ - - - - James Agnew - HAPI FHIR Changelog - - - - - Support for DSTU2 features introduced: New Bundle encoding style, as well as new - extension encoding in JSON. - - - 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 - -<<<<<<< HEAD - - Bump a few dependency JARs to the latest versions in Maven POM: - -
  • SLF4j (in base module) - Bumped to 1.7.9
  • -
  • Apache HTTPClient (in base module) - Bumped to 4.3.6
  • -
  • Hibernate (in JPA module) - Bumped to 4.3.7
  • - - ]]> -======= - - 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 - -
    - - - API CHANGE:]]> 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 - upgrading]]> - for more information. - - - Deprocated API Removal: The following classes (which were deprocated previously) - have now been removed: -
      -
    • ISecurityManager: If you are using this class, the same functionality - is available through the more general purpose - server interceptor - capabilities. -
    • CodingListParam: This class was made redundant by the - TokenOrListParam - class, which can be used in its place. -
    - ]]> -
    - - API Change: The IResource#getResourceMetadata() method has been changed - from returning - Map<ResourceMetadataKeyEnum<?>, Object> - to returning a new type called - ResourceMetadataMap. This new type implements - Map<ResourceMetadataKeyEnum<?>, Object> - itself, so this change should not break existing code, but may - require a clean build in order to run correctly. - ]]> - - - Profile generation on the server was not working due to IdDt being - incorrectly used. Thanks to Bill de Beaubien for the pull request! - - - 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! - - - Remove unnecessary IOException from narrative generator API. Thanks to - Petro Mykhailysyn for the pull request! - - - Introduced a new - @ProvidesResources]]> 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! -
    - - Introduced a new - @Destroy]]> 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! - - - Add a new method 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. - - - Add constants to BaseResource for the "_id" search parameter which all resources - should support. - - - DateRangeParam parameters on the server now return correct - getLowerBoundAsInstant()]]> - and - getUpperBoundAsInstant()]]> - values if a single unqualified value is passed in. For example, if - a query containing - &birthdate=2012-10-01]]> - is received, previously these two methods would both return the same - value, but with this fix - getUpperBoundAsInstant()]]> - now returns the instant at 23:59:59.9999. - - - 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! - - - 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. - - - 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 - 1.0000]]> 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! - - - When using Generic Client, if performing a - or operation using a String as the resource body, - the client will auto-detect the FHIR encoding style and send an appropriate - header. - - - 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! - - - Generic/Fluent Client "create" and "update" method requests were not setting a content type header - - - DateDt left precision value as in the constructor - . - - - 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! - - - JPA module Transaction operation was not correctly replacing logical IDs - beginning with "cid:" with server assigned IDs, as required by the - specification. - - - 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. - - - Add a method which returns the name of the - resource in question (e.g. "Patient", or "Observation"). This is intended as a - convenience to users. - - - Do not strip version from resource references in resources returned - from server search methods. Thanks to Bill de Beaubien for reporting! - - - Correct an issue with the validator where changes to the underlying - OperationOutcome produced by a validation cycle cause the validation - results to be incorrect. - - - 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) - - - 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.
    ]]> - 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! -
    - - Encoding a Binary resource without a content type set should not result in a NullPointerException. Thanks - to Alexander Kley for reporting! - - - Server gives a more helpful error message if multiple IResourceProvider implementations - are provided for the same resource type. Thanks to wanghaisheng for the idea! - - - Bring DSTU1 resource definitions up to version 0.0.82-2929]]> - Bring DEV resource definitions up to 0.4.0-3775]]> - Thanks to crinacimpian for reporting! - - - 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! - - - 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! - - - Clean up the way that Profile resources are automatically exported - by the server for custom resource profile classes. See the - @ResourceDef]]> - JavaDoc for information on how this works. - -
    - - - API CHANGE:]]> 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! - - - 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! - - - Documentation fixes - - - Add a collection of new methods on the generic client which support the - read, - read, - and search - ]]> - 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! - - - 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! - - - 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! - - - HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to - Bernard Gitaadji for reporting and diagnosing the issue! - - - 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! - - - 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! - - - XHTML (in narratives) containing escapable characters (e.g. < or ") will now always have those characters - escaped properly in encoded messages. - - - 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. - - - Add a new client interceptor which adds HTTP Authorization Bearer Tokens (for use with OAUTH2 servers) - to client requests. - - - Add phloc-commons dependency explicitly, which resolves an issue building HAPI from source on - some platforms. Thanks to Odysseas Pentakalos for the patch! - - - HAPI now logs a single line indicating the StAX implementation being used upon the - first time an XML parser is created. - - - 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! - - - Parser failed to correctly read contained Binary resources. Thanks to Alexander Kley for - the patch! - - - 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! - - - 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. - - - 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! - - - toString() method on TokenParam was incorrectly showing the system as the value. - Thanks to Bill de Beaubien for reporting! - - - Documentation on contained resources contained a typo and did not actually produce contained resources. Thanks - to David Hay of Orion Health for reporting! - - - Add a - Vagrant]]> - 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! - - - 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! - - - Add a client interceptor which adds an HTTP cookie to each client request. Thanks to - Petro Mykhailysyn for the pull request! - - - - - - Add server interceptor framework, and new interceptor for logging incoming - requests. - - - Add server validation framework for validating resources against the FHIR schemas and schematrons - - - Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University - Health Network for reporting! - - - 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! - - - 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 - - - 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! - - - Contained/included resource instances received by a client are now automatically - added to any ResourceReferenceDt instancea in other resources which reference them. - - - 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! - - - IResource interface did not expose the getLanguage/setLanguage methods from BaseResource, - so the resource language was difficult to access. - - - JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use - of single quotes - - - 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. - - - 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. - - - 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! - - - Added narrative generator template for OperationOutcome resource - - - 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! - - - 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! - - - Category header (for tags) is correctly read in client for "read" operation - - - Transaction method in server can now have parameter type Bundle instead of - List<IResource> - - - HAPI parsers now use field access to get/set values instead of method accessors and mutators. - This should give a small performance boost. - - - 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! - - - Rename NotImpementedException to NotImplementedException (to correct typo) - - - Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status) - - - Fix performance issue in date/time datatypes where pattern matchers were not static - - - 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! - - - Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health - for reporting! - - - QuantityParam correctly encodes approximate (~) prefix to values - - - 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! - - - 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! - - - 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) - - - Resource references previously encoded their children (display and reference) - in the wrong order so references with both would fail schema validation. - - - 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! - - - - - 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. -
    ]]> - All annotation-based clients and all server search method parameters are now named - (type)Param, for example: StringParam, TokenParam, etc. -
    ]]> - All generic/fluent client method parameters are now named - (type)ClientParam, for example: StringClientParam, TokenClientParam, etc. -
    ]]> - 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. -
    - - Allow server methods to return wildcard generic types (e.g. List<? extends IResource>) - - - Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as - "&identifier=system|codepart1\|codepart2" - - - Add support for OPTIONS verb (which returns the server conformance statement) - - - Add support for CORS headers in server - - - Bump SLF4j dependency to latest version (1.7.7) - - - Add interceptor framework for clients (annotation based and generic), and add interceptors - for configurable logging, capturing requests and responses, and HTTP basic auth. - - - 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! - - - Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion! - - - 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! - - - 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! - - - 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! - - - Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary - blobs from being used for nefarious purposes. See - FHIR Tracker Bug 3298]]> - for more information. - - - Support has been added for using an HTTP proxy for outgoing requests. - - - 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! - - - 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! - -
    - - - BREAKING CHANGE:]]>: 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. - - - API CHANGE:]]>: 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. - - - 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) - - - 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. - - - Parsers (XML/JSON) now support deleted entries in bundles - - - Transaction method now supported in servers - - - Support for Binary resources added (in servers, clients, parsers, etc.) - - - Support for Query resources fixed (in parser) - - - 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. - - - Server methods with @Include parameter would sometimes fail when no _include was actually - specified in query strings. - - - Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct - query string if the system is null. - - - Add support for paging responses from RESTful servers. - - - 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) - - - Server now automatically compresses responses if the client indicates support - - - Server failed to support optional parameters when type is String and :exact qualifier is used - - - Read method in client correctly populated resource ID in returned object - - - Support added for deleted-entry by/name, by/email, and comment from Tombstones spec - - - - - - diff --git a/src/site/xdoc/doc_resource_references.xml b/src/site/xdoc/doc_resource_references.xml index 89d30dbfa54..05c42eb81c3 100644 --- a/src/site/xdoc/doc_resource_references.xml +++ b/src/site/xdoc/doc_resource_references.xml @@ -103,52 +103,30 @@ patient.getManagingOrganization().setReference("Organization/124362");]]> - resources = new ArrayList(); -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);]]> - + + + + +

    This will give the following output:

    - + + + + + + + + + + + + - Organization Organization/65546 - http://example.com/base/Organization/65546 - 2014-10-14T09:22:54-04:00 - - - - - - - - - Patient Patient/1333 - http://example.com/base/Patient/1333 - 2014-10-14T09:22:54-04:00 - - + + @@ -157,9 +135,21 @@ System.out.println(encoded);]]> - + -]]> + + + + + + + + + + + + +]]>