Merge branch 'master' of https://github.com/jamesagnew/hapi-fhir
This commit is contained in:
commit
208d13ec5e
|
@ -47,5 +47,9 @@ public interface IFluentPath {
|
|||
*/
|
||||
<T extends IBase> Optional<T> evaluateFirst(IBase theInput, String thePath, Class<T> theReturnType);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parses the expression and throws an exception if it can not parse correctly
|
||||
*/
|
||||
void parse(String theExpression) throws Exception;
|
||||
}
|
||||
|
|
|
@ -1599,6 +1599,33 @@ public enum Pointcut {
|
|||
),
|
||||
|
||||
|
||||
/**
|
||||
* THIS IS AN EXPERIMENTAL HOOK AND MAY BE REMOVED OR CHANGED WITHOUT WARNING.
|
||||
*
|
||||
* Note that this is a performance tracing hook. Use with caution in production
|
||||
* systems, since calling it may (or may not) carry a cost.
|
||||
* <p>
|
||||
* This hook is invoked when a search has found an individual ID.
|
||||
* </p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* java.lang.Integer - The query ID
|
||||
* </li>
|
||||
* <li>
|
||||
* java.lang.Object - The ID
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
JPA_PERFTRACE_SEARCH_FOUND_ID(void.class,
|
||||
"java.lang.Integer",
|
||||
"java.lang.Object"
|
||||
),
|
||||
|
||||
|
||||
/**
|
||||
* Note that this is a performance tracing hook. Use with caution in production
|
||||
* systems, since calling it may (or may not) carry a cost.
|
||||
|
|
|
@ -109,6 +109,8 @@ ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor.failedToExtractPa
|
|||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1}
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1}
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.sourceParamDisabled=The _source parameter is disabled on this server
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidCodeMissingSystem=Invalid token specified for parameter {0} - No system specified: {1}|{2}
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidCodeMissingCode=Invalid token specified for parameter {0} - No code specified: {1}|{2}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.matchesFound=Matches found!
|
||||
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.noMatchesFound=No matches found!
|
||||
|
|
|
@ -1857,9 +1857,11 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
codes.addAll(myTerminologySvc.expandValueSet(code));
|
||||
} else if (modifier == TokenParamModifier.ABOVE) {
|
||||
system = determineSystemIfMissing(theParamName, code, system);
|
||||
validateHaveSystemAndCodeForToken(theParamName, code, system);
|
||||
codes.addAll(myTerminologySvc.findCodesAbove(system, code));
|
||||
} else if (modifier == TokenParamModifier.BELOW) {
|
||||
system = determineSystemIfMissing(theParamName, code, system);
|
||||
validateHaveSystemAndCodeForToken(theParamName, code, system);
|
||||
codes.addAll(myTerminologySvc.findCodesBelow(system, code));
|
||||
} else {
|
||||
codes.add(new VersionIndependentConcept(system, code));
|
||||
|
@ -1902,6 +1904,19 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void validateHaveSystemAndCodeForToken(String theParamName, String theCode, String theSystem) {
|
||||
String systemDesc = defaultIfBlank(theSystem, "(missing)");
|
||||
String codeDesc = defaultIfBlank(theCode, "(missing)");
|
||||
if (isBlank(theCode)) {
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, "invalidCodeMissingSystem", theParamName, systemDesc, codeDesc);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
if (isBlank(theSystem)) {
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, "invalidCodeMissingCode", theParamName, systemDesc, codeDesc);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private Predicate addPredicateToken(String theResourceName, String theParamName, CriteriaBuilder theBuilder, From<?, ResourceIndexedSearchParamToken> theFrom, List<VersionIndependentConcept> theTokens, TokenParamModifier theModifier, TokenModeEnum theTokenMode) {
|
||||
if (myDontUseHashesForSearch) {
|
||||
final Path<String> systemExpression = theFrom.get("mySystem");
|
||||
|
@ -2056,6 +2071,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
outerQuery.multiselect(myBuilder.countDistinct(myResourceTableRoot));
|
||||
} else {
|
||||
outerQuery.multiselect(myResourceTableRoot.get("myId").as(Long.class));
|
||||
outerQuery.distinct(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3125,6 +3141,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private final SearchRuntimeDetails mySearchRuntimeDetails;
|
||||
private final RequestDetails myRequest;
|
||||
private final boolean myHaveRawSqlHooks;
|
||||
private final boolean myHavePerftraceFoundIdHook;
|
||||
private boolean myFirst = true;
|
||||
private IncludesIterator myIncludesIterator;
|
||||
private Long myNext;
|
||||
|
@ -3143,13 +3161,16 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (myParams.getEverythingMode() != null) {
|
||||
myStillNeedToFetchIncludes = true;
|
||||
}
|
||||
|
||||
myHavePerftraceFoundIdHook =JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
|
||||
myHaveRawSqlHooks = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
|
||||
|
||||
}
|
||||
|
||||
private void fetchNext() {
|
||||
|
||||
boolean haveRawSqlHooks = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
|
||||
try {
|
||||
if (haveRawSqlHooks) {
|
||||
if (myHaveRawSqlHooks) {
|
||||
CurrentThreadCaptureQueriesListener.startCapturing();
|
||||
}
|
||||
|
||||
|
@ -3190,6 +3211,13 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (myNext == null) {
|
||||
while (myResultsIterator.hasNext()) {
|
||||
Long next = myResultsIterator.next();
|
||||
if (myHavePerftraceFoundIdHook) {
|
||||
HookParams params = new HookParams()
|
||||
.add(Integer.class, System.identityHashCode(this))
|
||||
.add(Object.class, next);
|
||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
|
||||
}
|
||||
|
||||
if (next != null) {
|
||||
if (myPidSet.add(next)) {
|
||||
myNext = next;
|
||||
|
@ -3228,7 +3256,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
mySearchRuntimeDetails.setFoundMatchesCount(myPidSet.size());
|
||||
|
||||
} finally {
|
||||
if (haveRawSqlHooks) {
|
||||
if (myHaveRawSqlHooks) {
|
||||
SqlQueryList capturedQueries = CurrentThreadCaptureQueriesListener.getCurrentQueueAndStopCapturing();
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, myRequest)
|
||||
|
|
|
@ -145,10 +145,9 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
|||
|
||||
} else {
|
||||
|
||||
FHIRPathEngine fhirPathEngine = new FHIRPathEngine(new HapiWorkerContext(theContext, VALIDATION_SUPPORT));
|
||||
try {
|
||||
fhirPathEngine.parse(theExpression);
|
||||
} catch (FHIRLexer.FHIRLexerException e) {
|
||||
theContext.newFluentPath().parse(theExpression);
|
||||
} catch (Exception e) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + theExpression + "\": " + e.getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated Use ca.uhn.fhir.jpa.binstore.BinaryAccessProvider instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class BinaryAccessProvider extends ca.uhn.fhir.jpa.binstore.BinaryAccessProvider {
|
||||
|
||||
// FIXME: JA delete before 4.0.0
|
||||
|
||||
}
|
|
@ -233,6 +233,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
ourLog.trace("Going to try to start next search");
|
||||
Optional<Search> newSearch = mySearchCacheSvc.tryToMarkSearchAsInProgress(search);
|
||||
if (newSearch.isPresent()) {
|
||||
ourLog.trace("Launching new search");
|
||||
search = newSearch.get();
|
||||
String resourceType = search.getResourceType();
|
||||
SearchParameterMap params = search.getSearchParameterMap().orElseThrow(() -> new IllegalStateException("No map in PASSCOMPLET search"));
|
||||
|
@ -1113,7 +1114,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
throw newResourceGoneException(getSearch().getUuid());
|
||||
}
|
||||
|
||||
ourLog.debug("Have {} previously added IDs in search: {}", previouslyAddedResourcePids.size(), getSearch().getUuid());
|
||||
ourLog.trace("Have {} previously added IDs in search: {}", previouslyAddedResourcePids.size(), getSearch().getUuid());
|
||||
setPreviouslyAddedResourcePids(previouslyAddedResourcePids);
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -2,10 +2,15 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
|
||||
|
@ -34,7 +39,11 @@ import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
|||
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
|
||||
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
|
||||
import org.junit.*;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
|
@ -45,11 +54,29 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@SuppressWarnings({"unchecked", "Duplicates"})
|
||||
|
@ -1978,6 +2005,52 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchOnCodesWithBelow() {
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setUrl("http://foo");
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.addConcept().setCode("111-1")
|
||||
.addConcept().setCode("111-2");
|
||||
cs.addConcept().setCode("222-1")
|
||||
.addConcept().setCode("222-2");
|
||||
myCodeSystemDao.create(cs);
|
||||
|
||||
Observation obs1 = new Observation();
|
||||
obs1.getCode().addCoding().setSystem("http://foo").setCode("111-1");
|
||||
String id1 = myObservationDao.create(obs1).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Observation obs2 = new Observation();
|
||||
obs2.getCode().addCoding().setSystem("http://foo").setCode("111-2");
|
||||
String id2 = myObservationDao.create(obs2).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
|
||||
IBundleProvider result;
|
||||
|
||||
result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE, new TokenParam("http://foo", "111-1")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result).toString(), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1));
|
||||
|
||||
result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE, new TokenParam("http://foo", "111-1").setModifier(TokenParamModifier.BELOW)));
|
||||
assertThat(toUnqualifiedVersionlessIds(result).toString(), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1, id2));
|
||||
|
||||
try {
|
||||
myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE, new TokenParam(null, "111-1").setModifier(TokenParamModifier.BELOW)));
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid token specified for parameter code - No code specified: (missing)|111-1", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE, new TokenParam("111-1", null).setModifier(TokenParamModifier.BELOW)));
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid token specified for parameter code - No system specified: 111-1|(missing)", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchParamChangesType() {
|
||||
String name = "testSearchParamChangesType";
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.gclient.ReferenceClientParam;
|
||||
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
|
@ -30,25 +37,32 @@ import org.springframework.transaction.TransactionStatus;
|
|||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderCustomSearchParamR4Test.class);
|
||||
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(new ModelConfig().isDefaultSearchParamsCanBeOverridden());
|
||||
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,7 +80,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
}
|
||||
|
||||
private Map<String, CapabilityStatementRestResourceSearchParamComponent> extractSearchParams(CapabilityStatement conformance, String resType) {
|
||||
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = new HashMap<String, CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent>();
|
||||
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = new HashMap<>();
|
||||
for (CapabilityStatementRestComponent nextRest : conformance.getRest()) {
|
||||
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
|
||||
if (!resType.equals(nextResource.getType())) {
|
||||
|
@ -81,7 +95,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
}
|
||||
|
||||
@Test
|
||||
public void saveCreateSearchParamInvalidWithMissingStatus() throws IOException {
|
||||
public void saveCreateSearchParamInvalidWithMissingStatus() {
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.setCode("foo");
|
||||
sp.setExpression("Patient.gender");
|
||||
|
@ -101,9 +115,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||
|
||||
CapabilityStatement conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
CapabilityStatementRestResourceSearchParamComponent param = map.get("foo");
|
||||
|
@ -153,9 +167,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
});
|
||||
|
||||
conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
param = map.get("foo");
|
||||
|
@ -171,9 +185,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
myModelConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||
|
||||
CapabilityStatement conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
CapabilityStatementRestResourceSearchParamComponent param = map.get("foo");
|
||||
|
@ -208,9 +222,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
param = map.get("foo");
|
||||
|
@ -247,7 +261,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(fooSp, mySrd);
|
||||
|
||||
runInTransaction(()->{
|
||||
runInTransaction(() -> {
|
||||
List<ResourceReindexJobEntity> allJobs = myResourceReindexJobDao.findAll();
|
||||
assertEquals(1, allJobs.size());
|
||||
assertEquals("Patient", allJobs.get(0).getResourceType());
|
||||
|
@ -314,9 +328,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp));
|
||||
|
||||
ourClient
|
||||
.create()
|
||||
.resource(eyeColourSp)
|
||||
.execute();
|
||||
.create()
|
||||
.resource(eyeColourSp)
|
||||
.execute();
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
|
@ -333,11 +347,11 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
|
||||
|
||||
Bundle bundle = ourClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||
|
||||
|
@ -380,11 +394,11 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
Bundle result;
|
||||
|
||||
result = ourClient
|
||||
.search()
|
||||
.forResource(Observation.class)
|
||||
.where(new ReferenceClientParam("foo").hasChainedProperty(Patient.GENDER.exactly().code("male")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
.search()
|
||||
.forResource(Observation.class)
|
||||
.where(new ReferenceClientParam("foo").hasChainedProperty(Patient.GENDER.exactly().code("male")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
foundResources = toUnqualifiedVersionlessIdValues(result);
|
||||
assertThat(foundResources, contains(obsId1.getValue()));
|
||||
|
||||
|
@ -420,17 +434,138 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
Bundle result;
|
||||
|
||||
result = ourClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new TokenClientParam("foo").exactly().code("male"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new TokenClientParam("foo").exactly().code("male"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
foundResources = toUnqualifiedVersionlessIdValues(result);
|
||||
assertThat(foundResources, contains(patId.getValue()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #1300
|
||||
*/
|
||||
@Test
|
||||
public void testCustomParameterMatchingManyValues() {
|
||||
|
||||
List<String> found = new ArrayList<>();
|
||||
|
||||
class Interceptor {
|
||||
@Hook(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID)
|
||||
public void foundId(Integer theSearchId, Object theId) {
|
||||
found.add(theSearchId + "/" + theId);
|
||||
}
|
||||
}
|
||||
Interceptor interceptor = new Interceptor();
|
||||
myInterceptorRegistry.registerInterceptor(interceptor);
|
||||
try {
|
||||
myDaoConfig.setAllowContainsSearches(true);
|
||||
|
||||
// Add a custom search parameter
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
fooSp.addBase("Questionnaire");
|
||||
fooSp.setCode("item-text");
|
||||
fooSp.setName("item-text");
|
||||
fooSp.setType(Enumerations.SearchParamType.STRING);
|
||||
fooSp.setTitle("FOO SP");
|
||||
fooSp.setExpression("Questionnaire.item.text | Questionnaire.item.item.text | Questionnaire.item.item.item.text");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(fooSp, mySrd);
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
int textIndex = 0;
|
||||
List<Long> ids = new ArrayList<>();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
//Lots and lots of matches
|
||||
Questionnaire q = new Questionnaire();
|
||||
q
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++));
|
||||
q
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++));
|
||||
q
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++));
|
||||
q
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++));
|
||||
q
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++))
|
||||
.addItem()
|
||||
.setText("Section " + (textIndex++));
|
||||
ids.add(myQuestionnaireDao.create(q).getId().getIdPartAsLong());
|
||||
}
|
||||
|
||||
int foundCount = 0;
|
||||
Bundle bundle = null;
|
||||
List<Long> actualIds = new ArrayList<>();
|
||||
do {
|
||||
|
||||
if (bundle == null) {
|
||||
bundle = ourClient
|
||||
.search()
|
||||
.byUrl(ourServerBase + "/Questionnaire?item-text=Section")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
} else {
|
||||
bundle = ourClient
|
||||
.loadPage()
|
||||
.next(bundle)
|
||||
.execute();
|
||||
}
|
||||
List<IBaseResource> resources = BundleUtil.toListOfResources(myFhirCtx, bundle);
|
||||
resources.forEach(t -> actualIds.add(t.getIdElement().getIdPartAsLong()));
|
||||
foundCount += resources.size();
|
||||
|
||||
} while (bundle.getLink("next") != null);
|
||||
|
||||
ourLog.info("Found: {}", found);
|
||||
|
||||
runInTransaction(() -> {
|
||||
|
||||
List<Search> searches = mySearchEntityDao.findAll();
|
||||
assertEquals(1, searches.size());
|
||||
Search search = searches.get(0);
|
||||
String message = "\nWanted: " + (ids) + "\n" +
|
||||
"Actual: " + (actualIds) + "\n" +
|
||||
"Found : " + (found) + "\n" +
|
||||
search.toString();
|
||||
assertEquals(message, 200, search.getNumFound());
|
||||
assertEquals(message, 200, search.getTotalCount().intValue());
|
||||
assertEquals(message, SearchStatusEnum.FINISHED, search.getStatus());
|
||||
});
|
||||
|
||||
assertEquals(200, foundCount);
|
||||
} finally {
|
||||
myInterceptorRegistry.unregisterInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
|
|
|
@ -7,7 +7,13 @@ import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
|||
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.PrimitiveType;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.Type;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -24,8 +30,13 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
|
||||
|
||||
|
@ -116,9 +127,7 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
|
||||
runInTransaction(() -> {
|
||||
await().until(() -> myTermConceptMapDao.count(), greaterThan(0L));
|
||||
});
|
||||
await().until(() -> runInTransaction(() -> myTermConceptMapDao.count()), greaterThan(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"resourceType": "SearchParameter",
|
||||
"id": "54805",
|
||||
"meta": {
|
||||
"versionId": "1",
|
||||
"lastUpdated": "2019-11-04T18:33:47.918+00:00",
|
||||
"source": "#pt4ERkOO6LGm5ZoA"
|
||||
},
|
||||
"text": {
|
||||
"status": "generated",
|
||||
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Search for a questionnaire by item.text nested field - up to 3 levels.</div>"
|
||||
},
|
||||
"url": "https://impact-fhir.mitre.org/r4/SearchParameter/QuestionnaireItemText",
|
||||
"version": "0.0.1",
|
||||
"name": "QuestionnaireItemText",
|
||||
"status": "active",
|
||||
"date": "2019-10-25T15:38:45-04:00",
|
||||
"description": "Search for a questionnaire by item.text nested field - up to 3 levels.",
|
||||
"code": "item-text",
|
||||
"base": [
|
||||
"Questionnaire"
|
||||
],
|
||||
"type": "string",
|
||||
"expression": "Questionnaire.item.text | Questionnaire.item.item.text | Questionnaire.item.item.item.text",
|
||||
"xpathUsage": "normal",
|
||||
"modifier": [
|
||||
"contains"
|
||||
]
|
||||
}
|
|
@ -49,4 +49,9 @@ public class FluentPathDstu3 implements IFluentPath {
|
|||
return evaluate(theInput, thePath, theReturnType).stream().findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String theExpression) {
|
||||
myEngine.parse(theExpression);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,5 +49,10 @@ public class FluentPathR4 implements IFluentPath {
|
|||
return evaluate(theInput, thePath, theReturnType).stream().findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String theExpression) {
|
||||
myEngine.parse(theExpression);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -49,5 +49,10 @@ public class FhirPathR5 implements IFluentPath {
|
|||
return evaluate(theInput, thePath, theReturnType).stream().findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String theExpression) {
|
||||
myEngine.parse(theExpression);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -501,6 +501,18 @@
|
|||
A NullPointerException in the XML Parser was fixed when serializing a resource containing an extension
|
||||
on a primitive datatype that was missing a URL declaration.
|
||||
</action>
|
||||
<action type="fix">
|
||||
When using the _filter search parameter in the JPA server with an untyped resource ID, the
|
||||
filter could bring in search results of the wrong type. Thanks to Anthony Sute for the Pull
|
||||
Request and Jens Villadsen for reporting!
|
||||
</action>
|
||||
<action type="fix" issue="1300">
|
||||
In some cases where where a single search parameter matches the same resource many times with
|
||||
different distinct values (e.g. a search by Patient:name where there are hundreds of patients having
|
||||
hundreds of distinct names each) the Search Coordinator would end up in an infinite loop and never
|
||||
return all of the possible results. Thanks to @imranmoezkhan for reporting, and to
|
||||
Tim Shaffer for providing a reproducible test case!
|
||||
</action>
|
||||
</release>
|
||||
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
|
||||
<action type="fix">
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<br/>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=jamesagnew&repo=hapi-fhir&type=fork&count=true&v=2" frameborder="0" scrolling="0" width="100px" height="20px"></iframe>
|
||||
<br/>
|
||||
<a href="https://travis-ci.org/jamesagnew/hapi-fhir"><img src="https://travis-ci.org/jamesagnew/hapi-fhir.svg?branch=master" alt="Build Status"/></a>
|
||||
<a href="https://dev.azure.com/jamesagnew214/jamesagnew214/_build/latest?definitionId=1&branchName=master"><img src="https://dev.azure.com/jamesagnew214/jamesagnew214/_apis/build/status/jamesagnew.hapi-fhir?branchName=master" alt="Build Status"/></a>
|
||||
<br/>
|
||||
<a href="https://coveralls.io/r/jamesagnew/hapi-fhir?branch=master"><img src="https://coveralls.io/repos/jamesagnew/hapi-fhir/badge.svg?branch=master" alt="Coverage Status"/></a>
|
||||
<a href="https://codecov.io/gh/jamesagnew/hapi-fhir"><img src="https://codecov.io/gh/jamesagnew/hapi-fhir/branch/master/graph/badge.svg" alt="Coverage Status"/></a>
|
||||
<br/>
|
||||
<a href="https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg"><img src="https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg" alt="Maven Central"/></a>
|
||||
<br/>
|
||||
|
|
Loading…
Reference in New Issue