diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java index 6ff03aaf4c4..5c629ee4288 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java @@ -21,20 +21,29 @@ package ca.uhn.fhir.interceptor.model; */ import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import javax.annotation.Nullable; + public class ReadPartitionIdRequestDetails { private final String myResourceType; private final RestOperationTypeEnum myRestOperationType; private final IIdType myReadResourceId; private final Object mySearchParams; + private final IBaseResource myConditionalTargetOrNull; - public ReadPartitionIdRequestDetails(String theResourceType, RestOperationTypeEnum theRestOperationType, IIdType theReadResourceId, Object theSearchParams) { + public ReadPartitionIdRequestDetails(String theResourceType, RestOperationTypeEnum theRestOperationType, IIdType theReadResourceId, Object theSearchParams, @Nullable IBaseResource theConditionalTargetOrNull) { myResourceType = theResourceType; myRestOperationType = theRestOperationType; myReadResourceId = theReadResourceId; mySearchParams = theSearchParams; + myConditionalTargetOrNull = theConditionalTargetOrNull; + } + + public IBaseResource getConditionalTargetOrNull() { + return myConditionalTargetOrNull; } public String getResourceType() { @@ -55,11 +64,11 @@ public class ReadPartitionIdRequestDetails { public static ReadPartitionIdRequestDetails forRead(String theResourceType, IIdType theId, boolean theIsVread) { RestOperationTypeEnum op = theIsVread ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ; - return new ReadPartitionIdRequestDetails(theResourceType, op, theId.withResourceType(theResourceType), null); + return new ReadPartitionIdRequestDetails(theResourceType, op, theId.withResourceType(theResourceType), null, null); } - public static ReadPartitionIdRequestDetails forSearchType(String theResourceType, Object theParams) { - return new ReadPartitionIdRequestDetails(theResourceType, RestOperationTypeEnum.SEARCH_TYPE, null, theParams); + public static ReadPartitionIdRequestDetails forSearchType(String theResourceType, Object theParams, IBaseResource theConditionalOperationTargetOrNull) { + return new ReadPartitionIdRequestDetails(theResourceType, RestOperationTypeEnum.SEARCH_TYPE, null, theParams, theConditionalOperationTargetOrNull); } public static ReadPartitionIdRequestDetails forHistory(String theResourceType, IIdType theIdType) { @@ -71,6 +80,6 @@ public class ReadPartitionIdRequestDetails { } else { restOperationTypeEnum = RestOperationTypeEnum.HISTORY_SYSTEM; } - return new ReadPartitionIdRequestDetails(theResourceType, restOperationTypeEnum, theIdType, null); + return new ReadPartitionIdRequestDetails(theResourceType, restOperationTypeEnum, theIdType, null, null); } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2727-patient-id-compartment-should-support-conditiona-create.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2727-patient-id-compartment-should-support-conditiona-create.yaml new file mode 100644 index 00000000000..7682526ca8c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2727-patient-id-compartment-should-support-conditiona-create.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2828 +title: "PatientIdPartitionInterceptor now supports conditional creates of resources where the resource is in + the patient compartment but the conditional URL does not contain a patietn reference." diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java index b0e1b0db28c..ff8fa2a2303 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java @@ -47,6 +47,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.servlet.http.HttpServletResponse; import java.util.Collection; import java.util.Date; @@ -80,7 +81,7 @@ public interface IFhirResourceDao extends IDao { * won't be indexed and searches won't work. * @param theRequestDetails TODO */ - DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails); + DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails); DaoMethodOutcome create(T theResource, String theIfNoneExist, RequestDetails theRequestDetails); @@ -211,7 +212,21 @@ public interface IFhirResourceDao extends IDao { /** * Search for IDs for processing a match URLs, etc. */ - Set searchForIds(SearchParameterMap theParams, RequestDetails theRequest); + default Set searchForIds(SearchParameterMap theParams, RequestDetails theRequest) { + return searchForIds(theParams, theRequest, null); + } + + /** + * Search for IDs for processing a match URLs, etc. + * + * @param theConditionalOperationTargetOrNull If we're searching for IDs in order to satisfy a conditional + * create/update, this is the resource being searched for + * @since 5.5.0 + */ + default Set searchForIds(SearchParameterMap theParams, RequestDetails theRequest, @Nullable IBaseResource theConditionalOperationTargetOrNull) { + return searchForIds(theParams, theRequest); + } + /** * Takes a map of incoming raw search parameters and translates/parses them into @@ -249,7 +264,7 @@ public interface IFhirResourceDao extends IDao { * @param theForceUpdateVersion Create a new version with the same contents as the current version even if the content hasn't changed (this is mostly useful for * resources mapping to external content such as external code systems) */ - DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails, @Nonnull TransactionDetails theTransactionDetails); + DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails, @Nonnull TransactionDetails theTransactionDetails); /** * Not supported in DSTU1! @@ -262,10 +277,11 @@ public interface IFhirResourceDao extends IDao { /** * Delete a list of resource Pids - * @param theUrl the original URL that triggered the delete - * @param theResourceIds the ids of the resources to be deleted + * + * @param theUrl the original URL that triggered the delete + * @param theResourceIds the ids of the resources to be deleted * @param theDeleteConflicts out parameter of conflicts preventing deletion - * @param theRequest the request that initiated the request + * @param theRequest the request that initiated the request * @return response back to the client */ DeleteMethodOutcome deletePidList(String theUrl, Collection theResourceIds, DeleteConflictList theDeleteConflicts, RequestDetails theRequest); @@ -273,8 +289,8 @@ public interface IFhirResourceDao extends IDao { /** * Returns the current version ID for the given resource */ - default String getCurrentVersionId(IIdType theReferenceElement) { - return read(theReferenceElement.toVersionless()).getIdElement().getVersionIdPart(); - } + default String getCurrentVersionId(IIdType theReferenceElement) { + return read(theReferenceElement.toVersionless()).getIdElement().getVersionIdPart(); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/ResourceToFileWriter.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/ResourceToFileWriter.java index df362a79f19..ac807768926 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/ResourceToFileWriter.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/ResourceToFileWriter.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.bulk.export.job; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; @@ -101,7 +102,7 @@ public class ResourceToFileWriter implements ItemWriter> { IBaseBinary binary = BinaryUtil.newBinary(myFhirContext); binary.setContentType(Constants.CT_FHIR_NDJSON); binary.setContent(myOutputStream.toByteArray()); - DaoMethodOutcome outcome = myBinaryDao.create(binary, new SystemRequestDetails()); + DaoMethodOutcome outcome = myBinaryDao.create(binary, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); return outcome.getResource().getIdElement(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 5d96742e857..7c53cb0a341 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -126,6 +126,7 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; +import javax.annotation.Nullable; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import javax.persistence.NoResultException; @@ -569,7 +570,7 @@ public abstract class BaseHapiFhirResourceDao extends B SearchParameterMap paramMap = resourceSearch.getSearchParameterMap(); paramMap.setLoadSynchronous(true); - Set resourceIds = myMatchResourceUrlService.search(paramMap, myResourceType, theRequest); + Set resourceIds = myMatchResourceUrlService.search(paramMap, myResourceType, theRequest, null); if (resourceIds.size() > 1) { if (!getConfig().isAllowMultipleDelete()) { @@ -1415,7 +1416,7 @@ public abstract class BaseHapiFhirResourceDao extends B cacheControlDirective.parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)); } - RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), theParams); + RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), theParams, null); IBundleProvider retVal = mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName(), cacheControlDirective, theRequest, requestPartitionId); if (retVal instanceof PersistedJpaBundleProvider) { @@ -1490,7 +1491,7 @@ public abstract class BaseHapiFhirResourceDao extends B } @Override - public Set searchForIds(SearchParameterMap theParams, RequestDetails theRequest) { + public Set searchForIds(SearchParameterMap theParams, RequestDetails theRequest, @Nullable IBaseResource theConditionalOperationTargetOrNull) { TransactionDetails transactionDetails = new TransactionDetails(); return myTransactionService.execute(theRequest, transactionDetails, tx -> { @@ -1506,9 +1507,9 @@ public abstract class BaseHapiFhirResourceDao extends B HashSet retVal = new HashSet<>(); String uuid = UUID.randomUUID().toString(); - SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(theRequest, uuid); + RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), theParams, theConditionalOperationTargetOrNull); - RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), theParams); + SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(theRequest, uuid); try (IResultIterator iter = builder.createQuery(theParams, searchRuntimeDetails, theRequest, requestPartitionId)) { while (iter.hasNext()) { retVal.add(iter.next()); @@ -1616,7 +1617,7 @@ public abstract class BaseHapiFhirResourceDao extends B IIdType resourceId; if (isNotBlank(theMatchUrl)) { - Set match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest); + Set match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest, theResource); if (match.size() > 1) { String msg = getContext().getLocalizer().getMessageSanitized(BaseHapiFhirDao.class, "transactionOperationWithMultipleMatchFailure", "UPDATE", theMatchUrl, match.size()); throw new PreconditionFailedException(msg); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java index b7d19a2b9fa..ae26b45e1e4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java @@ -91,7 +91,7 @@ public abstract class BaseHapiFhirResourceDaoObservation orderedSubjectReferenceMap = new TreeMap<>(); if(theSearchParameterMap.containsKey(getSubjectParamName())) { - RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap); + RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null); List> patientParams = new ArrayList<>(); if (theSearchParameterMap.get(getPatientParamName()) != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java index 57b2f37bed6..31375debf3f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java @@ -79,7 +79,7 @@ public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao Set processMatchUrl(String theMatchUrl, Class theResourceType, TransactionDetails theTransactionDetails, RequestDetails theRequest) { + return processMatchUrl(theMatchUrl, theResourceType, theTransactionDetails, theRequest, null); + } + + /** + * Note that this will only return a maximum of 2 results!! + */ + public Set processMatchUrl(String theMatchUrl, Class theResourceType, TransactionDetails theTransactionDetails, RequestDetails theRequest, IBaseResource theConditionalOperationTargetOrNull) { String resourceType = myContext.getResourceType(theResourceType); String matchUrl = massageForStorage(resourceType, theMatchUrl); @@ -92,7 +99,7 @@ public class MatchResourceUrlService { } paramMap.setLoadSynchronousUpTo(2); - Set retVal = search(paramMap, theResourceType, theRequest); + Set retVal = search(paramMap, theResourceType, theRequest, theConditionalOperationTargetOrNull); if (myDaoConfig.isMatchUrlCacheEnabled() && retVal.size() == 1) { ResourcePersistentId pid = retVal.iterator().next(); @@ -124,14 +131,14 @@ public class MatchResourceUrlService { return existing; } - public Set search(SearchParameterMap theParamMap, Class theResourceType, RequestDetails theRequest) { + public Set search(SearchParameterMap theParamMap, Class theResourceType, RequestDetails theRequest, @Nullable IBaseResource theConditionalOperationTargetOrNull) { StopWatch sw = new StopWatch(); IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceType); if (dao == null) { throw new InternalErrorException("No DAO for resource type: " + theResourceType.getName()); } - Set retVal = dao.searchForIds(theParamMap, theRequest); + Set retVal = dao.searchForIds(theParamMap, theRequest, theConditionalOperationTargetOrNull); // Interceptor broadcast: JPA_PERFTRACE_INFO if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java index 55d518375c1..490f909293a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java @@ -48,7 +48,7 @@ public class FhirResourceDaoObservationDstu3 extends BaseHapiFhirResourceDaoObse updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails); - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap); + RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null); return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java index 163d01363e6..bedc37a3998 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java @@ -74,7 +74,7 @@ public class FhirResourceDaoPatientDstu3 extends BaseHapiFhirResourceDaoim paramMap.setLoadSynchronous(true); } - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap); + RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap, null); return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest, requestPartitionId); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java index ae017465061..535697e9d87 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java @@ -48,7 +48,7 @@ public class FhirResourceDaoObservationR5 extends BaseHapiFhirResourceDaoObserva updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails); - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap); + RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null); return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java index c64e04b695f..af1d8f6d380 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java @@ -74,7 +74,7 @@ public class FhirResourceDaoPatientR5 extends BaseHapiFhirResourceDao i paramMap.setLoadSynchronous(true); } - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap); + RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap, null); return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest, requestPartitionId); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteExpungeJobSubmitterImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteExpungeJobSubmitterImpl.java index 5779cc592f1..5a4fff96a7a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteExpungeJobSubmitterImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteExpungeJobSubmitterImpl.java @@ -92,7 +92,7 @@ public class DeleteExpungeJobSubmitterImpl implements IDeleteExpungeJobSubmitter List retval = new ArrayList<>(); for (String url : theUrlsToDeleteExpunge) { ResourceSearch resourceSearch = myMatchUrlService.getResourceSearch(url); - RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, resourceSearch.getResourceName(), null); + RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, resourceSearch.getResourceName(), null, null); retval.add(requestPartitionId); } return retval; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java index 3706993ebb3..c301bd248a2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java @@ -118,17 +118,6 @@ public class PatientIdPartitionInterceptor { return provideCompartmentMemberInstanceResponse(theRequestDetails, compartmentIdentity); } - @Nonnull - private List getCompartmentSearchParams(RuntimeResourceDefinition resourceDef) { - return resourceDef - .getSearchParams() - .stream() - .filter(param -> param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) - .filter(param -> param.getProvidesMembershipInCompartments() != null && param.getProvidesMembershipInCompartments().contains("Patient")) - .collect(Collectors.toList()); - } - - @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_READ) public RequestPartitionId identifyForRead(ReadPartitionIdRequestDetails theReadDetails, RequestDetails theRequestDetails) { RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(theReadDetails.getResourceType()); @@ -169,7 +158,23 @@ public class PatientIdPartitionInterceptor { // nothing } - return provideUnsupportedQueryResponse(theReadDetails); + // If we couldn't identify a patient ID by the URL, let's try using the + // conditional target if we have one + if (theReadDetails.getConditionalTargetOrNull() != null) { + return identifyForCreate(theReadDetails.getConditionalTargetOrNull(), theRequestDetails); + } + + return provideNonPatientSpecificQueryResponse(theReadDetails); + } + + @Nonnull + private List getCompartmentSearchParams(RuntimeResourceDefinition resourceDef) { + return resourceDef + .getSearchParams() + .stream() + .filter(param -> param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) + .filter(param -> param.getProvidesMembershipInCompartments() != null && param.getProvidesMembershipInCompartments().contains("Patient")) + .collect(Collectors.toList()); } private String getSingleResourceIdValueOrNull(SearchParameterMap theParams, String theParamName, String theResourceType) { @@ -206,8 +211,8 @@ public class PatientIdPartitionInterceptor { /** * Return a partition or throw an error for FHIR operations that can not be used with this interceptor */ - protected RequestPartitionId provideUnsupportedQueryResponse(ReadPartitionIdRequestDetails theRequestDetails) { - throw new MethodNotAllowedException("This server is not able to handle this request of type " + theRequestDetails.getRestOperationType()); + protected RequestPartitionId provideNonPatientSpecificQueryResponse(ReadPartitionIdRequestDetails theRequestDetails) { + return RequestPartitionId.allPartitions(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java index 5a463fca932..14a3aaaef13 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java @@ -27,13 +27,13 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity; -import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController; @@ -63,6 +63,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import org.hl7.fhir.utilities.npm.NpmPackage; +import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import java.io.IOException; import java.util.ArrayList; @@ -358,16 +359,23 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { private IBundleProvider searchResource(IFhirResourceDao theDao, SearchParameterMap theMap) { if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = new SystemRequestDetails(); + SystemRequestDetails requestDetails = newSystemRequestDetails(); return theDao.search(theMap, requestDetails); } else { return theDao.search(theMap); } } + @Nonnull + private SystemRequestDetails newSystemRequestDetails() { + return + new SystemRequestDetails() + .setRequestPartitionId(RequestPartitionId.defaultPartition()); + } + private void createResource(IFhirResourceDao theDao, IBaseResource theResource) { if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = new SystemRequestDetails(); + SystemRequestDetails requestDetails = newSystemRequestDetails(); theDao.create(theResource, requestDetails); } else { theDao.create(theResource); @@ -376,7 +384,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { private DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) { if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = new SystemRequestDetails(); + SystemRequestDetails requestDetails = newSystemRequestDetails(); return theDao.update(theResource, requestDetails); } else { return theDao.update(theResource); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java index c9e8af42b90..178fe4be70c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.partition; import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; +import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -44,8 +45,8 @@ public interface IRequestPartitionHelperSvc { } @Nonnull - default RequestPartitionId determineReadPartitionForRequestForSearchType(RequestDetails theRequest, String theResourceType, SearchParameterMap theParams) { - ReadPartitionIdRequestDetails details = ReadPartitionIdRequestDetails.forSearchType(theResourceType, theParams); + default RequestPartitionId determineReadPartitionForRequestForSearchType(RequestDetails theRequest, String theResourceType, SearchParameterMap theParams, IBaseResource theConditionalOperationTargetOrNull) { + ReadPartitionIdRequestDetails details = ReadPartitionIdRequestDetails.forSearchType(theResourceType, theParams, theConditionalOperationTargetOrNull); return determineReadPartitionForRequest(theRequest, theResourceType, details); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java index 543fe75733d..b63df986ba1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java @@ -109,14 +109,14 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { if (myPartitionSettings.isPartitioningEnabled()) { // Handle system requests //TODO GGG eventually, theRequest will not be allowed to be null here, and we will pass through SystemRequestDetails instead. - if (theRequest == null && nonPartitionableResource) { + if ((theRequest == null || theRequest instanceof SystemRequestDetails) && nonPartitionableResource) { return RequestPartitionId.defaultPartition(); } - if (theRequest instanceof SystemRequestDetails) { + if (theRequest instanceof SystemRequestDetails && systemRequestHasExplicitPartition((SystemRequestDetails) theRequest)) { requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) theRequest, nonPartitionableResource); - // Interceptor call: STORAGE_PARTITION_IDENTIFY_READ } else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_READ, myInterceptorBroadcaster, theRequest)) { + // Interceptor call: STORAGE_PARTITION_IDENTIFY_READ HookParams params = new HookParams() .add(RequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest) @@ -186,15 +186,16 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { boolean nonPartitionableResource = myNonPartitionableResourceNames.contains(theResourceType); //TODO GGG eventually, theRequest will not be allowed to be null here, and we will pass through SystemRequestDetails instead. - if (theRequest == null && nonPartitionableResource) { + if ((theRequest == null || theRequest instanceof SystemRequestDetails) && nonPartitionableResource) { return RequestPartitionId.defaultPartition(); } - if (theRequest instanceof SystemRequestDetails) { + if (theRequest instanceof SystemRequestDetails && systemRequestHasExplicitPartition((SystemRequestDetails) theRequest)) { requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) theRequest, nonPartitionableResource); } else { //This is an external Request (e.g. ServletRequestDetails) so we want to figure out the partition via interceptor. - HookParams params = new HookParams()// Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE + // Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE + HookParams params = new HookParams() .add(IBaseResource.class, theResource) .add(RequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest); @@ -215,6 +216,10 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { return RequestPartitionId.allPartitions(); } + private boolean systemRequestHasExplicitPartition(@Nonnull SystemRequestDetails theRequest) { + return theRequest.getRequestPartitionId() != null || theRequest.getTenantId() != null; + } + @Nonnull @Override public PartitionablePartitionId toStoragePartition(@Nonnull RequestPartitionId theRequestPartitionId) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java index fca52f022ea..caa82cbdd5f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java @@ -72,8 +72,9 @@ public class SystemRequestDetails extends RequestDetails { return myRequestPartitionId; } - public void setRequestPartitionId(RequestPartitionId theRequestPartitionId) { + public SystemRequestDetails setRequestPartitionId(RequestPartitionId theRequestPartitionId) { myRequestPartitionId = theRequestPartitionId; + return this; } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 0d3da0d479c..dba0366e468 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -271,7 +271,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { String resourceType = search.getResourceType(); SearchParameterMap params = search.getSearchParameterMap().orElseThrow(() -> new IllegalStateException("No map in PASSCOMPLET search")); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(resourceType); - RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequestDetails, resourceType, params); + RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequestDetails, resourceType, params, null); SearchContinuationTask task = new SearchContinuationTask(search, resourceDao, params, resourceType, theRequestDetails, requestPartitionId); myIdToSearchTask.put(search.getUuid(), task); myExecutor.submit(task); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index c116819da7e..ebff3054e7f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -814,7 +814,9 @@ public class SearchBuilder implements ISearchBuilder { if (findVersionFieldName != null) { sqlBuilder.append(", r." + findVersionFieldName); } - sqlBuilder.append(" FROM ResourceLink r WHERE r."); + sqlBuilder.append(" FROM ResourceLink r WHERE "); + + sqlBuilder.append("r."); sqlBuilder.append(searchPidFieldName); sqlBuilder.append(" IN (:target_pids)"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java index d265473a300..ab74d2c3c0d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.bulk; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.batch.BatchJobsConfig; @@ -944,7 +945,7 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { public String getBinaryContents(IBulkDataExportSvc.JobInfo theJobInfo, int theIndex) { // Iterate over the files - Binary nextBinary = myBinaryDao.read(theJobInfo.getFiles().get(theIndex).getResourceId(), new SystemRequestDetails()); + Binary nextBinary = myBinaryDao.read(theJobInfo.getFiles().get(theIndex).getResourceId(), new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); assertEquals(Constants.CT_FHIR_NDJSON, nextBinary.getContentType()); String nextContents = new String(nextBinary.getContent(), Constants.CHARSET_UTF8); ourLog.info("Next contents for type {}:\n{}", nextBinary.getResourceType(), nextContents); @@ -1238,12 +1239,12 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { createCareTeamWithIndex(i, patId); } - myPatientGroupId = myGroupDao.update(group, new SystemRequestDetails()).getId(); + myPatientGroupId = myGroupDao.update(group, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())).getId(); //Manually create another golden record Patient goldenPatient2 = new Patient(); goldenPatient2.setId("PAT888"); - DaoMethodOutcome g2Outcome = myPatientDao.update(goldenPatient2, new SystemRequestDetails()); + DaoMethodOutcome g2Outcome = myPatientDao.update(goldenPatient2, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); Long goldenPid2 = myIdHelperService.getPidOrNull(g2Outcome.getResource()); //Create some nongroup patients MDM linked to a different golden resource. They shouldnt be included in the query. @@ -1272,14 +1273,14 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { patient.setGender(i % 2 == 0 ? Enumerations.AdministrativeGender.MALE : Enumerations.AdministrativeGender.FEMALE); patient.addName().setFamily("FAM" + i); patient.addIdentifier().setSystem("http://mrns").setValue("PAT" + i); - return myPatientDao.update(patient, new SystemRequestDetails()); + return myPatientDao.update(patient, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); } private void createCareTeamWithIndex(int i, IIdType patId) { CareTeam careTeam = new CareTeam(); careTeam.setId("CT" + i); careTeam.setSubject(new Reference(patId)); // This maps to the "patient" search parameter on CareTeam - myCareTeamDao.update(careTeam, new SystemRequestDetails()); + myCareTeamDao.update(careTeam, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); } private void createImmunizationWithIndex(int i, IIdType patId) { @@ -1297,7 +1298,7 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { cc.addCoding().setSystem("vaccines").setCode("COVID-19"); immunization.setVaccineCode(cc); } - myImmunizationDao.update(immunization, new SystemRequestDetails()); + myImmunizationDao.update(immunization, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); } private void createObservationWithIndex(int i, IIdType patId) { @@ -1308,7 +1309,7 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { if (patId != null) { obs.getSubject().setReference(patId.getValue()); } - myObservationDao.update(obs, new SystemRequestDetails()); + myObservationDao.update(obs, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition())); } public void linkToGoldenResource(Long theGoldenPid, Long theSourcePid) { 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 cf5550fa954..716db77adef 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 @@ -506,6 +506,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil myPagingProvider.setDefaultPageSize(BasePagingProvider.DEFAULT_DEFAULT_PAGE_SIZE); myPagingProvider.setMaximumPageSize(BasePagingProvider.DEFAULT_MAX_PAGE_SIZE); + + myPartitionSettings.setPartitioningEnabled(false); } @AfterEach diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java index b70a8378c3f..4cd403e2b2b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java @@ -49,6 +49,7 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.matchesPattern; import static org.hamcrest.Matchers.not; @@ -566,12 +567,10 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { /* * 20 should be prefetched since that's the initial page size */ - await().until(() -> { - return runInTransaction(() -> { - Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); - return search.getNumFound() == 20; - }); - }); + await().until(() -> runInTransaction(() -> { + Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); + return search.getNumFound(); + }), equalTo(20)); runInTransaction(() -> { Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); assertEquals(20, search.getNumFound()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java index 99047a7d948..faded1b6479 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java @@ -4,15 +4,24 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; 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.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; +import ca.uhn.fhir.jpa.util.MultimapCollector; +import ca.uhn.fhir.jpa.util.SqlQuery; +import ca.uhn.fhir.model.api.Include; 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 com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Encounter; import org.hl7.fhir.r4.model.Enumerations; +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; @@ -22,8 +31,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.matchesPattern; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -225,11 +244,9 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { createObservationB(); myCaptureQueriesListener.clear(); - try { - myObservationDao.search(SearchParameterMap.newSynchronous(), mySrd); - } catch (MethodNotAllowedException e) { - assertEquals("This server is not able to handle this request of type SEARCH_TYPE", e.getMessage()); - } + myObservationDao.search(SearchParameterMap.newSynchronous(), mySrd); + myCaptureQueriesListener.logSelectQueries(); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE ((t0.RES_TYPE = 'Observation') AND (t0.RES_DELETED_AT IS NULL)) limit '10'", myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false)); } @Test @@ -314,6 +331,139 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("PARTITION_ID=")); } + + @Test + public void testTransaction_NoRequestDetails() throws IOException { + Bundle input = loadResourceFromClasspath(Bundle.class, "/r4/load_bundle.json"); + + // Maybe in the future we'll make request details mandatory and if that + // causes this to fail that's ok + Bundle outcome = mySystemDao.transaction(null, input); + + ListMultimap resourceIds = outcome + .getEntry() + .stream() + .collect(MultimapCollector.toMultimap(t -> new IdType(t.getResponse().getLocation()).toUnqualifiedVersionless().getResourceType(), t -> new IdType(t.getResponse().getLocation()).toUnqualifiedVersionless().getValue())); + + Multimap resourcesByType = runInTransaction(() -> { + logAllResources(); + return myResourceTableDao.findAll().stream().collect(MultimapCollector.toMultimap(t->t.getResourceType(), t->t.getPartitionId().getPartitionId())); + }); + + assertThat(resourcesByType.get("Patient"), contains(4267)); + assertThat(resourcesByType.get("ExplanationOfBenefit"), contains(4267)); + assertThat(resourcesByType.get("Coverage"), contains(4267)); + assertThat(resourcesByType.get("Organization"), contains(-1, -1)); + assertThat(resourcesByType.get("Practitioner"), contains(-1, -1, -1)); + } + + @Test + public void testTransaction_SystemRequestDetails() throws IOException { + Bundle input = loadResourceFromClasspath(Bundle.class, "/r4/load_bundle.json"); + myCaptureQueriesListener.clear(); + Bundle outcome = mySystemDao.transaction(new SystemRequestDetails(), input); + myCaptureQueriesListener.logSelectQueries(); + List selectQueryStrings = myCaptureQueriesListener + .getSelectQueries() + .stream() + .map(t -> t.getSql(false, false).toUpperCase(Locale.US)) + .filter(t -> !t.contains("FROM HFJ_TAG_DEF")) + .collect(Collectors.toList()); + for (String next : selectQueryStrings) { + assertThat(next, either(containsString("PARTITION_ID =")).or(containsString("PARTITION_ID IN"))); + } + + ListMultimap resourceIds = outcome + .getEntry() + .stream() + .collect(MultimapCollector.toMultimap(t -> new IdType(t.getResponse().getLocation()).toUnqualifiedVersionless().getResourceType(), t -> new IdType(t.getResponse().getLocation()).toUnqualifiedVersionless().getValue())); + + String patientId = resourceIds.get("Patient").get(0); + + Multimap resourcesByType = runInTransaction(() -> { + logAllResources(); + return myResourceTableDao.findAll().stream().collect(MultimapCollector.toMultimap(t->t.getResourceType(), t->t.getPartitionId().getPartitionId())); + }); + + assertThat(resourcesByType.get("Patient"), contains(4267)); + assertThat(resourcesByType.get("ExplanationOfBenefit"), contains(4267)); + assertThat(resourcesByType.get("Coverage"), contains(4267)); + assertThat(resourcesByType.get("Organization"), contains(-1, -1)); + assertThat(resourcesByType.get("Practitioner"), contains(-1, -1, -1)); + + // Try Searching + SearchParameterMap map = new SearchParameterMap(); + map.add(ExplanationOfBenefit.SP_PATIENT, new ReferenceParam(patientId)); + map.addInclude(new Include("*")); + myCaptureQueriesListener.clear(); + IBundleProvider result = myExplanationOfBenefitDao.search(map); + List resultIds = toUnqualifiedVersionlessIdValues(result); + assertThat(resultIds.toString(), resultIds, containsInAnyOrder( + resourceIds.get("Coverage").get(0), + resourceIds.get("Organization").get(0), + resourceIds.get("ExplanationOfBenefit").get(0), + resourceIds.get("Patient").get(0), + resourceIds.get("Practitioner").get(0), + resourceIds.get("Practitioner").get(1), + resourceIds.get("Practitioner").get(2) + )); + + myCaptureQueriesListener.logSelectQueries(); + + List selectQueries = myCaptureQueriesListener.getSelectQueries(); + assertThat(selectQueries.get(0).getSql(true, false).toUpperCase(Locale.US), matchesPattern("SELECT.*FROM HFJ_RES_LINK.*WHERE.*PARTITION_ID = '4267'.*")); + + } + + + @Test + public void testSearch() throws IOException { + Bundle input = loadResourceFromClasspath(Bundle.class, "/r4/load_bundle.json"); + Bundle outcome = mySystemDao.transaction(new SystemRequestDetails(), input); + + ListMultimap resourceIds = outcome + .getEntry() + .stream() + .collect(MultimapCollector.toMultimap(t -> new IdType(t.getResponse().getLocation()).toUnqualifiedVersionless().getResourceType(), t -> new IdType(t.getResponse().getLocation()).toUnqualifiedVersionless().getValue())); + + String patientId = resourceIds.get("Patient").get(0); + + Multimap resourcesByType = runInTransaction(() -> { + logAllResources(); + return myResourceTableDao.findAll().stream().collect(MultimapCollector.toMultimap(t->t.getResourceType(), t->t.getPartitionId().getPartitionId())); + }); + + assertThat(resourcesByType.get("Patient"), contains(4267)); + assertThat(resourcesByType.get("ExplanationOfBenefit"), contains(4267)); + assertThat(resourcesByType.get("Coverage"), contains(4267)); + assertThat(resourcesByType.get("Organization"), contains(-1, -1)); + assertThat(resourcesByType.get("Practitioner"), contains(-1, -1, -1)); + + // Try Searching + SearchParameterMap map = new SearchParameterMap(); + map.add(ExplanationOfBenefit.SP_PATIENT, new ReferenceParam(patientId)); + map.addInclude(new Include("*")); + myCaptureQueriesListener.clear(); + IBundleProvider result = myExplanationOfBenefitDao.search(map); + List resultIds = toUnqualifiedVersionlessIdValues(result); + assertThat(resultIds.toString(), resultIds, containsInAnyOrder( + resourceIds.get("Coverage").get(0), + resourceIds.get("Organization").get(0), + resourceIds.get("ExplanationOfBenefit").get(0), + resourceIds.get("Patient").get(0), + resourceIds.get("Practitioner").get(0), + resourceIds.get("Practitioner").get(1), + resourceIds.get("Practitioner").get(2) + )); + + myCaptureQueriesListener.logSelectQueries(); + + List selectQueries = myCaptureQueriesListener.getSelectQueries(); + assertThat(selectQueries.get(0).getSql(true, false).toUpperCase(Locale.US), matchesPattern("SELECT.*FROM HFJ_RES_LINK.*WHERE.*PARTITION_ID = '4267'.*")); + + } + + @Test public void testHistory_Type() { myOrganizationDao.history(null, null, null, mySrd); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java index 29be4e2aebc..1e69896d972 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.interceptor.PatientIdPartitionInterceptor; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor; @@ -40,6 +41,8 @@ public class JpaPackageCacheTest extends BaseJpaR4Test { private IInterceptorService myInterceptorService; @Autowired private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor; + @Autowired + private ISearchParamExtractor mySearchParamExtractor; @AfterEach public void disablePartitioning() { @@ -75,7 +78,7 @@ public class JpaPackageCacheTest extends BaseJpaR4Test { public void testSaveAndDeletePackagePartitionsEnabled() throws IOException { myPartitionSettings.setPartitioningEnabled(true); myPartitionSettings.setDefaultPartitionId(1); - myInterceptorService.registerInterceptor(new PatientIdPartitionInterceptor()); + myInterceptorService.registerInterceptor(new PatientIdPartitionInterceptor(myFhirCtx, mySearchParamExtractor)); myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor); try (InputStream stream = ClasspathUtil.loadResourceAsStream("/packages/basisprofil.de.tar.gz")) { @@ -109,7 +112,7 @@ public class JpaPackageCacheTest extends BaseJpaR4Test { myPartitionSettings.setPartitioningEnabled(true); myPartitionSettings.setDefaultPartitionId(0); myPartitionSettings.setUnnamedPartitionMode(true); - myInterceptorService.registerInterceptor(new PatientIdPartitionInterceptor()); + myInterceptorService.registerInterceptor(new PatientIdPartitionInterceptor(myFhirCtx, mySearchParamExtractor)); myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor); try (InputStream stream = ClasspathUtil.loadResourceAsStream("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz")) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java index 84ef640e24c..13cfb4cd620 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java @@ -423,9 +423,9 @@ public class NpmR4Test extends BaseJpaR4Test { myDaoConfig.setAllowExternalReferences(true); byte[] bytes = loadClasspathBytes("/packages/test-draft-sample.tgz"); - myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", bytes); + myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.onlydrafts/0.11.1", bytes); - PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); + PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.onlydrafts").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec); assertEquals(0, outcome.getResourcesInstalled().size(), outcome.getResourcesInstalled().toString()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/MultimapCollector.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/MultimapCollector.java new file mode 100644 index 00000000000..66707836bde --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/MultimapCollector.java @@ -0,0 +1,64 @@ +package ca.uhn.fhir.jpa.util; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; + +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +/** + * Copied from https://stackoverflow.com/questions/23003542/cleanest-way-to-create-a-guava-multimap-from-a-java-8-stream + */ +public class MultimapCollector implements + Collector, ListMultimap> { + + private final Function keyGetter; + private final Function valueGetter; + + public MultimapCollector(Function keyGetter, Function valueGetter) { + this.keyGetter = keyGetter; + this.valueGetter = valueGetter; + } + + public static MultimapCollector toMultimap(Function keyGetter, Function valueGetter) { + return new MultimapCollector<>(keyGetter, valueGetter); + } + + public static MultimapCollector toMultimap(Function keyGetter) { + return new MultimapCollector<>(keyGetter, v -> v); + } + + @Override + public Supplier> supplier() { + return ArrayListMultimap::create; + } + + @Override + public BiConsumer, T> accumulator() { + return (map, element) -> map.put(keyGetter.apply(element), valueGetter.apply(element)); + } + + @Override + public BinaryOperator> combiner() { + return (map1, map2) -> { + map1.putAll(map2); + return map1; + }; + } + + @Override + public Function, ListMultimap> finisher() { + return map -> map; + } + + @Override + public Set characteristics() { + return ImmutableSet.of(Characteristics.IDENTITY_FINISH); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/resources/packages/test-draft-sample.tgz b/hapi-fhir-jpaserver-base/src/test/resources/packages/test-draft-sample.tgz index 56788688ce5..8509edd4320 100644 Binary files a/hapi-fhir-jpaserver-base/src/test/resources/packages/test-draft-sample.tgz and b/hapi-fhir-jpaserver-base/src/test/resources/packages/test-draft-sample.tgz differ diff --git a/hapi-fhir-jpaserver-base/src/test/resources/r4/load_bundle.json b/hapi-fhir-jpaserver-base/src/test/resources/r4/load_bundle.json new file mode 100644 index 00000000000..101b34b853c --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/r4/load_bundle.json @@ -0,0 +1,986 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "ExplanationOfBenefit", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-ExplanationOfBenefit-Professional-NonClinician" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType", + "code": "payerid" + } + ] + }, + "system": "https://hl7.org/fhir/sid/payerid", + "value": "5824473976" + }, + { + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType", + "code": "uc" + } + ] + }, + "system": "https://hl7.org/fhir/sid/claimid", + "value": "1234094" + } + ], + "status": "active", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/claim-type", + "code": "professional" + } + ] + }, + "use": "claim", + "patient": { + "reference": "Patient/d1a47e2c-509b-e326-deab-597e3f598ca5" + }, + "billablePeriod": { + "start": "2017-01-08", + "end": "2017-01-08" + }, + "created": "2017-01-11T00:00:00-08:00", + "insurer": { + "reference": "Organization/5954a17b-0779-334c-4f1c-e894e45d15fb" + }, + "provider": { + "reference": "Organization/68ae4f74-afdc-6242-c50e-02ef776d8e5d" + }, + "payee": { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/payeetype", + "code": "provider" + } + ], + "text": "Claim paid to VENDOR" + }, + "party": { + "reference": "Organization/68ae4f74-afdc-6242-c50e-02ef776d8e5d" + } + }, + "outcome": "complete", + "disposition": "PAID", + "careTeam": [ + { + "sequence": 1, + "provider": { + "reference": "Practitioner/23eccc61-ab67-bf8a-e464-6260f7989556" + }, + "responsible": false, + "role": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/claimcareteamrole", + "code": "primary" + } + ] + } + }, + { + "sequence": 2, + "provider": { + "reference": "Practitioner/dbbc9a06-f685-b481-d739-133755af138e" + }, + "responsible": false, + "role": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBClaimCareTeamRole", + "code": "referring" + } + ] + } + }, + { + "sequence": 3, + "provider": { + "reference": "Practitioner/39b9250c-0d01-cbb0-ea89-0de9c74af511" + }, + "responsible": true, + "role": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBClaimCareTeamRole", + "code": "performing" + } + ] + } + } + ], + "supportingInfo": [ + { + "sequence": 1, + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType", + "code": "clmrecvddate" + } + ] + }, + "timingDate": "2017-01-11" + }, + { + "sequence": 2, + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBSupportingInfoType", + "code": "billingnetworkcontractingstatus" + } + ] + }, + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBPayerAdjudicationStatus", + "code": "other" + } + ] + } + } + ], + "diagnosis": [ + { + "sequence": 4, + "diagnosisCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "I27.2", + "display": "Other secondary pulmonary hypertension" + } + ], + "text": "Other secondary pulmonary hypertension" + } + } + ], + "procedure": [ + { + "sequence": 1, + "date": "2017-01-08T00:00:00-08:00", + "procedureCodeableConcept": { + "coding": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "99233", + "display": "Subsequent hospital care for severe problem" + } + ], + "text": "SBSQ HOSPITAL CARE/DAY 35 MINUTES" + } + } + ], + "insurance": [ + { + "focal": true, + "coverage": { + "reference": "urn:uuid:175dbf4a-7ee2-446d-9938-82eea27871a7" + } + } + ], + "item": [ + { + "sequence": 1, + "diagnosisSequence": [ + 4 + ], + "procedureSequence": [ + 1 + ], + "productOrService": { + "coding": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "99233", + "display": "Subsequent hospital care for severe problem" + } + ], + "text": "SBSQ HOSPITAL CARE/DAY 35 MINUTES" + }, + "servicedPeriod": { + "start": "2017-01-08", + "end": "2017-01-08" + }, + "locationCodeableConcept": { + "coding": [ + { + "system": "https://www.cms.gov/Medicare/Coding/place-of-service-codes/Place_of_Service_Code_Set", + "code": "99" + } + ] + }, + "quantity": { + "value": 1, + "unit": "Units", + "system": "http://unitsofmeasure.org", + "code": "[arb'U]" + }, + "net": { + "value": 317.00, + "currency": "USD" + }, + "adjudication": [ + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "submitted" + } + ] + }, + "amount": { + "value": 317.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "benefit" + } + ] + }, + "amount": { + "value": 124.69, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "copay" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "deductible" + } + ] + }, + "amount": { + "value": 124.69, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "coinsurance" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "memberliability" + } + ] + }, + "amount": { + "value": 124.69, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "noncovered" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "priorpayerpaid" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "paidtoprovider" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBPayerAdjudicationStatus", + "code": "outofnetwork" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + } + ] + } + ], + "total": [ + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "submitted" + } + ] + }, + "amount": { + "value": 317.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "benefit" + } + ] + }, + "amount": { + "value": 124.69, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "copay" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/adjudication", + "code": "deductible" + } + ] + }, + "amount": { + "value": 124.69, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "coinsurance" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "memberliability" + } + ] + }, + "amount": { + "value": 124.69, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "noncovered" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "priorpayerpaid" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + }, + { + "category": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", + "code": "paidtoprovider" + } + ] + }, + "amount": { + "value": 0.00, + "currency": "USD" + } + } + ], + "payment": { + "date": "2017-02-02", + "amount": { + "value": 0.00, + "currency": "USD" + } + } + }, + "request": { + "method": "PUT", + "url": "ExplanationOfBenefit?identifier=5824473976" + } + }, + { + "resource": { + "resourceType": "Patient", + "id": "d1a47e2c-509b-e326-deab-597e3f598ca5", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "https://example.org/front-door", + "value": "412563524-CO" + } + ], + "name": [ + { + "use": "usual", + "text": "HYOHWAN MGUIRRE", + "family": "MGUIRRE", + "given": [ + "HYOHWAN" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "719-654-0220", + "use": "home" + } + ], + "gender": "unknown", + "birthDate": "1958-05-12", + "address": [ + { + "use": "home", + "type": "postal", + "line": [ + "20360 East 45Th Court", + "PO Box 523" + ], + "city": "COLORADO SPRINGS", + "postalCode": "80922-4166" + } + ] + }, + "request": { + "method": "PUT", + "url": "Patient/d1a47e2c-509b-e326-deab-597e3f598ca5" + } + }, + { + "fullUrl": "urn:uuid:175dbf4a-7ee2-446d-9938-82eea27871a7", + "resource": { + "resourceType": "Coverage", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Coverage" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "FILL" + } + ] + }, + "system": "https://hl7.org/fhir/sid/coverageid", + "value": "412563524-CO-80001" + } + ], + "status": "active", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "HMO", + "display": "health maintenance organization policy" + } + ], + "text": "HMO - HMO COMMERCIAL-HDHP-Signature" + }, + "subscriberId": "412563524", + "beneficiary": { + "reference": "Patient/d1a47e2c-509b-e326-deab-597e3f598ca5" + }, + "relationship": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", + "code": "self", + "display": "Self" + } + ], + "text": "The Beneficiary is the Subscriber" + }, + "period": { + "start": "2016-01-01", + "end": "2017-07-01" + }, + "payor": [ + { + "reference": "Organization/5954a17b-0779-334c-4f1c-e894e45d15fb" + } + ], + "class": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/coverage-class", + "code": "group", + "display": "Group" + } + ], + "text": "An employee group" + }, + "value": "80001", + "name": "CS BRZ HDHP 5500/30%/0 ONX S-NON-MEDICARE" + } + ] + }, + "request": { + "method": "PUT", + "url": "Coverage?identifier=412563524-CO-80001" + } + }, + { + "resource": { + "resourceType": "Organization", + "id": "68ae4f74-afdc-6242-c50e-02ef776d8e5d", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Organization" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType", + "code": "npi" + } + ] + }, + "system": "http://hl7.org/fhir/sid/us-npi", + "value": "1407833767" + }, + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "TAX" + } + ] + }, + "system": "urn:oid:2.16.840.1.113883.4.4" + } + ], + "active": true, + "type": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/organization-type", + "code": "prov" + } + ] + } + ], + "name": "PIKES PEAK NEPHROLOGY ASSOCIATES PC", + "address": [ + { + "use": "work", + "type": "physical", + "line": [ + "1914 LELARAY STREET" + ], + "city": "COLORADO SPRINGS", + "postalCode": "80909", + "country": "USA" + } + ] + }, + "request": { + "method": "PUT", + "url": "Organization/68ae4f74-afdc-6242-c50e-02ef776d8e5d" + } + }, + { + "resource": { + "resourceType": "Organization", + "id": "5954a17b-0779-334c-4f1c-e894e45d15fb", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Organization" + ] + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "FILL" + } + ] + }, + "system": "https://hl7.org/fhir/sid/organizationid", + "value": "NATLTAP CO-KFHP-PAY-CO" + } + ], + "active": true, + "type": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/organization-type", + "code": "pay", + "display": "Payer" + } + ] + } + ], + "name": "KAISER FOUNDATION HEALTHPLAN, INC", + "telecom": [ + { + "system": "phone", + "value": "1-800-382-4661", + "use": "work" + } + ], + "address": [ + { + "use": "work", + "type": "postal", + "line": [ + "NATIONAL CLAIMS ADMINISTRATION COLORADO", + "PO Box 629028" + ], + "city": "El Dorado Hills", + "state": "CA", + "postalCode": "95762-9028" + } + ] + }, + "request": { + "method": "PUT", + "url": "Organization/5954a17b-0779-334c-4f1c-e894e45d15fb" + } + }, + { + "resource": { + "resourceType": "Practitioner", + "id": "23eccc61-ab67-bf8a-e464-6260f7989556", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner" + ] + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "NPI" + } + ] + }, + "system": "http://hl7.org/fhir/sid/us-npi", + "value": "1497983654" + } + ], + "name": [ + { + "use": "usual", + "text": "CASSIDY, HEATHER M (MD)", + "family": "CASSIDY", + "given": [ + "HEATHER" + ], + "suffix": [ + "MD" + ] + } + ], + "address": [ + { + "use": "work", + "line": [ + "Briargate", + "1405 Briargate Pkwy #141" + ], + "city": "Colorado Springs", + "postalCode": "80920" + } + ] + }, + "request": { + "method": "PUT", + "url": "Practitioner/23eccc61-ab67-bf8a-e464-6260f7989556" + } + }, + { + "resource": { + "resourceType": "Practitioner", + "id": "dbbc9a06-f685-b481-d739-133755af138e", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner" + ] + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "NPI" + } + ] + }, + "system": "http://hl7.org/fhir/sid/us-npi", + "value": "1568467280" + }, + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "TAX" + } + ] + }, + "system": "urn:oid:2.16.840.1.113883.4.4", + "value": "311669909" + } + ], + "name": [ + { + "use": "usual", + "text": "MOHNSSEN, STEVEN R (MD)", + "family": "MOHNSSEN", + "given": [ + "STEVEN" + ], + "suffix": [ + "MD" + ] + } + ], + "address": [ + { + "use": "work", + "line": [ + "1725 E Boulder St", + "Ste 204" + ], + "city": "Colorado Springs", + "postalCode": "80909" + } + ] + }, + "request": { + "method": "PUT", + "url": "Practitioner/dbbc9a06-f685-b481-d739-133755af138e" + } + }, + { + "resource": { + "resourceType": "Practitioner", + "id": "39b9250c-0d01-cbb0-ea89-0de9c74af511", + "meta": { + "lastUpdated": "2021-06-30", + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner" + ] + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "NPI" + } + ] + }, + "system": "http://hl7.org/fhir/sid/us-npi", + "value": "1679605265" + }, + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "TAX" + } + ] + }, + "system": "urn:oid:2.16.840.1.113883.4.4", + "value": "840629252" + } + ], + "name": [ + { + "use": "usual", + "text": "ROSS, MICHAEL D (MD)", + "family": "ROSS", + "given": [ + "MICHAEL" + ], + "suffix": [ + "MD" + ] + } + ], + "address": [ + { + "use": "work", + "line": [ + "1914 Lelaray St" + ], + "city": "Colorado Springs", + "postalCode": "80909" + } + ] + }, + "request": { + "method": "PUT", + "url": "Practitioner/39b9250c-0d01-cbb0-ea89-0de9c74af511" + } + } + ] +} diff --git a/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle-smallchanges.json b/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle-smallchanges.json index 8cc029b5a47..3a0fcabe6fd 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle-smallchanges.json +++ b/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle-smallchanges.json @@ -550,7 +550,7 @@ } ] }, - "system": "https://healthy.kaiserpermanente.org/front-door", + "system": "https://example.org/front-door", "value": "1000116-GA" } ], diff --git a/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle.json b/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle.json index 8f3a8929fa1..8bbc1f4cc01 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle.json +++ b/hapi-fhir-jpaserver-base/src/test/resources/r4/transaction-perf-bundle.json @@ -550,7 +550,7 @@ } ] }, - "system": "https://healthy.kaiserpermanente.org/front-door", + "system": "https://example.org/front-door", "value": "1000116-GA" } ], diff --git a/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/common/helper/PartitionHelper.java b/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/common/helper/PartitionHelper.java index 7049a45b79d..e6ad651f2e1 100644 --- a/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/common/helper/PartitionHelper.java +++ b/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/common/helper/PartitionHelper.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -49,7 +49,7 @@ public class PartitionHelper implements BeforeEachCallback, AfterEachCallback { private boolean myCalled = false; @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_READ) - RequestPartitionId partitionIdentifyRead(ServletRequestDetails theRequestDetails) { + RequestPartitionId partitionIdentifyRead(RequestDetails theRequestDetails) { myCalled = true; if (theRequestDetails == null) { ourLog.info("useful breakpoint :-)"); @@ -67,7 +67,7 @@ public class PartitionHelper implements BeforeEachCallback, AfterEachCallback { } @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE) - RequestPartitionId partitionIdentifyCreate(ServletRequestDetails theRequestDetails) { + RequestPartitionId partitionIdentifyCreate(RequestDetails theRequestDetails) { return RequestPartitionId.defaultPartition(); } } diff --git a/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/dstu3/CqlMeasureEvaluationDstu3Test.java b/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/dstu3/CqlMeasureEvaluationDstu3Test.java index e7a7d7c50e4..072c3e14f04 100644 --- a/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/dstu3/CqlMeasureEvaluationDstu3Test.java +++ b/hapi-fhir-jpaserver-cql/src/test/java/ca/uhn/fhir/cql/dstu3/CqlMeasureEvaluationDstu3Test.java @@ -29,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test { - Logger ourLog = LoggerFactory.getLogger(CqlMeasureEvaluationDstu3Test.class); + private static final Logger ourLog = LoggerFactory.getLogger(CqlMeasureEvaluationDstu3Test.class); @Autowired MeasureOperationsProvider myMeasureOperationsProvider; @@ -60,7 +60,7 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test { String periodStart = this.getPeriodStart(expected); String periodEnd = this.getPeriodEnd(expected); - this.ourLog.info("Measure: %s, Patient: %s, Start: %s, End: %s", measureId, patientId, periodStart, periodEnd); + ourLog.info("Measure: {}, Patient: {}, Start: {}, End: {}", measureId, patientId, periodStart, periodEnd); MeasureReport actual = this.myMeasureOperationsProvider.evaluateMeasure(new IdType("Measure", measureId), periodStart, periodEnd, null, diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java index ff7dcaf0413..fe6e2c33b21 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java @@ -38,6 +38,7 @@ public class SearchRuntimeDetails { private String myQueryString; private SearchStatusEnum mySearchStatus; private int myFoundIndexMatchesCount; + public SearchRuntimeDetails(RequestDetails theRequestDetails, String theSearchUuid) { myRequestDetails = theRequestDetails; mySearchUuid = theSearchUuid;