JPA server transactions sometimes created an incorrect resource reference if a resource being saved contained references that had a display value but not an actual reference. Thanks to David Hay for reporting!

This commit is contained in:
jamesagnew 2016-04-29 09:21:12 -04:00
parent 649c6807cb
commit 2bc1950bc1
8 changed files with 299 additions and 77 deletions

View File

@ -42,6 +42,10 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetai
public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>implements IFhirResourceDaoPatient<Patient> { public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>implements IFhirResourceDaoPatient<Patient> {
public FhirResourceDaoPatientDstu2() {
super();
}
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) { private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) { if (theCount != null) {

View File

@ -460,6 +460,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
List<BaseResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class); List<BaseResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class);
for (BaseResourceReferenceDt nextRef : allRefs) { for (BaseResourceReferenceDt nextRef : allRefs) {
IdDt nextId = nextRef.getReference(); IdDt nextId = nextRef.getReference();
if (!nextId.hasIdPart()) {
continue;
}
if (idSubstitutions.containsKey(nextId)) { if (idSubstitutions.containsKey(nextId)) {
IdDt newId = idSubstitutions.get(nextId); IdDt newId = idSubstitutions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); ourLog.info(" * Replacing resource ref {} with {}", nextId, newId);

View File

@ -470,6 +470,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
List<IBaseReference> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class); List<IBaseReference> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class);
for (IBaseReference nextRef : allRefs) { for (IBaseReference nextRef : allRefs) {
IIdType nextId = nextRef.getReferenceElement(); IIdType nextId = nextRef.getReferenceElement();
if (!nextId.hasIdPart()) {
continue;
}
if (idSubstitutions.containsKey(nextId)) { if (idSubstitutions.containsKey(nextId)) {
IdType newId = idSubstitutions.get(nextId); IdType newId = idSubstitutions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); ourLog.info(" * Replacing resource ref {} with {}", nextId, newId);

View File

@ -18,7 +18,6 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@ -41,6 +40,7 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Appointment;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.ConceptMap; import ca.uhn.fhir.model.dstu2.resource.ConceptMap;
import ca.uhn.fhir.model.dstu2.resource.Device; import ca.uhn.fhir.model.dstu2.resource.Device;
@ -87,6 +87,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Qualifier("myConceptMapDaoDstu2") @Qualifier("myConceptMapDaoDstu2")
protected IFhirResourceDao<ConceptMap> myConceptMapDao; protected IFhirResourceDao<ConceptMap> myConceptMapDao;
@Autowired @Autowired
@Qualifier("myAppointmentDaoDstu2")
protected IFhirResourceDao<Appointment> myAppointmentDao;
@Autowired
@Qualifier("myBundleDaoDstu2") @Qualifier("myBundleDaoDstu2")
protected IFhirResourceDao<Bundle> myBundleDao; protected IFhirResourceDao<Bundle> myBundleDao;
@Autowired @Autowired

View File

@ -43,6 +43,7 @@ import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Appointment;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest; import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest;
@ -66,19 +67,12 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test @Test
public void testReindexing() { public void testReindexing() {
Patient p = new Patient(); Patient p = new Patient();
@ -151,7 +145,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
assertEquals(Long.valueOf(2), entity.getIndexStatus()); assertEquals(Long.valueOf(2), entity.getIndexStatus());
} }
@Test @Test
public void testSystemMetaOperation() { public void testSystemMetaOperation() {
@ -240,7 +234,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
assertEquals("http://profile/2", profiles.get(0).getValue()); assertEquals("http://profile/2", profiles.get(0).getValue());
} }
@Test @Test
public void testTransactionBatchWithFailingRead() { public void testTransactionBatchWithFailingRead() {
String methodName = "testTransactionBatchWithFailingRead"; String methodName = "testTransactionBatchWithFailingRead";
@ -287,6 +281,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
assertThat(respEntry.getStatus(), startsWith("404")); assertThat(respEntry.getStatus(), startsWith("404"));
} }
@Test @Test
public void testTransactionCreateMatchUrlWithOneMatch() { public void testTransactionCreateMatchUrlWithOneMatch() {
@ -329,7 +324,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
assertEquals("1", o.getId().getVersionIdPart()); assertEquals("1", o.getId().getVersionIdPart());
} }
@Test @Test
public void testTransactionCreateMatchUrlWithTwoMatch() { public void testTransactionCreateMatchUrlWithTwoMatch() {
String methodName = "testTransactionCreateMatchUrlWithTwoMatch"; String methodName = "testTransactionCreateMatchUrlWithTwoMatch";
@ -929,6 +924,25 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
} }
private Bundle testTransactionOrderingCreateBundle(String methodName, int pass, IdDt patientPlaceholderId) {
Bundle req = new Bundle();
req.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?identifier=" + methodName);
Observation obs = new Observation();
obs.getSubject().setReference(patientPlaceholderId);
obs.addIdentifier().setValue(methodName);
obs.getCode().setText(methodName + pass);
req.addEntry().setResource(obs).getRequest().setMethod(HTTPVerbEnum.PUT).setUrl("Observation?identifier=" + methodName);
Patient pat = new Patient();
pat.addIdentifier().setValue(methodName);
pat.addName().addFamily(methodName + pass);
req.addEntry().setResource(pat).setFullUrl(patientPlaceholderId.getValue()).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient");
req.addEntry().getRequest().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=" + methodName);
return req;
}
private void testTransactionOrderingValidateResponse(int pass, Bundle resp) { private void testTransactionOrderingValidateResponse(int pass, Bundle resp) {
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(4, resp.getEntry().size()); assertEquals(4, resp.getEntry().size());
@ -957,25 +971,6 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
assertThat(respGetBundle.getLink("self").getUrl(), endsWith("/Patient?identifier=testTransactionOrdering")); assertThat(respGetBundle.getLink("self").getUrl(), endsWith("/Patient?identifier=testTransactionOrdering"));
} }
private Bundle testTransactionOrderingCreateBundle(String methodName, int pass, IdDt patientPlaceholderId) {
Bundle req = new Bundle();
req.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?identifier=" + methodName);
Observation obs = new Observation();
obs.getSubject().setReference(patientPlaceholderId);
obs.addIdentifier().setValue(methodName);
obs.getCode().setText(methodName + pass);
req.addEntry().setResource(obs).getRequest().setMethod(HTTPVerbEnum.PUT).setUrl("Observation?identifier=" + methodName);
Patient pat = new Patient();
pat.addIdentifier().setValue(methodName);
pat.addName().addFamily(methodName + pass);
req.addEntry().setResource(pat).setFullUrl(patientPlaceholderId.getValue()).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient");
req.addEntry().getRequest().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=" + methodName);
return req;
}
@Test @Test
public void testTransactionReadAndSearch() { public void testTransactionReadAndSearch() {
String methodName = "testTransactionReadAndSearch"; String methodName = "testTransactionReadAndSearch";
@ -1308,6 +1303,87 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
} }
/**
* From a message from David Hay
*/
@Test
public void testTransactionWithAppointments() {
Patient p = new Patient();
p.addName().addFamily("family");
final IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
//@formatter:on
String input = "{\n" +
" \"resourceType\": \"Bundle\",\n" +
" \"type\": \"transaction\",\n" +
" \"entry\": [\n" +
" {\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Appointment\",\n" +
" \"status\": \"pending\",\n" +
" \"type\": {\"text\": \"Cardiology\"},\n" +
" \"description\": \"Investigate Angina\",\n" +
" \"start\": \"2016-04-30T18:48:29+12:00\",\n" +
" \"end\": \"2016-04-30T19:03:29+12:00\",\n" +
" \"minutesDuration\": 15,\n" +
" \"participant\": [\n" +
" {\n" +
" \"actor\": {\"display\": \"Clarence cardiology clinic\"},\n" +
" \"status\": \"accepted\"\n" +
" },\n" +
" {\n" +
" \"actor\": {\"reference\": \"Patient/" + id.getIdPart() + "\"},\n" +
" \"status\": \"accepted\"\n" +
" }\n" +
" ],\n" +
" \"text\": {\n" +
" \"status\": \"generated\",\n" +
" \"div\": \"<div><div>Investigate Angina<\\/div><div>Clarence cardiology clinic<\\/div><\\/div>\"\n" +
" }\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"POST\",\n" +
" \"url\": \"Appointment\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Appointment\",\n" +
" \"status\": \"pending\",\n" +
" \"type\": {\"text\": \"GP Visit\"},\n" +
" \"description\": \"Routine checkup\",\n" +
" \"start\": \"2016-05-03T18:48:29+12:00\",\n" +
" \"end\": \"2016-05-03T19:03:29+12:00\",\n" +
" \"minutesDuration\": 15,\n" +
" \"participant\": [\n" +
" {\n" +
" \"actor\": {\"display\": \"Dr Dave\"},\n" +
" \"status\": \"accepted\"\n" +
" },\n" +
" {\n" +
" \"actor\": {\"reference\": \"Patient/" + id.getIdPart() + "\"},\n" +
" \"status\": \"accepted\"\n" +
" }\n" +
" ],\n" +
" \"text\": {\n" +
" \"status\": \"generated\",\n" +
" \"div\": \"<div><div>Routine checkup<\\/div><div>Dr Dave<\\/div><\\/div>\"\n" +
" }\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"POST\",\n" +
" \"url\": \"Appointment\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
//@formatter:on
Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
Bundle outputBundle = mySystemDao.transaction(mySrd, inputBundle);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outputBundle));
}
@Test @Test
public void testTransactionWithInvalidType() { public void testTransactionWithInvalidType() {
Bundle request = new Bundle(); Bundle request = new Bundle();
@ -1324,6 +1400,65 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
} }
@Test
public void testTransactionWithNullReference() {
Patient p = new Patient();
p.addName().addFamily("family");
final IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Bundle inputBundle = new Bundle();
//@formatter:off
Patient app0 = new Patient();
app0.addName().addFamily("NEW PATIENT");
String placeholderId0 = IdDt.newRandomUuid().getValue();
inputBundle
.addEntry()
.setResource(app0)
.setFullUrl(placeholderId0)
.getRequest()
.setMethod(HTTPVerbEnum.POST)
.setUrl("Patient");
//@formatter:on
//@formatter:off
Appointment app1 = new Appointment();
app1.addParticipant().getActor().setReference(id);
inputBundle
.addEntry()
.setResource(app1)
.getRequest()
.setMethod(HTTPVerbEnum.POST)
.setUrl("Appointment");
//@formatter:on
//@formatter:off
Appointment app2 = new Appointment();
app2.addParticipant().getActor().setDisplay("NO REF");
app2.addParticipant().getActor().setDisplay("YES REF").setReference(placeholderId0);
inputBundle
.addEntry()
.setResource(app2)
.getRequest()
.setMethod(HTTPVerbEnum.POST)
.setUrl("Appointment");
//@formatter:on
Bundle outputBundle = mySystemDao.transaction(mySrd, inputBundle);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outputBundle));
assertEquals(3, outputBundle.getEntry().size());
IdDt id0 = new IdDt(outputBundle.getEntry().get(0).getResponse().getLocation());
IdDt id1 = new IdDt(outputBundle.getEntry().get(1).getResponse().getLocation());
IdDt id2 = new IdDt(outputBundle.getEntry().get(2).getResponse().getLocation());
app2 = myAppointmentDao.read(id2, mySrd);
assertEquals("NO REF", app2.getParticipant().get(0).getActor().getDisplay().getValue());
assertEquals(null, app2.getParticipant().get(0).getActor().getReference().getValue());
assertEquals("YES REF", app2.getParticipant().get(1).getActor().getDisplay().getValue());
assertEquals(id0.toUnqualifiedVersionless().getValue(), app2.getParticipant().get(1).getActor().getReference().getValue());
}
@Test @Test
public void testTransactionWithReferenceToCreateIfNoneExist() { public void testTransactionWithReferenceToCreateIfNoneExist() {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -1377,6 +1512,46 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
assertNotEquals(medOrderId1, medOrderId2); assertNotEquals(medOrderId1, medOrderId2);
} }
@Test
public void testTransactionWithRelativeOidIds() throws Exception {
Bundle res = new Bundle();
res.setType(BundleTypeEnum.TRANSACTION);
Patient p1 = new Patient();
p1.setId("urn:oid:0.1.2.3");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds01");
res.addEntry().setResource(p1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient");
Observation o1 = new Observation();
o1.setId("cid:observation1");
o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02");
o1.setSubject(new ResourceReferenceDt("urn:oid:0.1.2.3"));
res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Observation o2 = new Observation();
o2.setId("cid:observation2");
o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03");
o2.setSubject(new ResourceReferenceDt("urn:oid:0.1.2.3"));
res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Bundle resp = mySystemDao.transaction(mySrd, res);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
assertEquals(3, resp.getEntry().size());
assertTrue(resp.getEntry().get(0).getResponse().getLocation(), new IdDt(resp.getEntry().get(0).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(1).getResponse().getLocation(), new IdDt(resp.getEntry().get(1).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(2).getResponse().getLocation(), new IdDt(resp.getEntry().get(2).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
o1 = myObservationDao.read(new IdDt(resp.getEntry().get(1).getResponse().getLocation()), mySrd);
o2 = myObservationDao.read(new IdDt(resp.getEntry().get(2).getResponse().getLocation()), mySrd);
assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
}
// //
// //
// /** // /**
@ -1479,46 +1654,6 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
// //
// } // }
@Test
public void testTransactionWithRelativeOidIds() throws Exception {
Bundle res = new Bundle();
res.setType(BundleTypeEnum.TRANSACTION);
Patient p1 = new Patient();
p1.setId("urn:oid:0.1.2.3");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds01");
res.addEntry().setResource(p1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient");
Observation o1 = new Observation();
o1.setId("cid:observation1");
o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02");
o1.setSubject(new ResourceReferenceDt("urn:oid:0.1.2.3"));
res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Observation o2 = new Observation();
o2.setId("cid:observation2");
o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03");
o2.setSubject(new ResourceReferenceDt("urn:oid:0.1.2.3"));
res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Bundle resp = mySystemDao.transaction(mySrd, res);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
assertEquals(3, resp.getEntry().size());
assertTrue(resp.getEntry().get(0).getResponse().getLocation(), new IdDt(resp.getEntry().get(0).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(1).getResponse().getLocation(), new IdDt(resp.getEntry().get(1).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(2).getResponse().getLocation(), new IdDt(resp.getEntry().get(2).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
o1 = myObservationDao.read(new IdDt(resp.getEntry().get(1).getResponse().getLocation()), mySrd);
o2 = myObservationDao.read(new IdDt(resp.getEntry().get(2).getResponse().getLocation()), mySrd);
assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
}
/** /**
* This is not the correct way to do it, but we'll allow it to be lenient * This is not the correct way to do it, but we'll allow it to be lenient
*/ */
@ -1562,4 +1697,9 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
} }
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
} }

View File

@ -12,6 +12,7 @@ import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search; import org.hibernate.search.jpa.Search;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.model.Appointment;
import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.CodeableConcept;
@ -90,12 +91,15 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Autowired @Autowired
@Qualifier("myCodeSystemDaoDstu3") @Qualifier("myAppointmentDaoDstu3")
protected IFhirResourceDao<CodeSystem> myCodeSystemDao; protected IFhirResourceDao<Appointment> myAppointmentDao;
@Autowired @Autowired
@Qualifier("myBundleDaoDstu3") @Qualifier("myBundleDaoDstu3")
protected IFhirResourceDao<Bundle> myBundleDao; protected IFhirResourceDao<Bundle> myBundleDao;
@Autowired @Autowired
@Qualifier("myCodeSystemDaoDstu3")
protected IFhirResourceDao<CodeSystem> myCodeSystemDao;
@Autowired
@Qualifier("myConceptMapDaoDstu3") @Qualifier("myConceptMapDaoDstu3")
protected IFhirResourceDao<ConceptMap> myConceptMapDao; protected IFhirResourceDao<ConceptMap> myConceptMapDao;
@Autowired @Autowired
@ -139,14 +143,14 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Autowired @Autowired
@Qualifier("myNamingSystemDaoDstu3") @Qualifier("myNamingSystemDaoDstu3")
protected IFhirResourceDao<NamingSystem> myNamingSystemDao; protected IFhirResourceDao<NamingSystem> myNamingSystemDao;
@Autowired
@Qualifier("myObservationDaoDstu3")
protected IFhirResourceDao<Observation> myObservationDao;
@Autowired @Autowired
@Qualifier("myOrganizationDaoDstu3") @Qualifier("myObservationDaoDstu3")
protected IFhirResourceDao<Organization> myOrganizationDao; protected IFhirResourceDao<Observation> myObservationDao;
@Autowired
@Qualifier("myOrganizationDaoDstu3")
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired @Autowired
@Qualifier("myPatientDaoDstu3") @Qualifier("myPatientDaoDstu3")
protected IFhirResourceDaoPatient<Patient> myPatientDao; protected IFhirResourceDaoPatient<Patient> myPatientDao;

View File

@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Appointment;
import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryRequestComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryRequestComponent;
@ -58,6 +59,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test; import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
@ -77,6 +79,64 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
} }
@Test
public void testTransactionWithNullReference() {
Patient p = new Patient();
p.addName().addFamily("family");
final IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Bundle inputBundle = new Bundle();
//@formatter:off
Patient app0 = new Patient();
app0.addName().addFamily("NEW PATIENT");
String placeholderId0 = IdDt.newRandomUuid().getValue();
inputBundle
.addEntry()
.setResource(app0)
.setFullUrl(placeholderId0)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Patient");
//@formatter:on
//@formatter:off
Appointment app1 = new Appointment();
app1.addParticipant().getActor().setReference(id.getValue());
inputBundle
.addEntry()
.setResource(app1)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Appointment");
//@formatter:on
//@formatter:off
Appointment app2 = new Appointment();
app2.addParticipant().getActor().setDisplay("NO REF");
app2.addParticipant().getActor().setDisplay("YES REF").setReference(placeholderId0);
inputBundle
.addEntry()
.setResource(app2)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Appointment");
//@formatter:on
Bundle outputBundle = mySystemDao.transaction(mySrd, inputBundle);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outputBundle));
assertEquals(3, outputBundle.getEntry().size());
IdDt id0 = new IdDt(outputBundle.getEntry().get(0).getResponse().getLocation());
IdDt id2 = new IdDt(outputBundle.getEntry().get(2).getResponse().getLocation());
app2 = myAppointmentDao.read(id2, mySrd);
assertEquals("NO REF", app2.getParticipant().get(0).getActor().getDisplay());
assertEquals(null, app2.getParticipant().get(0).getActor().getReference());
assertEquals("YES REF", app2.getParticipant().get(1).getActor().getDisplay());
assertEquals(id0.toUnqualifiedVersionless().getValue(), app2.getParticipant().get(1).getActor().getReference());
}
@Test @Test
public void testReindexing() { public void testReindexing() {

View File

@ -51,6 +51,11 @@
Note that existing databases will need to modify index "IDX_FORCEDID" as Note that existing databases will need to modify index "IDX_FORCEDID" as
it is no longer unique, and perform a reindexing pass. it is no longer unique, and perform a reindexing pass.
</action> </action>
<action type="fix">
JPA server transactions sometimes created an incorrect resource reference
if a resource being saved contained references that had a display value but
not an actual reference. Thanks to David Hay for reporting!
</action>
</release> </release>
<release version="1.5" date="2016-04-20"> <release version="1.5" date="2016-04-20">
<action type="fix" issue="339"> <action type="fix" issue="339">