Merge branch '5745-ready-state-batch2' of github.com:hapifhir/hapi-fhir into 5745-ready-state-batch2
This commit is contained in:
commit
5f5ea1c49e
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 5731
|
||||
title: "Previously, transactions that had entries with `Bundle` requests failed with
|
||||
`HAPI-0339: Can not handle transaction with nested resource of type Bundle`. This has
|
||||
been fixed and now transactions are permitted that contain entries with Bundle requests
|
||||
that have a specified url (i.e. `POST` to `/Bundle`), but are rejected if no url is specified
|
||||
(i.e. nested transactions or batches)."
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 5735
|
||||
jira: SMILE-7592
|
||||
title: "Previously, complex _has then chain then _has searches would fail to return results when expected
|
||||
(ex: 'Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor._has:List:item:_id=list1')
|
||||
This has been fixed."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 5758
|
||||
title: "Batch2 jobs with reduction steps didn't fire the completion handler with the up-to-date job status.
|
||||
This has been fixed.
|
||||
"
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -119,6 +119,7 @@ import java.util.Objects;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with;
|
||||
|
@ -139,6 +140,7 @@ public class QueryStack {
|
|||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(QueryStack.class);
|
||||
public static final String LOCATION_POSITION = "Location.position";
|
||||
private static final Pattern PATTERN_DOT_AND_ALL_AFTER = Pattern.compile("\\..*");
|
||||
|
||||
private final FhirContext myFhirContext;
|
||||
private final SearchQueryBuilder mySqlBuilder;
|
||||
|
@ -1118,7 +1120,7 @@ public class QueryStack {
|
|||
targetResourceType = next.getTargetResourceType();
|
||||
paramReference = next.getReferenceFieldName();
|
||||
parameterName = next.getParameterName();
|
||||
paramName = parameterName.replaceAll("\\..*", "");
|
||||
paramName = PATTERN_DOT_AND_ALL_AFTER.matcher(parameterName).replaceAll("");
|
||||
parameters.add(QualifiedParamList.singleton(null, next.getValueAsQueryToken(myFhirContext)));
|
||||
}
|
||||
|
||||
|
@ -1175,11 +1177,23 @@ public class QueryStack {
|
|||
|
||||
// Handle internal chain inside the has.
|
||||
if (parameterName.contains(".")) {
|
||||
String chainedPartOfParameter = getChainedPart(parameterName);
|
||||
// Previously, for some unknown reason, we were calling getChainedPart() twice. This broke the _has
|
||||
// then chain, then _has use case by effectively cutting off the second part of the chain and
|
||||
// missing one iteration of the recursive call to build the query.
|
||||
// So, for example, for
|
||||
// Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor._has:List:item:_id=list1
|
||||
// instead of passing " payor._has:List:item:_id=list1" to the next recursion, the second call to
|
||||
// getChainedPart() was wrongly removing "payor." and passing down "_has:List:item:_id=list1" instead.
|
||||
// This resulted in running incorrect SQL with nonsensical join that resulted in 0 results.
|
||||
// However, after running the pipeline, I've concluded there's no use case at all for the
|
||||
// double call to "getChainedPart()", which is why there's no conditional logic at all to make a double
|
||||
// call to getChainedPart().
|
||||
final String chainedPart = getChainedPart(parameterName);
|
||||
|
||||
orValues.stream()
|
||||
.filter(qp -> qp instanceof ReferenceParam)
|
||||
.map(qp -> (ReferenceParam) qp)
|
||||
.forEach(rp -> rp.setChain(getChainedPart(chainedPartOfParameter)));
|
||||
.forEach(rp -> rp.setChain(chainedPart));
|
||||
|
||||
parameterName = parameterName.substring(0, parameterName.indexOf('.'));
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -249,10 +249,16 @@ public class Batch2CoordinatorIT extends BaseJpaR4Test {
|
|||
|
||||
AtomicBoolean completionBool = new AtomicBoolean();
|
||||
|
||||
AtomicBoolean jobStatusBool = new AtomicBoolean();
|
||||
|
||||
myCompletionHandler = (params) -> {
|
||||
// ensure our completion handler fires
|
||||
assertEquals(StatusEnum.COMPLETED, params.getInstance().getStatus());
|
||||
completionBool.getAndSet(true);
|
||||
|
||||
if (StatusEnum.COMPLETED.equals(params.getInstance().getStatus())){
|
||||
jobStatusBool.getAndSet(true);
|
||||
}
|
||||
};
|
||||
|
||||
buildAndDefine3StepReductionJob(jobId, new IReductionStepHandler() {
|
||||
|
@ -316,8 +322,9 @@ public class Batch2CoordinatorIT extends BaseJpaR4Test {
|
|||
assertTrue(instanceOp.isPresent());
|
||||
JobInstance jobInstance = instanceOp.get();
|
||||
|
||||
// ensure our completion handler fires
|
||||
// ensure our completion handler fires with the up-to-date job instance
|
||||
assertTrue(completionBool.get());
|
||||
assertTrue(jobStatusBool.get());
|
||||
|
||||
assertEquals(StatusEnum.COMPLETED, jobInstance.getStatus());
|
||||
assertEquals(1.0, jobInstance.getProgress());
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc;
|
|||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.matcher.AuthorizationSearchParamMatcher;
|
||||
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
||||
import ca.uhn.fhir.jpa.term.TermTestUtil;
|
||||
|
@ -17,6 +18,7 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
|||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
|
||||
|
@ -24,6 +26,7 @@ import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleTester;
|
|||
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
|
||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||
import ca.uhn.fhir.util.BundleBuilder;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
@ -32,13 +35,16 @@ import org.apache.http.client.methods.HttpGet;
|
|||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
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.CodeType;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Composition;
|
||||
import org.hl7.fhir.r4.model.Condition;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.ExplanationOfBenefit;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
|
@ -59,6 +65,8 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.NullSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -88,6 +96,7 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
|||
private ThreadSafeResourceDeleterSvc myThreadSafeResourceDeleterSvc;
|
||||
private AuthorizationInterceptor myReadAllBundleInterceptor;
|
||||
private AuthorizationInterceptor myReadAllPatientInterceptor;
|
||||
private AuthorizationInterceptor myWriteResourcesInTransactionAuthorizationInterceptor;
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
|
@ -98,6 +107,7 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
|||
myStorageSettings.setDeleteExpungeEnabled(true);
|
||||
myReadAllBundleInterceptor = new ReadAllAuthorizationInterceptor("Bundle");
|
||||
myReadAllPatientInterceptor = new ReadAllAuthorizationInterceptor("Patient");
|
||||
myWriteResourcesInTransactionAuthorizationInterceptor = new WriteResourcesInTransactionAuthorizationInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1653,12 +1663,136 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
|||
assertTrue(AuthorizationInterceptor.shouldExamineBundleChildResources(requestDetails, myFhirContext, bundle));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"collection", "document", "message"})
|
||||
public void testPermissionsToPostTransaction_withValidNestedBundleRequest_successfullyPostsTransactions(String theBundleType){
|
||||
BundleBuilder builder = new BundleBuilder(myFhirContext);
|
||||
builder.setType(theBundleType);
|
||||
IBaseBundle nestedBundle = builder.getBundle();
|
||||
|
||||
builder = new BundleBuilder(myFhirContext);
|
||||
builder.addTransactionCreateEntry(nestedBundle);
|
||||
IBaseBundle transaction = builder.getBundle();
|
||||
|
||||
myServer.getRestfulServer().registerInterceptor(myWriteResourcesInTransactionAuthorizationInterceptor);
|
||||
|
||||
myClient
|
||||
.transaction()
|
||||
.withBundle(transaction)
|
||||
.execute();
|
||||
|
||||
List<IBaseResource> savedBundles = myBundleDao.search(SearchParameterMap.newSynchronous(), mySrd).getAllResources();
|
||||
assertEquals(1, savedBundles.size());
|
||||
|
||||
Bundle savedBundle = (Bundle) savedBundles.get(0);
|
||||
assertEquals(theBundleType, savedBundle.getType().toCode());
|
||||
assertTrue(savedBundle.getEntry().isEmpty());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@NullSource
|
||||
@ValueSource(strings = {"", "/"})
|
||||
public void testPermissionsToPostTransaction_withInvalidNestedBundleRequest_blocksTransaction(String theInvalidUrl){
|
||||
// inner transaction
|
||||
Patient patient = new Patient();
|
||||
BundleBuilder builder = new BundleBuilder(myFhirContext);
|
||||
builder.addTransactionCreateEntry(patient);
|
||||
Bundle innerTransaction = (Bundle) builder.getBundle();
|
||||
|
||||
// outer transaction
|
||||
Bundle outerTransaction = new Bundle();
|
||||
outerTransaction.setType(Bundle.BundleType.TRANSACTION);
|
||||
Bundle.BundleEntryComponent entry = outerTransaction.addEntry();
|
||||
entry.setResource(innerTransaction);
|
||||
entry.getRequest().setUrl(theInvalidUrl).setMethod(Bundle.HTTPVerb.POST);
|
||||
|
||||
myServer.getRestfulServer().registerInterceptor(myWriteResourcesInTransactionAuthorizationInterceptor);
|
||||
|
||||
try {
|
||||
myClient
|
||||
.transaction()
|
||||
.withBundle(outerTransaction)
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
String expectedMessage = "HTTP 400 Bad Request: HAPI-2504: Can not handle nested Bundle request with url:";
|
||||
assertTrue(e.getMessage().contains(expectedMessage));
|
||||
}
|
||||
|
||||
// verify nested Patient transaction did NOT execute
|
||||
assertTrue(myPatientDao.search(SearchParameterMap.newSynchronous(), mySrd).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPermissionToPostTransaction_withUpdateParameters_blocksTransaction(){
|
||||
DateType originalBirthDate = new DateType("2000-01-01");
|
||||
Patient patient = createPatient(originalBirthDate);
|
||||
|
||||
DateType newBirthDate = new DateType("2005-01-01");
|
||||
Parameters birthDatePatch = createPatientBirthdatePatch(newBirthDate);
|
||||
|
||||
BundleBuilder bundleBuilder = new BundleBuilder(myFhirContext);
|
||||
bundleBuilder.addTransactionUpdateEntry(birthDatePatch);
|
||||
IBaseBundle transaction = bundleBuilder.getBundle();
|
||||
|
||||
myServer.getRestfulServer().registerInterceptor(myWriteResourcesInTransactionAuthorizationInterceptor);
|
||||
|
||||
try {
|
||||
myClient
|
||||
.transaction()
|
||||
.withBundle(transaction)
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
String expectedMessage = "HTTP 400 Bad Request: HAPI-0339: Can not handle nested Parameters with UPDATE operation";
|
||||
assertEquals(expectedMessage, e.getMessage());
|
||||
}
|
||||
|
||||
List<IBaseResource> allPatients = myPatientDao.search(SearchParameterMap.newSynchronous(), mySrd).getAllResources();
|
||||
assertEquals(1, allPatients.size());
|
||||
|
||||
Patient savedPatient = (Patient) allPatients.get(0);
|
||||
assertEquals(originalBirthDate.getValueAsString(), savedPatient.getBirthDateElement().getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPermissionToPostTransaction_withPatchParameters_successfullyPostsTransaction(){
|
||||
DateType originalBirthDate = new DateType("2000-01-01");
|
||||
Patient patient = createPatient(originalBirthDate);
|
||||
|
||||
DateType newBirthDate = new DateType("2005-01-01");
|
||||
Parameters birthDatePatch = createPatientBirthdatePatch(newBirthDate);
|
||||
|
||||
BundleBuilder bundleBuilder = new BundleBuilder(myFhirContext);
|
||||
bundleBuilder.addTransactionFhirPatchEntry(patient.getIdElement(), birthDatePatch);
|
||||
IBaseBundle transaction = bundleBuilder.getBundle();
|
||||
|
||||
myServer.getRestfulServer().registerInterceptor(myWriteResourcesInTransactionAuthorizationInterceptor);
|
||||
|
||||
myClient
|
||||
.transaction()
|
||||
.withBundle(transaction)
|
||||
.execute();
|
||||
|
||||
List<IBaseResource> allPatients = myPatientDao.search(SearchParameterMap.newSynchronous(), mySrd).getAllResources();
|
||||
assertEquals(1, allPatients.size());
|
||||
|
||||
Patient savedPatient = (Patient) allPatients.get(0);
|
||||
assertEquals(newBirthDate.getValueAsString(), savedPatient.getBirthDateElement().getValueAsString());
|
||||
}
|
||||
|
||||
private Patient createPatient(String theFirstName, String theLastName){
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addGiven(theFirstName).setFamily(theLastName);
|
||||
return (Patient) myPatientDao.create(patient, mySrd).getResource();
|
||||
}
|
||||
|
||||
private Patient createPatient(DateType theBirthDate) {
|
||||
Patient patient = new Patient();
|
||||
patient.setBirthDateElement(theBirthDate);
|
||||
return (Patient) myPatientDao.create(patient, mySrd).getResource();
|
||||
}
|
||||
|
||||
private Bundle createDocumentBundle(Patient thePatient){
|
||||
Composition composition = new Composition();
|
||||
composition.setType(new CodeableConcept().addCoding(new Coding().setSystem("http://example.org").setCode("some-type")));
|
||||
|
@ -1727,6 +1861,17 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
|||
return bundle;
|
||||
}
|
||||
|
||||
private Parameters createPatientBirthdatePatch(DateType theNewBirthDate){
|
||||
final Parameters patch = new Parameters();
|
||||
|
||||
final Parameters.ParametersParameterComponent op = patch.addParameter().setName("operation");
|
||||
op.addPart().setName("type").setValue(new CodeType("replace"));
|
||||
op.addPart().setName("path").setValue(new CodeType("Patient.birthDate"));
|
||||
op.addPart().setName("value").setValue(theNewBirthDate);
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
static class ReadAllAuthorizationInterceptor extends AuthorizationInterceptor {
|
||||
|
||||
private final String myResourceType;
|
||||
|
@ -1762,4 +1907,19 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
|||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
static class WriteResourcesInTransactionAuthorizationInterceptor extends AuthorizationInterceptor {
|
||||
|
||||
public WriteResourcesInTransactionAuthorizationInterceptor(){
|
||||
super(PolicyEnum.DENY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
return new RuleBuilder()
|
||||
.allow().transaction().withAnyOperation().andApplyNormalRules().andThen()
|
||||
.allow().write().allResources().withAnyId().andThen()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CarePlan;
|
||||
import org.hl7.fhir.r4.model.Coverage;
|
||||
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.Group;
|
||||
import org.hl7.fhir.r4.model.ListResource;
|
||||
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.Practitioner;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.SearchParameter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
|
||||
public class ResourceProviderR4SearchVariousScenariosTest extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4SearchVariousScenariosTest.class);
|
||||
|
||||
@Nested
|
||||
class HasMultipleNoChains {
|
||||
private static final String PAT_ID = "pat1";
|
||||
private static final String OBSERVATION_ID = "obs1";
|
||||
private static final String ENCOUNTER_ID = "enc1";
|
||||
private static final String CARE_PLAN_ID = "cp1";
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
final Patient patient = new Patient();
|
||||
patient.setId(PAT_ID);
|
||||
|
||||
final IIdType patientId = myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Observation observation = new Observation();
|
||||
observation.setId(OBSERVATION_ID);
|
||||
observation.setSubject(new Reference(patientId.getValue()));
|
||||
|
||||
final IIdType observationId = myObservationDao.update(observation, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Encounter encounter = new Encounter();
|
||||
encounter.setId(ENCOUNTER_ID);
|
||||
encounter.addReasonReference(new Reference(observationId.getValue()));
|
||||
|
||||
final IIdType encounterId = myEncounterDao.update(encounter, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final CarePlan carePlan = new CarePlan();
|
||||
carePlan.setId(CARE_PLAN_ID);
|
||||
carePlan.setEncounter(new Reference(encounterId.getValue()));
|
||||
|
||||
myCarePlanDao.update(carePlan, mySrd);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Patient?_has:Observation:subject:_has:Encounter:reason-reference:_id="+ENCOUNTER_ID,
|
||||
})
|
||||
void doubleHas(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Patient?_has:Observation:subject:_has:Encounter:reason-reference:_has:CarePlan:encounter:_id="+CARE_PLAN_ID
|
||||
})
|
||||
void tripleHas(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PractitionerEobCoveragePractitionerListNoCustomSearchParam {
|
||||
private static final String PAT_ID = "pat1";
|
||||
private static final String ORG_ID = "org1";
|
||||
private static final String ORG_NAME = "myOrg";
|
||||
private static final String PRACTITIONER_ID = "pra1";
|
||||
private static final String COVERAGE_ID = "cov1";
|
||||
private static final String LIST_ID = "list1";
|
||||
private static final String EOB_ID = "eob1";
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
myStorageSettings.setIndexOnContainedResources(false);
|
||||
final Patient patient = new Patient();
|
||||
patient.setId(PAT_ID);
|
||||
|
||||
final IIdType patientId = myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Organization organization = new Organization();
|
||||
organization.setId(ORG_ID);
|
||||
organization.setName(ORG_NAME);
|
||||
|
||||
final IIdType orgId = myOrganizationDao.update(organization, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Practitioner practitioner = new Practitioner();
|
||||
practitioner.setId(PRACTITIONER_ID);
|
||||
|
||||
final IIdType practitionerId = myPractitionerDao.update(practitioner, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Coverage coverage = new Coverage();
|
||||
coverage.setId(COVERAGE_ID);
|
||||
coverage.addPayor().setReference(orgId.getValue());
|
||||
|
||||
final IIdType coverageId = myCoverageDao.update(coverage, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final ListResource list = new ListResource();
|
||||
list.setId(LIST_ID);
|
||||
list.addEntry().setItem(new Reference(orgId.getValue()));
|
||||
|
||||
myListDao.update(list, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final ExplanationOfBenefit explanationOfBenefit = new ExplanationOfBenefit();
|
||||
explanationOfBenefit.setId(EOB_ID);
|
||||
explanationOfBenefit.setPatient(new Reference(patientId.getValue()));
|
||||
explanationOfBenefit.setInsurer(new Reference(orgId.getValue()));
|
||||
explanationOfBenefit.setProvider(new Reference(orgId.getValue()));
|
||||
explanationOfBenefit.addCareTeam().setProvider(new Reference(practitionerId.getValue()));
|
||||
explanationOfBenefit.addInsurance().setCoverage(new Reference(coverageId.getValue()));
|
||||
|
||||
myExplanationOfBenefitDao.update(explanationOfBenefit, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"ExplanationOfBenefit?coverage.payor.name="+ORG_NAME,
|
||||
})
|
||||
void chainSimple(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor="+ORG_ID, // This is the first half
|
||||
})
|
||||
void hasThenChainSimple(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"ExplanationOfBenefit?coverage.payor:Organization._has:List:item:_id="+LIST_ID
|
||||
})
|
||||
void chainThenHasSimple(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Coverage?payor._has:List:item:_id="+LIST_ID, // dot _has
|
||||
"Coverage?payor.name="+ORG_NAME,
|
||||
"Coverage?payor="+ORG_ID,
|
||||
"ExplanationOfBenefit?coverage="+COVERAGE_ID,
|
||||
"ExplanationOfBenefit?coverage.payor.name="+ORG_NAME,
|
||||
"ExplanationOfBenefit?coverage.payor="+ORG_ID,
|
||||
"ExplanationOfBenefit?coverage.payor._has:List:item:_id="+LIST_ID,
|
||||
"ExplanationOfBenefit?coverage.payor:Organization._has:List:item:_id="+LIST_ID,
|
||||
"Organization?_has:List:item:_id="+LIST_ID, // This the second half of the buggy query
|
||||
})
|
||||
void complexQueryFromList(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Organization?_has:Coverage:payor:_has:ExplanationOfBenefit:coverage:_id="+EOB_ID, // THIS WORKS!!!!!!!
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor="+ORG_ID, // this works
|
||||
// "Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor.name="+ORG_NAME, // this does not work
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:_id="+EOB_ID,
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage="+COVERAGE_ID,
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor="+ORG_ID, // This is the first half
|
||||
// "Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor._has:List:item:_id="+LIST_ID, // this doesn't work ... reomoves Organzation
|
||||
})
|
||||
void complexQueryFromPractitioner(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor._has:List:item:_id="+LIST_ID,
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor:Organization._has:List:item:_id="+LIST_ID // same thing
|
||||
})
|
||||
void hasThenChainThenHas(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@Test
|
||||
void searchWithSearchParameterHasThenChainThenChain() {
|
||||
final SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||
|
||||
final HasParam hasParam = new HasParam("ExplanationOfBenefit", "care-team", "coverage.payor:Organization._has:List:item:_id", LIST_ID);
|
||||
|
||||
searchParameterMap.add("_has", hasParam);
|
||||
|
||||
final IBundleProvider search = myPractitionerDao.search(searchParameterMap, mySrd);
|
||||
|
||||
assertFalse(search.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void searchWithSearchParameterFirstHalf() {
|
||||
final SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||
|
||||
// "Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor:Organization="+ORG_ID // This the first half of the buggy query
|
||||
final HasParam hasParam = new HasParam("ExplanationOfBenefit", "care-team", "coverage.payor", ORG_ID);
|
||||
|
||||
searchParameterMap.add("_has", hasParam);
|
||||
|
||||
final IBundleProvider search = myPractitionerDao.search(searchParameterMap, mySrd);
|
||||
|
||||
assertFalse(search.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void searchWithSearchParameterSecondHalf() {
|
||||
final SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||
|
||||
// "Organization?_has:List:item:_id="+LIST_ID, // This the second half of the buggy query
|
||||
final HasParam hasParam = new HasParam("List", "item", "_id", LIST_ID);
|
||||
|
||||
searchParameterMap.add("_has", hasParam);
|
||||
|
||||
final IBundleProvider search = myOrganizationDao.search(searchParameterMap, mySrd);
|
||||
|
||||
assertFalse(search.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PractitionerEobCoveragePractitionerGroupYesCustomSearchParam {
|
||||
private static final String PAT_ID = "pat1";
|
||||
private static final String ORG_ID = "org1";
|
||||
private static final String ORG_NAME = "myOrg";
|
||||
private static final String PRACTITIONER_ID = "pra1";
|
||||
private static final String COVERAGE_ID = "cov1";
|
||||
private static final String GROUP_ID = "grp1";
|
||||
private static final String EOB_ID = "eob1";
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
myStorageSettings.setMarkResourcesForReindexingUponSearchParameterChange(false);
|
||||
myStorageSettings.setIndexOnContainedResources(false);
|
||||
|
||||
// This is not exactly lik the production scenario but create SearchParameter first to create the RES_LINK
|
||||
// and avoid a call to reindex
|
||||
final SearchParameter searchParameterGroupValueReference = new SearchParameter();
|
||||
searchParameterGroupValueReference.setId("group-value-reference");
|
||||
searchParameterGroupValueReference.setName("group-value-reference");
|
||||
searchParameterGroupValueReference.addBase("Group");
|
||||
searchParameterGroupValueReference.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
searchParameterGroupValueReference.setCode("value-reference");
|
||||
searchParameterGroupValueReference.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
searchParameterGroupValueReference.setExpression("Group.characteristic.value.as(Reference)");
|
||||
searchParameterGroupValueReference.addTarget("Organization");
|
||||
searchParameterGroupValueReference.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||
|
||||
mySearchParameterDao.update(searchParameterGroupValueReference, mySrd);
|
||||
|
||||
final Patient patient = new Patient();
|
||||
patient.setId(PAT_ID);
|
||||
|
||||
final IIdType patientId = myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Organization organization = new Organization();
|
||||
organization.setId(ORG_ID);
|
||||
organization.setName(ORG_NAME);
|
||||
|
||||
final IIdType orgId = myOrganizationDao.update(organization, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Practitioner practitioner = new Practitioner();
|
||||
practitioner.setId(PRACTITIONER_ID);
|
||||
|
||||
final IIdType practitionerId = myPractitionerDao.update(practitioner, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Coverage coverage = new Coverage();
|
||||
coverage.setId(COVERAGE_ID);
|
||||
coverage.addPayor().setReference(orgId.getValue());
|
||||
|
||||
final IIdType coverageId = myCoverageDao.update(coverage, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
final Group group = new Group();
|
||||
group.setId(GROUP_ID);
|
||||
group.addCharacteristic().getValueReference().setReference(orgId.getValue());
|
||||
|
||||
myGroupDao.update(group, mySrd);
|
||||
|
||||
final ExplanationOfBenefit explanationOfBenefit = new ExplanationOfBenefit();
|
||||
explanationOfBenefit.setId(EOB_ID);
|
||||
explanationOfBenefit.setPatient(new Reference(patientId.getValue()));
|
||||
explanationOfBenefit.setInsurer(new Reference(orgId.getValue()));
|
||||
explanationOfBenefit.setProvider(new Reference(orgId.getValue()));
|
||||
explanationOfBenefit.addCareTeam().setProvider(new Reference(practitionerId.getValue()));
|
||||
explanationOfBenefit.addInsurance().setCoverage(new Reference(coverageId.getValue()));
|
||||
|
||||
myExplanationOfBenefitDao.update(explanationOfBenefit, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Coverage?payor.name="+ORG_NAME,
|
||||
"Coverage?payor="+ORG_ID,
|
||||
"ExplanationOfBenefit?coverage.payor.name="+ORG_NAME,
|
||||
"ExplanationOfBenefit?coverage.payor="+ORG_ID,
|
||||
"ExplanationOfBenefit?care-team="+PRACTITIONER_ID,
|
||||
"ExplanationOfBenefit?coverage="+COVERAGE_ID,
|
||||
"ExplanationOfBenefit?provider="+ORG_ID,
|
||||
})
|
||||
void complexQueryFromGroup(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Group?value-reference="+ORG_ID,
|
||||
"Organization?_has:Group:value-reference:_id="+GROUP_ID, // This the second half of the buggy query
|
||||
})
|
||||
void useCustomSearchParam(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor:Organization._has:Group:value-reference:_id="+GROUP_ID,
|
||||
})
|
||||
void hasThenChainThenHas(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:_id="+EOB_ID,
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage="+COVERAGE_ID,
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor="+ORG_ID,
|
||||
"Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor:Organization="+ORG_ID, // This the first half of the buggy query
|
||||
// "Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor._has:Group:value-reference:_id="+GROUP_ID, // this doesn't work
|
||||
// "Practitioner?_has:ExplanationOfBenefit:care-team:coverage.payor:Organization.name="+ORG_NAME // This doesn't work
|
||||
})
|
||||
void complexQueryFromPractitioner(String theQueryString) {
|
||||
runAndAssert(theQueryString);
|
||||
}
|
||||
}
|
||||
|
||||
private void runAndAssert(String theQueryString) {
|
||||
ourLog.info("queryString:\n{}", theQueryString);
|
||||
|
||||
final Bundle outcome = myClient.search()
|
||||
.byUrl(theQueryString)
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertFalse(outcome.getEntry().isEmpty());
|
||||
ourLog.info("result:\n{}", theQueryString);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.auth;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.IRuleApplier;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
|
||||
import ca.uhn.fhir.util.BundleBuilder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class RuleImplOpTest {
|
||||
private static final String OPERATION = "operation";
|
||||
private static final String TYPE = "type";
|
||||
private static final String PATH = "path";
|
||||
private static final String VALUE = "value";
|
||||
private static final String REPLACE = "replace";
|
||||
private static final String PATIENT_BIRTH_DATE = "Patient.birthDate";
|
||||
private static final Parameters PARAMETERS = buildParameters();
|
||||
private static final String DOCUMENT = "document";
|
||||
private static final String ERROR_TEMPLATE = "HAPI-0339: Can not handle transaction with nested resource of type %s";
|
||||
private static final String ERROR_PARAMETERS = String.format(ERROR_TEMPLATE, "Parameters");
|
||||
private static final String ERROR_BUNDLE = String.format(ERROR_TEMPLATE, "Bundle");
|
||||
|
||||
private static final String REQUEST_RULELIST = AuthorizationInterceptor.class.getName() + "_1_RULELIST";
|
||||
private final Patient myPatient = buildPatient();
|
||||
|
||||
private final List<IAuthRule> myRules = new RuleBuilder()
|
||||
.allow()
|
||||
.transaction()
|
||||
.withAnyOperation()
|
||||
.andApplyNormalRules()
|
||||
.andThen()
|
||||
.allow()
|
||||
.write()
|
||||
.allResources()
|
||||
.withAnyId()
|
||||
.build();
|
||||
|
||||
private final IAuthRule myRule = myRules.get(0);
|
||||
private final FhirContext myFhirContext = FhirContext.forR4Cached();
|
||||
private final IBaseBundle myInnerBundle = buildInnerBundler(myFhirContext);
|
||||
|
||||
private final RequestDetails mySystemRequestDetails = buildSystemRequestDetails(myFhirContext, myRules);
|
||||
private final IRuleApplier myRuleApplier = new AuthorizationInterceptor();
|
||||
|
||||
@Test
|
||||
void testTransactionBundleUpdateWithParameters() {
|
||||
final BundleBuilder bundleBuilder = new BundleBuilder(myFhirContext);
|
||||
bundleBuilder.addTransactionUpdateEntry(PARAMETERS);
|
||||
|
||||
try {
|
||||
applyRule(bundleBuilder.getBundle());
|
||||
fail("Expected an InvalidRequestException");
|
||||
} catch (InvalidRequestException exception) {
|
||||
assertEquals(ERROR_PARAMETERS, exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactionBundleWithNestedBundle() {
|
||||
final BundleBuilder bundleBuilder = new BundleBuilder(myFhirContext);
|
||||
bundleBuilder.addTransactionCreateEntry(myInnerBundle);
|
||||
|
||||
try {
|
||||
applyRule(bundleBuilder.getBundle());
|
||||
fail("Expected an InvalidRequestException");
|
||||
} catch (InvalidRequestException exception) {
|
||||
assertEquals(ERROR_BUNDLE, exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactionBundlePatchWithParameters() {
|
||||
final BundleBuilder bundleBuilder = new BundleBuilder(myFhirContext);
|
||||
bundleBuilder.addTransactionFhirPatchEntry(myPatient.getIdElement(), PARAMETERS);
|
||||
|
||||
final AuthorizationInterceptor.Verdict verdict = applyRule(bundleBuilder.getBundle());
|
||||
|
||||
assertThat(verdict.getDecision(), equalTo(PolicyEnum.ALLOW));
|
||||
}
|
||||
|
||||
private AuthorizationInterceptor.Verdict applyRule(IBaseBundle theBundle) {
|
||||
return myRule.applyRule(RestOperationTypeEnum.TRANSACTION, mySystemRequestDetails, theBundle, myPatient.getIdElement(), myPatient, myRuleApplier, new HashSet<>(), null);
|
||||
}
|
||||
|
||||
private static Parameters buildParameters() {
|
||||
final Parameters patch = new Parameters();
|
||||
|
||||
final Parameters.ParametersParameterComponent op = patch.addParameter().setName(OPERATION);
|
||||
op.addPart().setName(TYPE).setValue(new CodeType(REPLACE));
|
||||
op.addPart().setName(PATH).setValue(new CodeType(PATIENT_BIRTH_DATE));
|
||||
op.addPart().setName(VALUE).setValue(new StringType("1912-04-14"));
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
private static RequestDetails buildSystemRequestDetails(FhirContext theFhirContext, List<IAuthRule> theRules) {
|
||||
final SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||
systemRequestDetails.setFhirContext(theFhirContext);
|
||||
systemRequestDetails.getUserData().put(REQUEST_RULELIST, theRules);
|
||||
|
||||
return systemRequestDetails;
|
||||
}
|
||||
|
||||
private static Patient buildPatient() {
|
||||
final Patient patient = new Patient();
|
||||
patient.setId(new IdType("Patient", "1"));
|
||||
return patient;
|
||||
}
|
||||
|
||||
private static IBaseBundle buildInnerBundler(FhirContext theFhirContext) {
|
||||
final BundleBuilder innerBundleBuilder = new BundleBuilder(theFhirContext);
|
||||
innerBundleBuilder.setType(DOCUMENT);
|
||||
return innerBundleBuilder.getBundle();
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.hl7.fhir.instance.model.api.IAnyResource.SP_RES_ID;
|
||||
|
||||
|
@ -722,6 +723,11 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
boolean allComponentsAreGets = true;
|
||||
for (BundleEntryParts nextPart : inputResources) {
|
||||
|
||||
if (isInvalidNestedBundleRequest(nextPart)) {
|
||||
throw new InvalidRequestException(
|
||||
Msg.code(2504) + "Can not handle nested Bundle request with url: " + nextPart.getUrl());
|
||||
}
|
||||
|
||||
IBaseResource inputResource = nextPart.getResource();
|
||||
IIdType inputResourceId = null;
|
||||
if (isNotBlank(nextPart.getUrl())) {
|
||||
|
@ -766,20 +772,11 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
+ "Can not handle transaction with operation of type " + nextPart.getRequestType());
|
||||
}
|
||||
|
||||
/*
|
||||
* This is basically just being conservative - Be careful of transactions containing
|
||||
* nested operations and nested transactions. We block them by default. At some point
|
||||
* it would be nice to be more nuanced here.
|
||||
*/
|
||||
if (nextPart.getResource() != null) {
|
||||
RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource());
|
||||
|
||||
// TODO: LD: We should pursue a more ideal fix after the release to inspect the bundle more deeply
|
||||
// to ensure that it's a valid request
|
||||
if (shouldRejectBundleEntry(resourceDef, operation)) {
|
||||
throw new InvalidRequestException(Msg.code(339)
|
||||
+ "Can not handle transaction with nested resource of type " + resourceDef.getName());
|
||||
}
|
||||
// This is done because a Bundle can contain a nested PATCH with Parameters,
|
||||
// which is supported but a non-PATCH nested Parameters resource may be problematic.
|
||||
if (isInvalidNestedParametersRequest(ctx, nextPart, operation)) {
|
||||
throw new InvalidRequestException(Msg.code(339)
|
||||
+ String.format("Can not handle nested Parameters with %s operation", operation));
|
||||
}
|
||||
|
||||
String previousFixedConditionalUrl = theRequestDetails.getFixedConditionalUrl();
|
||||
|
@ -840,22 +837,31 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascertain whether this transaction request contains a nested operations or nested transactions.
|
||||
* This is done carefully because a bundle can contain a nested PATCH with Parameters, which is supported but
|
||||
* a non-PATCH nested Parameters resource may be problematic.
|
||||
*
|
||||
* @param theResourceDef The {@link RuntimeResourceDefinition} associated with this bundle entry
|
||||
* @param theOperation The {@link RestOperationTypeEnum} associated with this bundle entry
|
||||
* @return true if we should reject this reject
|
||||
*/
|
||||
private boolean shouldRejectBundleEntry(
|
||||
RuntimeResourceDefinition theResourceDef, RestOperationTypeEnum theOperation) {
|
||||
final boolean isResourceParameters = PARAMETERS.equals(theResourceDef.getName());
|
||||
final boolean isResourceBundle = BUNDLE.equals(theResourceDef.getName());
|
||||
private boolean isInvalidNestedBundleRequest(BundleEntryParts theEntry) {
|
||||
IBaseResource resource = theEntry.getResource();
|
||||
if (!(resource instanceof IBaseBundle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// transactions are permitted that contain entries with Bundle requests that
|
||||
// have a specified url (i.e. POST to /Bundle), but are rejected if no url is specified
|
||||
// (i.e. nested transactions or batches).
|
||||
String url = theEntry.getUrl();
|
||||
return isBlank(url) || "/".equals(url);
|
||||
}
|
||||
|
||||
private boolean isInvalidNestedParametersRequest(
|
||||
FhirContext theContext, BundleEntryParts theEntry, RestOperationTypeEnum theOperation) {
|
||||
IBaseResource resource = theEntry.getResource();
|
||||
if (resource == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition resourceDefinition = theContext.getResourceDefinition(resource);
|
||||
final boolean isResourceParameters = PARAMETERS.equals(resourceDefinition.getName());
|
||||
final boolean isOperationPatch = theOperation == RestOperationTypeEnum.PATCH;
|
||||
|
||||
return (isResourceParameters && !isOperationPatch) || isResourceBundle;
|
||||
return isResourceParameters && !isOperationPatch;
|
||||
}
|
||||
|
||||
private void setTargetFromResourceId(RequestDetails theRequestDetails, FhirContext ctx, RuleTarget target) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -65,6 +65,7 @@ import java.util.concurrent.Semaphore;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static ca.uhn.fhir.batch2.model.StatusEnum.COMPLETED;
|
||||
import static ca.uhn.fhir.batch2.model.StatusEnum.ERRORED;
|
||||
import static ca.uhn.fhir.batch2.model.StatusEnum.FINALIZE;
|
||||
import static ca.uhn.fhir.batch2.model.StatusEnum.IN_PROGRESS;
|
||||
|
@ -237,6 +238,10 @@ public class ReductionStepExecutorServiceImpl implements IReductionStepExecutorS
|
|||
|
||||
if (response.isSuccessful()) {
|
||||
reductionStepWorker.run(chunkDetails, dataSink);
|
||||
|
||||
// the ReductionStepDataSink will update the job status to COMPLETED
|
||||
// we should update instance here to keep it consistent with the newest version in persistence
|
||||
instance.setStatus(COMPLETED);
|
||||
}
|
||||
|
||||
if (response.hasSuccessfulChunksIds()) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -9,7 +9,7 @@
|
|||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<name>HAPI-FHIR</name>
|
||||
<description>An open-source implementation of the FHIR specification in Java.</description>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>7.1.5-SNAPSHOT</version>
|
||||
<version>7.1.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
Loading…
Reference in New Issue