Fix #103 - Don't encode contained resources unless they are actualy

referenced somewhere in the resource body
This commit is contained in:
James Agnew 2015-02-05 12:07:37 -05:00
parent 64944d6d81
commit 91f8c3f26f
14 changed files with 596 additions and 149 deletions

BIN
.swp Normal file

Binary file not shown.

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.parser;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.IOException;
import java.io.Reader;
@ -28,9 +28,11 @@ import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
@ -87,12 +89,18 @@ public abstract class BaseParser implements IParser {
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) {
Set<String> allIds = new HashSet<String>();
Map<String, IBaseResource> existingIdToContainedResource = null;
if (theTarget instanceof IResource) {
List<? extends IResource> containedResources = ((IResource) theTarget).getContained().getContainedResources();
for (IResource next : containedResources) {
String nextId = next.getId().getValue();
if (StringUtils.isNotBlank(nextId)) {
allIds.add(nextId);
if (existingIdToContainedResource == null) {
existingIdToContainedResource = new HashMap<String, IBaseResource>();
}
existingIdToContainedResource.put(nextId, next);
}
}
} else if (theTarget instanceof DomainResource) {
@ -101,6 +109,10 @@ public abstract class BaseParser implements IParser {
String nextId = next.getId();
if (StringUtils.isNotBlank(nextId)) {
allIds.add(nextId);
if (existingIdToContainedResource == null) {
existingIdToContainedResource = new HashMap<String, IBaseResource>();
}
existingIdToContainedResource.put(nextId, next);
}
}
} else {
@ -119,6 +131,14 @@ public abstract class BaseParser implements IParser {
}
containResourcesForEncoding(theContained, resource, theTarget);
} else if (next.getReference().isLocal()) {
if (existingIdToContainedResource != null) {
IBaseResource potentialTarget = existingIdToContainedResource.remove(next.getReference().getValue());
if (potentialTarget != null) {
theContained.addContained(potentialTarget);
containResourcesForEncoding(theContained, potentialTarget, theTarget);
}
}
}
}
}
@ -135,6 +155,14 @@ public abstract class BaseParser implements IParser {
}
containResourcesForEncoding(theContained, resource, theTarget);
} else if (next.getReference() != null && next.getReference().startsWith("#")) {
if (existingIdToContainedResource != null) {
IBaseResource potentialTarget = existingIdToContainedResource.remove(next.getReference());
if (potentialTarget != null) {
theContained.addContained(potentialTarget);
containResourcesForEncoding(theContained, potentialTarget, theTarget);
}
}
}
}
}

View File

@ -394,19 +394,22 @@ public class JsonParser extends BaseParser implements IParser {
break;
}
case CONTAINED_RESOURCES: {
theWriter.writeStartArray(theChildName);
ContainedDt value = (ContainedDt) theNextValue;
for (IResource next : value.getContainedResources()) {
if (getContainedResources().getResourceId(next) != null) {
continue;
/*
* Disabled per #103
* ContainedDt value = (ContainedDt) theNextValue; for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) {
* continue; } encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue())); }
*/
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
if (containedResources.size() > 0) {
theWriter.writeStartArray(theChildName);
for (IBaseResource next : containedResources) {
IdDt resourceId = getContainedResources().getResourceId(next);
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(resourceId.getValue()));
}
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue()));
theWriter.writeEnd();
}
for (IBaseResource next : getContainedResources().getContainedResources()) {
IdDt resourceId = getContainedResources().getResourceId(next);
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(resourceId.getValue()));
}
theWriter.writeEnd();
break;
}
case PRIMITIVE_XHTML: {

View File

@ -585,6 +585,8 @@ public class XmlParser extends BaseParser implements IParser {
}
case CONTAINED_RESOURCES: {
ContainedDt value = (ContainedDt) nextValue;
/*
* Disable per #103
for (IResource next : value.getContainedResources()) {
if (getContainedResources().getResourceId(next) != null) {
continue;
@ -593,6 +595,7 @@ public class XmlParser extends BaseParser implements IParser {
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
theEventWriter.writeEndElement();
}
*/
for (IBaseResource next : getContainedResources().getContainedResources()) {
IdDt resourceId = getContainedResources().getResourceId(next);
theEventWriter.writeStartElement("contained");
@ -755,13 +758,13 @@ public class XmlParser extends BaseParser implements IParser {
if (theResource instanceof IResource) {
// HAPI structs
IResource iResource = (IResource) theResource;
if (theIncludedResource && StringUtils.isNotBlank(iResource.getId().getValue())) {
resourceId = iResource.getId().getValue();
if (StringUtils.isNotBlank(iResource.getId().getValue())) {
resourceId = iResource.getId().getIdPart();
}
} else {
// HL7 structs
Resource resource = (Resource) theResource;
if (theIncludedResource && StringUtils.isNotBlank(resource.getId())) {
if (StringUtils.isNotBlank(resource.getId())) {
resourceId = resource.getId();
}
}
@ -769,8 +772,8 @@ public class XmlParser extends BaseParser implements IParser {
encodeResourceToXmlStreamWriter(theResource, theEventWriter, theIncludedResource, resourceId);
}
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource, String theResourceId) throws XMLStreamException {
if (!theIncludedResource) {
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, String theResourceId) throws XMLStreamException {
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
}
@ -786,7 +789,7 @@ public class XmlParser extends BaseParser implements IParser {
// DSTU2+
IResource resource = (IResource) theResource;
writeOptionalTagWithValue(theEventWriter, "id", resource.getId().getIdPart());
writeOptionalTagWithValue(theEventWriter, "id", theResourceId);
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
IdDt resourceId = resource.getId();
@ -802,9 +805,10 @@ public class XmlParser extends BaseParser implements IParser {
} else {
// DSTU1
if (theResourceId != null) {
if (theResourceId != null && theContainedResource) {
theEventWriter.writeAttribute("id", theResourceId);
}
}
if (theResource instanceof Binary) {
@ -821,10 +825,10 @@ public class XmlParser extends BaseParser implements IParser {
} else {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
// DSTU2+
encodeResourceToStreamWriterInDstu2Format(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource);
encodeResourceToStreamWriterInDstu2Format(resDef, theResource, theResource, theEventWriter, resDef, theContainedResource);
} else {
// DSTU1
encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource);
encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theContainedResource);
}
}

View File

@ -1,9 +1,13 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.StringReader;
import org.apache.commons.io.IOUtils;
@ -11,12 +15,13 @@ import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dev.composite.DurationDt;
import ca.uhn.fhir.model.dev.resource.AllergyIntolerance;
import ca.uhn.fhir.model.dev.resource.Composition;
import ca.uhn.fhir.model.dev.resource.Encounter;
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
import ca.uhn.fhir.model.dev.resource.Organization;
@ -98,7 +103,7 @@ public class XmlParserTest {
ourLog.info(str);
Bundle parsed = ourCtx.newXmlParser().parseBundle(str);
assertThat(parsed.getEntries().get(0).getResource().getId().getValue(), isEmptyOrNullString());
assertThat(parsed.getEntries().get(0).getResource().getId().getValue(), emptyOrNullString());
assertTrue(parsed.getEntries().get(0).getResource().getId().isEmpty());
}
@ -190,4 +195,54 @@ public class XmlParserTest {
}
/**
* See #103
*/
@Test
public void testEncodeAndReEncodeContainedXml() {
Composition comp = new Composition();
comp.addSection().getContent().setResource(new AllergyIntolerance().setComment("Section0_Allergy0"));
comp.addSection().getContent().setResource(new AllergyIntolerance().setComment("Section1_Allergy0"));
comp.addSection().getContent().setResource(new AllergyIntolerance().setComment("Section2_Allergy0"));
IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
String string = parser.encodeResourceToString(comp);
ourLog.info(string);
Composition parsed = parser.parseResource(Composition.class, string);
parsed.getSection().remove(0);
string = parser.encodeResourceToString(parsed);
ourLog.info(string);
parsed = parser.parseResource(Composition.class, string);
assertEquals(2, parsed.getContained().getContainedResources().size());
}
/**
* See #103
*/
@Test
public void testEncodeAndReEncodeContainedJson() {
Composition comp = new Composition();
comp.addSection().getContent().setResource(new AllergyIntolerance().setComment("Section0_Allergy0"));
comp.addSection().getContent().setResource(new AllergyIntolerance().setComment("Section1_Allergy0"));
comp.addSection().getContent().setResource(new AllergyIntolerance().setComment("Section2_Allergy0"));
IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
String string = parser.encodeResourceToString(comp);
ourLog.info(string);
Composition parsed = parser.parseResource(Composition.class, string);
parsed.getSection().remove(0);
string = parser.encodeResourceToString(parsed);
ourLog.info(string);
parsed = parser.parseResource(Composition.class, string);
assertEquals(2, parsed.getContained().getContainedResources().size());
}
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@ -41,7 +42,9 @@ import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.valueset.BundleEntryStatusEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.util.ElementUtil;
@ -75,13 +78,16 @@ public class IncludeTest {
}
@Test
public void testOneInclude() throws Exception {
public void testOneIncludeXml() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
ourLog.info(responseContent);
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
@ -91,8 +97,27 @@ public class IncludeTest {
assertEquals("foo", p.getName().get(0).getFamilyFirstRep().getValue());
}
@Test
public void testOneIncludeJson() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_format=json");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
// @Test
assertEquals(200, status.getStatusLine().getStatusCode());
ourLog.info(responseContent);
Bundle bundle = ourCtx.newJsonParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals(1, p.getName().size());
assertEquals("Hello", p.getId().getIdPart());
assertEquals("foo", p.getName().get(0).getFamilyFirstRep().getValue());
}
// @Test
public void testMixedContainedAndNonContained() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/DiagnosticReport?_query=stitchedInclude&_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
@ -106,7 +131,6 @@ public class IncludeTest {
assertEquals(4, bundle.size());
}
@Test
public void testIIncludedResourcesNonContained() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true");
@ -126,11 +150,10 @@ public class IncludeTest {
assertEquals(BundleEntryStatusEnum.INCLUDE, bundle.getEntries().get(2).getStatus().getValueAsEnum());
Patient p1 = (Patient) bundle.toListOfResources().get(0);
assertEquals(0,p1.getContained().getContainedResources().size());
assertEquals(0, p1.getContained().getContainedResources().size());
Patient p2 = (Patient) bundle.toListOfResources().get(1);
assertEquals(0,p2.getContained().getContainedResources().size());
assertEquals(0, p2.getContained().getContainedResources().size());
}
@ -153,15 +176,13 @@ public class IncludeTest {
assertEquals(BundleEntryStatusEnum.INCLUDE, bundle.getEntries().get(2).getStatus().getValueAsEnum());
Patient p1 = (Patient) bundle.toListOfResources().get(0);
assertEquals(0,p1.getContained().getContainedResources().size());
assertEquals(0, p1.getContained().getContainedResources().size());
Patient p2 = (Patient) bundle.toListOfResources().get(1);
assertEquals(0,p2.getContained().getContainedResources().size());
assertEquals(0, p2.getContained().getContainedResources().size());
}
@Test
public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json");
@ -181,11 +202,10 @@ public class IncludeTest {
assertEquals(BundleEntryStatusEnum.INCLUDE, bundle.getEntries().get(2).getStatus().getValueAsEnum());
Patient p1 = (Patient) bundle.toListOfResources().get(0);
assertEquals(0,p1.getContained().getContainedResources().size());
assertEquals(0, p1.getContained().getContainedResources().size());
Patient p2 = (Patient) bundle.toListOfResources().get(1);
assertEquals(0,p2.getContained().getContainedResources().size());
assertEquals(0, p2.getContained().getContainedResources().size());
}
@ -210,16 +230,13 @@ public class IncludeTest {
assertEquals(BundleEntryStatusEnum.INCLUDE, bundle.getEntries().get(3).getStatus().getValueAsEnum());
Patient p1 = (Patient) bundle.toListOfResources().get(0);
assertEquals(0,p1.getContained().getContainedResources().size());
assertEquals(0, p1.getContained().getContainedResources().size());
Patient p2 = (Patient) bundle.toListOfResources().get(1);
assertEquals(0,p2.getContained().getContainedResources().size());
assertEquals(0, p2.getContained().getContainedResources().size());
}
@Test
public void testTwoInclude() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar");
@ -236,8 +253,8 @@ public class IncludeTest {
assertEquals("Hello", p.getId().getIdPart());
Set<String> values = new HashSet<String>();
values.add( p.getName().get(0).getFamilyFirstRep().getValue());
values.add( p.getName().get(1).getFamilyFirstRep().getValue());
values.add(p.getName().get(0).getFamilyFirstRep().getValue());
values.add(p.getName().get(1).getFamilyFirstRep().getValue());
assertThat(values, containsInAnyOrder("foo", "bar"));
}
@ -279,11 +296,10 @@ public class IncludeTest {
}
@ResourceDef(name="Patient")
public static class ExtPatient extends Patient
{
@Child(name="secondOrg")
@Extension(url="http://foo#secondOrg", definedLocally = false, isModifier = false)
@ResourceDef(name = "Patient")
public static class ExtPatient extends Patient {
@Child(name = "secondOrg")
@Extension(url = "http://foo#secondOrg", definedLocally = false, isModifier = false)
private ResourceReferenceDt mySecondOrg;
@Override
@ -292,8 +308,8 @@ public class IncludeTest {
}
public ResourceReferenceDt getSecondOrg() {
if (mySecondOrg==null) {
mySecondOrg= new ResourceReferenceDt();
if (mySecondOrg == null) {
mySecondOrg = new ResourceReferenceDt();
}
return mySecondOrg;
}
@ -350,11 +366,8 @@ public class IncludeTest {
return Collections.singletonList(rep);
}
}
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -379,7 +392,6 @@ public class IncludeTest {
return Arrays.asList(p1, p2);
}
@Search(queryName = "extInclude")
public List<Patient> extInclude() {
Organization o1 = new Organization();
@ -423,7 +435,6 @@ public class IncludeTest {
return Arrays.asList(p1, p2);
}
@Search(queryName = "containedInclude")
public List<Patient> containedInclude() {
Organization o1 = new Organization();
@ -468,7 +479,6 @@ public class IncludeTest {
}
public static void main(String[] args) {
Organization org = new Organization();
@ -480,7 +490,6 @@ public class IncludeTest {
patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
patient.getManagingOrganization().setResource(patient);
System.out.println(new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
patient.getManagingOrganization().getReference();

View File

@ -0,0 +1,120 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
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.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dev.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class ReadTest {
private static CloseableHttpClient ourClient;
private static int ourPort;
private static Server ourServer;
private static FhirContext ourCtx;
/**
* In DSTU2+ the resource ID appears in the resource body
*/
@Test
public void testReadXml() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("p1ReadValue"));
assertThat(responseContent, containsString("p1ReadId"));
}
/**
* In DSTU2+ the resource ID appears in the resource body
*/
@Test
public void testReadJson() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=json");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("p1ReadValue"));
assertThat(responseContent, containsString("p1ReadId"));
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourCtx = new FhirContext();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setFhirContext(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider {
@Read
public Patient read(@IdParam IdDt theId) {
Patient p1 = new Patient();
p1.setId("p1ReadId");
p1.addIdentifier().setLabel("p1ReadValue");
return p1;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
}
}

View File

@ -39,7 +39,7 @@ public class FhirContextTest {
new FhirContext(FhirVersionEnum.DEV);
fail();
} catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("Could not find the HAPI-FHIR structure JAR on the classpath for version {0}"));
assertThat(e.getMessage(), containsString("Could not find the HAPI-FHIR structure JAR on the classpath for version DEV"));
}
}

View File

@ -0,0 +1,141 @@
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 org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.AllergyIntolerance;
import ca.uhn.fhir.model.dstu.resource.Composition;
public class BaseParserTest {
private static final FhirContext ourCtx = new FhirContext();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParserTest.class);
private AllergyIntolerance createAllergy(String theId, String theIdentifierValue) {
AllergyIntolerance retVal = new AllergyIntolerance();
if (theId != null) {
retVal.setId(theId);
}
return retVal.addIdentifier("urn:system", theIdentifierValue);
}
/**
* See #103
*/
@Test
public void testDontEncodeUnnecessaryContainedResources01() {
Composition comp = new Composition();
comp.addAuthor().setReference("Practitioner/123");
comp.getContained().getContainedResources().add(createAllergy("#AAA", "allergy0"));
String xml = ourCtx.newXmlParser().encodeResourceToString(comp);
assertThat(xml, not(containsString("contained")));
String json = ourCtx.newJsonParser().encodeResourceToString(comp);
assertThat(json, not(containsString("contained")));
}
/**
* See #103
*/
@Test
public void testDontEncodeUnnecessaryContainedResources02() {
Composition comp = new Composition();
comp.addAuthor().setReference("Practitioner/123");
comp.getContained().getContainedResources().add(createAllergy("#AAA", "allergy0"));
comp.getContained().getContainedResources().add(createAllergy("#BBB", "allergy1"));
comp.addSection().getContent().setReference("#AAA");
String xml = ourCtx.newXmlParser().encodeResourceToString(comp);
assertThat(xml, stringContainsInOrder("<contained>", "id=\"AAA\"", "reference value=\"#AAA\""));
assertThat(xml, not(containsString("BBB")));
String json = ourCtx.newJsonParser().encodeResourceToString(comp);
assertThat(json, stringContainsInOrder("\"contained\"", "\"id\":\"AAA\"", "\"reference\":\"#AAA\""));
assertThat(json, not(containsString("BBB")));
}
/**
* See #103
*/
@Test
public void testDontEncodeUnnecessaryContainedResources03() {
Composition comp = new Composition();
comp.addAuthor().setReference("Practitioner/123");
AllergyIntolerance allergy = createAllergy("#AAA", "allergy0");
comp.getContained().getContainedResources().add(allergy);
comp.addSection().getContent().setResource(allergy);
String xml = ourCtx.newXmlParser().encodeResourceToString(comp);
assertThat(xml, stringContainsInOrder("<contained>", "id=\"AAA\"", "reference value=\"#AAA\""));
assertThat(xml, not(stringContainsInOrder("AAA", ">", "AAA", ">", "AAA"))); // only once in the contained tag, and once in the reference
String json = ourCtx.newJsonParser().encodeResourceToString(comp);
assertThat(json, stringContainsInOrder("\"contained\"", "\"id\":\"AAA\"", "\"reference\":\"#AAA\""));
assertThat(json, not(stringContainsInOrder("AAA", "\"", "AAA", "\"", "AAA"))); // only once in the contained tag, and once in the reference
}
/**
* See #103
*/
@Test
public void testDontEncodeUnnecessaryContainedResources04() {
Composition comp = new Composition();
comp.addAuthor().setReference("Practitioner/999");
AllergyIntolerance allergy = createAllergy(null, "allergy0");
comp.getContained().getContainedResources().add(allergy);
comp.addSection().getContent().setResource(allergy);
String xml = ourCtx.newXmlParser().encodeResourceToString(comp);
assertThat(xml, stringContainsInOrder("<contained>", "id=\"1\"", "reference value=\"#1\""));
assertThat(xml, not(stringContainsInOrder("1", ">", "1", ">", "1"))); // only once in the contained tag, and once in the reference
String json = ourCtx.newJsonParser().encodeResourceToString(comp);
assertThat(json, stringContainsInOrder("\"contained\"", "\"id\":\"1\"", "\"reference\":\"#1\""));
assertThat(json, not(stringContainsInOrder("1", "\"", "1", "\"", "1"))); // only once in the contained tag, and once in the reference
}
/**
* See #103
*/
@Test
public void testDontEncodeUnnecessaryContainedResources05() {
Composition comp = new Composition();
comp.addAuthor().setReference("Practitioner/999");
AllergyIntolerance allergy = createAllergy(null, "allergy0");
comp.addSection().getContent().setResource(allergy);
String xml = ourCtx.newXmlParser().encodeResourceToString(comp);
assertThat(xml, stringContainsInOrder("<contained>", "id=\"1\"", "reference value=\"#1\""));
assertThat(xml, not(stringContainsInOrder("1", ">", "1", ">", "1"))); // only once in the contained tag, and once in the reference
String json = ourCtx.newJsonParser().encodeResourceToString(comp);
assertThat(json, stringContainsInOrder("\"contained\"", "\"id\":\"1\"", "\"reference\":\"#1\""));
assertThat(json, not(stringContainsInOrder("1", "\"", "1", "\"", "1"))); // only once in the contained tag, and once in the reference
}
/**
* See #103
*/
@Test
public void testDontEncodeUnnecessaryContainedResources06() {
Composition comp = new Composition();
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
String string = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(comp);
ourLog.info(string);
String xml = ourCtx.newXmlParser().encodeResourceToString(comp);
assertThat(xml, stringContainsInOrder("<contained>", "id=\"1\"", "reference value=\"#1\""));
}
}

View File

@ -3,7 +3,13 @@ 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.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -43,6 +49,7 @@ import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.AllergyIntolerance;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.resource.Composition;
import ca.uhn.fhir.model.dstu.resource.Conformance;
@ -1609,4 +1616,54 @@ public class XmlParserTest {
System.setProperty("file.encoding", "ISO-8859-1");
}
/**
* See #103
*/
@Test
public void testEncodeAndReEncodeContainedXml() {
Composition comp = new Composition();
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
String string = parser.encodeResourceToString(comp);
ourLog.info(string);
Composition parsed = parser.parseResource(Composition.class, string);
parsed.getSection().remove(0);
string = parser.encodeResourceToString(parsed);
ourLog.info(string);
parsed = parser.parseResource(Composition.class, string);
assertEquals(2, parsed.getContained().getContainedResources().size());
}
/**
* See #103
*/
@Test
public void testEncodeAndReEncodeContainedJson() {
Composition comp = new Composition();
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
comp.addSection().getContent().setResource(new AllergyIntolerance().addIdentifier("foo", "bar"));
IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
String string = parser.encodeResourceToString(comp);
ourLog.info(string);
Composition parsed = parser.parseResource(Composition.class, string);
parsed.getSection().remove(0);
string = parser.encodeResourceToString(parsed);
ourLog.info(string);
parsed = parser.parseResource(Composition.class, string);
assertEquals(2, parsed.getContained().getContainedResources().size());
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.*;
import java.util.concurrent.TimeUnit;
@ -39,29 +41,51 @@ public class ReadTest {
private static int ourPort;
private static Server ourServer;
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadTest.class);
@Test
public void testRead() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
public void testReadXml() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=xml");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class,responseContent).getIdentifierFirstRep();
assertEquals(200, status.getStatusLine().getStatusCode());
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class, responseContent).getIdentifierFirstRep();
assertEquals("1", dt.getSystem().getValueAsString());
assertEquals(null, dt.getValue().getValueAsString());
assertEquals("1", dt.getSystem().getValueAsString());
assertEquals(null, dt.getValue().getValueAsString());
Header cl = status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION_LC);
assertNotNull(cl);
assertEquals("http://localhost:" + ourPort + "/Patient/1/_history/1", cl.getValue());
}
Header cl = status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION_LC);
assertNotNull(cl);
assertEquals("http://localhost:" + ourPort + "/Patient/1/_history/1", cl.getValue());
assertThat(responseContent, stringContainsInOrder("1", "\""));
assertThat(responseContent, not(stringContainsInOrder("1", "\"", "1")));
}
@Test
public void testReadJson() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=json");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
IdentifierDt dt = ourCtx.newJsonParser().parseResource(Patient.class, responseContent).getIdentifierFirstRep();
assertEquals("1", dt.getSystem().getValueAsString());
assertEquals(null, dt.getValue().getValueAsString());
Header cl = status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION_LC);
assertNotNull(cl);
assertEquals("http://localhost:" + ourPort + "/Patient/1/_history/1", cl.getValue());
assertThat(responseContent, stringContainsInOrder("1", "\""));
assertThat(responseContent, not(stringContainsInOrder("1", "\"", "1")));
}
@Test
public void testReadForProviderWithAbstractReturnType() throws Exception {
@ -72,7 +96,7 @@ public class ReadTest {
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Organization.class,responseContent).getIdentifierFirstRep();
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Organization.class, responseContent).getIdentifierFirstRep();
assertEquals("1", dt.getSystem().getValueAsString());
assertEquals(null, dt.getValue().getValueAsString());
@ -93,7 +117,6 @@ public class ReadTest {
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("application/x-foo", status.getEntity().getContentType().getValue());
@ -105,9 +128,9 @@ public class ReadTest {
assertNotNull(cd);
assertEquals("Attachment;", cd.getValue());
assertEquals(4,responseContent.length);
assertEquals(4, responseContent.length);
for (int i = 0; i < 4; i++) {
assertEquals(i+1, responseContent[i]); // should be 1,2,3,4
assertEquals(i + 1, responseContent[i]); // should be 1,2,3,4
}
}
@ -123,7 +146,7 @@ public class ReadTest {
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class,responseContent).getIdentifierFirstRep();
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class, responseContent).getIdentifierFirstRep();
assertEquals("1", dt.getSystem().getValueAsString());
assertEquals("2", dt.getValue().getValueAsString());
@ -167,7 +190,7 @@ public class ReadTest {
public static class DummyProvider implements IResourceProvider {
@Read(version = true)
public Patient findPatient(@IdParam IdDt theId) {
public Patient read(@IdParam IdDt theId) {
Patient patient = new Patient();
patient.addIdentifier(theId.getIdPart(), theId.getVersionIdPart());
patient.setId("Patient/1/_history/1");
@ -181,7 +204,6 @@ public class ReadTest {
}
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -202,7 +224,6 @@ public class ReadTest {
}
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -212,7 +233,7 @@ public class ReadTest {
public Binary findPatient(@IdParam IdDt theId) {
Binary bin = new Binary();
bin.setContentType("application/x-foo");
bin.setContent(new byte[] {1,2,3,4});
bin.setContent(new byte[] { 1, 2, 3, 4 });
bin.setId("Binary/1/_history/1");
return bin;
}
@ -224,5 +245,4 @@ public class ReadTest {
}
}

View File

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

View File

@ -101,6 +101,12 @@
if StAX API was configured to use a different provider. Thanks to
James Butler for reporting and figuring out where the issue was!
</action>
<action type="fix" issue="103">
Encoders (both XML and JSON) will no longer encode contained resources if they are
not referenced anywhere in the resource via a local reference. This is just a convenience
for users who have parsed a resource with contained resources and want to remove some
before re-encoding. Thanks to Alexander Kley for reporting!
</action>
</release>
<release version="0.8" date="2014-Dec-17">
<action type="add">

View File

@ -132,8 +132,13 @@
</p>
<source>08:01:32.044 [main] INFO ca.uhn.fhir.util.XmlUtil - FHIR XML procesing will use StAX implementation 'Woodstox XML-processor' version '4.4.0'</source>
<p>
If a different implementation is being used, you may want to consider using
Woodstox instead by setting the following system properties:
Although most testing is done using the Woodstox implementation of
StAX, it is not required and HAPI should work correctly with any
compliant implementation of StAX.
</p>
<p>
You can force Woodstox in an environment where multiple StAX libraries are
present by setting the following system properties:
</p>
<source>System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");