Accept placeholder IDs in the JPA server even if they have no resource type in references to them (as is actually correct)

This commit is contained in:
jamesagnew 2015-06-11 21:20:27 -04:00
parent 4ab8871a41
commit e536486638
5 changed files with 257 additions and 284 deletions

View File

@ -210,9 +210,10 @@ public class FhirSystemDaoDstu1 extends BaseFhirSystemDao<List<IResource>> {
ourLog.info("Transaction resource ID[{}] is being updated", newId);
} else {
if (!nextId.getIdPart().startsWith("#")) {
nextId = new IdDt(resourceName + '/' + nextId.getIdPart());
nextId = new IdDt(resourceName, nextId.getIdPart());
ourLog.info("Transaction resource ID[{}] has been assigned new ID[{}]", nextId, newId);
idConversions.put(nextId, newId);
idConversions.put(new IdDt(nextId.getIdPart()), newId);
}
}
}

View File

@ -45,6 +45,7 @@ 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.EntryTransactionResponse;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.primitive.IdDt;
@ -165,257 +166,97 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
}
switch (verb) {
case POST: {
// CREATE
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = getDao(res.getClass());
res.setId((String)null);
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
outcome = resourceDao.create(res, nextEntry.getTransaction().getIfNoneExist(), false);
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
break;
case POST: {
// CREATE
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = getDao(res.getClass());
res.setId((String) null);
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
outcome = resourceDao.create(res, nextEntry.getTransaction().getIfNoneExist(), false);
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
break;
}
case DELETE: {
// DELETE
Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
if (parts.getResourceId() != null) {
parts.getDao().delete(new IdDt(parts.getResourceType(), parts.getResourceId()));
} else {
parts.getDao().deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
}
case DELETE: {
// DELETE
Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
if (parts.getResourceId() != null) {
parts.getDao().delete(new IdDt(parts.getResourceType(), parts.getResourceId()));
newEntry.getTransactionResponse().setStatus(Integer.toString(Constants.STATUS_HTTP_204_NO_CONTENT));
break;
}
case PUT: {
// UPDATE
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = getDao(res.getClass());
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
if (isNotBlank(parts.getResourceId())) {
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
outcome = resourceDao.update(res, null, false);
} else {
res.setId((String) null);
outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false);
}
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
break;
}
case GET: {
// SEARCH/READ/VREAD
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = parts.getDao();
if (parts.getResourceId() != null && parts.getParams() == null) {
IResource found;
if (parts.getVersionId() != null) {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
} else {
parts.getDao().deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
}
EntryTransactionResponse resp = response.addEntry().setResource(found).getTransactionResponse();
resp.setLocation(found.getId().toUnqualified().getValue());
resp.setEtag(found.getId().getVersionIdPart());
} else if (parts.getParams() != null) {
RuntimeResourceDefinition def = getContext().getResourceDefinition(parts.getDao().getResourceType());
SearchParameterMap params = translateMatchUrl(url, def);
IBundleProvider bundle = parts.getDao().search(params);
Bundle searchBundle = new Bundle();
searchBundle.setTotal(bundle.size());
int configuredMax = 100; // this should probably be configurable or something
if (bundle.size() > configuredMax) {
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
}
List<IBaseResource> resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
for (IBaseResource next : resourcesToAdd) {
searchBundle.addEntry().setResource((IResource) next);
}
newEntry.getTransactionResponse().setStatus(Integer.toString(Constants.STATUS_HTTP_204_NO_CONTENT));
break;
}
case PUT: {
// UPDATE
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = getDao(res.getClass());
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
// if (res.getId().hasIdPart() && isBlank(parts.getResourceId())) {
// parts.setResourceId(res.getId().getIdPart());
// }
if (isNotBlank(parts.getResourceId())) {
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
outcome = resourceDao.update(res, null, false);
} else {
res.setId((String)null);
outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false);
}
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
break;
}
case GET: {
// SEARCH/READ/VREAD
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = parts.getDao();
if (parts.getResourceId() != null && parts.getParams() == null) {
IResource found;
if (parts.getVersionId() != null) {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
} else {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
}
EntryTransactionResponse resp = response.addEntry().setResource(found).getTransactionResponse();
resp.setLocation(found.getId().toUnqualified().getValue());
resp.setEtag(found.getId().getVersionIdPart());
} else if (parts.getParams() != null) {
RuntimeResourceDefinition def = getContext().getResourceDefinition(parts.getDao().getResourceType());
SearchParameterMap params = translateMatchUrl(url, def);
IBundleProvider bundle = parts.getDao().search(params);
Bundle searchBundle = new Bundle();
searchBundle.setTotal(bundle.size());
int configuredMax = 100; // this should probably be configurable or something
if (bundle.size() > configuredMax) {
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
}
List<IBaseResource> resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
for (IBaseResource next : resourcesToAdd) {
searchBundle.addEntry().setResource((IResource) next);
}
response.addEntry().setResource(searchBundle);
}
response.addEntry().setResource(searchBundle);
}
}
}
}
FhirTerser terser = getContext().newTerser();
// int creations = 0;
// int updates = 0;
//
// Map<IdDt, IdDt> idConversions = new HashMap<IdDt, IdDt>();
//
// List<ResourceTable> persistedResources = new ArrayList<ResourceTable>();
//
// List<IResource> retVal = new ArrayList<IResource>();
// OperationOutcome oo = new OperationOutcome();
// retVal.add(oo);
//
// for (int resourceIdx = 0; resourceIdx < theResources.size(); resourceIdx++) {
// IResource nextResource = theResources.get(resourceIdx);
//
// IdDt nextId = nextResource.getId();
// if (nextId == null) {
// nextId = new IdDt();
// }
//
// String resourceName = toResourceName(nextResource);
// BundleEntryTransactionOperationEnum nextResouceOperationIn =
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(nextResource);
// if (nextResouceOperationIn == null && hasValue(ResourceMetadataKeyEnum.DELETED_AT.get(nextResource))) {
// nextResouceOperationIn = BundleEntryTransactionOperationEnum.DELETE;
// }
//
// String matchUrl = ResourceMetadataKeyEnum.LINK_SEARCH.get(nextResource);
// Set<Long> candidateMatches = null;
// if (StringUtils.isNotBlank(matchUrl)) {
// candidateMatches = processMatchUrl(matchUrl, nextResource.getClass());
// }
//
// ResourceTable entity;
// if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.CREATE) {
// entity = null;
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.UPDATE || nextResouceOperationIn ==
// BundleEntryTransactionOperationEnum.DELETE) {
// if (candidateMatches == null || candidateMatches.size() == 0) {
// if (nextId == null || StringUtils.isBlank(nextId.getIdPart())) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationFailedNoId", nextResouceOperationIn.name()));
// }
// entity = tryToLoadEntity(nextId);
// if (entity == null) {
// if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.UPDATE) {
// ourLog.debug("Attempting to UPDATE resource with unknown ID '{}', will CREATE instead", nextId);
// } else if (candidateMatches == null) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationFailedUnknownId", nextResouceOperationIn.name(), nextId));
// } else {
// ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl);
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(nextResource,
// BundleEntryTransactionOperationEnum.NOOP);
// persistedResources.add(null);
// retVal.add(nextResource);
// continue;
// }
// }
// } else if (candidateMatches.size() == 1) {
// entity = loadFirstEntityFromCandidateMatches(candidateMatches);
// } else {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationWithMultipleMatchFailure", nextResouceOperationIn.name(), matchUrl,
// candidateMatches.size()));
// }
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.NOOP) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "incomingNoopInTransaction"));
// } else if (nextId.isEmpty()) {
// entity = null;
// } else {
// entity = tryToLoadEntity(nextId);
// }
//
// BundleEntryTransactionOperationEnum nextResouceOperationOut;
// if (entity == null) {
// nextResouceOperationOut = BundleEntryTransactionOperationEnum.CREATE;
// entity = toEntity(nextResource);
// if (nextId.isEmpty() == false && nextId.getIdPart().startsWith("cid:")) {
// ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart());
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.CREATE) {
// if (nextId.isEmpty() == false) {
// ourLog.debug("Resource in transaction has ID[{}] but is marked for CREATE, will ignore ID",
// nextId.getIdPart());
// }
// if (candidateMatches != null) {
// if (candidateMatches.size() == 1) {
// ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl);
// BaseHasResource existingEntity = loadFirstEntityFromCandidateMatches(candidateMatches);
// IResource existing = (IResource) toResource(existingEntity);
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(existing, BundleEntryTransactionOperationEnum.NOOP);
// persistedResources.add(null);
// retVal.add(existing);
// continue;
// }
// if (candidateMatches.size() > 1) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationWithMultipleMatchFailure", BundleEntryTransactionOperationEnum.CREATE.name(), matchUrl,
// candidateMatches.size()));
// }
// }
// } else {
// createForcedIdIfNeeded(entity, nextId);
// }
// myEntityManager.persist(entity);
// if (entity.getForcedId() != null) {
// myEntityManager.persist(entity.getForcedId());
// }
// creations++;
// ourLog.info("Resource Type[{}] with ID[{}] does not exist, creating it", resourceName, nextId);
// } else {
// nextResouceOperationOut = nextResouceOperationIn;
// if (nextResouceOperationOut == null) {
// nextResouceOperationOut = BundleEntryTransactionOperationEnum.UPDATE;
// }
// updates++;
// ourLog.info("Resource Type[{}] with ID[{}] exists, updating it", resourceName, nextId);
// }
//
// persistedResources.add(entity);
// retVal.add(nextResource);
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(nextResource, nextResouceOperationOut);
// }
//
// ourLog.info("Flushing transaction to database");
// myEntityManager.flush();
//
// for (int i = 0; i < persistedResources.size(); i++) {
// ResourceTable entity = persistedResources.get(i);
//
// String resourceName = toResourceName(theResources.get(i));
// IdDt nextId = theResources.get(i).getId();
//
// IdDt newId;
//
// if (entity == null) {
// newId = retVal.get(i + 1).getId().toUnqualifiedVersionless();
// } else {
// newId = entity.getIdDt().toUnqualifiedVersionless();
// }
//
// if (nextId == null || nextId.isEmpty()) {
// ourLog.info("Transaction resource (with no preexisting ID) has been assigned new ID[{}]", nextId, newId);
// } else {
// if (nextId.toUnqualifiedVersionless().equals(newId)) {
// ourLog.info("Transaction resource ID[{}] is being updated", newId);
// } else {
// if (!nextId.getIdPart().startsWith("#")) {
// nextId = new IdDt(resourceName + '/' + nextId.getIdPart());
// ourLog.info("Transaction resource ID[{}] has been assigned new ID[{}]", nextId, newId);
// idConversions.put(nextId, newId);
// }
// }
// }
//
// }
//
for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) {
IResource nextResource = nextOutcome.getResource();
if (nextResource == null) {
@ -438,49 +279,41 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false);
}
//
// ourLog.info("Re-flushing updated resource references and extracting search criteria");
//
// for (int i = 0; i < theResources.size(); i++) {
// IResource resource = theResources.get(i);
// ResourceTable table = persistedResources.get(i);
// if (table == null) {
// continue;
// }
//
// InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(resource);
// Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
// if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(resource) ==
// BundleEntryTransactionOperationEnum.DELETE) {
// deletedTimestampOrNull = new Date();
// ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull));
// }
//
// updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull);
// }
long delay = System.currentTimeMillis() - start;
ourLog.info("Transaction completed in {}ms", new Object[]{delay});
ourLog.info("Transaction completed in {}ms", new Object[] { delay });
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Transaction completed in " + delay + "ms");
for (IdDt next : allIds) {
IdDt replacement = idSubstitutions.get(next);
if (replacement == null) {
continue;
}
if (replacement.equals(next)) {
continue;
}
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
}
notifyWriteCompleted();
response.setType(BundleTypeEnum.TRANSACTION_RESPONSE);
return response;
}
@Override
public MetaDt metaGetOperation() {
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList();
MetaDt retVal = super.toMetaDt(tagDefinitions);
return retVal;
}
private String extractTransactionUrlOrThrowException(Entry nextEntry, HTTPVerbEnum verb) {
String url = nextEntry.getTransaction().getUrl();
if (isBlank(url)) {
@ -493,7 +326,12 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
IdDt newId = outcome.getId().toUnqualifiedVersionless();
IdDt resourceId = nextResourceId.toUnqualifiedVersionless();
if (newId.equals(resourceId) == false) {
/*
* The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified
* kind too just to be lenient.
*/
idSubstitutions.put(resourceId, newId);
idSubstitutions.put(resourceId.withResourceType(null), newId);
}
idToPersistedOutcome.put(newId, outcome);
if (outcome.getCreated().booleanValue()) {

View File

@ -303,7 +303,8 @@ public class FhirSystemDaoDstu1Test {
/**
* Issue #55
* Issue #55. Note that this is the incorrect way to
* do this but we'll leave it since people may depend on it.
*/
@Test
public void testTransactionWithCidIds() throws Exception {
@ -337,6 +338,41 @@ public class FhirSystemDaoDstu1Test {
}
/**
* This is the correct way to do this, not {@link #testTransactionWithCidIds()}
*/
@Test
public void testTransactionWithCidIdsUnqualified() throws Exception {
List<IResource> res = new ArrayList<IResource>();
Patient p1 = new Patient();
p1.setId("cid:patient1");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithCidIdsUnqualified01");
res.add(p1);
Observation o1 = new Observation();
o1.setId("cid:observation1");
o1.getIdentifier().setSystem("system").setValue("testTransactionWithCidIdsUnqualified02");
o1.setSubject(new ResourceReferenceDt("cid:patient1"));
res.add(o1);
Observation o2 = new Observation();
o2.setId("cid:observation2");
o2.getIdentifier().setSystem("system").setValue("testTransactionWithCidIdsUnqualified03");
o2.setSubject(new ResourceReferenceDt("cid:patient1"));
res.add(o2);
ourSystemDao.transaction(res);
assertTrue(p1.getId().getValue(), p1.getId().getIdPart().matches("^[0-9]+$"));
assertTrue(o1.getId().getValue(), o1.getId().getIdPart().matches("^[0-9]+$"));
assertTrue(o2.getId().getValue(), o2.getId().getIdPart().matches("^[0-9]+$"));
assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
}
@Test
public void testTransactionWithDelete() throws Exception {

View File

@ -5,12 +5,7 @@ import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
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.fail;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.util.ArrayList;
@ -31,6 +26,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
@ -57,7 +53,7 @@ public class FhirSystemDaoDstu2Test {
private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirSystemDao<Bundle> ourSystemDao;
private static IFhirResourceDao<Observation> ourObservationDao;
@Test
public void testTransactionCreateMatchUrlWithOneMatch() {
String methodName = "testTransactionCreateMatchUrlWithOneMatch";
@ -144,24 +140,22 @@ public class FhirSystemDaoDstu2Test {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml");
String bundleStr = IOUtils.toString(bundleRes);
Bundle bundle = ourFhirContext.newXmlParser().parseResource(Bundle.class, bundleStr);
Bundle resp = ourSystemDao.transaction(bundle);
ourLog.info(ourFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
OperationOutcome oo = (OperationOutcome) resp.getEntry().get(0).getResource();
assertThat(oo.getIssue().get(0).getDetailsElement().getValue(), containsString("Transaction completed"));
assertThat(resp.getEntry().get(1).getTransactionResponse().getLocation(), startsWith("Patient/a555-44-4444/_history/"));
assertThat(resp.getEntry().get(2).getTransactionResponse().getLocation(), startsWith("Patient/temp6789/_history/"));
assertThat(resp.getEntry().get(3).getTransactionResponse().getLocation(), startsWith("Organization/GHH/_history/"));
Patient p = ourPatientDao.read(new IdDt("Patient/a555-44-4444/_history/1"));
assertEquals("Patient/temp6789", p.getLink().get(0).getOther().getReference().getValue());
}
@Test
public void testTransactionReadAndSearch() {
String methodName = "testTransactionReadAndSearch";
@ -561,7 +555,7 @@ public class FhirSystemDaoDstu2Test {
Observation o = new Observation();
o.getCode().setText("Some Observation");
o.getSubject().setReference("Patient/"+methodName);
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);
@ -623,6 +617,99 @@ public class FhirSystemDaoDstu2Test {
}
@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).getTransaction().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).getTransaction().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).getTransaction().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Bundle resp = ourSystemDao.transaction(res);
ourLog.info(ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
assertEquals(4, resp.getEntry().size());
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
OperationOutcome outcome = (OperationOutcome) resp.getEntry().get(0).getResource();
assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"Patient/urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
assertTrue(resp.getEntry().get(1).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(2).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(3).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(3).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
o1 = ourObservationDao.read(new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()));
o2 = ourObservationDao.read(new IdDt(resp.getEntry().get(3).getTransactionResponse().getLocation()));
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
*/
@Test
public void testTransactionWithRelativeOidIdsQualified() 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).getTransaction().setMethod(HTTPVerbEnum.POST).setUrl("Patient");
Observation o1 = new Observation();
o1.setId("cid:observation1");
o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02");
o1.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3"));
res.addEntry().setResource(o1).getTransaction().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Observation o2 = new Observation();
o2.setId("cid:observation2");
o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03");
o2.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3"));
res.addEntry().setResource(o2).getTransaction().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
Bundle resp = ourSystemDao.transaction(res);
ourLog.info(ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
assertEquals(4, resp.getEntry().size());
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
OperationOutcome outcome = (OperationOutcome) resp.getEntry().get(0).getResource();
assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"Patient/urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
assertTrue(resp.getEntry().get(1).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(2).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(3).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(3).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
o1 = ourObservationDao.read(new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()));
o2 = ourObservationDao.read(new IdDt(resp.getEntry().get(3).getTransactionResponse().getLocation()));
assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
}
//
//
// /**
@ -744,7 +831,7 @@ public class FhirSystemDaoDstu2Test {
@Test
public void testSystemMetaOperation() {
deleteEverything();
MetaDt meta = ourSystemDao.metaGetOperation();
List<CodingDt> published = meta.getTag();
assertEquals(0, published.size());
@ -841,7 +928,7 @@ public class FhirSystemDaoDstu2Test {
for (IBaseResource iResource : allRes) {
if (ResourceMetadataKeyEnum.DELETED_AT.get((IResource) iResource) == null) {
ourLog.info("Deleting: {}", iResource.getIdElement());
Bundle b = new Bundle();
b.setType(BundleTypeEnum.TRANSACTION);
String url = iResource.getIdElement().toVersionless().getValue();
@ -849,7 +936,7 @@ public class FhirSystemDaoDstu2Test {
systemDao.transaction(b);
}
}
systemDao.deleteAllTagsOnServer();
}

View File

@ -73,6 +73,17 @@
Some HTML entities were not correctly converted during parsing. Thanks to
Nick Kitto for reporting!
</action>
<action type="fix">
In the JPA Server:
Transactions creating resources with temporary/placeholder resource IDs
and other resources with references to those placeholder IDs previously
did not work if the reference did not contain the resource type
(e.g. Patient/urn:oid:0.1.2.3 instead of urn:oid:0.1.2.3). The
latter is actually the correct way of specifying a reference to a
placeholder, but the former was the only way that worked. Both forms
now work, in order to be lenient. Thanks to Bill De Beaubien for
reporting!
</action>
</release>
<release version="1.0" date="2015-May-8">
<action type="add">