diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 2159f48d9ac..a652451e07b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -158,7 +158,7 @@ public abstract class BaseParser implements IParser { } - public void containResourcesForEncoding(IBaseResource theResource) { + protected void containResourcesForEncoding(IBaseResource theResource) { ContainedResources contained = new ContainedResources(); containResourcesForEncoding(contained, theResource, theResource); myContainedResources = contained; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 6a66c5c3e32..b30990034c7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -457,7 +457,7 @@ public class JsonParser extends BaseParser implements IParser { case RESOURCE: IBaseResource resource = (IBaseResource) theNextValue; RuntimeResourceDefinition def = myContext.getResourceDefinition(resource); - encodeResourceToJsonStreamWriter(def, resource, theWriter, theChildName, true); + encodeResourceToJsonStreamWriter(def, resource, theWriter, theChildName, false); break; case UNDECL_EXT: default: @@ -632,12 +632,12 @@ public class JsonParser extends BaseParser implements IParser { } private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, - boolean theIsSubElementWithinResource) throws IOException { + boolean theContainedResource) throws IOException { String resourceId = null; if (theResource instanceof IResource) { IResource res = (IResource) theResource; if (StringUtils.isNotBlank(res.getId().getIdPart())) { - if (theIsSubElementWithinResource) { + if (theContainedResource) { resourceId = res.getId().getIdPart(); } else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { resourceId = res.getId().getIdPart(); @@ -645,17 +645,17 @@ public class JsonParser extends BaseParser implements IParser { } } else if (theResource instanceof IAnyResource) { IAnyResource res = (IAnyResource) theResource; - if (theIsSubElementWithinResource && StringUtils.isNotBlank(res.getId())) { + if (theContainedResource && StringUtils.isNotBlank(res.getId())) { resourceId = res.getId(); } } - encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId); + encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theContainedResource, resourceId); } private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, - boolean theIsSubElementWithinResource, String theResourceId) throws IOException { - if (!theIsSubElementWithinResource) { + boolean theContainedResource, String theResourceId) throws IOException { + if (!theContainedResource) { super.containResourcesForEncoding(theResource); } @@ -704,7 +704,7 @@ public class JsonParser extends BaseParser implements IParser { for (BaseCodingDt securityLabel : securityLabels) { theEventWriter.writeStartObject(); BaseRuntimeElementCompositeDefinition def = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(securityLabel.getClass()); - encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, def.getChildren(), theIsSubElementWithinResource); + encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource); theEventWriter.writeEnd(); } theEventWriter.writeEnd(); @@ -731,7 +731,7 @@ public class JsonParser extends BaseParser implements IParser { theEventWriter.write("contentType", bin.getContentType()); theEventWriter.write("content", bin.getContentAsBase64()); } else { - encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theIsSubElementWithinResource); + encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theContainedResource); } theEventWriter.writeEnd(); 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 f6a7da0becd..edce214a543 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 @@ -30,8 +30,6 @@ import java.util.Map; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import ca.uhn.fhir.model.base.composite.BaseCodingDt; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.IBase; @@ -39,8 +37,6 @@ import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.ICompositeType; import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.api.IBaseBinary; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseElement; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; @@ -65,7 +61,6 @@ import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ICompositeDatatype; import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.api.IExtension; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IIdentifiableElement; import ca.uhn.fhir.model.api.IPrimitiveDatatype; @@ -74,6 +69,7 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.TagList; +import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.resource.ResourceMetadataMap; import ca.uhn.fhir.model.primitive.IdDt; @@ -81,6 +77,7 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.IModelVisitor; class ParserState { @@ -1465,6 +1462,10 @@ class ParserState { private class ElementCompositeState extends BaseState { private BaseRuntimeElementCompositeDefinition myDefinition; + public BaseRuntimeElementCompositeDefinition getDefinition() { + return myDefinition; + } + private IBase myInstance; public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition theDef, IBase theInstance) { @@ -1570,8 +1571,13 @@ class ParserState { return; } case RESOURCE: { + if (myInstance instanceof IResource) { + ParserState.PreResourceStateHapi state = new PreResourceStateHapi(myInstance, child.getMutator(), null); + push(state); + } else { ParserState.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null); push(state); + } return; } case UNDECL_EXT: @@ -1816,12 +1822,20 @@ class ParserState { private class PreResourceStateHapi extends PreResourceState { private BundleEntry myEntry; + private Object myTarget; + private IMutator myMutator; public PreResourceStateHapi(BundleEntry theEntry, Class theResourceType) { super(theResourceType); myEntry = theEntry; } + public PreResourceStateHapi(Object theTarget, IMutator theMutator, Class theResourceType) { + super(theResourceType); + myTarget = theTarget; + myMutator = theMutator; + } + public PreResourceStateHapi(Class theResourceType) { super(theResourceType); } @@ -1833,7 +1847,7 @@ class ParserState { if (myEntry == null) { myObject = (T) getCurrentElement(); } - + IResource nextResource = (IResource) getCurrentElement(); String version = ResourceMetadataKeyEnum.VERSION.get(nextResource); String resourceName = myContext.getResourceDefinition(nextResource).getName(); @@ -1846,15 +1860,20 @@ class ParserState { // } } - + } + + @Override public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { super.enteringNewElement(theNamespaceURI, theLocalPart); if (myEntry != null) { myEntry.setResource((IResource) getCurrentElement()); } + if (myMutator != null) { + myMutator.addValue(myTarget, getCurrentElement()); + } } } @@ -1976,7 +1995,10 @@ class ParserState { @Override public void wereBack() { - myContext.newTerser().visit(myInstance, new IModelVisitor() { + final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName()); + + FhirTerser terser = myContext.newTerser(); + terser.visit(myInstance, new IModelVisitor() { @Override public void acceptElement(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { @@ -2002,6 +2024,35 @@ class ParserState { } }); + if (bundle) { + /* + * Stitch together resource references + */ + Map idToResource = new HashMap(); + FhirTerser t = myContext.newTerser(); + List resources = t.getAllPopulatedChildElementsOfType(myInstance, IResource.class); + for (IResource next : resources) { + IdDt id = next.getId(); + if (id != null && id.isEmpty() == false) { + String resName = myContext.getResourceDefinition(next).getName(); + idToResource.put(id.withResourceType(resName).toUnqualifiedVersionless(), next); + } + } + + for (IResource next : resources) { + List refs = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class); + for (BaseResourceReferenceDt nextRef : refs) { + if (nextRef.isEmpty() == false && nextRef.getReference() != null) { + IResource target = idToResource.get(nextRef.getReference().toUnqualifiedVersionless()); + if (target != null) { + nextRef.setResource(target); + } + } + } + } + + } + } } @@ -2264,7 +2315,6 @@ class ParserState { super.enteringNewElement(theNamespace, theChildName); } } - } private class ResourceStateHl7Org extends ElementCompositeState { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java index 101b5de87bb..ba5a207a529 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index 9de59a3d9ef..823266bc205 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -220,7 +220,7 @@ public class FhirTerser { } - private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, IModelVisitor theCallback) { + private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, IModelVisitor theCallback) { theCallback.acceptElement(theElement, theChildDefinition, theDefinition); addUndeclaredExtensions(theElement, theDefinition, theChildDefinition, theCallback); @@ -248,12 +248,7 @@ public class FhirTerser { break; case RESOURCE: case RESOURCE_BLOCK: - case COMPOSITE_DATATYPE: { - if (theChildDefinition instanceof RuntimeChildDirectResource) { - // Don't descend into embedded resources - return; - } - + case COMPOSITE_DATATYPE: { BaseRuntimeElementCompositeDefinition childDef = (BaseRuntimeElementCompositeDefinition) theDefinition; for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) { List values = nextChild.getAccessor().getValues(theElement); @@ -286,7 +281,13 @@ public class FhirTerser { } throw new DataFormatException(b.toString()); } - visit(nextValue, nextChild, childElementDef, theCallback); + + if (nextChild instanceof RuntimeChildDirectResource) { + // Don't descend into embedded resources + theCallback.acceptElement(nextValue, nextChild, childElementDef); + } else { + visit(nextValue, nextChild, childElementDef, theCallback); + } } } } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java index 575b6f99234..6ae30ade1d6 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.rest.server; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; -import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.util.ArrayList; @@ -18,6 +18,7 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.hamcrest.Matchers; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -63,8 +64,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(5, bundle.getEntries().size()); - assertEquals("0", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("4", bundle.getEntries().get(4).getId().getIdPart()); + assertEquals("0", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("4", bundle.getEntries().get(4).getResource().getId().getIdPart()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=5&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkNext() .getValue()); assertNull(bundle.getLinkPrevious().getValue()); @@ -79,8 +80,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newJsonParser().parseBundle(responseContent); assertEquals(5, bundle.getEntries().size()); - assertEquals("5", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("9", bundle.getEntries().get(4).getId().getIdPart()); + assertEquals("5", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("9", bundle.getEntries().get(4).getResource().getId().getIdPart()); assertNull(bundle.getLinkNext().getValue()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=5&_format=json&_pretty=true", bundle.getLinkPrevious() .getValue()); @@ -104,8 +105,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("8", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("9", bundle.getEntries().get(1).getId().getIdPart()); + assertEquals("8", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("9", bundle.getEntries().get(1).getResource().getId().getIdPart()); assertNull(bundle.getLinkNext().getValue()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=3&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkPrevious() .getValue()); @@ -131,8 +132,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("0", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("1", bundle.getEntries().get(1).getId().getIdPart()); + assertEquals("0", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("1", bundle.getEntries().get(1).getResource().getId().getIdPart()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkNext().getValue()); assertNull(bundle.getLinkPrevious().getValue()); link = bundle.getLinkNext().getValue(); @@ -146,8 +147,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("2", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("3", bundle.getEntries().get(1).getId().getIdPart()); + assertEquals("2", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("3", bundle.getEntries().get(1).getResource().getId().getIdPart()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkNext().getValue()); assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkSelf().getValue()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkPrevious().getValue()); @@ -175,9 +176,11 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("0", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("1", bundle.getEntries().get(1).getId().getIdPart()); - assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkNext().getValue()); + assertEquals("0", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("1", bundle.getEntries().get(1).getResource().getId().getIdPart()); + assertThat(bundle.getLinkNext().getValue(), Matchers.startsWith(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml")); + assertThat(bundle.getLinkNext().getValue(), containsString("&_include=foo")); + assertThat(bundle.getLinkNext().getValue(), containsString("&_include=Patient.managingOrganization")); assertNull(bundle.getLinkPrevious().getValue()); link = bundle.getLinkNext().getValue(); } @@ -190,11 +193,19 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("2", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("3", bundle.getEntries().get(1).getId().getIdPart()); - assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkNext().getValue()); - assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkSelf().getValue()); - assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkPrevious().getValue()); + assertEquals("2", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("3", bundle.getEntries().get(1).getResource().getId().getIdPart()); + assertThat(bundle.getLinkNext().getValue(), Matchers.startsWith(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2&_format=xml")); + assertThat(bundle.getLinkNext().getValue(), containsString("&_include=foo")); + assertThat(bundle.getLinkNext().getValue(), containsString("&_include=Patient.managingOrganization")); + + assertThat(bundle.getLinkSelf().getValue(), Matchers.startsWith(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml")); + assertThat(bundle.getLinkSelf().getValue(), containsString("&_include=foo")); + assertThat(bundle.getLinkSelf().getValue(), containsString("&_include=Patient.managingOrganization")); + + assertThat(bundle.getLinkPrevious().getValue(), Matchers.startsWith(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2&_format=xml")); + assertThat(bundle.getLinkPrevious().getValue(), containsString("&_include=foo")); + assertThat(bundle.getLinkPrevious().getValue(), containsString("&_include=Patient.managingOrganization")); } } @@ -216,8 +227,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("0", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("1", bundle.getEntries().get(1).getId().getIdPart()); + assertEquals("0", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("1", bundle.getEntries().get(1).getResource().getId().getIdPart()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2", bundle.getLinkNext().getValue()); assertNull(bundle.getLinkPrevious().getValue()); link = bundle.getLinkNext().getValue(); @@ -231,8 +242,8 @@ public class PagingTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent); assertEquals(2, bundle.getEntries().size()); - assertEquals("2", bundle.getEntries().get(0).getId().getIdPart()); - assertEquals("3", bundle.getEntries().get(1).getId().getIdPart()); + assertEquals("2", bundle.getEntries().get(0).getResource().getId().getIdPart()); + assertEquals("3", bundle.getEntries().get(1).getResource().getId().getIdPart()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2", bundle.getLinkNext().getValue()); assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2", bundle.getLinkSelf().getValue()); assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2", bundle.getLinkPrevious().getValue()); diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java index acd0449fbaa..e74d73967c5 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java @@ -98,7 +98,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider capt = ArgumentCaptor.forClass(HttpUriRequest.class); + + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + if (myFirstResponse) { + myFirstResponse=false; + return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8")); + } else { + return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8")); + } + }}); + + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + + myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE); + IGenericClient client = myCtx.newRestfulGenericClient("http://foo"); + + // don't load the conformance until the first time the client is actually used + assertTrue(myFirstResponse); + client.read(new UriDt("http://foo/Patient/123")); + assertFalse(myFirstResponse); + myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123")); + myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123")); + + // Conformance only loaded once, then 3 reads + verify(myHttpClient, times(4)).execute(Matchers.any(HttpUriRequest.class)); + } + @Test public void testServerReturnsWrongVersionForDstu2() throws Exception { Conformance conf = new Conformance(); diff --git a/hapi-fhir-structures-dstu2/src/test/resources/bundle-example2.xml b/hapi-fhir-structures-dstu2/src/test/resources/bundle-example2.xml new file mode 100644 index 00000000000..a71a0bc4e07 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/resources/bundle-example2.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + +
Thyroxine 0.112 MG Oral Tablet [Levoxyl] (rxnorm: 206485)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
\ No newline at end of file diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1e4a5e78152..b9a13a44a78 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -102,6 +102,18 @@ server behind an Apache proxy (e.g. for load balancing or other reasons). Thanks to Bill de Beaubien for contributing! + + Resource references between separate resources found in a single + bundle did not get populated with the actual resource when parsing a + DSTU2 style bundle. Thanks to Nick Peterson for reporting and figuring + out why none of our unit tests were actually catching the problem! + + + JSON encoder did not encode contained resources when encoding + a DSTU2 style bundle. Thanks to Mohammad Jafari and baopingle + for all of their help in tracking this issue down and developing + useful unit tests to demonstrate it. +