stashing minor refactors

This commit is contained in:
leif stawnyczy 2021-08-25 10:41:58 -04:00
parent ee9c630228
commit b51c722755
7 changed files with 357 additions and 166 deletions

View File

@ -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;

View File

@ -240,8 +240,10 @@ public abstract class BaseTransactionProcessor {
myVersionAdapter.populateEntryWithOperationOutcome(caughtEx, nextEntry);
}
private void handleTransactionCreateOrUpdateOutcome(Map<IIdType, IIdType> idSubstitutions, Map<IIdType, DaoMethodOutcome> idToPersistedOutcome, IIdType nextResourceId, DaoMethodOutcome outcome,
IBase newEntry, String theResourceType, IBaseResource theRes, RequestDetails theRequestDetails) {
private void handleTransactionCreateOrUpdateOutcome(Map<IIdType, IIdType> idSubstitutions, Map<IIdType, DaoMethodOutcome> 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<IBase, IIdType> doTransactionWriteOperations(final RequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set<IIdType> theAllIds,
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap<IBase, Integer> theOriginalRequestOrder, List<IBase> theEntries, StopWatch theTransactionStopWatch) {
/**
* Searches for duplicate conditional creates and consolidates them.
*
* @param theEntries
*/
private void consolidateDuplicateConditionalCreates(List<IBase> theEntries) {
final HashMap<String, String> 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<IIdType> 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<IBase, IIdType> doTransactionWriteOperations(final RequestDetails theRequest, String theActionName,
TransactionDetails theTransactionDetails, Set<IIdType> theAllIds,
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome,
IBaseBundle theResponse, IdentityHashMap<IBase, Integer> theOriginalRequestOrder,
List<IBase> 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<String> deletedResources = new HashSet<>();
DeleteConflictList deleteConflicts = new DeleteConflictList();
Map<IBase, IIdType> entriesToProcess = new IdentityHashMap<>();
@ -673,117 +795,20 @@ public abstract class BaseTransactionProcessor {
/*
* Look for duplicate conditional creates and consolidate them
*/
final HashMap<String, String> 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<DeleteConflict> 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<IBaseResource> updatedSource = updatedResources
.stream()
.filter(t -> sourceId.equals(t.getIdElement().toUnqualifiedVersionless().getValue()))
.findFirst();
if (updatedSource.isPresent()) {
List<ResourceReferenceInfo> 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<String> theDeletedResources,
List<IBaseResource> theUpdatedResources) {
for (Iterator<DeleteConflict> 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<IBaseResource> updatedSource = theUpdatedResources
.stream()
.filter(t -> sourceId.equals(t.getIdElement().toUnqualifiedVersionless().getValue()))
.findFirst();
if (updatedSource.isPresent()) {
List<ResourceReferenceInfo> 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<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, StopWatch theTransactionStopWatch, Map<IBase, IIdType> entriesToProcess, Set<IIdType> nonUpdatedEntities, Set<IBasePersistedResource> updatedEntities) {
private void resolveReferencesThenSaveAndIndexResources(RequestDetails theRequest, TransactionDetails theTransactionDetails,
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome,
StopWatch theTransactionStopWatch, Map<IBase, IIdType> entriesToProcess,
Set<IIdType> nonUpdatedEntities, Set<IBasePersistedResource> updatedEntities) {
FhirTerser terser = myContext.newTerser();
theTransactionStopWatch.startTask("Index " + theIdToPersistedOutcome.size() + " resources");
IdentityHashMap<DaoMethodOutcome, Set<IBaseReference>> deferredIndexesForAutoVersioning = null;
@ -1105,14 +1153,28 @@ public abstract class BaseTransactionProcessor {
Set<IBaseReference> 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<IBaseReference> 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<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, Map<IBase, IIdType> entriesToProcess, Set<IIdType> nonUpdatedEntities, Set<IBasePersistedResource> updatedEntities, FhirTerser terser, DaoMethodOutcome nextOutcome, IBaseResource nextResource, Set<IBaseReference> theReferencesToAutoVersion) {
private void resolveReferencesThenSaveAndIndexResource(RequestDetails theRequest, TransactionDetails theTransactionDetails,
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome,
Map<IBase, IIdType> entriesToProcess, Set<IIdType> nonUpdatedEntities,
Set<IBasePersistedResource> updatedEntities, FhirTerser terser,
DaoMethodOutcome nextOutcome, IBaseResource nextResource,
Set<IBaseReference> theReferencesToAutoVersion) {
// References
List<ResourceReferenceInfo> allRefs = terser.getAllResourceReferences(nextResource);
for (ResourceReferenceInfo nextRef : allRefs) {

View File

@ -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());
}
}

View File

@ -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<String>(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"));

View File

@ -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;

View File

@ -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;

View File

@ -1,19 +1,3 @@
package ca.uhn.fhir.model.dstu2.composite;
/*