Collapse conditional UPDATES as well in JPA transactions

This commit is contained in:
James Agnew 2019-02-22 13:49:38 -05:00
parent 43c07077be
commit 820a3d2296
4 changed files with 1678 additions and 16 deletions

View File

@ -508,36 +508,60 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
/*
* Look for duplicate conditional creates and consolidate them
*/
Map<String, String> ifNoneExistToUuid = new HashMap<>();
Map<String, String> keyToUuid = new HashMap<>();
for (int index = 0, originalIndex = 0; index < theEntries.size(); index++, originalIndex++) {
BUNDLEENTRY nextReqEntry = theEntries.get(index);
// String encoded = myContext.newJsonParser().encodeResourceToString(myVersionAdapter.getResource(nextReqEntry));
// if (encoded.contains("00000011111")) {
// ourLog.info("Resource contains 00000011111");
// }
String verb = myVersionAdapter.getEntryRequestVerb(nextReqEntry);
String entryUrl = myVersionAdapter.getFullUrl(nextReqEntry);
String requestUrl = myVersionAdapter.getEntryRequestUrl(nextReqEntry);
String ifNoneExist = myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry);
String key = verb + "|"+ requestUrl + "|" + ifNoneExist;
// Conditional UPDATE
boolean consolidateEntry = false;
if ("PUT".equals(verb)) {
if (isNotBlank(entryUrl) && isNotBlank(requestUrl)) {
int questionMarkIndex = requestUrl.indexOf('?');
if (questionMarkIndex >= 0 && requestUrl.length() > (questionMarkIndex+1)) {
consolidateEntry = true;
}
}
}
// Conditional CREATE
if ("POST".equals(verb)) {
if (isNotBlank(entryUrl) && isNotBlank(requestUrl) && isNotBlank(ifNoneExist)) {
if (!entryUrl.equals(requestUrl)) {
String key = requestUrl + "|" + ifNoneExist; // just in case the ifNoneExist doesn't include the resource type
if (!ifNoneExistToUuid.containsKey(key)) {
ifNoneExistToUuid.put(key, entryUrl);
} else {
ourLog.info("Discarding transaction bundle entry {} as it contained a duplicate conditional create: {}", originalIndex, ifNoneExist);
theEntries.remove(index);
index--;
String existingUuid = ifNoneExistToUuid.get(key);
for (BUNDLEENTRY nextEntry : theEntries) {
IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
for (ResourceReferenceInfo nextReference : myContext.newTerser().getAllResourceReferences(nextResource)) {
if (entryUrl.equals(nextReference.getResourceReference().getReferenceElement().getValue())) {
nextReference.getResourceReference().setReference(existingUuid);
}
}
consolidateEntry = true;
}
}
}
if (consolidateEntry) {
if (!keyToUuid.containsKey(key)) {
keyToUuid.put(key, entryUrl);
} else {
ourLog.info("Discarding transaction bundle entry {} as it contained a duplicate conditional {}", originalIndex, verb);
theEntries.remove(index);
index--;
String existingUuid = keyToUuid.get(key);
for (BUNDLEENTRY nextEntry : theEntries) {
IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
for (ResourceReferenceInfo nextReference : myContext.newTerser().getAllResourceReferences(nextResource)) {
if (entryUrl.equals(nextReference.getResourceReference().getReferenceElement().getValue())) {
nextReference.getResourceReference().setReference(existingUuid);
}
}
}
}
}
}

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.GZipUtil;
import ca.uhn.fhir.jpa.dao.r4.FhirSystemDaoR4;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
@ -39,6 +40,7 @@ import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
@ -176,6 +178,22 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
return myFhirCtx.newXmlParser().parseResource(Bundle.class, req);
}
@Test
public void testTransactionWithDuplicateConditionalCreates2() throws IOException {
Bundle request = myFhirCtx.newJsonParser().parseResource(Bundle.class, IOUtils.toString(FhirSystemDaoR4.class.getResourceAsStream("/dstu3/duplicate-conditional-create.json"), Constants.CHARSET_UTF8));
Bundle response = mySystemDao.transaction(null, request);
ourLog.info("Response:\n{}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
List<String> responseTypes = response
.getEntry()
.stream()
.map(t -> new org.hl7.fhir.r4.model.IdType(t.getResponse().getLocation()).getResourceType())
.collect(Collectors.toList());
assertThat(responseTypes.toString(), responseTypes, contains("Patient", "Encounter", "Location", "Location", "Practitioner", "ProcedureRequest", "DiagnosticReport", "Specimen", "Practitioner", "Observation", "Observation", "Observation", "Observation", "Observation", "Observation", "Observation", "Observation", "Observation"));
}
@Test
public void testBatchCreateWithBadRead() {
Bundle request = new Bundle();

View File

@ -1105,6 +1105,64 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
}
@Test
public void testTransactionWithDuplicateConditionalUpdates() {
Bundle request = new Bundle();
request.setType(BundleType.TRANSACTION);
Practitioner p = new Practitioner();
p.setId(IdType.newRandomUuid());
p.addIdentifier().setSystem("http://foo").setValue("bar");
request.addEntry()
.setFullUrl(p.getId())
.setResource(p)
.getRequest()
.setMethod(HTTPVerb.PUT)
.setUrl("Practitioner?identifier=http://foo|bar");
Observation o = new Observation();
o.setId(IdType.newRandomUuid());
o.getPerformerFirstRep().setReference(p.getId());
request.addEntry()
.setFullUrl(o.getId())
.setResource(o)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Observation/");
p = new Practitioner();
p.setId(IdType.newRandomUuid());
p.addIdentifier().setSystem("http://foo").setValue("bar");
request.addEntry()
.setFullUrl(p.getId())
.setResource(p)
.getRequest()
.setMethod(HTTPVerb.PUT)
.setUrl("Practitioner?identifier=http://foo|bar");
o = new Observation();
o.setId(IdType.newRandomUuid());
o.getPerformerFirstRep().setReference(p.getId());
request.addEntry()
.setFullUrl(o.getId())
.setResource(o)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Observation/");
Bundle response = mySystemDao.transaction(null, request);
ourLog.info("Response:\n{}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
List<String> responseTypes = response
.getEntry()
.stream()
.map(t -> new IdType(t.getResponse().getLocation()).getResourceType())
.collect(Collectors.toList());
assertThat(responseTypes.toString(), responseTypes, contains("Practitioner", "Observation", "Observation"));
}
@Test
public void testTransactionCreateInlineMatchUrlWithTwoMatches() {
String methodName = "testTransactionCreateInlineMatchUrlWithTwoMatches";