From 72cf22e3a9a8de5c564a31281e6b7c05ba5d98b6 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 20 Feb 2018 07:32:49 -0500 Subject: [PATCH] Fix #854 - Process ID substitutions on URIs for JPA --- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 19 + .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 104 ++-- .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 81 ++- .../fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java | 3 + .../jpa/dao/dstu2/FhirSystemDaoDstu2Test.java | 545 ++++++++++-------- .../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 3 + .../jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 103 +++- .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 5 +- .../fhir/jpa/dao/r4/FhirSystemDaoR4Test.java | 269 +++++---- src/changes/changes.xml | 6 + 10 files changed, 669 insertions(+), 469 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index b044f6d57c2..f5e21cbb986 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -25,6 +25,7 @@ import java.util.*; import javax.persistence.TypedQuery; +import ca.uhn.fhir.model.primitive.UriDt; import org.apache.http.NameValuePair; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -453,6 +454,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { continue; } + // References List allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class); for (BaseResourceReferenceDt nextRef : allRefs) { IdDt nextId = nextRef.getReference(); @@ -468,6 +470,23 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { } } + // URIs + List allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriDt.class); + for (UriDt nextRef : allUris) { + if (nextRef instanceof IIdType) { + continue; // No substitution on the resource ID itself! + } + IdDt nextUriString = new IdDt(nextRef.getValueAsString()); + if (idSubstitutions.containsKey(nextUriString)) { + IdDt newId = idSubstitutions.get(nextUriString); + ourLog.info(" * Replacing resource ref {} with {}", nextUriString, newId); + nextRef.setValue(newId.getValue()); + } else { + ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString); + } + } + + InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 70ed8ec955b..2eee6e8b3b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -311,7 +311,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { @SuppressWarnings("unchecked") private Map doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set allIds, - Map idSubstitutions, Map idToPersistedOutcome, Bundle response, IdentityHashMap originalRequestOrder, List theEntries) { + Map theIdSubstitutions, Map idToPersistedOutcome, Bundle response, IdentityHashMap originalRequestOrder, List theEntries) { Set deletedResources = new HashSet(); List deleteConflicts = new ArrayList(); Map entriesToProcess = new IdentityHashMap(); @@ -379,10 +379,10 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { res.setId((String) null); DaoMethodOutcome outcome; String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); - matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); if (nextResourceId != null) { - handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); } entriesToProcess.put(nextRespEntry, outcome.getEntity()); if (outcome.getCreated() == false) { @@ -412,7 +412,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } } else { String matchUrl = parts.getResourceType() + '?' + parts.getParams(); - matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails); List allDeleted = deleteOutcome.getDeletedEntities(); for (ResourceTable deleted : allDeleted) { @@ -453,14 +453,14 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } else { matchUrl = parts.getResourceType(); } - matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); outcome = resourceDao.update(res, matchUrl, false, theRequestDetails); if (Boolean.TRUE.equals(outcome.getCreated())) { conditionalRequestUrls.put(matchUrl, res.getClass()); } } - handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); entriesToProcess.put(nextRespEntry, outcome.getEntity()); break; } @@ -496,14 +496,15 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { continue; } + // Refererences List allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class); for (IBaseReference nextRef : allRefs) { IIdType nextId = nextRef.getReferenceElement(); if (!nextId.hasIdPart()) { continue; } - if (idSubstitutions.containsKey(nextId)) { - IdType newId = idSubstitutions.get(nextId); + if (theIdSubstitutions.containsKey(nextId)) { + IdType newId = theIdSubstitutions.get(nextId); ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); nextRef.setReference(newId.getValue()); } else if (nextId.getValue().startsWith("urn:")) { @@ -513,6 +514,22 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } } + // URIs + List allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriType.class); + for (UriType nextRef : allUris) { + if (nextRef instanceof IIdType) { + continue; // No substitution on the resource ID itself! + } + IdType nextUriString = new IdType(nextRef.getValueAsString()); + if (theIdSubstitutions.containsKey(nextUriString)) { + IdType newId = theIdSubstitutions.get(nextUriString); + ourLog.info(" * Replacing resource ref {} with {}", nextUriString, newId); + nextRef.setValue(newId.getValue()); + } else { + ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString); + } + } + IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); @@ -546,7 +563,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } for (IdType next : allIds) { - IdType replacement = idSubstitutions.get(next); + IdType replacement = theIdSubstitutions.get(next); if (replacement == null) { continue; } @@ -603,6 +620,23 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { return retVal; } + private String performIdSubstitutionsInMatchUrl(Map theIdSubstitutions, String theMatchUrl) { + String matchUrl = theMatchUrl; + if (isNotBlank(matchUrl)) { + for (Entry nextSubstitutionEntry : theIdSubstitutions.entrySet()) { + IdType nextTemporaryId = nextSubstitutionEntry.getKey(); + IdType nextReplacementId = nextSubstitutionEntry.getValue(); + String nextTemporaryIdPart = nextTemporaryId.getIdPart(); + String nextReplacementIdPart = nextReplacementId.getValueAsString(); + if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) { + matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart); + matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart); + } + } + } + return matchUrl; + } + private void populateEntryWithOperationOutcome(BaseServerResponseException caughtEx, BundleEntryComponent nextEntry) { OperationOutcome oo = new OperationOutcome(); oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage()); @@ -664,27 +698,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName); } - - - private String performIdSubstitutionsInMatchUrl(Map theIdSubstitutions, String theMatchUrl) { - String matchUrl = theMatchUrl; - if (isNotBlank(matchUrl)) { - for (Entry nextSubstitutionEntry : theIdSubstitutions.entrySet()) { - IdType nextTemporaryId = nextSubstitutionEntry.getKey(); - IdType nextReplacementId = nextSubstitutionEntry.getValue(); - String nextTemporaryIdPart = nextTemporaryId.getIdPart(); - String nextReplacementIdPart = nextReplacementId.getValueAsString(); - if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) { - matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart); - matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart); - } - } - } - return matchUrl; - } - - - private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) { super.markRequestAsProcessingSubRequest(theRequestDetails); try { @@ -742,23 +755,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode)); } - private static class BaseServerResponseExceptionHolder - { - private BaseServerResponseException myException; - - public BaseServerResponseException getException() { - return myException; - } - - public void setException(BaseServerResponseException myException) { - this.myException = myException; - } - } - - //@formatter:off /** * Transaction Order, per the spec: - * + * * Process any DELETE interactions * Process any POST interactions * Process any PUT interactions @@ -859,4 +858,19 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } + //@formatter:off + + private static class BaseServerResponseExceptionHolder + { + private BaseServerResponseException myException; + + public BaseServerResponseException getException() { + return myException; + } + + public void setException(BaseServerResponseException myException) { + this.myException = myException; + } + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 6b2ad8519f1..89698e98d53 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao.r4; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -144,7 +144,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } long delay = System.currentTimeMillis() - start; - ourLog.info("Batch completed in {}ms", new Object[]{delay}); + ourLog.info("Batch completed in {}ms", new Object[] {delay}); return resp; } @@ -169,9 +169,9 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { long start = System.currentTimeMillis(); final Date updateTime = new Date(); - final Set allIds = new LinkedHashSet(); - final Map idSubstitutions = new HashMap(); - final Map idToPersistedOutcome = new HashMap(); + final Set allIds = new LinkedHashSet<>(); + final Map idSubstitutions = new HashMap<>(); + final Map idToPersistedOutcome = new HashMap<>(); // Do all entries have a verb? for (int i = 0; i < theRequest.getEntry().size(); i++) { @@ -193,8 +193,8 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { * we want the GET processing to use the final indexing state */ final Bundle response = new Bundle(); - List getEntries = new ArrayList(); - final IdentityHashMap originalRequestOrder = new IdentityHashMap(); + List getEntries = new ArrayList<>(); + final IdentityHashMap originalRequestOrder = new IdentityHashMap<>(); for (int i = 0; i < theRequest.getEntry().size(); i++) { originalRequestOrder.put(theRequest.getEntry().get(i), i); response.addEntry(); @@ -265,7 +265,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { paramValues.put(next.getName(), next.getValue()); } for (java.util.Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) { - String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); + String[] nextValue = nextParamEntry.getValue().toArray(new String[ nextParamEntry.getValue().size() ]); requestDetails.addParameter(nextParamEntry.getKey(), nextValue); } url = url.substring(0, qIndex); @@ -309,20 +309,20 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } long delay = System.currentTimeMillis() - start; - ourLog.info(theActionName + " completed in {}ms", new Object[]{delay}); + ourLog.info(theActionName + " completed in {}ms", new Object[] {delay}); response.setType(BundleType.TRANSACTIONRESPONSE); return response; } @SuppressWarnings("unchecked") - private Map doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set allIds, - Map idSubstitutions, Map idToPersistedOutcome, Bundle response, IdentityHashMap originalRequestOrder, List theEntries) { - Set deletedResources = new HashSet(); - List deleteConflicts = new ArrayList(); - Map entriesToProcess = new IdentityHashMap(); - Set nonUpdatedEntities = new HashSet(); - Map> conditionalRequestUrls = new HashMap>(); + private Map doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set theAllIds, + Map theIdSubstitutions, Map theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap theOriginalRequestOrder, List theEntries) { + Set deletedResources = new HashSet<>(); + List deleteConflicts = new ArrayList<>(); + Map entriesToProcess = new IdentityHashMap<>(); + Set nonUpdatedEntities = new HashSet<>(); + Map> conditionalRequestUrls = new HashMap<>(); /* * Loop through the request and process any entries of type @@ -341,7 +341,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { nextResourceId = res.getIdElement(); - if (nextResourceId.hasIdPart() == false) { + if (!nextResourceId.hasIdPart()) { if (isNotBlank(nextReqEntry.getFullUrl())) { nextResourceId = new IdType(nextReqEntry.getFullUrl()); } @@ -360,12 +360,12 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness */ if (isPlaceholder(nextResourceId)) { - if (!allIds.add(nextResourceId)) { + if (!theAllIds.add(nextResourceId)) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId)); } } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) { IdType nextId = nextResourceId.toUnqualifiedVersionless(); - if (!allIds.add(nextId)) { + if (!theAllIds.add(nextId)) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId)); } } @@ -375,7 +375,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { HTTPVerb verb = nextReqEntry.getRequest().getMethodElement().getValue(); String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null; - BundleEntryComponent nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry)); + BundleEntryComponent nextRespEntry = theResponse.getEntry().get(theOriginalRequestOrder.get(nextReqEntry)); switch (verb) { case POST: { @@ -385,10 +385,10 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { res.setId((String) null); DaoMethodOutcome outcome; String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); - matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); if (nextResourceId != null) { - handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); } entriesToProcess.put(nextRespEntry, outcome.getEntity()); if (outcome.getCreated() == false) { @@ -418,7 +418,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } } else { String matchUrl = parts.getResourceType() + '?' + parts.getParams(); - matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails); List allDeleted = deleteOutcome.getDeletedEntities(); for (ResourceTable deleted : allDeleted) { @@ -459,14 +459,14 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } else { matchUrl = parts.getResourceType(); } - matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); outcome = resourceDao.update(res, matchUrl, false, theRequestDetails); if (Boolean.TRUE.equals(outcome.getCreated())) { conditionalRequestUrls.put(matchUrl, res.getClass()); } } - handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); entriesToProcess.put(nextRespEntry, outcome.getEntity()); break; } @@ -499,20 +499,21 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { */ FhirTerser terser = getContext().newTerser(); - for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) { + for (DaoMethodOutcome nextOutcome : theIdToPersistedOutcome.values()) { IBaseResource nextResource = nextOutcome.getResource(); if (nextResource == null) { continue; } + // References List allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class); for (IBaseReference nextRef : allRefs) { IIdType nextId = nextRef.getReferenceElement(); if (!nextId.hasIdPart()) { continue; } - if (idSubstitutions.containsKey(nextId)) { - IdType newId = idSubstitutions.get(nextId); + if (theIdSubstitutions.containsKey(nextId)) { + IdType newId = theIdSubstitutions.get(nextId); ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); nextRef.setReference(newId.getValue()); } else if (nextId.getValue().startsWith("urn:")) { @@ -522,11 +523,27 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } } + // URIs + List allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriType.class); + for (UriType nextRef : allUris) { + if (nextRef instanceof IIdType) { + continue; // No substitution on the resource ID itself! + } + IdType nextUriString = new IdType(nextRef.getValueAsString()); + if (theIdSubstitutions.containsKey(nextUriString)) { + IdType newId = theIdSubstitutions.get(nextUriString); + ourLog.info(" * Replacing resource ref {} with {}", nextUriString, newId); + nextRef.setValue(newId.getValue()); + } else { + ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString); + } + } + IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); if (shouldUpdate) { - updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, false, updateTime, false, true); + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, false, theUpdateTime, false, true); } } @@ -548,8 +565,8 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } } - for (IdType next : allIds) { - IdType replacement = idSubstitutions.get(next); + for (IdType next : theAllIds) { + IdType replacement = theIdSubstitutions.get(next); if (replacement == null) { continue; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index 2e5461c8697..2a39357dba2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -75,6 +75,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest { @Qualifier("myDiagnosticReportDaoDstu2") protected IFhirResourceDao myDiagnosticReportDao; @Autowired + @Qualifier("myBinaryDaoDstu2") + protected IFhirResourceDao myBinaryDao; + @Autowired @Qualifier("myEncounterDaoDstu2") protected IFhirResourceDao myEncounterDao; // @PersistenceContext() diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index 77c460d1031..cd1f4652783 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -1,37 +1,23 @@ package ca.uhn.fhir.jpa.dao.dstu2; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.AfterClass; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; - import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.entity.*; +import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test; -import ca.uhn.fhir.model.api.*; +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.*; +import ca.uhn.fhir.model.dstu2.composite.AttachmentDt; +import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import ca.uhn.fhir.model.dstu2.composite.MetaDt; +import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.resource.*; -import ca.uhn.fhir.model.dstu2.resource.Bundle; -import ca.uhn.fhir.model.dstu2.resource.Bundle.*; -import ca.uhn.fhir.model.dstu2.valueset.*; +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.EntryResponse; +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; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.api.Constants; @@ -40,101 +26,28 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.AfterClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class); - /** - * See #638 - */ - @Test - public void testTransactionBug638() throws Exception { - String input = loadClasspath("/bug638.xml"); - Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); - - Bundle resp = mySystemDao.transaction(mySrd, request); - - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); - - assertEquals(18, resp.getEntry().size()); - } - - @Test - public void testTransactionWhichFailsPersistsNothing() { - - // Run a transaction which points to that practitioner - // in a field that isn't allowed to refer to a practitioner - Bundle input = new Bundle(); - input.setType(BundleTypeEnum.TRANSACTION); - - Patient pt = new Patient(); - pt.setId("PT"); - pt.setActive(true); - pt.addName().addFamily("FAMILY"); - input.addEntry() - .setResource(pt) - .getRequest().setMethod(HTTPVerbEnum.PUT).setUrl("Patient/PT"); - - Observation obs = new Observation(); - obs.setId("OBS"); - obs.getCode().addCoding().setSystem("foo").setCode("bar"); - obs.addPerformer().setReference("Practicioner/AAAAA"); - input.addEntry() - .setResource(obs) - .getRequest().setMethod(HTTPVerbEnum.PUT).setUrl("Observation/OBS"); - - try { - mySystemDao.transaction(mySrd, input); - fail(); - } catch (UnprocessableEntityException e) { - assertThat(e.getMessage(), containsString("Resource type 'Practicioner' is not valid for this path")); - } - - assertThat(myResourceTableDao.findAll(), empty()); - assertThat(myResourceIndexedSearchParamStringDao.findAll(), empty()); - - } - - - /** - * Per a message on the mailing list - */ - @Test - public void testTransactionWithPostDoesntUpdate() throws Exception { - - // First bundle (name is Joshua) - - String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8); - Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); - Bundle response = mySystemDao.transaction(mySrd, request); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); - - assertEquals(1, response.getEntry().size()); - assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus()); - assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); - String id = response.getEntry().get(0).getResponse().getLocation(); - - // Now the second (name is Adam, shouldn't get used) - - input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8); - request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); - response = mySystemDao.transaction(mySrd, request); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); - - assertEquals(1, response.getEntry().size()); - assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus()); - assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); - String id2 = response.getEntry().get(0).getResponse().getLocation(); - assertEquals(id, id2); - - Patient patient = myPatientDao.read(new IdType(id), mySrd); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient)); - assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString()); - } - - - @Test public void testSystemMetaOperation() { @@ -223,7 +136,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertEquals("http://profile/2", profiles.get(0).getValue()); } - + @Test public void testTransactionBatchWithFailingRead() { String methodName = "testTransactionBatchWithFailingRead"; @@ -265,7 +178,21 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertThat(respEntry.getStatus(), startsWith("404")); } - + + /** + * See #638 + */ + @Test + public void testTransactionBug638() throws Exception { + String input = loadClasspath("/bug638.xml"); + Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + + Bundle resp = mySystemDao.transaction(mySrd, request); + + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + + assertEquals(18, resp.getEntry().size()); + } @Test public void testTransactionCreateMatchUrlWithOneMatch() { @@ -308,7 +235,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertEquals("1", o.getId().getVersionIdPart()); } - + /** * ?identifier= */ @@ -871,7 +798,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { Bundle resp = mySystemDao.transaction(mySrd, request); assertEquals(1, resp.getEntry().size()); assertEquals("404 Not Found", resp.getEntry().get(0).getResponse().getStatus()); - + // fail(); // } catch (ResourceNotFoundException e) { // assertThat(e.getMessage(), containsString("resource matching URL \"Patient?")); @@ -966,32 +893,32 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { @Test public void testTransactionOrdering() { String methodName = "testTransactionOrdering"; - + //@formatter:off /* * Transaction Order, per the spec: - * + * * Process any DELETE interactions * Process any POST interactions * Process any PUT interactions * Process any GET interactions - * - * This test creates a transaction bundle that includes + * + * This test creates a transaction bundle that includes * these four operations in the reverse order and verifies * that they are invoked correctly. */ //@formatter:off - + int pass = 0; IdDt patientPlaceholderId = IdDt.newRandomUuid(); - + Bundle req = testTransactionOrderingCreateBundle(methodName, pass, patientPlaceholderId); Bundle resp = mySystemDao.transaction(mySrd, req); testTransactionOrderingValidateResponse(pass, resp); - + pass = 1; patientPlaceholderId = IdDt.newRandomUuid(); - + req = testTransactionOrderingCreateBundle(methodName, pass, patientPlaceholderId); resp = mySystemDao.transaction(mySrd, req); testTransactionOrderingValidateResponse(pass, resp); @@ -1001,18 +928,18 @@ 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; } @@ -1037,11 +964,11 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } else { assertEquals("204 No Content", resp.getEntry().get(3).getResponse().getStatus()); } - - + + Bundle respGetBundle = (Bundle) resp.getEntry().get(0).getResource(); assertEquals(1, respGetBundle.getEntry().size()); - assertEquals("testTransactionOrdering" + pass, ((Patient)respGetBundle.getEntry().get(0).getResource()).getNameFirstRep().getFamilyFirstRep().getValue()); + assertEquals("testTransactionOrdering" + pass, ((Patient) respGetBundle.getEntry().get(0).getResource()).getNameFirstRep().getFamilyFirstRep().getValue()); assertThat(respGetBundle.getLink("self").getUrl(), endsWith("/Patient?identifier=testTransactionOrdering")); } @@ -1112,7 +1039,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { details = detailsCapt.getValue(); assertEquals("Patient", details.getResourceType()); - + } @Test @@ -1246,7 +1173,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { Bundle resp = mySystemDao.transaction(mySrd, request); assertEquals(2, resp.getEntry().size()); - + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); Entry nextEntry = resp.getEntry().get(0); @@ -1379,6 +1306,42 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } + @Test + public void testTransactionWhichFailsPersistsNothing() { + + // Run a transaction which points to that practitioner + // in a field that isn't allowed to refer to a practitioner + Bundle input = new Bundle(); + input.setType(BundleTypeEnum.TRANSACTION); + + Patient pt = new Patient(); + pt.setId("PT"); + pt.setActive(true); + pt.addName().addFamily("FAMILY"); + input.addEntry() + .setResource(pt) + .getRequest().setMethod(HTTPVerbEnum.PUT).setUrl("Patient/PT"); + + Observation obs = new Observation(); + obs.setId("OBS"); + obs.getCode().addCoding().setSystem("foo").setCode("bar"); + obs.addPerformer().setReference("Practicioner/AAAAA"); + input.addEntry() + .setResource(obs) + .getRequest().setMethod(HTTPVerbEnum.PUT).setUrl("Observation/OBS"); + + try { + mySystemDao.transaction(mySrd, input); + fail(); + } catch (UnprocessableEntityException e) { + assertThat(e.getMessage(), containsString("Resource type 'Practicioner' is not valid for this path")); + } + + assertThat(myResourceTableDao.findAll(), empty()); + assertThat(myResourceIndexedSearchParamStringDao.findAll(), empty()); + + } + /** * From a message from David Hay */ @@ -1389,72 +1352,72 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { 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\": \"
Investigate Angina<\\/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\": \"
Routine checkup<\\/div>
Dr Dave<\\/div><\\/div>\"\n" + - " }\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"POST\",\n" + - " \"url\": \"Appointment\"\n" + - " }\n" + - " }\n" + - " ]\n" + - "}"; + 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\": \"
Investigate Angina<\\/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\": \"
Routine checkup<\\/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)); @@ -1481,7 +1444,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { Patient p = new Patient(); p.addName().addFamily("family"); final IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - + Bundle inputBundle = new Bundle(); //@formatter:off @@ -1490,22 +1453,22 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { String placeholderId0 = IdDt.newRandomUuid().getValue(); inputBundle .addEntry() - .setResource(app0) - .setFullUrl(placeholderId0) - .getRequest() - .setMethod(HTTPVerbEnum.POST) - .setUrl("Patient"); + .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"); + .setResource(app1) + .getRequest() + .setMethod(HTTPVerbEnum.POST) + .setUrl("Appointment"); //@formatter:on //@formatter:off @@ -1514,20 +1477,20 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { app2.addParticipant().getActor().setDisplay("YES REF").setReference(placeholderId0); inputBundle .addEntry() - .setResource(app2) - .getRequest() - .setMethod(HTTPVerbEnum.POST) - .setUrl("Appointment"); + .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()); @@ -1535,6 +1498,42 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertEquals(id0.toUnqualifiedVersionless().getValue(), app2.getParticipant().get(1).getActor().getReference().getValue()); } + /** + * Per a message on the mailing list + */ + @Test + public void testTransactionWithPostDoesntUpdate() throws Exception { + + // First bundle (name is Joshua) + + String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8); + Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + Bundle response = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); + + assertEquals(1, response.getEntry().size()); + assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus()); + assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); + String id = response.getEntry().get(0).getResponse().getLocation(); + + // Now the second (name is Adam, shouldn't get used) + + input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8); + request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + response = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); + + assertEquals(1, response.getEntry().size()); + assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus()); + assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); + String id2 = response.getEntry().get(0).getResponse().getLocation(); + assertEquals(id, id2); + + Patient patient = myPatientDao.read(new IdType(id), mySrd); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient)); + assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString()); + } + @Test public void testTransactionWithReferenceToCreateIfNoneExist() { Bundle bundle = new Bundle(); @@ -1626,6 +1625,47 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } + /** + * 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).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient"); + + Observation o1 = new Observation(); + o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); + o1.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3")); + res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); + + Observation o2 = new Observation(); + o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); + o2.setSubject(new ResourceReferenceDt("Patient/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())); + + } + // // // /** @@ -1728,45 +1768,52 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { // // } - /** - * 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); + public void testTransactionWithReplacement() { + byte[] bytes = new byte[] {0, 1, 2, 3, 4}; - 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"); + Binary binary = new Binary(); + binary.setId(IdDt.newRandomUuid()); + binary.setContent(bytes); + binary.setContentType("application/pdf"); - Observation o1 = new Observation(); - o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); - o1.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3")); - res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); + DiagnosticReport dr = new DiagnosticReport(); + dr.setId(IdDt.newRandomUuid()); - Observation o2 = new Observation(); - o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); - o2.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3")); - res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); + AttachmentDt attachment = new AttachmentDt(); + attachment.setContentType("application/pdf"); + attachment.getUrlElement().setValueAsString(binary.getId().getValueAsString()); // this one has substitution + dr.addPresentedForm(attachment); - Bundle resp = mySystemDao.transaction(mySrd, res); + AttachmentDt attachment2 = new AttachmentDt(); + attachment2.getUrlElement().setValueAsString(IdDt.newRandomUuid().getValue()); // this one has no subscitution + dr.addPresentedForm(attachment2); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + Bundle transactionBundle = new Bundle(); + transactionBundle.setType(BundleTypeEnum.TRANSACTION); - assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum()); - assertEquals(3, resp.getEntry().size()); + Entry binaryEntry = new Bundle.Entry(); + binaryEntry.setResource(binary).setFullUrl(binary.getId()).getRequest().setUrl("Binary").setMethod(HTTPVerbEnum.POST); + transactionBundle.addEntry(binaryEntry); - 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]+$")); + Entry drEntry = new Entry(); + drEntry.setResource(dr).setFullUrl(dr.getId()).getRequest().setUrl("DiagnosticReport").setMethod(HTTPVerbEnum.POST); + transactionBundle.addEntry(drEntry); - 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())); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(transactionBundle)); + Bundle transactionResp = mySystemDao.transaction(mySrd, transactionBundle); + + assertEquals(2, transactionResp.getEntry().size()); + + // Validate Binary + binary = myBinaryDao.read(new IdType(transactionResp.getEntry().get(0).getResponse().getLocation())); + assertArrayEquals(bytes, binary.getContent()); + + // Validate DiagnosticReport + dr = myDiagnosticReportDao.read(new IdType(transactionResp.getEntry().get(1).getResponse().getLocation())); + assertEquals(binary.getIdElement().toUnqualifiedVersionless().getValue(), dr.getPresentedForm().get(0).getUrl()); + assertEquals(attachment2.getUrl(), dr.getPresentedForm().get(1).getUrl()); } @AfterClass diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index 949ba8b7b51..dc8bfeae1fb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -99,6 +99,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { @Qualifier("myDiagnosticReportDaoDstu3") protected IFhirResourceDao myDiagnosticReportDao; @Autowired + @Qualifier("myBinaryDaoDstu3") + protected IFhirResourceDao myBinaryDao; + @Autowired @Qualifier("myEncounterDaoDstu3") protected IFhirResourceDao myEncounterDao; // @PersistenceContext() diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index 35cbb43de6a..61b25430e87 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -2520,7 +2520,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString()); } - @Test public void testTransactionWithReferenceResource() { Bundle request = new Bundle(); @@ -2548,7 +2547,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals(1, found.size().intValue()); } - @Test public void testTransactionWithReferenceToCreateIfNoneExist() { Bundle bundle = new Bundle(); @@ -2602,6 +2600,33 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertNotEquals(medOrderId1, medOrderId2); } + @Test + public void testTransactionWithReferenceUuid() { + Bundle request = new Bundle(); + + Patient p = new Patient(); + p.setActive(true); + p.setId(IdType.newRandomUuid()); + request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId()); + + Observation o = new Observation(); + o.getCode().setText("Some Observation"); + o.getSubject().setReference(p.getId()); + request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST); + + Bundle resp = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + + String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue(); + assertThat(patientId, startsWith("Patient/")); + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("subject", new ReferenceParam(patientId)); + IBundleProvider found = myObservationDao.search(params); + assertEquals(1, found.size().intValue()); + } + // // // /** @@ -2704,34 +2729,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { // // } - - @Test - public void testTransactionWithReferenceUuid() { - Bundle request = new Bundle(); - - Patient p = new Patient(); - p.setActive(true); - p.setId(IdType.newRandomUuid()); - request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId()); - - Observation o = new Observation(); - o.getCode().setText("Some Observation"); - o.getSubject().setReference(p.getId()); - request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST); - - Bundle resp = mySystemDao.transaction(mySrd, request); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); - - String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue(); - assertThat(patientId, startsWith("Patient/")); - - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - params.add("subject", new ReferenceParam(patientId)); - IBundleProvider found = myObservationDao.search(params); - assertEquals(1, found.size().intValue()); - } - @Test public void testTransactionWithRelativeOidIds() throws Exception { Bundle res = new Bundle(); @@ -2811,6 +2808,52 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } + @Test + public void testTransactionWithReplacement() { + byte[] bytes = new byte[] {0, 1, 2, 3, 4}; + + Binary binary = new Binary(); + binary.setId(IdType.newRandomUuid()); + binary.setContent(bytes); + binary.setContentType("application/pdf"); + + DiagnosticReport dr = new DiagnosticReport(); + dr.setId(IdDt.newRandomUuid()); + + Attachment attachment = new Attachment(); + attachment.setContentType("application/pdf"); + attachment.setUrl(binary.getId()); // this one has substitution + dr.addPresentedForm(attachment); + + Attachment attachment2 = new Attachment(); + attachment2.setUrl(IdType.newRandomUuid().getValue()); // this one has no subscitution + dr.addPresentedForm(attachment2); + + Bundle transactionBundle = new Bundle(); + transactionBundle.setType(BundleType.TRANSACTION); + + Bundle.BundleEntryComponent binaryEntry = new Bundle.BundleEntryComponent(); + binaryEntry.setResource(binary).setFullUrl(binary.getId()).getRequest().setUrl("Binary").setMethod(Bundle.HTTPVerb.POST); + transactionBundle.addEntry(binaryEntry); + + Bundle.BundleEntryComponent drEntry = new Bundle.BundleEntryComponent(); + drEntry.setResource(dr).setFullUrl(dr.getId()).getRequest().setUrl("DiagnosticReport").setMethod(Bundle.HTTPVerb.POST); + transactionBundle.addEntry(drEntry); + + Bundle transactionResp = mySystemDao.transaction(mySrd, transactionBundle); + + assertEquals(2, transactionResp.getEntry().size()); + + // Validate Binary + binary = myBinaryDao.read(new IdType(transactionResp.getEntry().get(0).getResponse().getLocation())); + assertArrayEquals(bytes, binary.getContent()); + + // Validate DiagnosticReport + dr = myDiagnosticReportDao.read(new IdType(transactionResp.getEntry().get(1).getResponse().getLocation())); + assertEquals(binary.getIdElement().toUnqualifiedVersionless().getValue(), dr.getPresentedForm().get(0).getUrl()); + assertEquals(attachment2.getUrl(), dr.getPresentedForm().get(1).getUrl()); + } + /** * See #467 */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index f33bc8aa732..77105b8bc2f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -157,6 +157,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Autowired protected DatabaseBackedPagingProvider myPagingProvider; @Autowired + @Qualifier("myBinaryDaoR4") + protected IFhirResourceDao myBinaryDao; + @Autowired @Qualifier("myPatientDaoR4") protected IFhirResourceDaoPatient myPatientDao; @Autowired @@ -315,7 +318,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { linkNext = linkNext.substring(linkNext.indexOf('?')); Map params = UrlUtil.parseQueryString(linkNext); String[] uuidParams = params.get(Constants.PARAM_PAGINGACTION); - String uuid = uuidParams[0]; + String uuid = uuidParams[ 0 ]; return uuid; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index cc44d89cafc..65422c8b3ce 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -159,8 +159,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { for (BundleEntryComponent nextEntry : theBundle.getEntry()) { if (nextEntry.getResource() != null && theType.isAssignableFrom(nextEntry.getResource().getClass())) { if (count == theIndex) { - T t = (T) nextEntry.getResource(); - return t; + return (T) nextEntry.getResource(); } count++; } @@ -729,78 +728,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { } - @Test - public void testTransactionWithCircularReferences() { - Bundle request = new Bundle(); - request.setType(BundleType.TRANSACTION); - - Encounter enc = new Encounter(); - enc.addIdentifier().setSystem("A").setValue("1"); - enc.setId(IdType.newRandomUuid()); - - Condition cond = new Condition(); - cond.addIdentifier().setSystem("A").setValue("2"); - cond.setId(IdType.newRandomUuid()); - - enc.addDiagnosis().getCondition().setReference(cond.getId()); - cond.getContext().setReference(enc.getId()); - - request - .addEntry() - .setFullUrl(enc.getId()) - .setResource(enc) - .getRequest() - .setMethod(HTTPVerb.PUT) - .setUrl("Encounter?identifier=A|1"); - request - .addEntry() - .setFullUrl(cond.getId()) - .setResource(cond) - .getRequest() - .setMethod(HTTPVerb.PUT) - .setUrl("Condition?identifier=A|2"); - - Bundle resp = mySystemDao.transaction(mySrd, request); - assertEquals(2, resp.getEntry().size()); - - BundleEntryComponent respEntry = resp.getEntry().get(0); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - - respEntry = resp.getEntry().get(1); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - - } - - @Test - public void testTransactionWithCircularReferences2() throws IOException { - Bundle request = loadResourceFromClasspath(Bundle.class, "/dstu3_transaction.xml"); - - Bundle resp = mySystemDao.transaction(mySrd, request); - assertEquals(3, resp.getEntry().size()); - - BundleEntryComponent respEntry = resp.getEntry().get(0); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - - respEntry = resp.getEntry().get(1); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - - } - - @Test - public void testTransactionWithCircularReferences3() throws IOException { - Bundle request = loadResourceFromClasspath(Bundle.class, "/dstu3_transaction2.xml"); - - Bundle resp = mySystemDao.transaction(mySrd, request); - assertEquals(3, resp.getEntry().size()); - - BundleEntryComponent respEntry = resp.getEntry().get(0); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - - respEntry = resp.getEntry().get(1); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - - } - @Test public void testTransactionCreateInlineMatchUrlWithOneMatchLastUpdated() { Bundle request = new Bundle(); @@ -2260,6 +2187,78 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { } + @Test + public void testTransactionWithCircularReferences() { + Bundle request = new Bundle(); + request.setType(BundleType.TRANSACTION); + + Encounter enc = new Encounter(); + enc.addIdentifier().setSystem("A").setValue("1"); + enc.setId(IdType.newRandomUuid()); + + Condition cond = new Condition(); + cond.addIdentifier().setSystem("A").setValue("2"); + cond.setId(IdType.newRandomUuid()); + + enc.addDiagnosis().getCondition().setReference(cond.getId()); + cond.getContext().setReference(enc.getId()); + + request + .addEntry() + .setFullUrl(enc.getId()) + .setResource(enc) + .getRequest() + .setMethod(HTTPVerb.PUT) + .setUrl("Encounter?identifier=A|1"); + request + .addEntry() + .setFullUrl(cond.getId()) + .setResource(cond) + .getRequest() + .setMethod(HTTPVerb.PUT) + .setUrl("Condition?identifier=A|2"); + + Bundle resp = mySystemDao.transaction(mySrd, request); + assertEquals(2, resp.getEntry().size()); + + BundleEntryComponent respEntry = resp.getEntry().get(0); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + + respEntry = resp.getEntry().get(1); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + + } + + @Test + public void testTransactionWithCircularReferences2() throws IOException { + Bundle request = loadResourceFromClasspath(Bundle.class, "/dstu3_transaction.xml"); + + Bundle resp = mySystemDao.transaction(mySrd, request); + assertEquals(3, resp.getEntry().size()); + + BundleEntryComponent respEntry = resp.getEntry().get(0); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + + respEntry = resp.getEntry().get(1); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + + } + + @Test + public void testTransactionWithCircularReferences3() throws IOException { + Bundle request = loadResourceFromClasspath(Bundle.class, "/dstu3_transaction2.xml"); + + Bundle resp = mySystemDao.transaction(mySrd, request); + assertEquals(3, resp.getEntry().size()); + + BundleEntryComponent respEntry = resp.getEntry().get(0); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + + respEntry = resp.getEntry().get(1); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + + } + @Test public void testTransactionWithIfMatch() { Patient p = new Patient(); @@ -2719,6 +2718,44 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { assertEquals(1, found.size().intValue()); } + @Test + public void testTransactionWithRelativeOidIds() throws Exception { + Bundle res = new Bundle(); + res.setType(BundleType.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(HTTPVerb.POST).setUrl("Patient"); + + Observation o1 = new Observation(); + o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); + o1.setSubject(new Reference("urn:oid:0.1.2.3")); + res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); + + Observation o2 = new Observation(); + o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); + o2.setSubject(new Reference("urn:oid:0.1.2.3")); + res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); + + Bundle resp = mySystemDao.transaction(mySrd, res); + + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + + assertEquals(BundleType.TRANSACTIONRESPONSE, resp.getTypeElement().getValue()); + assertEquals(3, resp.getEntry().size()); + + assertTrue(resp.getEntry().get(0).getResponse().getLocation(), new IdType(resp.getEntry().get(0).getResponse().getLocation()).getIdPart().matches("^[0-9]+$")); + assertTrue(resp.getEntry().get(1).getResponse().getLocation(), new IdType(resp.getEntry().get(1).getResponse().getLocation()).getIdPart().matches("^[0-9]+$")); + assertTrue(resp.getEntry().get(2).getResponse().getLocation(), new IdType(resp.getEntry().get(2).getResponse().getLocation()).getIdPart().matches("^[0-9]+$")); + + o1 = myObservationDao.read(new IdType(resp.getEntry().get(1).getResponse().getLocation()), mySrd); + o2 = myObservationDao.read(new IdType(resp.getEntry().get(2).getResponse().getLocation()), mySrd); + assertThat(o1.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart())); + assertThat(o2.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart())); + + } + // // // /** @@ -2821,44 +2858,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { // // } - @Test - public void testTransactionWithRelativeOidIds() throws Exception { - Bundle res = new Bundle(); - res.setType(BundleType.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(HTTPVerb.POST).setUrl("Patient"); - - Observation o1 = new Observation(); - o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); - o1.setSubject(new Reference("urn:oid:0.1.2.3")); - res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); - - Observation o2 = new Observation(); - o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); - o2.setSubject(new Reference("urn:oid:0.1.2.3")); - res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); - - Bundle resp = mySystemDao.transaction(mySrd, res); - - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); - - assertEquals(BundleType.TRANSACTIONRESPONSE, resp.getTypeElement().getValue()); - assertEquals(3, resp.getEntry().size()); - - assertTrue(resp.getEntry().get(0).getResponse().getLocation(), new IdType(resp.getEntry().get(0).getResponse().getLocation()).getIdPart().matches("^[0-9]+$")); - assertTrue(resp.getEntry().get(1).getResponse().getLocation(), new IdType(resp.getEntry().get(1).getResponse().getLocation()).getIdPart().matches("^[0-9]+$")); - assertTrue(resp.getEntry().get(2).getResponse().getLocation(), new IdType(resp.getEntry().get(2).getResponse().getLocation()).getIdPart().matches("^[0-9]+$")); - - o1 = myObservationDao.read(new IdType(resp.getEntry().get(1).getResponse().getLocation()), mySrd); - o2 = myObservationDao.read(new IdType(resp.getEntry().get(2).getResponse().getLocation()), mySrd); - assertThat(o1.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart())); - assertThat(o2.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart())); - - } - /** * This is not the correct way to do it, but we'll allow it to be lenient */ @@ -2900,6 +2899,52 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { } + @Test + public void testTransactionWithReplacement() { + byte[] bytes = new byte[] {0, 1, 2, 3, 4}; + + Binary binary = new Binary(); + binary.setId(IdType.newRandomUuid()); + binary.setContent(bytes); + binary.setContentType("application/pdf"); + + DiagnosticReport dr = new DiagnosticReport(); + dr.setId(IdDt.newRandomUuid()); + + Attachment attachment = new Attachment(); + attachment.setContentType("application/pdf"); + attachment.setUrl(binary.getId()); // this one has substitution + dr.addPresentedForm(attachment); + + Attachment attachment2 = new Attachment(); + attachment2.setUrl(IdType.newRandomUuid().getValue()); // this one has no subscitution + dr.addPresentedForm(attachment2); + + Bundle transactionBundle = new Bundle(); + transactionBundle.setType(BundleType.TRANSACTION); + + Bundle.BundleEntryComponent binaryEntry = new Bundle.BundleEntryComponent(); + binaryEntry.setResource(binary).setFullUrl(binary.getId()).getRequest().setUrl("Binary").setMethod(Bundle.HTTPVerb.POST); + transactionBundle.addEntry(binaryEntry); + + Bundle.BundleEntryComponent drEntry = new Bundle.BundleEntryComponent(); + drEntry.setResource(dr).setFullUrl(dr.getId()).getRequest().setUrl("DiagnosticReport").setMethod(Bundle.HTTPVerb.POST); + transactionBundle.addEntry(drEntry); + + Bundle transactionResp = mySystemDao.transaction(mySrd, transactionBundle); + + assertEquals(2, transactionResp.getEntry().size()); + + // Validate Binary + binary = myBinaryDao.read(new IdType(transactionResp.getEntry().get(0).getResponse().getLocation())); + assertArrayEquals(bytes, binary.getContent()); + + // Validate DiagnosticReport + dr = myDiagnosticReportDao.read(new IdType(transactionResp.getEntry().get(1).getResponse().getLocation())); + assertEquals(binary.getIdElement().toUnqualifiedVersionless().getValue(), dr.getPresentedForm().get(0).getUrl()); + assertEquals(attachment2.getUrl(), dr.getPresentedForm().get(1).getUrl()); + } + /** * See #467 */ diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 797bac874a6..c09699b15df 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -144,6 +144,12 @@ Avoid an endless loop of reindexing in JPA if a SearchParameter is created which indexed the SearchParameter resource itself + + JPA server now performs temporary/placeholder ID substitution processing on elements in + resources which are of type "URI" in addition to the current substitution for + elements of type "Reference". Thanks to GitHub user @t4deon for supplying + a testcase! +