Automatically remove duplicate conditional creates in JPA
This commit is contained in:
parent
89b08cd627
commit
43c07077be
|
@ -44,10 +44,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||||
import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding;
|
import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.*;
|
||||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
|
@ -98,6 +95,14 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
||||||
String actionName = "Transaction";
|
String actionName = "Transaction";
|
||||||
BUNDLE response = processTransactionAsSubRequest((ServletRequestDetails) theRequestDetails, theRequest, actionName);
|
BUNDLE response = processTransactionAsSubRequest((ServletRequestDetails) theRequestDetails, theRequest, actionName);
|
||||||
|
|
||||||
|
List<BUNDLEENTRY> entries = myVersionAdapter.getEntries(response);
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
if (ElementUtil.isEmpty(entries.get(i))) {
|
||||||
|
entries.remove(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,6 +505,42 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
||||||
Set<ResourceTable> updatedEntities = new HashSet<>();
|
Set<ResourceTable> updatedEntities = new HashSet<>();
|
||||||
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<>();
|
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for duplicate conditional creates and consolidate them
|
||||||
|
*/
|
||||||
|
Map<String, String> ifNoneExistToUuid = new HashMap<>();
|
||||||
|
for (int index = 0, originalIndex = 0; index < theEntries.size(); index++, originalIndex++) {
|
||||||
|
BUNDLEENTRY nextReqEntry = theEntries.get(index);
|
||||||
|
String verb = myVersionAdapter.getEntryRequestVerb(nextReqEntry);
|
||||||
|
String entryUrl = myVersionAdapter.getFullUrl(nextReqEntry);
|
||||||
|
String requestUrl = myVersionAdapter.getEntryRequestUrl(nextReqEntry);
|
||||||
|
String ifNoneExist = myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop through the request and process any entries of type
|
* Loop through the request and process any entries of type
|
||||||
* PUT, POST or DELETE
|
* PUT, POST or DELETE
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
@ -1044,6 +1045,66 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithDuplicateConditionalCreates() {
|
||||||
|
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.POST)
|
||||||
|
.setUrl("Practitioner/")
|
||||||
|
.setIfNoneExist("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.POST)
|
||||||
|
.setUrl("Practitioner/")
|
||||||
|
.setIfNoneExist("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
|
@Test
|
||||||
public void testTransactionCreateInlineMatchUrlWithTwoMatches() {
|
public void testTransactionCreateInlineMatchUrlWithTwoMatches() {
|
||||||
String methodName = "testTransactionCreateInlineMatchUrlWithTwoMatches";
|
String methodName = "testTransactionCreateInlineMatchUrlWithTwoMatches";
|
||||||
|
|
|
@ -52,6 +52,12 @@
|
||||||
The JPA query builder has been optimized to take better advantage of SQL IN (..) expressions
|
The JPA query builder has been optimized to take better advantage of SQL IN (..) expressions
|
||||||
when performing token searches with multiple OR values.
|
when performing token searches with multiple OR values.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
The JPA server transaction processor will now automatically detect if the request
|
||||||
|
Bundle contains multiple entries having identical conditional create operations, and
|
||||||
|
collapse these into a single operation. This is done as a convenience, since many
|
||||||
|
conversion algorithms can accidentally generate such duplicates.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.7.0" date="2019-02-06" description="Gale">
|
<release version="3.7.0" date="2019-02-06" description="Gale">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue