From b51c7227559fd222040e3ea0dd0038ef53a20ffd Mon Sep 17 00:00:00 2001 From: leif stawnyczy Date: Wed, 25 Aug 2021 10:41:58 -0400 Subject: [PATCH] stashing minor refactors --- .../java/ca/uhn/fhir/util/FhirTerser.java | 1 + .../jpa/dao/BaseTransactionProcessor.java | 368 +++++++++++------- .../ca/uhn/fhir/jpa/dao/r4/AAAATests.java | 83 ++++ ...irResourceDaoR4VersionedReferenceTest.java | 15 +- .../uhn/fhir/rest/server/mail/MailConfig.java | 20 + .../ca/uhn/fhir/rest/server/mail/MailSvc.java | 20 + .../model/dstu2/composite/NarrativeDt.java | 16 - 7 files changed, 357 insertions(+), 166 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index 8cc1c9be1de..b0dca947658 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -961,6 +961,7 @@ public class FhirTerser { for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) { List values = nextChild.getAccessor().getValues(theElement); + if (values != null) { for (Object nextValueObject : values) { IBase nextValue; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index f599b5bc435..3e30960b590 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -240,8 +240,10 @@ public abstract class BaseTransactionProcessor { myVersionAdapter.populateEntryWithOperationOutcome(caughtEx, nextEntry); } - private void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, IIdType nextResourceId, DaoMethodOutcome outcome, - IBase newEntry, String theResourceType, IBaseResource theRes, RequestDetails theRequestDetails) { + private void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, + IIdType nextResourceId, DaoMethodOutcome outcome, + IBase newEntry, String theResourceType, + IBaseResource theRes, RequestDetails theRequestDetails) { IIdType newId = outcome.getId().toUnqualified(); IIdType resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless(); if (newId.equals(resourceId) == false) { @@ -652,8 +654,129 @@ public abstract class BaseTransactionProcessor { myModelConfig = theModelConfig; } - protected Map doTransactionWriteOperations(final RequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set theAllIds, - Map theIdSubstitutions, Map theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap theOriginalRequestOrder, List theEntries, StopWatch theTransactionStopWatch) { + /** + * Searches for duplicate conditional creates and consolidates them. + * + * @param theEntries + */ + private void consolidateDuplicateConditionalCreates(List theEntries) { + final HashMap keyToUuid = new HashMap<>(); + for (int index = 0, originalIndex = 0; index < theEntries.size(); index++, originalIndex++) { + IBase nextReqEntry = theEntries.get(index); + IBaseResource resource = myVersionAdapter.getResource(nextReqEntry); + if (resource != null) { + String verb = myVersionAdapter.getEntryRequestVerb(myContext, 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)) { + 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 (IBase nextEntry : theEntries) { + IBaseResource nextResource = myVersionAdapter.getResource(nextEntry); + for (IBaseReference nextReference : myContext.newTerser().getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class)) { + // We're interested in any references directly to the placeholder ID, but also + // references that have a resource target that has the placeholder ID. + String nextReferenceId = nextReference.getReferenceElement().getValue(); + if (isBlank(nextReferenceId) && nextReference.getResource() != null) { + nextReferenceId = nextReference.getResource().getIdElement().getValue(); + } + if (entryUrl.equals(nextReferenceId)) { + nextReference.setReference(existingUuid); + nextReference.setResource(null); + } + } + } + } + } + } + } + } + + /** + * Retrieves teh next resource id (IIdType) from the base resource and next request entry. + * @param theBaseResource - base resource + * @param theNextReqEntry - next request entry + * @param theAllIds - set of all IIdType values + * @return + */ + private IIdType getNextResourceIdFromBaseResource(IBaseResource theBaseResource, + IBase theNextReqEntry, + Set theAllIds) { + IIdType nextResourceId = null; + if (theBaseResource != null) { + nextResourceId = theBaseResource.getIdElement(); + + String fullUrl = myVersionAdapter.getFullUrl(theNextReqEntry); + if (isNotBlank(fullUrl)) { + IIdType fullUrlIdType = newIdType(fullUrl); + if (isPlaceholder(fullUrlIdType)) { + nextResourceId = fullUrlIdType; + } else if (!nextResourceId.hasIdPart()) { + nextResourceId = fullUrlIdType; + } + } + + if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+:.*") && !isPlaceholder(nextResourceId)) { + throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'"); + } + + if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) { + nextResourceId = newIdType(toResourceName(theBaseResource.getClass()), nextResourceId.getIdPart()); + theBaseResource.setId(nextResourceId); + } + + /* + * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness + */ + if (isPlaceholder(nextResourceId)) { + if (!theAllIds.add(nextResourceId)) { + throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId)); + } + } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) { + IIdType nextId = nextResourceId.toUnqualifiedVersionless(); + if (!theAllIds.add(nextId)) { + throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId)); + } + } + + } + + return nextResourceId; + } + + protected Map doTransactionWriteOperations(final RequestDetails theRequest, String theActionName, + TransactionDetails theTransactionDetails, Set theAllIds, + Map theIdSubstitutions, Map theIdToPersistedOutcome, + IBaseBundle theResponse, IdentityHashMap theOriginalRequestOrder, + List theEntries, StopWatch theTransactionStopWatch) { theTransactionDetails.beginAcceptingDeferredInterceptorBroadcasts( Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, @@ -661,7 +784,6 @@ public abstract class BaseTransactionProcessor { Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED ); try { - Set deletedResources = new HashSet<>(); DeleteConflictList deleteConflicts = new DeleteConflictList(); Map entriesToProcess = new IdentityHashMap<>(); @@ -673,117 +795,20 @@ public abstract class BaseTransactionProcessor { /* * Look for duplicate conditional creates and consolidate them */ - final HashMap keyToUuid = new HashMap<>(); - for (int index = 0, originalIndex = 0; index < theEntries.size(); index++, originalIndex++) { - IBase nextReqEntry = theEntries.get(index); - IBaseResource resource = myVersionAdapter.getResource(nextReqEntry); - if (resource != null) { - String verb = myVersionAdapter.getEntryRequestVerb(myContext, 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)) { - 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 (IBase nextEntry : theEntries) { - IBaseResource nextResource = myVersionAdapter.getResource(nextEntry); - for (IBaseReference nextReference : myContext.newTerser().getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class)) { - // We're interested in any references directly to the placeholder ID, but also - // references that have a resource target that has the placeholder ID. - String nextReferenceId = nextReference.getReferenceElement().getValue(); - if (isBlank(nextReferenceId) && nextReference.getResource() != null) { - nextReferenceId = nextReference.getResource().getIdElement().getValue(); - } - if (entryUrl.equals(nextReferenceId)) { - nextReference.setReference(existingUuid); - nextReference.setResource(null); - } - } - } - } - } - } - } - + consolidateDuplicateConditionalCreates(theEntries); /* * Loop through the request and process any entries of type * PUT, POST or DELETE */ for (int i = 0; i < theEntries.size(); i++) { - if (i % 250 == 0) { ourLog.debug("Processed {} non-GET entries out of {} in transaction", i, theEntries.size()); } IBase nextReqEntry = theEntries.get(i); IBaseResource res = myVersionAdapter.getResource(nextReqEntry); - IIdType nextResourceId = null; - if (res != null) { - - nextResourceId = res.getIdElement(); - - String fullUrl = myVersionAdapter.getFullUrl(nextReqEntry); - if (isNotBlank(fullUrl)) { - IIdType fullUrlIdType = newIdType(fullUrl); - if (isPlaceholder(fullUrlIdType)) { - nextResourceId = fullUrlIdType; - } else if (!nextResourceId.hasIdPart()) { - nextResourceId = fullUrlIdType; - } - } - - if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+:.*") && !isPlaceholder(nextResourceId)) { - throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'"); - } - - if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) { - nextResourceId = newIdType(toResourceName(res.getClass()), nextResourceId.getIdPart()); - res.setId(nextResourceId); - } - - /* - * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness - */ - if (isPlaceholder(nextResourceId)) { - if (!theAllIds.add(nextResourceId)) { - throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId)); - } - } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) { - IIdType nextId = nextResourceId.toUnqualifiedVersionless(); - if (!theAllIds.add(nextId)) { - throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId)); - } - } - - } + IIdType nextResourceId = getNextResourceIdFromBaseResource(res, nextReqEntry, theAllIds); String verb = myVersionAdapter.getEntryRequestVerb(myContext, nextReqEntry); String resourceType = res != null ? myContext.getResourceType(res) : null; @@ -891,7 +916,8 @@ public abstract class BaseTransactionProcessor { } } - handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequest); + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, + outcome, nextRespEntry, resourceType, res, theRequest); entriesToProcess.put(nextRespEntry, outcome.getId()); break; } @@ -958,52 +984,24 @@ public abstract class BaseTransactionProcessor { * was also deleted as a part of this transaction, which is why we check this now at the * end. */ - for (Iterator iter = deleteConflicts.iterator(); iter.hasNext(); ) { - DeleteConflict nextDeleteConflict = iter.next(); + checkForDeleteConflicts(deleteConflicts, deletedResources, updatedResources); - /* - * If we have a conflict, it means we can't delete Resource/A because - * Resource/B has a reference to it. We'll ignore that conflict though - * if it turns out we're also deleting Resource/B in this transaction. - */ - if (deletedResources.contains(nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue())) { - iter.remove(); - continue; - } - - /* - * And then, this is kind of a last ditch check. It's also ok to delete - * Resource/A if Resource/B isn't being deleted, but it is being UPDATED - * in this transaction, and the updated version of it has no references - * to Resource/A any more. - */ - String sourceId = nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue(); - String targetId = nextDeleteConflict.getTargetId().toUnqualifiedVersionless().getValue(); - Optional updatedSource = updatedResources - .stream() - .filter(t -> sourceId.equals(t.getIdElement().toUnqualifiedVersionless().getValue())) - .findFirst(); - if (updatedSource.isPresent()) { - List referencesInSource = myContext.newTerser().getAllResourceReferences(updatedSource.get()); - boolean sourceStillReferencesTarget = referencesInSource - .stream() - .anyMatch(t -> targetId.equals(t.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue())); - if (!sourceStillReferencesTarget) { - iter.remove(); - } - } - } - DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(myContext, deleteConflicts); - - theIdToPersistedOutcome.entrySet().forEach(t -> theTransactionDetails.addResolvedResourceId(t.getKey(), t.getValue().getPersistentId())); + theIdToPersistedOutcome.entrySet().forEach(idAndOutcome -> { + theTransactionDetails.addResolvedResourceId(idAndOutcome.getKey(), idAndOutcome.getValue().getPersistentId()); + }); /* * Perform ID substitutions and then index each resource we have saved */ - resolveReferencesThenSaveAndIndexResources(theRequest, theTransactionDetails, theIdSubstitutions, theIdToPersistedOutcome, theTransactionStopWatch, entriesToProcess, nonUpdatedEntities, updatedEntities); + resolveReferencesThenSaveAndIndexResources(theRequest, theTransactionDetails, + theIdSubstitutions, theIdToPersistedOutcome, + theTransactionStopWatch, entriesToProcess, + nonUpdatedEntities, updatedEntities); theTransactionStopWatch.endCurrentTask(); + + // flush writes to db theTransactionStopWatch.startTask("Flush writes to database"); flushSession(theIdToPersistedOutcome); @@ -1061,6 +1059,53 @@ public abstract class BaseTransactionProcessor { } } + /** + * Checks for any delete conflicts. + * @param theDeleteConflicts - set of delete conflicts + * @param theDeletedResources - set of deleted resources + * @param theUpdatedResources - list of updated resources + */ + private void checkForDeleteConflicts(DeleteConflictList theDeleteConflicts, + Set theDeletedResources, + List theUpdatedResources) { + for (Iterator iter = theDeleteConflicts.iterator(); iter.hasNext(); ) { + DeleteConflict nextDeleteConflict = iter.next(); + + /* + * If we have a conflict, it means we can't delete Resource/A because + * Resource/B has a reference to it. We'll ignore that conflict though + * if it turns out we're also deleting Resource/B in this transaction. + */ + if (theDeletedResources.contains(nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue())) { + iter.remove(); + continue; + } + + /* + * And then, this is kind of a last ditch check. It's also ok to delete + * Resource/A if Resource/B isn't being deleted, but it is being UPDATED + * in this transaction, and the updated version of it has no references + * to Resource/A any more. + */ + String sourceId = nextDeleteConflict.getSourceId().toUnqualifiedVersionless().getValue(); + String targetId = nextDeleteConflict.getTargetId().toUnqualifiedVersionless().getValue(); + Optional updatedSource = theUpdatedResources + .stream() + .filter(t -> sourceId.equals(t.getIdElement().toUnqualifiedVersionless().getValue())) + .findFirst(); + if (updatedSource.isPresent()) { + List referencesInSource = myContext.newTerser().getAllResourceReferences(updatedSource.get()); + boolean sourceStillReferencesTarget = referencesInSource + .stream() + .anyMatch(t -> targetId.equals(t.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue())); + if (!sourceStillReferencesTarget) { + iter.remove(); + } + } + } + DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(myContext, theDeleteConflicts); + } + /** * This method replaces any placeholder references in the * source transaction Bundle with their actual targets, then stores the resource contents and indexes @@ -1083,7 +1128,10 @@ public abstract class BaseTransactionProcessor { * pass because it's too complex to try and insert the auto-versioned references and still * account for NOPs, so we block NOPs in that pass. */ - private void resolveReferencesThenSaveAndIndexResources(RequestDetails theRequest, TransactionDetails theTransactionDetails, Map theIdSubstitutions, Map theIdToPersistedOutcome, StopWatch theTransactionStopWatch, Map entriesToProcess, Set nonUpdatedEntities, Set updatedEntities) { + private void resolveReferencesThenSaveAndIndexResources(RequestDetails theRequest, TransactionDetails theTransactionDetails, + Map theIdSubstitutions, Map theIdToPersistedOutcome, + StopWatch theTransactionStopWatch, Map entriesToProcess, + Set nonUpdatedEntities, Set updatedEntities) { FhirTerser terser = myContext.newTerser(); theTransactionStopWatch.startTask("Index " + theIdToPersistedOutcome.size() + " resources"); IdentityHashMap> deferredIndexesForAutoVersioning = null; @@ -1105,14 +1153,28 @@ public abstract class BaseTransactionProcessor { Set referencesToAutoVersion = BaseStorageDao.extractReferencesToAutoVersion(myContext, myModelConfig, nextResource); if (referencesToAutoVersion.isEmpty()) { - resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails, theIdSubstitutions, theIdToPersistedOutcome, entriesToProcess, nonUpdatedEntities, updatedEntities, terser, nextOutcome, nextResource, referencesToAutoVersion); + // no references to autoversion - we can do the resolve and save now + resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails, + theIdSubstitutions, theIdToPersistedOutcome, + entriesToProcess, nonUpdatedEntities, + updatedEntities, terser, + nextOutcome, nextResource, + referencesToAutoVersion); // this is empty } else { if (deferredIndexesForAutoVersioning == null) { deferredIndexesForAutoVersioning = new IdentityHashMap<>(); } deferredIndexesForAutoVersioning.put(nextOutcome, referencesToAutoVersion); - } + // TODO - add the references to the + // idsToPersistedOutcomes +// for (IBaseReference autoVersion: referencesToAutoVersion) { +// IBaseResource resource = myVersionAdapter.getResource(autoVersion); +// IFhirResourceDao dao = getDaoOrThrowException(resource.getClass()); +// +// } +// theIdToPersistedOutcome.put() + } } // If we have any resources we'll be auto-versioning, index these next @@ -1121,12 +1183,22 @@ public abstract class BaseTransactionProcessor { DaoMethodOutcome nextOutcome = nextEntry.getKey(); Set referencesToAutoVersion = nextEntry.getValue(); IBaseResource nextResource = nextOutcome.getResource(); - resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails, theIdSubstitutions, theIdToPersistedOutcome, entriesToProcess, nonUpdatedEntities, updatedEntities, terser, nextOutcome, nextResource, referencesToAutoVersion); + resolveReferencesThenSaveAndIndexResource(theRequest, theTransactionDetails, + theIdSubstitutions, theIdToPersistedOutcome, + entriesToProcess, nonUpdatedEntities, + updatedEntities, terser, + nextOutcome, nextResource, + referencesToAutoVersion); } } } - private void resolveReferencesThenSaveAndIndexResource(RequestDetails theRequest, TransactionDetails theTransactionDetails, Map theIdSubstitutions, Map theIdToPersistedOutcome, Map entriesToProcess, Set nonUpdatedEntities, Set updatedEntities, FhirTerser terser, DaoMethodOutcome nextOutcome, IBaseResource nextResource, Set theReferencesToAutoVersion) { + private void resolveReferencesThenSaveAndIndexResource(RequestDetails theRequest, TransactionDetails theTransactionDetails, + Map theIdSubstitutions, Map theIdToPersistedOutcome, + Map entriesToProcess, Set nonUpdatedEntities, + Set updatedEntities, FhirTerser terser, + DaoMethodOutcome nextOutcome, IBaseResource nextResource, + Set theReferencesToAutoVersion) { // References List allRefs = terser.getAllResourceReferences(nextResource); for (ResourceReferenceInfo nextRef : allRefs) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java new file mode 100644 index 00000000000..8f92f333392 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/AAAATests.java @@ -0,0 +1,83 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.ParserOptions; +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.model.entity.ModelConfig; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; +import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.util.BundleBuilder; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Condition; +import org.hl7.fhir.r4.model.Encounter; +import org.hl7.fhir.r4.model.ExplanationOfBenefit; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.matchesPattern; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AAAATests extends BaseJpaR4Test { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4VersionedReferenceTest.class); + + @AfterEach + public void afterEach() { + myFhirCtx.getParserOptions().setStripVersionsFromReferences(true); + myFhirCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths().clear(); + myDaoConfig.setDeleteEnabled(new DaoConfig().isDeleteEnabled()); + myModelConfig.setRespectVersionsForSearchIncludes(new ModelConfig().isRespectVersionsForSearchIncludes()); + myModelConfig.setAutoVersionReferenceAtPaths(new ModelConfig().getAutoVersionReferenceAtPaths()); + } + + + @Test + @DisplayName("GH-2901 Test no NPE is thrown on autoversioned references") + public void testNoNpeMinimal() { + myDaoConfig.setAutoCreatePlaceholderReferenceTargets(false); + myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject"); + +// ParserOptions options = new ParserOptions(); +// options.setDontStripVersionsFromReferencesAtPaths("Observation.subject"); +// myFhirCtx.setParserOptions(options); + + Patient patient = new Patient(); + patient.setId("Patient/RED"); + myPatientDao.update(patient); + + Observation obs = new Observation(); + obs.setId("Observation/DEF"); + obs.setSubject(new Reference("Patient/RED")); + BundleBuilder builder = new BundleBuilder(myFhirCtx); + builder.addTransactionUpdateEntry(obs); + + mySystemDao.transaction(new SystemRequestDetails(), (Bundle) builder.getBundle()); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java index f2c661b4522..73063fe38cb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.ParserOptions; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.model.entity.ModelConfig; @@ -802,7 +803,9 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test { ); myModelConfig.setAutoVersionReferenceAtPaths(new HashSet(strings)); - Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, new InputStreamReader(FhirResourceDaoR4VersionedReferenceTest.class.getResourceAsStream("/npe-causing-bundle.json"))); + Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, + new InputStreamReader( + FhirResourceDaoR4VersionedReferenceTest.class.getResourceAsStream("/npe-causing-bundle.json"))); Bundle transaction = mySystemDao.transaction(new SystemRequestDetails(), bundle); } @@ -834,9 +837,17 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test { @Test @DisplayName("GH-2901 Test no NPE is thrown on autoversioned references") public void testNoNpeMinimal() { - myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true); + myDaoConfig.setAutoCreatePlaceholderReferenceTargets(false); myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject"); +// ParserOptions options = new ParserOptions(); +// options.setDontStripVersionsFromReferencesAtPaths("Observation.subject"); +// myFhirCtx.setParserOptions(options); + + Patient patient = new Patient(); + patient.setId("Patient/RED"); + myPatientDao.update(patient); + Observation obs = new Observation(); obs.setId("Observation/DEF"); obs.setSubject(new Reference("Patient/RED")); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java index 02bb16f9fdc..2202f27b77e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailConfig.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.rest.server.mail; +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java index 5c87c4256dd..df1cd4e9dca 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.rest.server.mail; +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import org.apache.commons.lang3.Validate; import org.simplejavamail.MailException; import org.simplejavamail.api.email.Email; diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java index ef74811416d..085008a62b1 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java @@ -1,19 +1,3 @@ - - - - - - - - - - - - - - - - package ca.uhn.fhir.model.dstu2.composite; /*