Fix issue with partitioning in patient ID compartment mode (#2810)

* Fix issue with partitioning in patient ID compartment mode

* Add changelog
This commit is contained in:
James Agnew 2021-07-21 17:21:23 -04:00 committed by GitHub
parent 9f77c57e5a
commit 6af022062f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 48 additions and 14 deletions

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 2810
title: "An issue in the FHIRPath evaluator prevented Encounters from being stored when using the new
PatientIdPartitionInterceptor. This has been corrected."

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
@ -31,6 +30,7 @@ import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -45,7 +45,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -62,6 +61,9 @@ public class PatientIdPartitionInterceptor {
@Autowired
private FhirContext myFhirContext;
@Autowired
private ISearchParamExtractor mySearchParamExtractor;
/**
* Constructor
*/
@ -72,9 +74,10 @@ public class PatientIdPartitionInterceptor {
/**
* Constructor
*/
public PatientIdPartitionInterceptor(FhirContext theFhirContext) {
public PatientIdPartitionInterceptor(FhirContext theFhirContext, ISearchParamExtractor theSearchParamExtractor) {
this();
myFhirContext = theFhirContext;
mySearchParamExtractor = theSearchParamExtractor;
}
@Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE)
@ -92,14 +95,15 @@ public class PatientIdPartitionInterceptor {
throw new MethodNotAllowedException("Patient resource IDs must be client-assigned in patient compartment mode");
}
} else {
IFhirPath fhirPath = myFhirContext.newFhirPath();
compartmentIdentity = compartmentSps
.stream()
.flatMap(param -> Arrays.stream(BaseSearchParamExtractor.splitPathsR4(param.getPath())))
.filter(StringUtils::isNotBlank)
.map(path -> fhirPath.evaluateFirst(theResource, path, IBaseReference.class))
.filter(Optional::isPresent)
.map(Optional::get)
.map(path -> mySearchParamExtractor.getPathValueExtractor(theResource, path).get())
.filter(t -> !t.isEmpty())
.map(t -> t.get(0))
.filter(t -> t instanceof IBaseReference)
.map(t -> (IBaseReference) t)
.map(t -> t.getReferenceElement().getValue())
.map(t -> new IdType(t).getIdPart())
.filter(StringUtils::isNotBlank)

View File

@ -5,11 +5,13 @@ import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4SystemTest;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
@ -18,6 +20,7 @@ import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@ -31,9 +34,12 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest {
private PatientIdPartitionInterceptor mySvc;
private ForceOffsetSearchModeInterceptor myForceOffsetSearchModeInterceptor;
@Autowired
private ISearchParamExtractor mySearchParamExtractor;
@BeforeEach
public void before() {
mySvc = new PatientIdPartitionInterceptor(myFhirCtx);
mySvc = new PatientIdPartitionInterceptor(myFhirCtx, mySearchParamExtractor);
myForceOffsetSearchModeInterceptor = new ForceOffsetSearchModeInterceptor();
myInterceptorRegistry.registerInterceptor(mySvc);
@ -95,6 +101,24 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest {
});
}
/**
* Encounter.subject has a FHIRPath expression with a resolve() on it
*/
@Test
public void testCreateEncounter_ValidMembershipInCompartment() {
createPatientA();
Encounter encounter = new Encounter();
encounter.getSubject().setReference("Patient/A");
Long id = myEncounterDao.create(encounter).getId().getIdPartAsLong();
runInTransaction(() -> {
ResourceTable observation = myResourceTableDao.findById(id).orElseThrow(() -> new IllegalArgumentException());
assertEquals("Encounter", observation.getResourceType());
assertEquals(65, observation.getPartitionId().getPartitionId());
});
}
/**
* Type is not in the patient compartment
*/

View File

@ -534,8 +534,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return values;
}
protected abstract IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
protected FhirContext getContext() {
return myContext;
}

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import java.util.ArrayList;
import java.util.Collections;
@ -81,6 +82,8 @@ public interface ISearchParamExtractor {
String getDisplayTextForCoding(IBase theValue);
BaseSearchParamExtractor.IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
List<IBase> getCodingsFromCodeableConcept(IBase theValue);
String getDisplayTextFromCodeableConcept(IBase theValue);

View File

@ -48,7 +48,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
}
@Override
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
public IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
return () -> {
String path = theSinglePath;

View File

@ -56,7 +56,7 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
}
@Override
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
public IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
return () -> {
List<IBase> values = new ArrayList<>();
List<Base> allValues = myFhirPathEngine.evaluate((Base) theResource, theSinglePath);

View File

@ -68,7 +68,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
}
@Override
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
public IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
return () -> {
List<Base> allValues = myFhirPathEngine.evaluate((Base) theResource, theSinglePath);
return (List<IBase>) new ArrayList<IBase>(allValues);

View File

@ -78,7 +78,7 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
}
@Override
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String nextPath) {
public IValueExtractor getPathValueExtractor(IBaseResource theResource, String nextPath) {
return () -> myFhirPathEngine.evaluate((Base) theResource, nextPath);
}