entityChunk = prefetchResourceTableHistoryAndProvenance(idChunk);
if (thePreFetchIndexes) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
index 0bccd1b6c67..5e3037a60b8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
@@ -135,13 +135,13 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
@Override
public ExtendedHSearchIndexData extractLuceneIndexData(
- IBaseResource theResource, ResourceIndexedSearchParams theNewParams) {
+ IBaseResource theResource, ResourceTable theEntity, ResourceIndexedSearchParams theNewParams) {
String resourceType = myFhirContext.getResourceType(theResource);
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(
resourceType, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH);
ExtendedHSearchIndexExtractor extractor = new ExtendedHSearchIndexExtractor(
myStorageSettings, myFhirContext, activeSearchParams, mySearchParamExtractor);
- return extractor.extract(theResource, theNewParams);
+ return extractor.extract(theResource, theEntity, theNewParams);
}
@Override
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFulltextSearchSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFulltextSearchSvc.java
index 52dd7589947..0b795fb36a8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFulltextSearchSvc.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFulltextSearchSvc.java
@@ -89,7 +89,7 @@ public interface IFulltextSearchSvc {
boolean isDisabled();
ExtendedHSearchIndexData extractLuceneIndexData(
- IBaseResource theResource, ResourceIndexedSearchParams theNewParams);
+ IBaseResource theResource, ResourceTable theEntity, ResourceIndexedSearchParams theNewParams);
/**
* Returns true if the parameter map can be handled for hibernate search.
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java
index 37a2a8830ef..e33b4c293d3 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java
@@ -392,7 +392,7 @@ public class ExtendedHSearchClauseBuilder {
/**
* Create date clause from date params. The date lower and upper bounds are taken
- * into considertion when generating date query ranges
+ * into consideration when generating date query ranges
*
* Example 1 ('eq' prefix/empty): http://fhirserver/Observation?date=eq2020
* would generate the following search clause
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java
index 63642b2b4f1..86f99a60316 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java
@@ -25,6 +25,8 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
+import ca.uhn.fhir.jpa.model.entity.ResourceTable;
+import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.jpa.model.search.CompositeSearchIndexData;
import ca.uhn.fhir.jpa.model.search.DateSearchIndexData;
import ca.uhn.fhir.jpa.model.search.ExtendedHSearchIndexData;
@@ -37,6 +39,7 @@ import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
import ca.uhn.fhir.util.MetaUtil;
import com.google.common.base.Strings;
import jakarta.annotation.Nonnull;
+import org.apache.commons.lang3.ObjectUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -73,9 +76,10 @@ public class ExtendedHSearchIndexExtractor {
mySearchParamExtractor = theSearchParamExtractor;
}
- @Nonnull
- public ExtendedHSearchIndexData extract(IBaseResource theResource, ResourceIndexedSearchParams theNewParams) {
- ExtendedHSearchIndexData retVal = new ExtendedHSearchIndexData(myContext, myJpaStorageSettings, theResource);
+ public ExtendedHSearchIndexData extract(
+ IBaseResource theResource, ResourceTable theEntity, ResourceIndexedSearchParams theNewParams) {
+ ExtendedHSearchIndexData retVal =
+ new ExtendedHSearchIndexData(myContext, myJpaStorageSettings, theResource, theEntity);
if (myJpaStorageSettings.isStoreResourceInHSearchIndex()) {
retVal.setRawResourceData(myContext.newJsonParser().encodeResourceToString(theResource));
@@ -113,11 +117,27 @@ public class ExtendedHSearchIndexExtractor {
.filter(nextParam -> !nextParam.isMissing())
.forEach(nextParam -> retVal.addUriIndexData(nextParam.getParamName(), nextParam.getUri()));
- theResource.getMeta().getTag().forEach(tag -> retVal.addTokenIndexData("_tag", tag));
+ theEntity.getTags().forEach(tag -> {
+ TagDefinition td = tag.getTag();
- theResource.getMeta().getSecurity().forEach(sec -> retVal.addTokenIndexData("_security", sec));
-
- theResource.getMeta().getProfile().forEach(prof -> retVal.addUriIndexData("_profile", prof.getValue()));
+ IBaseCoding coding = (IBaseCoding) myContext.getVersion().newCodingDt();
+ coding.setVersion(td.getVersion());
+ coding.setDisplay(td.getDisplay());
+ coding.setCode(td.getCode());
+ coding.setSystem(td.getSystem());
+ coding.setUserSelected(ObjectUtils.defaultIfNull(td.getUserSelected(), false));
+ switch (td.getTagType()) {
+ case TAG:
+ retVal.addTokenIndexData("_tag", coding);
+ break;
+ case PROFILE:
+ retVal.addUriIndexData("_profile", coding.getCode());
+ break;
+ case SECURITY_LABEL:
+ retVal.addTokenIndexData("_security", coding);
+ break;
+ }
+ });
String source = MetaUtil.getSource(myContext, theResource.getMeta());
if (isNotBlank(source)) {
@@ -127,20 +147,14 @@ public class ExtendedHSearchIndexExtractor {
theNewParams.myCompositeParams.forEach(nextParam ->
retVal.addCompositeIndexData(nextParam.getSearchParamName(), buildCompositeIndexData(nextParam)));
- if (theResource.getMeta().getLastUpdated() != null) {
- int ordinal = ResourceIndexedSearchParamDate.calculateOrdinalValue(
- theResource.getMeta().getLastUpdated())
+ if (theEntity.getUpdated() != null && !theEntity.getUpdated().isEmpty()) {
+ int ordinal = ResourceIndexedSearchParamDate.calculateOrdinalValue(theEntity.getUpdatedDate())
.intValue();
retVal.addDateIndexData(
- "_lastUpdated",
- theResource.getMeta().getLastUpdated(),
- ordinal,
- theResource.getMeta().getLastUpdated(),
- ordinal);
+ "_lastUpdated", theEntity.getUpdatedDate(), ordinal, theEntity.getUpdatedDate(), ordinal);
}
if (!theNewParams.myLinks.isEmpty()) {
-
// awkwardly, links are indexed by jsonpath, not by search param.
// so we re-build the linkage.
Map> linkPathToParamName = new HashMap<>();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java
index 79029f95585..e67d0907705 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchSearchBuilder.java
@@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
+import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
@@ -89,19 +90,29 @@ public class ExtendedHSearchSearchBuilder {
* be inaccurate and wrong.
*/
public boolean canUseHibernateSearch(
- String theResourceType, SearchParameterMap myParams, ISearchParamRegistry theSearchParamRegistry) {
+ String theResourceType, SearchParameterMap theParams, ISearchParamRegistry theSearchParamRegistry) {
boolean canUseHibernate = false;
ResourceSearchParams resourceActiveSearchParams = theSearchParamRegistry.getActiveSearchParams(
- theResourceType, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH);
- for (String paramName : myParams.keySet()) {
+ theResourceType, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH);
+
+ // special SearchParam handling:
+ // _lastUpdated
+ if (theParams.getLastUpdated() != null) {
+ canUseHibernate = !illegalForHibernateSearch(Constants.PARAM_LASTUPDATED, resourceActiveSearchParams);
+ if (!canUseHibernate) {
+ return false;
+ }
+ }
+
+ for (String paramName : theParams.keySet()) {
// is this parameter supported?
if (illegalForHibernateSearch(paramName, resourceActiveSearchParams)) {
canUseHibernate = false;
} else {
// are the parameter values supported?
canUseHibernate =
- myParams.get(paramName).stream()
+ theParams.get(paramName).stream()
.flatMap(Collection::stream)
.collect(Collectors.toList())
.stream()
@@ -136,6 +147,7 @@ public class ExtendedHSearchSearchBuilder {
// not yet supported in HSearch
myParams.getSearchContainedMode() == SearchContainedModeEnum.FALSE
+ && supportsLastUpdated(myParams)
&& // ???
myParams.entrySet().stream()
.filter(e -> !ourUnsafeSearchParmeters.contains(e.getKey()))
@@ -145,6 +157,19 @@ public class ExtendedHSearchSearchBuilder {
.allMatch(this::isParamTypeSupported);
}
+ private boolean supportsLastUpdated(SearchParameterMap theMap) {
+ if (theMap.getLastUpdated() == null || theMap.getLastUpdated().isEmpty()) {
+ return true;
+ }
+
+ DateRangeParam lastUpdated = theMap.getLastUpdated();
+
+ return lastUpdated.getLowerBound() != null
+ && isParamTypeSupported(lastUpdated.getLowerBound())
+ && lastUpdated.getUpperBound() != null
+ && isParamTypeSupported(lastUpdated.getUpperBound());
+ }
+
/**
* Do we support this query param type+modifier?
*
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
index 477999b3d3e..f169fba81d8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
@@ -605,12 +605,12 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(SearchParameterMap.class, theParams)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
- Object outcome = CompositeInterceptorBroadcaster.doCallHooksAndReturnObject(
+ boolean canUseCache = CompositeInterceptorBroadcaster.doCallHooks(
myInterceptorBroadcaster,
theRequestDetails,
Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH,
params);
- if (Boolean.FALSE.equals(outcome)) {
+ if (!canUseCache) {
return null;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java
index e9f07b798a5..f443a55be83 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java
@@ -1044,7 +1044,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
if (theExpansionOptions != null
&& !theExpansionOptions.isFailOnMissingCodeSystem()
// Code system is unknown, therefore NOT_FOUND
- && e.getCodeValidationIssue().getCoding() == CodeValidationIssueCoding.NOT_FOUND) {
+ && e.getCodeValidationIssue()
+ .hasIssueDetailCode(CodeValidationIssueCoding.NOT_FOUND.getCode())) {
return;
}
throw new InternalErrorException(Msg.code(888) + e);
@@ -2190,7 +2191,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
.setSeverity(IssueSeverity.ERROR)
.setCodeSystemVersion(theCodeSystemVersion)
.setMessage(theMessage)
- .addCodeValidationIssue(new CodeValidationIssue(
+ .addIssue(new CodeValidationIssue(
theMessage,
IssueSeverity.ERROR,
CodeValidationIssueCode.CODE_INVALID,
diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java
index 1770bcd910c..3216b970201 100644
--- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java
+++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java
@@ -121,7 +121,6 @@ import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -130,13 +129,11 @@ import java.util.stream.Collectors;
import static ca.uhn.fhir.jpa.model.util.UcumServiceUtil.UCUM_CODESYSTEM_URL;
import static ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8;
-import static ca.uhn.fhir.rest.api.Constants.HEADER_CACHE_CONTROL;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.when;
@ExtendWith(SpringExtension.class)
@ExtendWith(MockitoExtension.class)
@@ -294,6 +291,20 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl
}
}
+ @Test //TODO LS : This test fails, and did not before.
+ public void testNoOpUpdateDoesNotModifyLastUpdated() throws InterruptedException {
+ myStorageSettings.setAdvancedHSearchIndexing(true);
+ Patient patient = new Patient();
+ patient.getNameFirstRep().setFamily("graham").addGiven("gary");
+
+ patient = (Patient) myPatientDao.create(patient).getResource();
+ Date originalLastUpdated = patient.getMeta().getLastUpdated();
+
+ patient = (Patient) myPatientDao.update(patient).getResource();
+ Date newLastUpdated = patient.getMeta().getLastUpdated();
+
+ assertThat(originalLastUpdated).isEqualTo(newLastUpdated);
+ }
@Test
public void testFullTextSearchesArePerformanceLogged() {
diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java
index 2bfc2ae1b2a..1ef05d8abf4 100644
--- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java
+++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java
@@ -108,7 +108,6 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest implements I
when(mySrd.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE)).thenReturn(Boolean.TRUE);
}
-
@AfterEach
public void after() {
myStorageSettings.setMaximumExpansionSize(JpaStorageSettings.DEFAULT_MAX_EXPANSION_SIZE);
@@ -217,7 +216,6 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest implements I
}
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd(CS_URL, additions);
-
// Codes available exceeds the max
myStorageSettings.setMaximumExpansionSize(50);
ValueSet vs = new ValueSet();
@@ -236,7 +234,6 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest implements I
include.setSystem(CS_URL);
ValueSet outcome = myTermSvc.expandValueSet(null, vs);
assertThat(outcome.getExpansion().getContains()).hasSize(109);
-
}
@Test
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
index 33a288ee811..ee0e08ef7ea 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
+++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
@@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.mdm.helper;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
@@ -23,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.function.Supplier;
import static org.awaitility.Awaitility.await;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
/**
@@ -78,6 +80,7 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
//they are coming from an external HTTP Request.
MockitoAnnotations.initMocks(this);
when(myMockSrd.getInterceptorBroadcaster()).thenReturn(myMockInterceptorBroadcaster);
+ when(myMockInterceptorBroadcaster.callHooks(any(Pointcut.class), any(HookParams.class))).thenReturn(true);
when(myMockSrd.getServletRequest()).thenReturn(myMockServletRequest);
when(myMockSrd.getServer()).thenReturn(myMockRestfulServer);
when(myMockSrd.getRequestId()).thenReturn("MOCK_REQUEST");
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/ExtendedHSearchIndexData.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/ExtendedHSearchIndexData.java
index 9af2aabf4de..ef8f9967286 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/ExtendedHSearchIndexData.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/ExtendedHSearchIndexData.java
@@ -20,6 +20,7 @@
package ca.uhn.fhir.jpa.model.search;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import com.google.common.collect.HashMultimap;
@@ -57,12 +58,17 @@ public class ExtendedHSearchIndexData {
private String myForcedId;
private String myResourceJSON;
private IBaseResource myResource;
+ private ResourceTable myEntity;
public ExtendedHSearchIndexData(
- FhirContext theFhirContext, StorageSettings theStorageSettings, IBaseResource theResource) {
+ FhirContext theFhirContext,
+ StorageSettings theStorageSettings,
+ IBaseResource theResource,
+ ResourceTable theEntity) {
this.myFhirContext = theFhirContext;
this.myStorageSettings = theStorageSettings;
myResource = theResource;
+ myEntity = theEntity;
}
private BiConsumer ifNotContained(BiConsumer theIndexWriter) {
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java
index f73670fbddc..2eb8a60aa2e 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java
@@ -62,6 +62,9 @@ public abstract class BaseComboParamsR4Test extends BaseJpaR4Test {
myMessages.add("REUSING CACHED SEARCH");
return null;
});
+
+ // allow searches to use cached results
+ when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH), ArgumentMatchers.any(HookParams.class))).thenReturn(true);
}
@AfterEach
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
index 636ffd02b40..6b10598174b 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
@@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.i18n.Msg;
+import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@@ -23,6 +24,8 @@ import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.ServiceRequest;
import org.hl7.fhir.r4.model.ServiceRequest.ServiceRequestIntent;
import org.hl7.fhir.r4.model.ServiceRequest.ServiceRequestStatus;
+import org.hl7.fhir.r4.model.Specimen;
+import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
index 4c9464fa4d1..4668a4f590a 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
@@ -2549,7 +2549,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
- assertEquals(3, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
+ assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
index 4ad723edfa3..f835c300c19 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4VersionedReferenceTest.java
@@ -428,7 +428,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
IdType observationId = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
// Make sure we're not introducing any extra DB operations
- assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(3);
+ assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(2);
// Read back and verify that reference is now versioned
observation = myObservationDao.read(observationId);
@@ -463,7 +463,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
IdType observationId = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
// Make sure we're not introducing any extra DB operations
- assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(4);
+ assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(3);
// Read back and verify that reference is now versioned
observation = myObservationDao.read(observationId);
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSearchDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSearchDaoR4Test.java
index a0f80459464..14fa8d5dc88 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSearchDaoR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSearchDaoR4Test.java
@@ -14,9 +14,6 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
-import ca.uhn.fhir.rest.param.TokenAndListParam;
-import ca.uhn.fhir.rest.param.TokenOrListParam;
-import ca.uhn.fhir.rest.param.TokenParam;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.AfterEach;
@@ -29,7 +26,6 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
-import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@@ -304,169 +300,4 @@ public class FhirSearchDaoR4Test extends BaseJpaR4Test implements IR4SearchIndex
assertThat(JpaPid.toLongList(found)).isEmpty();
}
}
-
- @Test
- public void testSearchNarrativeWithLuceneSearch() {
- final int numberOfPatientsToCreate = SearchBuilder.getMaximumPageSize() + 10;
- List expectedActivePatientIds = new ArrayList<>(numberOfPatientsToCreate);
-
- for (int i = 0; i < numberOfPatientsToCreate; i++) {
- Patient patient = new Patient();
- patient.getText().setDivAsString("");
- expectedActivePatientIds.add(myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getIdPart());
- }
-
- {
- Patient patient = new Patient();
- patient.getText().setDivAsString("");
- myPatientDao.create(patient, mySrd);
- }
- {
- Patient patient = new Patient();
- patient.getText().setDivAsString("ZZYZXY
");
- myPatientDao.create(patient, mySrd);
- }
-
- SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
- map.add(Constants.PARAM_TEXT, new StringParam("AAAS"));
-
- IBundleProvider searchResultBundle = myPatientDao.search(map, mySrd);
- List resourceIdsFromSearchResult = searchResultBundle.getAllResourceIds();
-
- assertThat(resourceIdsFromSearchResult).containsExactlyInAnyOrderElementsOf(expectedActivePatientIds);
- }
-
- @Test
- public void searchLuceneAndJPA_withLuceneMatchingButJpaNot_returnsNothing() {
- // setup
- int numToCreate = 2 * SearchBuilder.getMaximumPageSize() + 10;
-
- // create resources
- for (int i = 0; i < numToCreate; i++) {
- Patient patient = new Patient();
- patient.setActive(true);
- patient.addIdentifier()
- .setSystem("http://fhir.com")
- .setValue("ZYX");
- patient.getText().setDivAsString("ABC
");
- myPatientDao.create(patient, mySrd);
- }
-
- // test
- SearchParameterMap map = new SearchParameterMap();
- map.setLoadSynchronous(true);
- map.setSearchTotalMode(SearchTotalModeEnum.ACCURATE);
- TokenAndListParam tokenAndListParam = new TokenAndListParam();
- tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
- map.add("active", tokenAndListParam);
- map.add(Constants.PARAM_TEXT, new StringParam("ABC"));
- map.add("identifier", new TokenParam(null, "not found"));
- IBundleProvider provider = myPatientDao.search(map, mySrd);
-
- // verify
- assertEquals(0, provider.getAllResources().size());
- }
-
- @Test
- public void searchLuceneAndJPA_withLuceneBroadAndJPASearchNarrow_returnsFoundResults() {
- // setup
- int numToCreate = 2 * SearchBuilder.getMaximumPageSize() + 10;
- String identifierToFind = "bcde";
-
- // create patients
- for (int i = 0; i < numToCreate; i++) {
- Patient patient = new Patient();
- patient.setActive(true);
- String identifierVal = i == numToCreate - 10 ? identifierToFind:
- "abcd";
- patient.addIdentifier()
- .setSystem("http://fhir.com")
- .setValue(identifierVal);
-
- patient.getText().setDivAsString(
- "FINDME
"
- );
- myPatientDao.create(patient, mySrd);
- }
-
- // test
- SearchParameterMap map = new SearchParameterMap();
- map.setLoadSynchronous(true);
- map.setSearchTotalMode(SearchTotalModeEnum.ACCURATE);
- TokenAndListParam tokenAndListParam = new TokenAndListParam();
- tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
- map.add("active", tokenAndListParam);
- map.add(Constants.PARAM_TEXT, new StringParam("FINDME"));
- map.add("identifier", new TokenParam(null, identifierToFind));
- IBundleProvider provider = myPatientDao.search(map, mySrd);
-
- // verify
- List ids = provider.getAllResourceIds();
- assertEquals(1, ids.size());
- }
-
- @Test
- public void testLuceneNarrativeSearchQueryIntersectingJpaQuery() {
- final int numberOfPatientsToCreate = SearchBuilder.getMaximumPageSize() + 10;
- List expectedActivePatientIds = new ArrayList<>(numberOfPatientsToCreate);
-
- // create active and non-active patients with the same narrative
- for (int i = 0; i < numberOfPatientsToCreate; i++) {
- Patient activePatient = new Patient();
- activePatient.getText().setDivAsString("");
- activePatient.setActive(true);
- String patientId = myPatientDao.create(activePatient, mySrd).getId().toUnqualifiedVersionless().getIdPart();
- expectedActivePatientIds.add(patientId);
-
- Patient nonActivePatient = new Patient();
- nonActivePatient.getText().setDivAsString("");
- nonActivePatient.setActive(false);
- myPatientDao.create(nonActivePatient, mySrd);
- }
-
- SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
-
- TokenAndListParam tokenAndListParam = new TokenAndListParam();
- tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
-
- map.add("active", tokenAndListParam);
- map.add(Constants.PARAM_TEXT, new StringParam("AAAS"));
-
- IBundleProvider searchResultBundle = myPatientDao.search(map, mySrd);
- List resourceIdsFromSearchResult = searchResultBundle.getAllResourceIds();
-
- assertThat(resourceIdsFromSearchResult).containsExactlyInAnyOrderElementsOf(expectedActivePatientIds);
- }
-
- @Test
- public void testLuceneContentSearchQueryIntersectingJpaQuery() {
- final int numberOfPatientsToCreate = SearchBuilder.getMaximumPageSize() + 10;
- final String patientFamilyName = "Flanders";
- List expectedActivePatientIds = new ArrayList<>(numberOfPatientsToCreate);
-
- // create active and non-active patients with the same narrative
- for (int i = 0; i < numberOfPatientsToCreate; i++) {
- Patient activePatient = new Patient();
- activePatient.addName().setFamily(patientFamilyName);
- activePatient.setActive(true);
- String patientId = myPatientDao.create(activePatient, mySrd).getId().toUnqualifiedVersionless().getIdPart();
- expectedActivePatientIds.add(patientId);
-
- Patient nonActivePatient = new Patient();
- nonActivePatient.addName().setFamily(patientFamilyName);
- nonActivePatient.setActive(false);
- myPatientDao.create(nonActivePatient, mySrd);
- }
-
- SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
- TokenAndListParam tokenAndListParam = new TokenAndListParam();
- tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
- map.add("active", tokenAndListParam);
- map.add(Constants.PARAM_CONTENT, new StringParam(patientFamilyName));
-
- IBundleProvider searchResultBundle = myPatientDao.search(map, mySrd);
- List resourceIdsFromSearchResult = searchResultBundle.getAllResourceIds();
-
- assertThat(resourceIdsFromSearchResult).containsExactlyInAnyOrderElementsOf(expectedActivePatientIds);
- }
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java
index 1ddb1cca9d3..8f3e849719f 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java
@@ -263,6 +263,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds());
}
+
+
@Test
public void testParameterWithNoValueThrowsError_InvalidChainOnCustomSearch() throws IOException {
SearchParameter searchParameter = new SearchParameter();
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexTaskTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexTaskTest.java
index c04a40f1ac7..290dfcdf9b4 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexTaskTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ReindexTaskTest.java
@@ -180,6 +180,59 @@ public class ReindexTaskTest extends BaseJpaR4Test {
}
+ @Test
+ public void testOptimizeStorage_AllVersions_SingleResourceWithMultipleVersion() {
+
+ // this difference of this test from testOptimizeStorage_AllVersions is that this one has only 1 resource
+ // (with multiple versions) in the db. There was a bug where if only one resource were being re-indexed, the
+ // resource wasn't processed for optimize storage.
+
+ // Setup
+ IIdType patientId = createPatient(withActiveTrue());
+ for (int i = 0; i < 10; i++) {
+ Patient p = new Patient();
+ p.setId(patientId.toUnqualifiedVersionless());
+ p.setActive(true);
+ p.addIdentifier().setValue(String.valueOf(i));
+ myPatientDao.update(p, mySrd);
+ }
+
+ // Move resource text to compressed storage, which we don't write to anymore but legacy
+ // data may exist that was previously stored there, so we're simulating that.
+ List allHistoryEntities = runInTransaction(() -> myResourceHistoryTableDao.findAll());
+ allHistoryEntities.forEach(t->relocateResourceTextToCompressedColumn(t.getResourceId(), t.getVersion()));
+
+ runInTransaction(()->{
+ assertEquals(11, myResourceHistoryTableDao.count());
+ for (ResourceHistoryTable history : myResourceHistoryTableDao.findAll()) {
+ assertNull(history.getResourceTextVc());
+ assertNotNull(history.getResource());
+ }
+ });
+
+ // execute
+ JobInstanceStartRequest startRequest = new JobInstanceStartRequest();
+ startRequest.setJobDefinitionId(JOB_REINDEX);
+ startRequest.setParameters(
+ new ReindexJobParameters()
+ .setOptimizeStorage(ReindexParameters.OptimizeStorageModeEnum.ALL_VERSIONS)
+ .setReindexSearchParameters(ReindexParameters.ReindexSearchParametersEnum.NONE)
+ );
+ Batch2JobStartResponse startResponse = myJobCoordinator.startInstance(mySrd, startRequest);
+ myBatch2JobHelper.awaitJobCompletion(startResponse);
+
+ // validate
+ runInTransaction(()->{
+ assertEquals(11, myResourceHistoryTableDao.count());
+ for (ResourceHistoryTable history : myResourceHistoryTableDao.findAll()) {
+ assertNotNull(history.getResourceTextVc());
+ assertNull(history.getResource());
+ }
+ });
+ Patient patient = myPatientDao.read(patientId, mySrd);
+ assertTrue(patient.getActive());
+ }
+
@Test
public void testOptimizeStorage_AllVersions_CopyProvenanceEntityData() {
// Setup
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/ExtendedHSearchResourceProjectionTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/ExtendedHSearchResourceProjectionTest.java
index 8ec02d2d8d9..17b41a4485c 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/ExtendedHSearchResourceProjectionTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/ExtendedHSearchResourceProjectionTest.java
@@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.search;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.dao.search.ExtendedHSearchResourceProjection;
@@ -12,6 +11,7 @@ import org.junit.jupiter.api.Test;
import static ca.uhn.fhir.jpa.dao.search.ExtendedHSearchResourceProjection.RESOURCE_NOT_STORED_ERROR;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ExtendedHSearchResourceProjectionTest {
@@ -57,8 +57,4 @@ class ExtendedHSearchResourceProjectionTest {
() -> new ExtendedHSearchResourceProjection(22, null, ""));
assertEquals(Msg.code(2130) + RESOURCE_NOT_STORED_ERROR + "22", ex.getMessage());
}
-
-
-
-
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ValidateCodeOperationWithRemoteTerminologyR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java
similarity index 59%
rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ValidateCodeOperationWithRemoteTerminologyR4Test.java
rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java
index 4da204217f0..3787c611eb7 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ValidateCodeOperationWithRemoteTerminologyR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java
@@ -1,29 +1,21 @@
-package ca.uhn.fhir.jpa.provider.r4;
+package ca.uhn.fhir.jpa.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.JpaConfig;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
-import ca.uhn.fhir.rest.annotation.IdParam;
-import ca.uhn.fhir.rest.annotation.Operation;
-import ca.uhn.fhir.rest.annotation.OperationParam;
-import ca.uhn.fhir.rest.annotation.RequiredParam;
-import ca.uhn.fhir.rest.annotation.Search;
-import ca.uhn.fhir.rest.param.UriParam;
-import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
-import jakarta.servlet.http.HttpServletRequest;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
-import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Coding;
-import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters;
-import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.AfterEach;
@@ -33,9 +25,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import java.util.ArrayList;
-import java.util.List;
-
+import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -43,15 +33,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
-/*
+/**
* This set of integration tests that instantiates and injects an instance of
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
- * implementation. It also exercises the code found in
- * {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
+ * implementation. It also exercises the validateCode output translation code found in
+ * {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
*/
-public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeOperationWithRemoteTerminologyR4Test.class);
+public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeWithRemoteTerminologyR4Test.class);
private static final String DISPLAY = "DISPLAY";
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
private static final String CODE_BODY_MASS_INDEX = "39156-5";
@@ -64,8 +54,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private RemoteTerminologyServiceValidationSupport mySvc;
- private MyCodeSystemProvider myCodeSystemProvider;
- private MyValueSetProvider myValueSetProvider;
+ private IValidationProviders.MyValidationProvider myCodeSystemProvider;
+ private IValidationProviders.MyValidationProvider myValueSetProvider;
@Autowired
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
@@ -76,8 +66,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
myValidationSupportChain.addValidationSupport(0, mySvc);
- myCodeSystemProvider = new MyCodeSystemProvider();
- myValueSetProvider = new MyValueSetProvider();
+ myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
+ myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
ourRestfulServerExtension.registerProvider(myValueSetProvider);
}
@@ -103,11 +93,11 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
@Test
public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
- myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
- myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
- myCodeSystemProvider.myReturnParams = new Parameters();
- myCodeSystemProvider.myReturnParams.addParameter("result", true);
- myCodeSystemProvider.myReturnParams.addParameter("display", DISPLAY);
+ final String code = "P";
+ final String system = CODE_SYSTEM_V2_0247_URI;;
+
+ Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY);
+ setupCodeSystemValidateCode(system, code, params);
logAllConcepts();
@@ -115,8 +105,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_VALIDATE_CODE)
- .withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
- .andParameter("url", new UriType(CODE_SYSTEM_V2_0247_URI))
+ .withParameter(Parameters.class, "coding", new Coding().setSystem(system).setCode(code))
+ .andParameter("url", new UriType(system))
.execute();
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@@ -128,7 +118,7 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
@Test
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
- myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
+ myCodeSystemProvider.setShouldThrowExceptionForResourceNotFound(false);
Parameters respParam = myClient
.operation()
@@ -166,21 +156,21 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
@Test
public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
- myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
- myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
- myValueSetProvider.myReturnValueSets = new ArrayList<>();
- myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
- myValueSetProvider.myReturnParams = new Parameters();
- myValueSetProvider.myReturnParams.addParameter("result", true);
- myValueSetProvider.myReturnParams.addParameter("display", DISPLAY);
+ final String code = "alerts";
+ final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes";
+ final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes";
+
+ Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY);
+ setupValueSetValidateCode(valueSetUrl, system, code, params);
+ setupCodeSystemValidateCode(system, code, params);
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named(JpaConstants.OPERATION_VALIDATE_CODE)
- .withParameter(Parameters.class, "code", new CodeType("alerts"))
- .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/list-example-use-codes"))
- .andParameter("url", new UriType("http://hl7.org/fhir/ValueSet/list-example-codes"))
+ .withParameter(Parameters.class, "code", new CodeType(code))
+ .andParameter("system", new UriType(system))
+ .andParameter("url", new UriType(valueSetUrl))
.useHttpGet()
.execute();
@@ -193,21 +183,20 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
@Test
public void validateCodeOperationOnValueSet_byUrlSystemAndCode() {
- myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
- myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
- myValueSetProvider.myReturnValueSets = new ArrayList<>();
- myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
- myValueSetProvider.myReturnParams = new Parameters();
- myValueSetProvider.myReturnParams.addParameter("result", true);
- myValueSetProvider.myReturnParams.addParameter("display", DISPLAY_BODY_MASS_INDEX);
+ final String code = CODE_BODY_MASS_INDEX;
+ final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes";
+ final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes";
+
+ Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY_BODY_MASS_INDEX);
+ setupValueSetValidateCode(valueSetUrl, system, code, params);
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named(JpaConstants.OPERATION_VALIDATE_CODE)
- .withParameter(Parameters.class, "code", new CodeType(CODE_BODY_MASS_INDEX))
- .andParameter("url", new UriType("https://loinc.org"))
- .andParameter("system", new UriType("http://loinc.org"))
+ .withParameter(Parameters.class, "code", new CodeType(code))
+ .andParameter("url", new UriType(valueSetUrl))
+ .andParameter("system", new UriType(system))
.execute();
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@@ -219,7 +208,7 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
@Test
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
- myValueSetProvider.myReturnValueSets = new ArrayList<>();
+ myValueSetProvider.setShouldThrowExceptionForResourceNotFound(false);
Parameters respParam = myClient
.operation()
@@ -238,70 +227,18 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
}
- @SuppressWarnings("unused")
- private static class MyCodeSystemProvider implements IResourceProvider {
- private List myReturnCodeSystems;
- private Parameters myReturnParams;
+ private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, IBaseParameters theResponseParams) {
+ ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl);
+ myValueSetProvider.addTerminologyResource(theSystem);
+ myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, theResponseParams);
- @Operation(name = "validate-code", idempotent = true, returnParameters = {
- @OperationParam(name = "result", type = BooleanType.class, min = 1),
- @OperationParam(name = "message", type = StringType.class),
- @OperationParam(name = "display", type = StringType.class)
- })
- public Parameters validateCode(
- HttpServletRequest theServletRequest,
- @IdParam(optional = true) IdType theId,
- @OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
- @OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
- @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
- ) {
- return myReturnParams;
- }
-
- @Search
- public List find(@RequiredParam(name = "url") UriParam theUrlParam) {
- assert myReturnCodeSystems != null;
- return myReturnCodeSystems;
- }
-
- @Override
- public Class extends IBaseResource> getResourceType() {
- return CodeSystem.class;
- }
+ // we currently do this because VersionSpecificWorkerContextWrapper has logic to infer the system when missing
+ // based on the ValueSet by calling ValidationSupportUtils#extractCodeSystemForCode.
+ valueSet.getCompose().addInclude().setSystem(theSystem);
}
- @SuppressWarnings("unused")
- private static class MyValueSetProvider implements IResourceProvider {
- private Parameters myReturnParams;
- private List myReturnValueSets;
-
- @Operation(name = "validate-code", idempotent = true, returnParameters = {
- @OperationParam(name = "result", type = BooleanType.class, min = 1),
- @OperationParam(name = "message", type = StringType.class),
- @OperationParam(name = "display", type = StringType.class)
- })
- public Parameters validateCode(
- HttpServletRequest theServletRequest,
- @IdParam(optional = true) IdType theId,
- @OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
- @OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
- @OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
- @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
- @OperationParam(name = "valueSet") ValueSet theValueSet
- ) {
- return myReturnParams;
- }
-
- @Search
- public List find(@RequiredParam(name = "url") UriParam theUrlParam) {
- assert myReturnValueSets != null;
- return myReturnValueSets;
- }
-
- @Override
- public Class extends IBaseResource> getResourceType() {
- return ValueSet.class;
- }
-
+ private void setupCodeSystemValidateCode(String theUrl, String theCode, IBaseParameters theResponseParams) {
+ CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
+ myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, theResponseParams);
}
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateWithRemoteTerminologyTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateWithRemoteTerminologyTest.java
new file mode 100644
index 00000000000..79a656db39c
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateWithRemoteTerminologyTest.java
@@ -0,0 +1,261 @@
+package ca.uhn.fhir.jpa.validation;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.config.JpaConfig;
+import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
+import ca.uhn.fhir.util.ClasspathUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
+import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.CodeSystem;
+import org.hl7.fhir.r4.model.Encounter;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.OperationOutcome;
+import org.hl7.fhir.r4.model.Procedure;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.StructureDefinition;
+import org.hl7.fhir.r4.model.ValueSet;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+import java.util.List;
+
+import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests resource validation with Remote Terminology bindings.
+ * To create a new test, you need to do 3 things:
+ * (1) the resource profile, if any custom one is needed should be stored in the FHIR repository
+ * (2) all the CodeSystem and ValueSet terminology resources need to be added to the corresponding resource provider.
+ * At the moment only placeholder CodeSystem/ValueSet resources are returned with id and url populated. For the moment
+ * there was no need to load the full resource, but that can be done if there is logic run which requires it.
+ * This is a minimal setup.
+ * (3) the Remote Terminology operation responses that are needed for the test need to be added to the corresponding
+ * resource provider. The intention is to record and use the responses of an actual terminology server
+ * e.g. OntoServer.
+ * This is done as a result of the fact that unit test cannot always catch bugs which are introduced as a result of
+ * changes in the OntoServer or FHIR Validator library, or both.
+ * @see #setupValueSetValidateCode
+ * @see #setupCodeSystemValidateCode
+ * The responses are in Parameters resource format where issues is an OperationOutcome resource.
+ */
+public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Test {
+ private static final FhirContext ourCtx = FhirContext.forR4Cached();
+
+ @RegisterExtension
+ protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
+ private RemoteTerminologyServiceValidationSupport mySvc;
+ @Autowired
+ @Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
+ private ValidationSupportChain myValidationSupportChain;
+ private IValidationProviders.MyValidationProvider myCodeSystemProvider;
+ private IValidationProviders.MyValidationProvider myValueSetProvider;
+
+ @BeforeEach
+ public void before() {
+ String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
+ mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
+ myValidationSupportChain.addValidationSupport(0, mySvc);
+ myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
+ myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
+ ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
+ ourRestfulServerExtension.registerProvider(myValueSetProvider);
+ }
+
+ @AfterEach
+ public void after() {
+ myValidationSupportChain.removeValidationSupport(mySvc);
+ ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
+ ourRestfulServerExtension.unregisterProvider(myCodeSystemProvider);
+ ourRestfulServerExtension.unregisterProvider(myValueSetProvider);
+ }
+
+ @Test
+ public void validate_withProfileWithValidCodesFromAllBindingTypes_returnsNoErrors() {
+ // setup
+ final StructureDefinition profileEncounter = ClasspathUtil.loadResource(ourCtx, StructureDefinition.class, "validation/encounter/profile-encounter-custom.json");
+ myClient.update().resource(profileEncounter).execute();
+
+ final String statusCode = "planned";
+ final String classCode = "IMP";
+ final String identifierTypeCode = "VN";
+
+ final String statusSystem = "http://hl7.org/fhir/encounter-status"; // implied system
+ final String classSystem = "http://terminology.hl7.org/CodeSystem/v3-ActCode";
+ final String identifierTypeSystem = "http://terminology.hl7.org/CodeSystem/v2-0203";
+
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json");
+ setupValueSetValidateCode("http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "http://terminology.hl7.org/CodeSystem/v3-ActCode", classCode, "validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json");
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/identifier-type", identifierTypeCode, "validation/encounter/validateCode-ValueSet-identifier-type.json");
+
+ setupCodeSystemValidateCode(statusSystem, statusCode, "validation/encounter/validateCode-CodeSystem-encounter-status.json");
+ setupCodeSystemValidateCode(classSystem, classCode, "validation/encounter/validateCode-CodeSystem-v3-ActCode.json");
+ setupCodeSystemValidateCode(identifierTypeSystem, identifierTypeCode, "validation/encounter/validateCode-CodeSystem-v2-0203.json");
+
+ Encounter encounter = new Encounter();
+ encounter.getMeta().addProfile("http://example.ca/fhir/StructureDefinition/profile-encounter");
+
+ // required binding
+ encounter.setStatus(Encounter.EncounterStatus.fromCode(statusCode));
+
+ // preferred binding
+ encounter.getClass_()
+ .setSystem(classSystem)
+ .setCode(classCode)
+ .setDisplay("inpatient encounter");
+
+ // extensible binding
+ encounter.addIdentifier()
+ .getType().addCoding()
+ .setSystem(identifierTypeSystem)
+ .setCode(identifierTypeCode)
+ .setDisplay("Visit number");
+
+ // execute
+ List errors = getValidationErrors(encounter);
+
+ // verify
+ assertThat(errors).isEmpty();
+ }
+
+ @Test
+ public void validate_withInvalidCode_returnsErrors() {
+ // setup
+ final String statusCode = "final";
+ final String code = "10xx";
+
+ final String statusSystem = "http://hl7.org/fhir/observation-status";
+ final String loincSystem = "http://loinc.org";
+ final String system = "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM";
+
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json");
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-codes", loincSystem, statusCode, "validation/observation/validateCode-ValueSet-codes.json");
+
+ setupCodeSystemValidateCode(statusSystem, statusCode, "validation/observation/validateCode-CodeSystem-observation-status.json");
+ setupCodeSystemValidateCode(system, code, "validation/observation/validateCode-CodeSystem-ICD9CM.json");
+
+ Observation obs = new Observation();
+ obs.setStatus(Observation.ObservationStatus.fromCode(statusCode));
+ obs.getCode().addCoding().setCode(code).setSystem(system);
+
+ // execute
+ List errors = getValidationErrors(obs);
+ assertThat(errors).hasSize(1);
+
+ // verify
+ assertThat(errors.get(0))
+ .contains("Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM");
+ }
+
+ @Test
+ public void validate_withProfileWithInvalidCode_returnsErrors() {
+ // setup
+ String profile = "http://example.ca/fhir/StructureDefinition/profile-procedure";
+ StructureDefinition profileProcedure = ClasspathUtil.loadResource(myFhirContext, StructureDefinition.class, "validation/procedure/profile-procedure.json");
+ myClient.update().resource(profileProcedure).execute();
+
+ final String statusCode = "completed";
+ final String procedureCode1 = "417005";
+ final String procedureCode2 = "xx417005";
+
+ final String statusSystem = "http://hl7.org/fhir/event-status";
+ final String snomedSystem = "http://snomed.info/sct";
+
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode1, "validation/procedure/validateCode-ValueSet-procedure-code-valid.json");
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode2, "validation/procedure/validateCode-ValueSet-procedure-code-invalid.json");
+
+ setupCodeSystemValidateCode(statusSystem, statusCode, "validation/procedure/validateCode-CodeSystem-event-status.json");
+ setupCodeSystemValidateCode(snomedSystem, procedureCode1, "validation/procedure/validateCode-CodeSystem-snomed-valid.json");
+ setupCodeSystemValidateCode(snomedSystem, procedureCode2, "validation/procedure/validateCode-CodeSystem-snomed-invalid.json");
+
+ Procedure procedure = new Procedure();
+ procedure.setSubject(new Reference("Patient/P1"));
+ procedure.setStatus(Procedure.ProcedureStatus.fromCode(statusCode));
+ procedure.getCode().addCoding().setSystem(snomedSystem).setCode(procedureCode1);
+ procedure.getCode().addCoding().setSystem(snomedSystem).setCode(procedureCode2);
+ procedure.getMeta().addProfile(profile);
+
+ // execute
+ List errors = getValidationErrors(procedure);
+ // TODO: there is currently some duplication in the errors returned. This needs to be investigated and fixed.
+ // assertThat(errors).hasSize(1);
+
+ // verify
+ // note that we're not selecting an explicit versions (using latest) so the message verification does not include it.
+ assertThat(StringUtils.join("", errors))
+ .contains("Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct'")
+ .doesNotContain("The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code")
+ .doesNotContain("http://snomed.info/sct#417005");
+ }
+
+ @Test
+ public void validate_withProfileWithSlicingWithValidCode_returnsNoErrors() {
+ // setup
+ String profile = "http://example.ca/fhir/StructureDefinition/profile-procedure-with-slicing";
+ StructureDefinition profileProcedure = ClasspathUtil.loadResource(myFhirContext, StructureDefinition.class, "validation/procedure/profile-procedure-slicing.json");
+ myClient.update().resource(profileProcedure).execute();
+
+ final String statusCode = "completed";
+ final String procedureCode = "no-procedure-info";
+
+ final String statusSystem = "http://hl7.org/fhir/event-status";
+ final String snomedSystem = "http://snomed.info/sct";
+ final String absentUnknownSystem = "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips";
+
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
+ setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode, "validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json");
+ setupValueSetValidateCode("http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips", absentUnknownSystem, procedureCode, "validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json");
+
+ setupCodeSystemValidateCode(statusSystem, statusCode, "validation/procedure/validateCode-CodeSystem-event-status.json");
+ setupCodeSystemValidateCode(absentUnknownSystem, procedureCode, "validation/procedure/validateCode-CodeSystem-absent-or-unknown.json");
+
+ Procedure procedure = new Procedure();
+ procedure.setSubject(new Reference("Patient/P1"));
+ procedure.setStatus(Procedure.ProcedureStatus.fromCode(statusCode));
+ procedure.getCode().addCoding().setSystem(absentUnknownSystem).setCode(procedureCode);
+ procedure.getMeta().addProfile(profile);
+
+ // execute
+ List errors = getValidationErrors(procedure);
+ assertThat(errors).hasSize(0);
+ }
+
+ private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, String theTerminologyResponseFile) {
+ ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl);
+ myCodeSystemProvider.addTerminologyResource(theSystem);
+ myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
+
+ // we currently do this because VersionSpecificWorkerContextWrapper has logic to infer the system when missing
+ // based on the ValueSet by calling ValidationSupportUtils#extractCodeSystemForCode.
+ valueSet.getCompose().addInclude().setSystem(theSystem);
+
+ // you will notice each of these calls require also a call to setupCodeSystemValidateCode
+ // that is necessary because VersionSpecificWorkerContextWrapper#validateCodeInValueSet
+ // which also attempts a validateCode against the CodeSystem after the validateCode against the ValueSet
+ }
+
+ private void setupCodeSystemValidateCode(String theUrl, String theCode, String theTerminologyResponseFile) {
+ CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
+ myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
+ }
+
+ private List getValidationErrors(IBaseResource theResource) {
+ MethodOutcome resultProcedure = myClient.validate().resource(theResource).execute();
+ OperationOutcome operationOutcome = (OperationOutcome) resultProcedure.getOperationOutcome();
+ return operationOutcome.getIssue().stream()
+ .filter(issue -> issue.getSeverity() == OperationOutcome.IssueSeverity.ERROR)
+ .map(OperationOutcome.OperationOutcomeIssueComponent::getDiagnostics)
+ .toList();
+ }
+}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/logback-test.xml
index dcada7fec76..f52688d2cbc 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/resources/logback-test.xml
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/logback-test.xml
@@ -31,7 +31,7 @@
-
+-->
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/profile-encounter-custom.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/profile-encounter-custom.json
new file mode 100644
index 00000000000..a553a61a1c2
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/profile-encounter-custom.json
@@ -0,0 +1,49 @@
+{
+ "resourceType": "StructureDefinition",
+ "id": "profile-encounter",
+ "url": "http://example.ca/fhir/StructureDefinition/profile-encounter",
+ "version": "0.11.0",
+ "name": "EncounterProfile",
+ "title": "Encounter Profile",
+ "status": "active",
+ "date": "2022-10-15T12:00:00+00:00",
+ "publisher": "Example Organization",
+ "fhirVersion": "4.0.1",
+ "kind": "resource",
+ "abstract": false,
+ "type": "Encounter",
+ "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Encounter",
+ "derivation": "constraint",
+ "differential": {
+ "element": [
+ {
+ "id": "Encounter.identifier.type.coding",
+ "path": "Encounter.identifier.type.coding",
+ "min": 1,
+ "max": "1",
+ "mustSupport": true
+ },
+ {
+ "id": "Encounter.identifier.type.coding.system",
+ "path": "Encounter.identifier.type.coding.system",
+ "min": 1,
+ "fixedUri": "http://terminology.hl7.org/CodeSystem/v2-0203",
+ "mustSupport": true
+ },
+ {
+ "id": "Encounter.identifier.type.coding.code",
+ "path": "Encounter.identifier.type.coding.code",
+ "min": 1,
+ "fixedCode": "VN",
+ "mustSupport": true
+ },
+ {
+ "id": "Encounter.identifier.type.coding.display",
+ "path": "Encounter.identifier.type.coding.display",
+ "min": 1,
+ "fixedString": "Visit number",
+ "mustSupport": true
+ }
+ ]
+ }
+}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-encounter-status.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-encounter-status.json
new file mode 100644
index 00000000000..2399dc870ec
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-encounter-status.json
@@ -0,0 +1,59 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "planned"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/encounter-status"
+ },
+ {
+ "name": "version",
+ "valueString": "5.0.0-ballot"
+ },
+ {
+ "name": "display",
+ "valueString": "Planned"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
+ }
+ },
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to draft CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-v2-0203.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-v2-0203.json
new file mode 100644
index 00000000000..10747c14ee3
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-v2-0203.json
@@ -0,0 +1,25 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "VN"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://terminology.hl7.org/CodeSystem/v2-0203"
+ },
+ {
+ "name": "version",
+ "valueString": "3.0.0"
+ },
+ {
+ "name": "display",
+ "valueString": "Visit number"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-v3-ActCode.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-v3-ActCode.json
new file mode 100644
index 00000000000..b692847e0fb
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-CodeSystem-v3-ActCode.json
@@ -0,0 +1,46 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "IMP"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://terminology.hl7.org/CodeSystem/v3-ActCode"
+ },
+ {
+ "name": "version",
+ "valueString": "2018-08-12"
+ },
+ {
+ "name": "display",
+ "valueString": "inpatient encounter"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to draft CodeSystem http://terminology.hl7.org/CodeSystem/v3-ActCode|2018-08-12"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-encounter-status.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-encounter-status.json
new file mode 100644
index 00000000000..2399dc870ec
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-encounter-status.json
@@ -0,0 +1,59 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "planned"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/encounter-status"
+ },
+ {
+ "name": "version",
+ "valueString": "5.0.0-ballot"
+ },
+ {
+ "name": "display",
+ "valueString": "Planned"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
+ }
+ },
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to draft CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-identifier-type.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-identifier-type.json
new file mode 100644
index 00000000000..b0767dc2f18
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-identifier-type.json
@@ -0,0 +1,52 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": false
+ },
+ {
+ "name": "code",
+ "valueCode": "VN"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://terminology.hl7.org/CodeSystem/v2-0203"
+ },
+ {
+ "name": "version",
+ "valueString": "3.0.0"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "not-in-vs"
+ }
+ ],
+ "text": "The provided code 'http://terminology.hl7.org/CodeSystem/v2-0203#VN' was not found in the value set 'http://hl7.org/fhir/ValueSet/identifier-type|5.0.0-ballot'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "message",
+ "valueString": "The provided code 'http://terminology.hl7.org/CodeSystem/v2-0203#VN' was not found in the value set 'http://hl7.org/fhir/ValueSet/identifier-type|5.0.0-ballot'"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json
new file mode 100644
index 00000000000..083dbffae43
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json
@@ -0,0 +1,59 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "IMP"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://terminology.hl7.org/CodeSystem/v3-ActCode"
+ },
+ {
+ "name": "version",
+ "valueString": "2018-08-12"
+ },
+ {
+ "name": "display",
+ "valueString": "inpatient encounter"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use ValueSet http://terminology.hl7.org/ValueSet/v3-ActEncounterCode|2014-03-26"
+ }
+ },
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to draft CodeSystem http://terminology.hl7.org/CodeSystem/v3-ActCode|2018-08-12"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-CodeSystem-ICD9CM.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-CodeSystem-ICD9CM.json
new file mode 100644
index 00000000000..831ac6660fa
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-CodeSystem-ICD9CM.json
@@ -0,0 +1,48 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": false
+ },
+ {
+ "name": "code",
+ "valueCode": "10xx"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "invalid-code"
+ }
+ ],
+ "text": "Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM' version '0.1.0'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "message",
+ "valueString": "Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM' version '0.1.0'"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-CodeSystem-observation-status.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-CodeSystem-observation-status.json
new file mode 100644
index 00000000000..7914321876c
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-CodeSystem-observation-status.json
@@ -0,0 +1,25 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "final"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/observation-status"
+ },
+ {
+ "name": "version",
+ "valueString": "5.0.0-ballot"
+ },
+ {
+ "name": "display",
+ "valueString": "Final"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-ValueSet-codes.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-ValueSet-codes.json
new file mode 100644
index 00000000000..4571362033f
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-ValueSet-codes.json
@@ -0,0 +1,48 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": false
+ },
+ {
+ "name": "code",
+ "valueCode": "10xx"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "not-in-vs"
+ }
+ ],
+ "text": "The provided code 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM#10xx' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-codes|5.0.0-ballot'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "message",
+ "valueString": "The provided code 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM#10xx' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-codes|5.0.0-ballot'"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-ValueSet-observation-status.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-ValueSet-observation-status.json
new file mode 100644
index 00000000000..7914321876c
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/observation/validateCode-ValueSet-observation-status.json
@@ -0,0 +1,25 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "final"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/observation-status"
+ },
+ {
+ "name": "version",
+ "valueString": "5.0.0-ballot"
+ },
+ {
+ "name": "display",
+ "valueString": "Final"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/profile-procedure-slicing.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/profile-procedure-slicing.json
new file mode 100644
index 00000000000..8bc05c70cf0
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/profile-procedure-slicing.json
@@ -0,0 +1,79 @@
+{
+ "resourceType": "StructureDefinition",
+ "id": "profile-procedure-with-slicing",
+ "url": "http://example.ca/fhir/StructureDefinition/profile-procedure-with-slicing",
+ "version": "0.11.0",
+ "name": "ProcedureProfile",
+ "title": "Procedure Profile",
+ "status": "active",
+ "date": "2022-10-15T12:00:00+00:00",
+ "publisher": "Example Organization",
+ "fhirVersion": "4.0.1",
+ "kind": "resource",
+ "abstract": false,
+ "type": "Procedure",
+ "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Procedure",
+ "derivation": "constraint",
+ "differential": {
+ "element": [
+ {
+ "id": "Procedure.code.coding",
+ "path": "Procedure.code.coding",
+ "slicing": {
+ "discriminator": [
+ {
+ "type": "pattern",
+ "path": "$this"
+ }
+ ],
+ "description": "Discriminated by the bound value set",
+ "rules": "open"
+ },
+ "mustSupport": true,
+ "binding": {
+ "strength": "preferred",
+ "valueSet": "http://hl7.org/fhir/ValueSet/procedure-code"
+ }
+ },
+ {
+ "id": "Procedure.code.coding.display.extension:translation",
+ "path": "Procedure.code.coding.display.extension",
+ "sliceName": "translation"
+ },
+ {
+ "id": "Procedure.code.coding.display.extension:translation.extension",
+ "path": "Procedure.code.coding.display.extension.extension",
+ "min": 2
+ },
+ {
+ "id": "Procedure.code.coding:absentOrUnknownProcedure",
+ "path": "Procedure.code.coding",
+ "sliceName": "absentOrUnknownProcedure",
+ "short": "Optional slice for representing a code for absent problem or for unknown procedure",
+ "definition": "Code representing the statement \"absent problem\" or the statement \"procedures unknown\"",
+ "mustSupport": true,
+ "binding": {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName",
+ "valueString": "absentOrUnknownProcedure"
+ }
+ ],
+ "strength": "required",
+ "description": "A code to identify absent or unknown procedures",
+ "valueSet": "http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips"
+ }
+ },
+ {
+ "id": "Procedure.code.coding:absentOrUnknownProcedure.display.extension:translation",
+ "path": "Procedure.code.coding.display.extension",
+ "sliceName": "translation"
+ },
+ {
+ "id": "Procedure.code.coding:absentOrUnknownProcedure.display.extension:translation.extension",
+ "path": "Procedure.code.coding.display.extension.extension",
+ "min": 2
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/profile-procedure.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/profile-procedure.json
new file mode 100644
index 00000000000..5315694dece
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/profile-procedure.json
@@ -0,0 +1,50 @@
+{
+ "resourceType": "StructureDefinition",
+ "id": "profile-procedure",
+ "url": "http://example.ca/fhir/StructureDefinition/profile-procedure",
+ "version": "0.11.0",
+ "name": "ProcedureProfile",
+ "title": "Procedure Profile",
+ "status": "active",
+ "date": "2022-10-15T12:00:00+00:00",
+ "publisher": "Example Organization",
+ "fhirVersion": "4.0.1",
+ "kind": "resource",
+ "abstract": false,
+ "type": "Procedure",
+ "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Procedure",
+ "derivation": "constraint",
+ "differential": {
+ "element": [
+ {
+ "id": "Procedure.code.coding",
+ "path": "Procedure.code.coding",
+ "slicing": {
+ "discriminator": [
+ {
+ "type": "pattern",
+ "path": "$this"
+ }
+ ],
+ "description": "Discriminated by the bound value set",
+ "rules": "open"
+ },
+ "mustSupport": true,
+ "binding": {
+ "strength": "preferred",
+ "valueSet": "http://hl7.org/fhir/ValueSet/procedure-code"
+ }
+ },
+ {
+ "id": "Procedure.code.coding.display.extension:translation",
+ "path": "Procedure.code.coding.display.extension",
+ "sliceName": "translation"
+ },
+ {
+ "id": "Procedure.code.coding.display.extension:translation.extension",
+ "path": "Procedure.code.coding.display.extension.extension",
+ "min": 2
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-absent-or-unknown.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-absent-or-unknown.json
new file mode 100644
index 00000000000..4d7b20f0881
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-absent-or-unknown.json
@@ -0,0 +1,46 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "no-procedure-info"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
+ },
+ {
+ "name": "version",
+ "valueString": "1.1.0"
+ },
+ {
+ "name": "display",
+ "valueString": "No information about past history of procedures"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use CodeSystem http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips|1.1.0"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-event-status.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-event-status.json
new file mode 100644
index 00000000000..620624a991e
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-event-status.json
@@ -0,0 +1,59 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "completed"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/event-status"
+ },
+ {
+ "name": "version",
+ "valueString": "5.0.0-ballot"
+ },
+ {
+ "name": "display",
+ "valueString": "Completed"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use CodeSystem http://hl7.org/fhir/event-status|5.0.0-ballot"
+ }
+ },
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to experimental CodeSystem http://hl7.org/fhir/event-status|5.0.0-ballot"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-snomed-invalid.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-snomed-invalid.json
new file mode 100644
index 00000000000..f6a86048d6e
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-snomed-invalid.json
@@ -0,0 +1,48 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": false
+ },
+ {
+ "name": "code",
+ "valueCode": "xx417005"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://snomed.info/sct"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "invalid-code"
+ }
+ ],
+ "text": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "message",
+ "valueString": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-snomed-valid.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-snomed-valid.json
new file mode 100644
index 00000000000..a602bfda9f0
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-CodeSystem-snomed-valid.json
@@ -0,0 +1,25 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "417005"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://snomed.info/sct"
+ },
+ {
+ "name": "version",
+ "valueString": "http://snomed.info/sct/32506021000036107/version/20241031"
+ },
+ {
+ "name": "display",
+ "valueString": "Hospital re-admission"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json
new file mode 100644
index 00000000000..aaee02a0023
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json
@@ -0,0 +1,59 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "no-procedure-info"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
+ },
+ {
+ "name": "version",
+ "valueString": "1.1.0"
+ },
+ {
+ "name": "display",
+ "valueString": "No information about past history of procedures"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use CodeSystem http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips|1.1.0"
+ }
+ },
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to trial-use ValueSet http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips|1.1.0"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-event-status.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-event-status.json
new file mode 100644
index 00000000000..aaad08b83e8
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-event-status.json
@@ -0,0 +1,25 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "final"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/procedure-status"
+ },
+ {
+ "name": "version",
+ "valueString": "5.0.0-ballot"
+ },
+ {
+ "name": "display",
+ "valueString": "Final"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json
new file mode 100644
index 00000000000..4dcb4791944
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json
@@ -0,0 +1,48 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": false
+ },
+ {
+ "name": "code",
+ "valueCode": "no-procedure-info"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "not-in-vs"
+ }
+ ],
+ "text": "The provided code 'http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips#no-procedure-info' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "message",
+ "valueString": "The provided code 'http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips#no-procedure-info' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-invalid.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-invalid.json
new file mode 100644
index 00000000000..fac3785fe2d
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-invalid.json
@@ -0,0 +1,67 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": false
+ },
+ {
+ "name": "code",
+ "valueCode": "xx417005"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://snomed.info/sct"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "not-in-vs"
+ }
+ ],
+ "text": "The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ },
+ {
+ "severity": "error",
+ "code": "code-invalid",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "invalid-code"
+ }
+ ],
+ "text": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
+ },
+ "location": [
+ "code"
+ ],
+ "expression": [
+ "code"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "name": "message",
+ "valueString": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'; The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-valid.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-valid.json
new file mode 100644
index 00000000000..4554379edad
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/validation/procedure/validateCode-ValueSet-procedure-code-valid.json
@@ -0,0 +1,59 @@
+{
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "result",
+ "valueBoolean": true
+ },
+ {
+ "name": "code",
+ "valueCode": "417005"
+ },
+ {
+ "name": "system",
+ "valueUri": "http://snomed.info/sct"
+ },
+ {
+ "name": "version",
+ "valueString": "http://snomed.info/sct/32506021000036107/version/20241031"
+ },
+ {
+ "name": "display",
+ "valueString": "Hospital re-admission"
+ },
+ {
+ "name": "issues",
+ "resource": {
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to draft ValueSet http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot"
+ }
+ },
+ {
+ "severity": "information",
+ "code": "business-rule",
+ "details": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
+ "code": "status-check"
+ }
+ ],
+ "text": "Reference to experimental ValueSet http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesLuceneTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesLuceneTest.java
index 814b342aab7..31ce2f00f93 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesLuceneTest.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesLuceneTest.java
@@ -2,19 +2,29 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
+import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient;
+import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
+import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
+import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper;
import ca.uhn.fhir.jpa.dao.TestDaoSearch;
import ca.uhn.fhir.jpa.search.BaseSourceSearchParameterTestCases;
import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases;
import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases;
+import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.test.BaseJpaTest;
import ca.uhn.fhir.jpa.test.config.TestR4Config;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
+import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.storage.test.BaseDateSearchDaoTests;
import ca.uhn.fhir.storage.test.DaoTestDataBuilder;
+import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Meta;
import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PractitionerRole;
import org.hl7.fhir.r4.model.Reference;
@@ -32,15 +42,15 @@ import org.springframework.transaction.PlatformTransactionManager;
import java.util.List;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {
TestR4Config.class,
DaoTestDataBuilder.Config.class,
TestDaoSearch.Config.class
})
-public class FhirResourceDaoR4StandardQueriesLuceneTest extends BaseJpaTest {
+public class FhirResourceDaoR4StandardQueriesLuceneTest extends BaseJpaTest
+ implements ILuceneSearchR4Test {
+
FhirContext myFhirContext = FhirContext.forR4Cached();
@Autowired
PlatformTransactionManager myTxManager;
@@ -53,6 +63,19 @@ public class FhirResourceDaoR4StandardQueriesLuceneTest extends BaseJpaTest {
@Qualifier("myObservationDaoR4")
IFhirResourceDao myObservationDao;
@Autowired
+ @Qualifier("myPatientDaoR4")
+ protected IFhirResourceDaoPatient myPatientDao;
+ @Autowired
+ private IFhirSystemDao mySystemDao;
+ @Autowired
+ private IResourceReindexingSvc myResourceReindexingSvc;
+ @Autowired
+ protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
+ @Autowired
+ protected ISearchParamRegistry mySearchParamRegistry;
+ @Autowired
+ private IBulkDataExportJobSchedulingHelper myBulkDataScheduleHelper;
+ @Autowired
IFhirResourceDao myPractitionerDao;
@Autowired
IFhirResourceDao myPractitionerRoleDao;
@@ -60,6 +83,7 @@ public class FhirResourceDaoR4StandardQueriesLuceneTest extends BaseJpaTest {
// todo mb create an extension to restore via clone or xstream + BeanUtils.copyProperties().
@BeforeEach
void setUp() {
+ purgeDatabase(myStorageSettings, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataScheduleHelper);
myStorageSettings.setAdvancedHSearchIndexing(true);
}
@@ -79,6 +103,11 @@ public class FhirResourceDaoR4StandardQueriesLuceneTest extends BaseJpaTest {
return myTxManager;
}
+ @Override
+ public DaoRegistry getDaoRegistry() {
+ return myDaoRegistry;
+ }
+
@Nested
public class DateSearchTests extends BaseDateSearchDaoTests {
@Override
diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ILuceneSearchR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ILuceneSearchR4Test.java
new file mode 100644
index 00000000000..9dae86edcf6
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ILuceneSearchR4Test.java
@@ -0,0 +1,317 @@
+package ca.uhn.fhir.jpa.dao.r4;
+
+import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.model.dao.JpaPid;
+import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
+import ca.uhn.fhir.rest.param.DateRangeParam;
+import ca.uhn.fhir.rest.param.StringParam;
+import ca.uhn.fhir.rest.param.TokenAndListParam;
+import ca.uhn.fhir.rest.param.TokenOrListParam;
+import ca.uhn.fhir.rest.param.TokenParam;
+import ca.uhn.fhir.util.DateRangeUtil;
+import org.hl7.fhir.r4.model.Patient;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public interface ILuceneSearchR4Test {
+
+ DaoRegistry getDaoRegistry();
+
+ @SuppressWarnings("rawtypes")
+ private IFhirResourceDao getResourceDao(String theResourceType) {
+ return getDaoRegistry()
+ .getResourceDao(theResourceType);
+ }
+
+ void runInTransaction(Runnable theRunnable);
+
+ @Test
+ default void testNoOpUpdateDoesNotModifyLastUpdated() throws InterruptedException {
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+ Patient patient = new Patient();
+ patient.getNameFirstRep().setFamily("graham").addGiven("gary");
+
+ patient = (Patient) patientDao.create(patient).getResource();
+ Date originalLastUpdated = patient.getMeta().getLastUpdated();
+
+ patient = (Patient) patientDao.update(patient).getResource();
+ Date newLastUpdated = patient.getMeta().getLastUpdated();
+
+ assertThat(originalLastUpdated).isEqualTo(newLastUpdated);
+ }
+
+ @Test
+ default void luceneSearch_forTagsAndLastUpdated_shouldReturn() {
+ // setup
+ SystemRequestDetails requestDeatils = new SystemRequestDetails();
+ String system = "http://fhir";
+ String code = "cv";
+ Date start = Date.from(Instant.now().minus(1, ChronoUnit.SECONDS).truncatedTo(ChronoUnit.SECONDS));
+ Date end = Date.from(Instant.now().plus(10, ChronoUnit.SECONDS).truncatedTo(ChronoUnit.SECONDS));
+
+ @SuppressWarnings("unchecked")
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+ // create a patient with some tag
+ Patient patient = new Patient();
+ patient.getMeta()
+ .addTag(system, code, "");
+ patient.addName().addGiven("homer")
+ .setFamily("simpson");
+ patient.addAddress()
+ .setCity("springfield")
+ .addLine("742 evergreen terrace");
+ Long id = patientDao.create(patient, requestDeatils).getId().toUnqualifiedVersionless().getIdPartAsLong();
+
+ // create base search map
+ SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
+ TokenOrListParam goldenRecordStatusToken = new TokenOrListParam(system, code);
+ map.add(Constants.PARAM_TAG, goldenRecordStatusToken);
+ DateRangeParam lastUpdated = DateRangeUtil.narrowDateRange(map.getLastUpdated(), start, end);
+ map.setLastUpdated(lastUpdated);
+
+ runInTransaction(() -> {
+ Stream stream;
+ List list;
+ Optional first;
+
+ // tag search only; should return our resource
+ map.setLastUpdated(null);
+ stream = patientDao.searchForIdStream(map, new SystemRequestDetails(), null);
+ list = stream.toList();
+ assertEquals(1, list.size());
+ first = list.stream().findFirst();
+ assertTrue(first.isPresent());
+ assertEquals(id, first.get().getId());
+
+ // last updated search only; should return our resource
+ map.setLastUpdated(lastUpdated);
+ map.remove(Constants.PARAM_TAG);
+ stream = patientDao.searchForIdStream(map, new SystemRequestDetails(), null);
+ list = stream.toList();
+ assertEquals(1, list.size());
+ first = list.stream().findFirst();
+ assertTrue(first.isPresent());
+ assertEquals(id, first.get().getId());
+
+ // both last updated and tags; should return our resource
+ map.add(Constants.PARAM_TAG, goldenRecordStatusToken);
+ stream = patientDao.searchForIdStream(map, new SystemRequestDetails(), null);
+ list = stream.toList();
+ assertEquals(1, list.size());
+ first = list.stream().findFirst();
+ assertTrue(first.isPresent());
+ assertEquals(id, first.get().getId());
+ });
+ }
+
+ @Test
+ default void searchLuceneAndJPA_withLuceneMatchingButJpaNot_returnsNothing() {
+ // setup
+ int numToCreate = 2 * SearchBuilder.getMaximumPageSize() + 10;
+ SystemRequestDetails requestDetails = new SystemRequestDetails();
+
+ @SuppressWarnings("unchecked")
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+ // create resources
+ for (int i = 0; i < numToCreate; i++) {
+ Patient patient = new Patient();
+ patient.setActive(true);
+ patient.addIdentifier()
+ .setSystem("http://fhir.com")
+ .setValue("ZYX");
+ patient.getText().setDivAsString("ABC
");
+ patientDao.create(patient, requestDetails);
+ }
+
+ // test
+ SearchParameterMap map = new SearchParameterMap();
+ map.setLoadSynchronous(true);
+ map.setSearchTotalMode(SearchTotalModeEnum.ACCURATE);
+ TokenAndListParam tokenAndListParam = new TokenAndListParam();
+ tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
+ map.add("active", tokenAndListParam);
+ map.add(Constants.PARAM_TEXT, new StringParam("ABC"));
+ map.add("identifier", new TokenParam(null, "not found"));
+ IBundleProvider provider = patientDao.search(map, requestDetails);
+
+ // verify
+ assertEquals(0, provider.getAllResources().size());
+ }
+
+ @Test
+ default void testLuceneNarrativeSearchQueryIntersectingJpaQuery() {
+ final int numberOfPatientsToCreate = SearchBuilder.getMaximumPageSize() + 10;
+ List expectedActivePatientIds = new ArrayList<>(numberOfPatientsToCreate);
+
+ SystemRequestDetails requestDetails = new SystemRequestDetails();
+
+ @SuppressWarnings("unchecked")
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+
+ // create active and non-active patients with the same narrative
+ for (int i = 0; i < numberOfPatientsToCreate; i++) {
+ Patient activePatient = new Patient();
+ activePatient.getText().setDivAsString("");
+ activePatient.setActive(true);
+ String patientId = patientDao.create(activePatient, requestDetails).getId().toUnqualifiedVersionless().getIdPart();
+ expectedActivePatientIds.add(patientId);
+
+ Patient nonActivePatient = new Patient();
+ nonActivePatient.getText().setDivAsString("");
+ nonActivePatient.setActive(false);
+ patientDao.create(nonActivePatient, requestDetails);
+ }
+
+ SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
+
+ TokenAndListParam tokenAndListParam = new TokenAndListParam();
+ tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
+
+ map.add("active", tokenAndListParam);
+ map.add(Constants.PARAM_TEXT, new StringParam("AAAS"));
+
+ IBundleProvider searchResultBundle = patientDao.search(map, requestDetails);
+ List resourceIdsFromSearchResult = searchResultBundle.getAllResourceIds();
+
+ assertThat(resourceIdsFromSearchResult).containsExactlyInAnyOrderElementsOf(expectedActivePatientIds);
+ }
+
+ @Test
+ default void testLuceneContentSearchQueryIntersectingJpaQuery() {
+ final int numberOfPatientsToCreate = SearchBuilder.getMaximumPageSize() + 10;
+ final String patientFamilyName = "Flanders";
+ List expectedActivePatientIds = new ArrayList<>(numberOfPatientsToCreate);
+
+ SystemRequestDetails requestDetails = new SystemRequestDetails();
+
+ @SuppressWarnings("unchecked")
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+
+ // create active and non-active patients with the same narrative
+ for (int i = 0; i < numberOfPatientsToCreate; i++) {
+ Patient activePatient = new Patient();
+ activePatient.addName().setFamily(patientFamilyName);
+ activePatient.setActive(true);
+ String patientId = patientDao.create(activePatient, requestDetails).getId().toUnqualifiedVersionless().getIdPart();
+ expectedActivePatientIds.add(patientId);
+
+ Patient nonActivePatient = new Patient();
+ nonActivePatient.addName().setFamily(patientFamilyName);
+ nonActivePatient.setActive(false);
+ patientDao.create(nonActivePatient, requestDetails);
+ }
+
+ SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
+ TokenAndListParam tokenAndListParam = new TokenAndListParam();
+ tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
+ map.add("active", tokenAndListParam);
+ map.add(Constants.PARAM_CONTENT, new StringParam(patientFamilyName));
+
+ IBundleProvider searchResultBundle = patientDao.search(map, requestDetails);
+ List resourceIdsFromSearchResult = searchResultBundle.getAllResourceIds();
+
+ assertThat(resourceIdsFromSearchResult).containsExactlyInAnyOrderElementsOf(expectedActivePatientIds);
+ }
+
+ @Test
+ default void searchLuceneAndJPA_withLuceneBroadAndJPASearchNarrow_returnsFoundResults() {
+ // setup
+ int numToCreate = 2 * SearchBuilder.getMaximumPageSize() + 10;
+ String identifierToFind = "bcde";
+ SystemRequestDetails requestDetails = new SystemRequestDetails();
+
+ @SuppressWarnings("unchecked")
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+ // create patients
+ for (int i = 0; i < numToCreate; i++) {
+ Patient patient = new Patient();
+ patient.setActive(true);
+ String identifierVal = i == numToCreate - 10 ? identifierToFind:
+ "abcd";
+ patient.addIdentifier()
+ .setSystem("http://fhir.com")
+ .setValue(identifierVal);
+
+ patient.getText().setDivAsString(
+ "FINDME
"
+ );
+ patientDao.create(patient, requestDetails);
+ }
+
+ // test
+ SearchParameterMap map = new SearchParameterMap();
+ map.setLoadSynchronous(true);
+ map.setSearchTotalMode(SearchTotalModeEnum.ACCURATE);
+ TokenAndListParam tokenAndListParam = new TokenAndListParam();
+ tokenAndListParam.addAnd(new TokenOrListParam().addOr(new TokenParam().setValue("true")));
+ map.add("active", tokenAndListParam);
+ map.add(Constants.PARAM_TEXT, new StringParam("FINDME"));
+ map.add("identifier", new TokenParam(null, identifierToFind));
+ IBundleProvider provider = patientDao.search(map, requestDetails);
+
+ // verify
+ List ids = provider.getAllResourceIds();
+ assertEquals(1, ids.size());
+ }
+
+ @Test
+ default void testSearchNarrativeWithLuceneSearch() {
+ final int numberOfPatientsToCreate = SearchBuilder.getMaximumPageSize() + 10;
+ List expectedActivePatientIds = new ArrayList<>(numberOfPatientsToCreate);
+
+ SystemRequestDetails requestDetails = new SystemRequestDetails();
+
+ @SuppressWarnings("unchecked")
+ IFhirResourceDao patientDao = getResourceDao("Patient");
+
+
+ for (int i = 0; i < numberOfPatientsToCreate; i++) {
+ Patient patient = new Patient();
+ patient.getText().setDivAsString("");
+ expectedActivePatientIds.add(patientDao.create(patient, requestDetails).getId().toUnqualifiedVersionless().getIdPart());
+ }
+
+ {
+ Patient patient = new Patient();
+ patient.getText().setDivAsString("");
+ patientDao.create(patient, requestDetails);
+ }
+ {
+ Patient patient = new Patient();
+ patient.getText().setDivAsString("ZZYZXY
");
+ patientDao.create(patient, requestDetails);
+ }
+
+ SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true);
+ map.add(Constants.PARAM_TEXT, new StringParam("AAAS"));
+
+ IBundleProvider searchResultBundle = patientDao.search(map, requestDetails);
+ List resourceIdsFromSearchResult = searchResultBundle.getAllResourceIds();
+
+ assertThat(resourceIdsFromSearchResult).containsExactlyInAnyOrderElementsOf(expectedActivePatientIds);
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractorTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractorTest.java
index cb7ae2cf928..46a0db47ff3 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractorTest.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractorTest.java
@@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
+import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.CompositeSearchIndexData;
import ca.uhn.fhir.jpa.model.search.DateSearchIndexData;
import ca.uhn.fhir.jpa.model.search.ExtendedHSearchIndexData;
@@ -55,7 +56,7 @@ class ExtendedHSearchIndexExtractorTest implements ITestDataBuilder.WithSupport
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams("Observation", ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH);
ExtendedHSearchIndexExtractor extractor = new ExtendedHSearchIndexExtractor(
myJpaStorageSettings, myFhirContext, activeSearchParams, mySearchParamExtractor);
- ExtendedHSearchIndexData indexData = extractor.extract(new Observation(), extractedParams);
+ ExtendedHSearchIndexData indexData = extractor.extract(new Observation(), new ResourceTable(), extractedParams);
// validate
Set spIndexData = indexData.getSearchParamComposites().get("component-code-value-concept");
@@ -78,14 +79,13 @@ class ExtendedHSearchIndexExtractorTest implements ITestDataBuilder.WithSupport
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams("Patient", ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH);
ExtendedHSearchIndexExtractor extractor = new ExtendedHSearchIndexExtractor(
myJpaStorageSettings, myFhirContext, activeSearchParams, mySearchParamExtractor);
- ExtendedHSearchIndexData indexData = extractor.extract(new SearchParameter(), searchParams);
+ ExtendedHSearchIndexData indexData = extractor.extract(new SearchParameter(), new ResourceTable(), searchParams);
// validate
Set dIndexData = indexData.getDateIndexData().get("Date");
assertThat(dIndexData).hasSize(0);
Set qIndexData = indexData.getQuantityIndexData().get("Quantity");
assertThat(qIndexData).hasSize(0);
-
}
@Override
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java
index 39d75793f28..6670ab15a4c 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java
@@ -81,7 +81,7 @@ public class CompositeInterceptorBroadcaster {
}
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal) {
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
- interceptorBroadcaster.callHooks(thePointcut, theParams);
+ retVal = interceptorBroadcaster.callHooks(thePointcut, theParams);
}
return retVal;
}
diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcasterTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcasterTest.java
new file mode 100644
index 00000000000..2a670d15811
--- /dev/null
+++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcasterTest.java
@@ -0,0 +1,161 @@
+package ca.uhn.fhir.rest.server.util;
+
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class CompositeInterceptorBroadcasterTest {
+
+ @Mock
+ private IInterceptorBroadcaster myModuleBroadcasterMock;
+ @Mock
+ private IInterceptorBroadcaster myReqDetailsBroadcasterMock;
+ @Mock
+ private Pointcut myPointcutMock;
+ @Mock
+ private HookParams myHookParamsMock;
+ @Mock
+ private RequestDetails myRequestDetailsMock;
+
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() {
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
+ when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock,
+ myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isTrue();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() {
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
+ when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock,
+ myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isFalse();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsFalse_ThenSkipsBroadcasterInRequestDetails_And_ReturnsFalse() {
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock,
+ myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isFalse();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ verify(myReqDetailsBroadcasterMock, never()).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetailsBroadcaster_ThenReturnsTrue() {
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(null);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, myPointcutMock,
+ myHookParamsMock);
+
+ assertThat(retVal).isTrue();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetailsBroadcaster_ThenReturnsFalse() {
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(null);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, myPointcutMock,
+ myHookParamsMock);
+
+ assertThat(retVal).isFalse();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetails_ThenReturnsTrue() {
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, null, myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isTrue();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetails_ThenReturnsFalse() {
+
+ when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, null, myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isFalse();
+
+ verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+ @Test
+ void doCallHooks_WhenNullModuleBroadcaster_And_NullRequestDetails_ThenReturnsTrue() {
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, null, myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isTrue();
+ }
+
+ @Test
+ void doCallHooks_WhenNullModuleBroadcaster_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() {
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
+ when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, myRequestDetailsMock, myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isTrue();
+ verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+
+
+ @Test
+ void doCallHooks_WhenNullModuleBroadcaster_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() {
+ when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
+ when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
+
+ boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, myRequestDetailsMock, myPointcutMock, myHookParamsMock);
+
+ assertThat(retVal).isFalse();
+ verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
+ }
+}
diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/v1/ReindexV1Config.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/v1/ReindexV1Config.java
index fe8f736aef5..4dd95638937 100644
--- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/v1/ReindexV1Config.java
+++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/v1/ReindexV1Config.java
@@ -92,7 +92,7 @@ public class ReindexV1Config {
"Load IDs of resources to reindex",
ResourceIdListWorkChunkJson.class,
myReindexLoadIdsStep)
- .addLastStep("reindex-start", "Start the resource reindex", reindexStepV1())
+ .addLastStep("reindex", "Start the resource reindex", reindexStepV1())
.build();
}
diff --git a/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java b/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java
index cd7f6019568..2cb4ce83e53 100644
--- a/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java
+++ b/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java
@@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
+import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
import ca.uhn.fhir.util.BundleBuilder;
import com.google.common.collect.HashMultimap;
@@ -66,18 +67,42 @@ public class DaoTestDataBuilder implements ITestDataBuilder.WithSupport, ITestDa
}
//noinspection rawtypes
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass());
+
+ // manipulate the transaction details to provide a fake transaction date
+ TransactionDetails details = null;
+ if (theResource.getMeta() != null && theResource.getMeta().getLastUpdated() != null) {
+ details = new TransactionDetails(theResource.getMeta().getLastUpdated());
+ } else {
+ details = new TransactionDetails();
+ }
+
//noinspection unchecked
- IIdType id = dao.create(theResource, mySrd).getId().toUnqualifiedVersionless();
+ IIdType id = dao.create(theResource, null, true, mySrd, details)
+ .getId().toUnqualifiedVersionless();
myIds.put(theResource.fhirType(), id);
return id;
}
@Override
public IIdType doUpdateResource(IBaseResource theResource) {
+ // manipulate the transaction details to provdie a fake transaction date
+ TransactionDetails details = null;
+ if (theResource.getMeta() != null && theResource.getMeta().getLastUpdated() != null) {
+ details = new TransactionDetails(theResource.getMeta().getLastUpdated());
+ } else {
+ details = new TransactionDetails();
+ }
+
//noinspection rawtypes
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass());
+
//noinspection unchecked
- IIdType id = dao.update(theResource, mySrd).getId().toUnqualifiedVersionless();
+ IIdType id = dao.update(theResource,
+ null,
+ true,
+ false,
+ mySrd,
+ details).getId().toUnqualifiedVersionless();
myIds.put(theResource.fhirType(), id);
return id;
}
diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
index 25100484649..31d69a14067 100644
--- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
+++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
@@ -23,6 +23,7 @@ import org.hl7.fhir.r4.model.Composition;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.DecimalType;
import org.hl7.fhir.r4.model.Device;
+import org.hl7.fhir.r4.model.DiagnosticReport;
import org.hl7.fhir.r4.model.DocumentReference;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Extension;
@@ -43,6 +44,7 @@ import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.QuestionnaireResponse;
import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.Specimen;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.codesystems.DataAbsentReason;
@@ -55,6 +57,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.Nonnull;
+import org.testcontainers.shaded.com.trilead.ssh2.packets.PacketDisconnect;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
@@ -261,7 +265,81 @@ public class JsonParserR4Test extends BaseTest {
idx = encoded.indexOf("\"Medication\"", idx + 1);
assertEquals(-1, idx);
+ }
+ @Test
+ public void testDuplicateContainedResourcesAcrossABundleAreReplicated() {
+ Bundle b = new Bundle();
+ Specimen specimen = new Specimen();
+ Practitioner practitioner = new Practitioner();
+ DiagnosticReport report = new DiagnosticReport();
+ report.addSpecimen(new Reference(specimen));
+ b.addEntry().setResource(report).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("/DiagnosticReport");
+
+ Observation obs = new Observation();
+ obs.addPerformer(new Reference(practitioner));
+ obs.setSpecimen(new Reference(specimen));
+
+ b.addEntry().setResource(obs).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("/Observation");
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(b);
+ //Then: Diag should contain one local contained specimen
+ assertThat(encoded).contains("[{\"resource\":{\"resourceType\":\"DiagnosticReport\",\"contained\":[{\"resourceType\":\"Specimen\",\"id\":\"1\"}]");
+ //Then: Obs should contain one local contained specimen, and one local contained pract
+ assertThat(encoded).contains("\"resource\":{\"resourceType\":\"Observation\",\"contained\":[{\"resourceType\":\"Specimen\",\"id\":\"1\"},{\"resourceType\":\"Practitioner\",\"id\":\"2\"}]");
+ assertThat(encoded).contains("\"performer\":[{\"reference\":\"#2\"}],\"specimen\":{\"reference\":\"#1\"}");
+
+ //Also, reverting the operation should work too!
+ Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, encoded);
+ IBaseResource resource1 = ((DiagnosticReport) bundle.getEntry().get(0).getResource()).getSpecimenFirstRep().getResource();
+ IBaseResource resource = ((Observation) bundle.getEntry().get(1).getResource()).getSpecimen().getResource();
+ assertThat(resource1.getIdElement().getIdPart()).isEqualTo(resource.getIdElement().getIdPart());
+ assertThat(resource1).isNotSameAs(resource);
+
+ }
+
+ @Test
+ public void testAutoAssignedContainedCollisionOrderDependent() {
+ {
+ Specimen specimen = new Specimen();
+ Practitioner practitioner = new Practitioner();
+ DiagnosticReport report = new DiagnosticReport();
+ report.addSpecimen(new Reference(specimen));
+
+ Observation obs = new Observation();
+ //When: The practitioner (which is parsed first, has an assigned id that will collide with auto-assigned
+ practitioner.setId("#1");
+ obs.addPerformer(new Reference(practitioner));
+ obs.setSpecimen(new Reference(specimen));
+
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(obs);
+ assertThat(encoded).contains("\"contained\":[{\"resourceType\":\"Practitioner\",\"id\":\"1\"},{\"resourceType\":\"Specimen\",\"id\":\"2\"}]");
+ assertThat(encoded).contains("\"performer\":[{\"reference\":\"#1\"}]");
+ assertThat(encoded).contains("\"specimen\":{\"reference\":\"#2\"}}");
+ ourLog.info(encoded);
+ }
+
+ {
+ Specimen specimen = new Specimen();
+ Practitioner practitioner = new Practitioner();
+ DiagnosticReport report = new DiagnosticReport();
+ report.addSpecimen(new Reference(specimen));
+
+ Observation obs = new Observation();
+
+ //When: The specimen (which is parsed second, has an assigned id that will collide with auto-assigned practitioner
+ specimen.setId("#1");
+ obs.addPerformer(new Reference(practitioner));
+ obs.setSpecimen(new Reference(specimen));
+
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(obs);
+ assertThat(encoded).contains("\"contained\":[{\"resourceType\":\"Specimen\",\"id\":\"1\"},{\"resourceType\":\"Practitioner\",\"id\":\"2\"}]");
+ assertThat(encoded).contains("\"performer\":[{\"reference\":\"#2\"}]");
+ assertThat(encoded).contains("\"specimen\":{\"reference\":\"#1\"}}");
+ ourLog.info(encoded);
+ }
}
@Test
diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java
index 87a33805db6..478d54e8e49 100644
--- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java
+++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java
@@ -6,7 +6,10 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.parser.JsonParser;
import com.google.common.collect.Lists;
+import org.apache.jena.base.Sys;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseReference;
@@ -47,6 +50,8 @@ import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException;
+import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -1545,23 +1550,29 @@ public class FhirTerserR4Test {
@Test
void copyingAndParsingCreatesDuplicateContainedResources() {
- var input = new Library();
+ var library = new Library();
var params = new Parameters();
var id = "#expansion-parameters-ecr";
params.setId(id);
params.addParameter("system-version", new StringType("test2"));
var paramsExt = new Extension();
+
paramsExt.setUrl("test").setValue(new Reference(id));
- input.addContained(params);
- input.addExtension(paramsExt);
+ library.addContained(params);
+ library.addExtension(paramsExt);
+
final var parser = FhirContext.forR4Cached().newJsonParser();
- var stringified = parser.encodeResourceToString(input);
+ var stringified = parser.encodeResourceToString(library);
+
+
var parsed = parser.parseResource(stringified);
var copy = ((Library) parsed).copy();
+
assertEquals(1, copy.getContained().size());
- var stringifiedCopy = parser.encodeResourceToString(copy);
- var parsedCopy = parser.parseResource(stringifiedCopy);
- assertEquals(1, ((Library) parsedCopy).getContained().size());
+
+ String stringifiedCopy = FhirContext.forR4Cached().newJsonParser().encodeResourceToString(copy);
+ Library parsedCopy = (Library) parser.parseResource(stringifiedCopy);
+ assertEquals(1, parsedCopy.getContained().size());
}
/**
diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java
index d9797d85e69..ca5cb382a14 100644
--- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java
+++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java
@@ -209,6 +209,12 @@ public interface ITestDataBuilder {
return t -> ((IBaseResource)t).getMeta().setLastUpdated(theLastUpdated);
}
+ /**
+ * Sets a _lastUpdated value.
+ *
+ * This value will also be used to control the transaction time, which is what determines
+ * what the Updated date is.
+ */
default ICreationArgument withLastUpdated(String theIsoDate) {
return t -> ((IBaseResource)t).getMeta().setLastUpdated(new InstantType(theIsoDate).getValue());
}
diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProviders.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProviders.java
new file mode 100644
index 00000000000..d9933708e47
--- /dev/null
+++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProviders.java
@@ -0,0 +1,123 @@
+/*-
+ * #%L
+ * HAPI FHIR Test Utilities
+ * %%
+ * Copyright (C) 2014 - 2024 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package ca.uhn.fhir.test.utilities.validation;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.rest.annotation.RequiredParam;
+import ca.uhn.fhir.rest.annotation.Search;
+import ca.uhn.fhir.rest.param.UriParam;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.util.ClasspathUtil;
+import org.hl7.fhir.instance.model.api.IBaseParameters;
+import org.hl7.fhir.instance.model.api.IDomainResource;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public interface IValidationProviders {
+ String CODE_SYSTEM = "http://code.system/url";
+ String CODE_SYSTEM_VERSION = "1.0.0";
+ String CODE_SYSTEM_NAME = "Test Code System";
+ String CODE = "CODE";
+ String VALUE_SET_URL = "http://value.set/url";
+ String DISPLAY = "Explanation for code TestCode.";
+ String LANGUAGE = "en";
+ String ERROR_MESSAGE = "This is an error message";
+
+ interface IMyValidationProvider extends IResourceProvider {
+ void addException(String theOperation, String theUrl, String theCode, Exception theException);
+ void addTerminologyResponse(String theOperation, String theUrl, String theCode, P theReturnParams);
+ IBaseParameters addTerminologyResponse(String theOperation, String theUrl, String theCode, FhirContext theFhirContext, String theTerminologyResponseFile);
+ }
+
+ abstract class MyValidationProvider implements IMyValidationProvider {
+ private final Map myExceptionMap = new HashMap<>();
+ private boolean myShouldThrowExceptionForResourceNotFound = true;
+ private final Map myTerminologyResponseMap = new HashMap<>();
+ private final Map myTerminologyResourceMap = new HashMap<>();
+
+ static String getInputKey(String theOperation, String theUrl, String theCode) {
+ return theOperation + "-" + theUrl + "#" + theCode;
+ }
+
+ public void setShouldThrowExceptionForResourceNotFound(boolean theShouldThrowExceptionForResourceNotFound) {
+ myShouldThrowExceptionForResourceNotFound = theShouldThrowExceptionForResourceNotFound;
+ }
+
+ public void addException(String theOperation, String theUrl, String theCode, Exception theException) {
+ String inputKey = getInputKey(theOperation, theUrl, theCode);
+ myExceptionMap.put(inputKey, theException);
+ }
+
+ abstract Class extends IBaseParameters> getParameterType();
+
+ @Override
+ public void addTerminologyResponse(String theOperation, String theUrl, String theCode, P theReturnParams) {
+ myTerminologyResponseMap.put(getInputKey(theOperation, theUrl, theCode), theReturnParams);
+ }
+
+ public IBaseParameters addTerminologyResponse(String theOperation, String theUrl, String theCode, FhirContext theFhirContext, String theTerminologyResponseFile) {
+ IBaseParameters responseParams = ClasspathUtil.loadResource(theFhirContext, getParameterType(), theTerminologyResponseFile);
+ addTerminologyResponse(theOperation, theUrl, theCode, responseParams);
+ return responseParams;
+ }
+
+ protected void addTerminologyResource(String theUrl, T theResource) {
+ myTerminologyResourceMap.put(theUrl, theResource);
+ }
+
+ public abstract T addTerminologyResource(String theUrl);
+
+ protected IBaseParameters getTerminologyResponse(String theOperation, String theUrl, String theCode) throws Exception {
+ String inputKey = getInputKey(theOperation, theUrl, theCode);
+ if (myExceptionMap.containsKey(inputKey)) {
+ throw myExceptionMap.get(inputKey);
+ }
+ IBaseParameters params = myTerminologyResponseMap.get(inputKey);
+ if (params == null) {
+ throw new IllegalStateException("Test setup incomplete. Missing return params for " + inputKey);
+ }
+ return params;
+ }
+
+ protected T getTerminologyResource(UriParam theUrlParam) {
+ if (theUrlParam.isEmpty()) {
+ throw new IllegalStateException("CodeSystem url should not be null.");
+ }
+ String urlValue = theUrlParam.getValue();
+ if (!myTerminologyResourceMap.containsKey(urlValue) && myShouldThrowExceptionForResourceNotFound) {
+ throw new IllegalStateException("Test setup incomplete. CodeSystem not found " + urlValue);
+ }
+ return myTerminologyResourceMap.get(urlValue);
+ }
+
+ @Search
+ public List find(@RequiredParam(name = "url") UriParam theUrlParam) {
+ T resource = getTerminologyResource(theUrlParam);
+ return resource != null ? List.of(resource) : List.of();
+ }
+ }
+
+ interface IMyLookupCodeProvider extends IResourceProvider {
+ void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult);
+ }
+}
diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProvidersDstu3.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProvidersDstu3.java
new file mode 100644
index 00000000000..11c5244df41
--- /dev/null
+++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProvidersDstu3.java
@@ -0,0 +1,137 @@
+/*-
+ * #%L
+ * HAPI FHIR Test Utilities
+ * %%
+ * Copyright (C) 2014 - 2024 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package ca.uhn.fhir.test.utilities.validation;
+
+import ca.uhn.fhir.rest.annotation.IdParam;
+import ca.uhn.fhir.rest.annotation.Operation;
+import ca.uhn.fhir.rest.annotation.OperationParam;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import jakarta.servlet.http.HttpServletRequest;
+import org.hl7.fhir.dstu3.model.BooleanType;
+import org.hl7.fhir.dstu3.model.CodeSystem;
+import org.hl7.fhir.dstu3.model.CodeType;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.IdType;
+import org.hl7.fhir.dstu3.model.Parameters;
+import org.hl7.fhir.dstu3.model.StringType;
+import org.hl7.fhir.dstu3.model.UriType;
+import org.hl7.fhir.dstu3.model.ValueSet;
+import org.hl7.fhir.instance.model.api.IBaseParameters;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+import java.util.List;
+
+public interface IValidationProvidersDstu3 {
+ @SuppressWarnings("unused")
+ class MyCodeSystemProviderDstu3 extends IValidationProviders.MyValidationProvider {
+ @Operation(name = "$validate-code", idempotent = true, returnParameters = {
+ @OperationParam(name = "result", type = BooleanType.class, min = 1),
+ @OperationParam(name = "message", type = StringType.class),
+ @OperationParam(name = "display", type = StringType.class)
+ })
+ public IBaseParameters validateCode(
+ HttpServletRequest theServletRequest,
+ @IdParam(optional = true) IdType theId,
+ @OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
+ @OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
+ @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
+ ) throws Exception {
+ String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
+ String code = theCode != null ? theCode.getValue() : null;
+ return getTerminologyResponse("$validate-code", url, code);
+ }
+
+ @Operation(name = "$lookup", idempotent = true, returnParameters= {
+ @OperationParam(name = "name", type = StringType.class, min = 1),
+ @OperationParam(name = "version", type = StringType.class),
+ @OperationParam(name = "display", type = StringType.class, min = 1),
+ @OperationParam(name = "abstract", type = BooleanType.class, min = 1),
+ @OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
+ })
+ public IBaseParameters lookup(
+ HttpServletRequest theServletRequest,
+ @OperationParam(name = "code", max = 1) CodeType theCode,
+ @OperationParam(name = "system",max = 1) UriType theSystem,
+ @OperationParam(name = "coding", max = 1) Coding theCoding,
+ @OperationParam(name = "version", max = 1) StringType theVersion,
+ @OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
+ @OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
+ RequestDetails theRequestDetails
+ ) throws Exception {
+ String url = theSystem != null ? theSystem.getValue() : null;
+ String code = theCode != null ? theCode.getValue() : null;
+ return getTerminologyResponse("$lookup", url, code);
+ }
+ @Override
+ public Class extends IBaseResource> getResourceType() {
+ return CodeSystem.class;
+ }
+ @Override
+ Class getParameterType() {
+ return Parameters.class;
+ }
+ @Override
+ public CodeSystem addTerminologyResource(String theUrl) {
+ CodeSystem codeSystem = new CodeSystem();
+ codeSystem.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
+ codeSystem.setUrl(theUrl);
+ addTerminologyResource(theUrl, codeSystem);
+ return codeSystem;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ class MyValueSetProviderDstu3 extends IValidationProviders.MyValidationProvider {
+ @Operation(name = "$validate-code", idempotent = true, returnParameters = {
+ @OperationParam(name = "result", type = BooleanType.class, min = 1),
+ @OperationParam(name = "message", type = StringType.class),
+ @OperationParam(name = "display", type = StringType.class)
+ })
+ public IBaseParameters validateCode(
+ HttpServletRequest theServletRequest,
+ @IdParam(optional = true) IdType theId,
+ @OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
+ @OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
+ @OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
+ @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
+ @OperationParam(name = "valueSet") ValueSet theValueSet
+ ) throws Exception {
+ String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null;
+ String code = theCode != null ? theCode.getValue() : null;
+ return getTerminologyResponse("$validate-code", url, code);
+ }
+ @Override
+ public Class extends IBaseResource> getResourceType() {
+ return ValueSet.class;
+ }
+ @Override
+ Class getParameterType() {
+ return Parameters.class;
+ }
+ @Override
+ public ValueSet addTerminologyResource(String theUrl) {
+ ValueSet valueSet = new ValueSet();
+ valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
+ valueSet.setUrl(theUrl);
+ addTerminologyResource(theUrl, valueSet);
+ return valueSet;
+ }
+ }
+}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/IValidateCodeProvidersR4.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProvidersR4.java
similarity index 55%
rename from hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/IValidateCodeProvidersR4.java
rename to hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProvidersR4.java
index fd03a8163ff..87a3dacb1fc 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/IValidateCodeProvidersR4.java
+++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/validation/IValidationProvidersR4.java
@@ -1,12 +1,29 @@
-package org.hl7.fhir.r4.validation;
+/*-
+ * #%L
+ * HAPI FHIR Test Utilities
+ * %%
+ * Copyright (C) 2014 - 2024 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package ca.uhn.fhir.test.utilities.validation;
-import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.servlet.http.HttpServletRequest;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.BooleanType;
@@ -21,38 +38,29 @@ import org.hl7.fhir.r4.model.ValueSet;
import java.util.List;
-public interface IValidateCodeProvidersR4 {
+public interface IValidationProvidersR4 {
@SuppressWarnings("unused")
- class MyCodeSystemProviderR4 implements IValidationProviders.IMyCodeSystemProvider {
- private UriType mySystemUrl;
- private CodeType myCode;
- private StringType myDisplay;
- private Exception myException;
- private Parameters myReturnParams;
+ class MyCodeSystemProviderR4 extends IValidationProviders.MyValidationProvider {
- @Operation(name = "validate-code", idempotent = true, returnParameters = {
+ @Operation(name = "$validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
- public Parameters validateCode(
+ public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
) throws Exception {
- mySystemUrl = theCodeSystemUrl;
- myCode = theCode;
- myDisplay = theDisplay;
- if (myException != null) {
- throw myException;
- }
- return myReturnParams;
+ String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
+ String code = theCode != null ? theCode.getValue() : null;
+ return getTerminologyResponse("$validate-code", url, code);
}
- @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
+ @Operation(name = "$lookup", idempotent = true, returnParameters= {
@OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class),
@OperationParam(name = "display", type = StringType.class, min = 1),
@@ -69,54 +77,39 @@ public interface IValidateCodeProvidersR4 {
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
RequestDetails theRequestDetails
) throws Exception {
- mySystemUrl = theSystem;
- myCode = theCode;
- if (myException != null) {
- throw myException;
- }
- return myReturnParams;
+ String url = theSystem != null ? theSystem.getValue() : null;
+ String code = theCode != null ? theCode.getValue() : null;
+ return getTerminologyResponse("$lookup", url, code);
}
-
@Override
public Class extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
- public void setException(Exception theException) {
- myException = theException;
- }
@Override
- public void setReturnParams(IBaseParameters theParameters) {
- myReturnParams = (Parameters) theParameters;
+ Class getParameterType() {
+ return Parameters.class;
}
+
@Override
- public String getCode() {
- return myCode != null ? myCode.getValueAsString() : null;
- }
- @Override
- public String getSystem() {
- return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
- }
- public String getDisplay() {
- return myDisplay != null ? myDisplay.getValue() : null;
+ public CodeSystem addTerminologyResource(String theUrl) {
+ CodeSystem codeSystem = new CodeSystem();
+ codeSystem.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
+ codeSystem.setUrl(theUrl);
+ addTerminologyResource(theUrl, codeSystem);
+ return codeSystem;
}
}
@SuppressWarnings("unused")
- class MyValueSetProviderR4 implements IValidationProviders.IMyValueSetProvider {
- private Exception myException;
- private Parameters myReturnParams;
- private UriType mySystemUrl;
- private UriType myValueSetUrl;
- private CodeType myCode;
- private StringType myDisplay;
+ class MyValueSetProviderR4 extends IValidationProviders.MyValidationProvider {
- @Operation(name = "validate-code", idempotent = true, returnParameters = {
+ @Operation(name = "$validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
- public Parameters validateCode(
+ public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
@@ -125,41 +118,25 @@ public interface IValidateCodeProvidersR4 {
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "valueSet") ValueSet theValueSet
) throws Exception {
- mySystemUrl = theSystem;
- myValueSetUrl = theValueSetUrl;
- myCode = theCode;
- myDisplay = theDisplay;
- if (myException != null) {
- throw myException;
- }
- return myReturnParams;
+ String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null;
+ String code = theCode != null ? theCode.getValue() : null;
+ return getTerminologyResponse("$validate-code", url, code);
}
-
@Override
public Class extends IBaseResource> getResourceType() {
return ValueSet.class;
}
- public void setException(Exception theException) {
- myException = theException;
+ @Override
+ Class getParameterType() {
+ return Parameters.class;
}
@Override
- public void setReturnParams(IBaseParameters theParameters) {
- myReturnParams = (Parameters) theParameters;
- }
- @Override
- public String getCode() {
- return myCode != null ? myCode.getValueAsString() : null;
- }
- @Override
- public String getSystem() {
- return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
- }
- @Override
- public String getValueSet() {
- return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
- }
- public String getDisplay() {
- return myDisplay != null ? myDisplay.getValue() : null;
+ public ValueSet addTerminologyResource(String theUrl) {
+ ValueSet valueSet = new ValueSet();
+ valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
+ valueSet.setUrl(theUrl);
+ addTerminologyResource(theUrl, valueSet);
+ return valueSet;
}
}
}
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java
index 1b12e20ec62..7a05e197ae6 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java
@@ -190,7 +190,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
return new CodeValidationResult()
.setSeverity(IssueSeverity.ERROR)
.setMessage(theMessage)
- .setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
+ .setIssues(Collections.singletonList(new CodeValidationIssue(
theMessage,
IssueSeverity.ERROR,
CodeValidationIssueCode.INVALID,
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java
index 67e553e3d3b..a617e04c41b 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java
@@ -28,7 +28,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Enumerations;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
import java.util.ArrayList;
import java.util.Collections;
@@ -258,7 +257,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode);
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
CodeValidationResult codeValidationResult = new CodeValidationResult();
- codeValidationResult.setSeverityCode("error");
+ codeValidationResult.setSeverity(IssueSeverity.ERROR);
String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code "
+ theCodeSystemUrlAndVersion + "#" + theCode;
@@ -267,7 +266,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
}
codeValidationResult.setMessage(msg);
- codeValidationResult.addCodeValidationIssue(e.getCodeValidationIssue());
+ codeValidationResult.addIssue(e.getCodeValidationIssue());
return codeValidationResult;
}
@@ -551,18 +550,18 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
if (valueSetResult != null) {
codeValidationResult = valueSetResult;
} else {
- ValidationMessage.IssueSeverity severity;
+ IValidationSupport.IssueSeverity severity;
String message;
CodeValidationIssueCode issueCode = CodeValidationIssueCode.CODE_INVALID;
CodeValidationIssueCoding issueCoding = CodeValidationIssueCoding.INVALID_CODE;
if ("fragment".equals(codeSystemResourceContentMode)) {
- severity = ValidationMessage.IssueSeverity.WARNING;
+ severity = IValidationSupport.IssueSeverity.WARNING;
message = "Unknown code in fragment CodeSystem '"
+ getFormattedCodeSystemAndCodeForMessage(
theCodeSystemUrlAndVersionToValidate, theCodeToValidate)
+ "'";
} else {
- severity = ValidationMessage.IssueSeverity.ERROR;
+ severity = IValidationSupport.IssueSeverity.ERROR;
message = "Unknown code '"
+ getFormattedCodeSystemAndCodeForMessage(
theCodeSystemUrlAndVersionToValidate, theCodeToValidate)
@@ -574,10 +573,9 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
}
codeValidationResult = new CodeValidationResult()
- .setSeverityCode(severity.toCode())
+ .setSeverity(severity)
.setMessage(message)
- .addCodeValidationIssue(new CodeValidationIssue(
- message, getIssueSeverityFromCodeValidationIssue(severity), issueCode, issueCoding));
+ .addIssue(new CodeValidationIssue(message, severity, issueCode, issueCoding));
}
return codeValidationResult;
@@ -589,19 +587,6 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
+ theCodeToValidate;
}
- private IValidationSupport.IssueSeverity getIssueSeverityFromCodeValidationIssue(
- ValidationMessage.IssueSeverity theSeverity) {
- switch (theSeverity) {
- case ERROR:
- return IValidationSupport.IssueSeverity.ERROR;
- case WARNING:
- return IValidationSupport.IssueSeverity.WARNING;
- case INFORMATION:
- return IValidationSupport.IssueSeverity.INFORMATION;
- }
- return null;
- }
-
private CodeValidationResult findCodeInExpansion(
String theCodeToValidate,
String theDisplayToValidate,
@@ -1123,8 +1108,8 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
new CodeValidationIssue(
theMessage,
IssueSeverity.ERROR,
- CodeValidationIssueCode.OTHER,
- CodeValidationIssueCoding.OTHER));
+ CodeValidationIssueCode.INVALID,
+ CodeValidationIssueCoding.VS_INVALID));
}
for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next :
subExpansion.getExpansion().getContains()) {
@@ -1376,7 +1361,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
.setCodeSystemVersion(theCodeSystemVersion)
.setDisplay(theExpectedDisplay);
if (issueSeverity != null) {
- codeValidationResult.setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
+ codeValidationResult.setIssues(Collections.singletonList(new CodeValidationIssue(
message,
theIssueSeverityForCodeDisplayMismatch,
CodeValidationIssueCode.INVALID,
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java
index 370f8b423dd..398eacc52a2 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java
@@ -28,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.CodeSystem;
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.OperationOutcome;
import org.hl7.fhir.r4.model.Parameters;
@@ -631,7 +632,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
return new CodeValidationResult()
.setSeverity(severity)
.setMessage(theMessage)
- .addCodeValidationIssue(new CodeValidationIssue(
+ .addIssue(new CodeValidationIssue(
theMessage, severity, theIssueCode, CodeValidationIssueCoding.INVALID_CODE));
}
@@ -680,13 +681,13 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
createCodeValidationIssues(
(IBaseOperationOutcome) issuesValue.get(),
fhirContext.getVersion().getVersion())
- .ifPresent(i -> i.forEach(result::addCodeValidationIssue));
+ .ifPresent(i -> i.forEach(result::addIssue));
} else {
// create a validation issue out of the message
// this is a workaround to overcome an issue in the FHIR Validator library
// where ValueSet bindings are only reading issues but not messages
// @see https://github.com/hapifhir/org.hl7.fhir.core/issues/1766
- result.addCodeValidationIssue(createCodeValidationIssue(result.getMessage()));
+ result.addIssue(createCodeValidationIssue(result.getMessage()));
}
return result;
}
@@ -717,23 +718,42 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
private static Collection createCodeValidationIssuesR4(OperationOutcome theOperationOutcome) {
return theOperationOutcome.getIssue().stream()
- .map(issueComponent ->
- createCodeValidationIssue(issueComponent.getDetails().getText()))
+ .map(issueComponent -> {
+ String diagnostics = issueComponent.getDiagnostics();
+ IssueSeverity issueSeverity =
+ IssueSeverity.fromCode(issueComponent.getSeverity().toCode());
+ String issueTypeCode = issueComponent.getCode().toCode();
+ CodeableConcept details = issueComponent.getDetails();
+ CodeValidationIssue issue = new CodeValidationIssue(diagnostics, issueSeverity, issueTypeCode);
+ CodeValidationIssueDetails issueDetails = new CodeValidationIssueDetails(details.getText());
+ details.getCoding().forEach(coding -> issueDetails.addCoding(coding.getSystem(), coding.getCode()));
+ issue.setDetails(issueDetails);
+ return issue;
+ })
.collect(Collectors.toList());
}
private static Collection createCodeValidationIssuesDstu3(
org.hl7.fhir.dstu3.model.OperationOutcome theOperationOutcome) {
return theOperationOutcome.getIssue().stream()
- .map(issueComponent ->
- createCodeValidationIssue(issueComponent.getDetails().getText()))
+ .map(issueComponent -> {
+ String diagnostics = issueComponent.getDiagnostics();
+ IssueSeverity issueSeverity =
+ IssueSeverity.fromCode(issueComponent.getSeverity().toCode());
+ String issueTypeCode = issueComponent.getCode().toCode();
+ org.hl7.fhir.dstu3.model.CodeableConcept details = issueComponent.getDetails();
+ CodeValidationIssue issue = new CodeValidationIssue(diagnostics, issueSeverity, issueTypeCode);
+ CodeValidationIssueDetails issueDetails = new CodeValidationIssueDetails(details.getText());
+ details.getCoding().forEach(coding -> issueDetails.addCoding(coding.getSystem(), coding.getCode()));
+ issue.setDetails(issueDetails);
+ return issue;
+ })
.collect(Collectors.toList());
}
private static CodeValidationIssue createCodeValidationIssue(String theMessage) {
return new CodeValidationIssue(
theMessage,
- // assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match
IssueSeverity.ERROR,
CodeValidationIssueCode.INVALID,
CodeValidationIssueCoding.INVALID_CODE);
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java
index 1898292c451..265debed058 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java
@@ -87,7 +87,7 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup
result.setSeverity(null);
result.setMessage(null);
} else {
- result.addCodeValidationIssue(new CodeValidationIssue(
+ result.addIssue(new CodeValidationIssue(
theMessage,
myNonExistentCodeSystemSeverity,
CodeValidationIssueCode.NOT_FOUND,
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportUtils.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportUtils.java
index 7321f33d8c8..7093ab2a7ff 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportUtils.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportUtils.java
@@ -11,6 +11,17 @@ public final class ValidationSupportUtils {
private ValidationSupportUtils() {}
+ /**
+ * This method extracts a code system that can be (potentially) associated with a code when
+ * performing validation against a ValueSet. This method was created for internal purposes.
+ * Please use this method with care because it will only cover some
+ * use-cases (e.g. standard bindings) while for others it may not return correct results or return null.
+ * An incorrect result could be considered if the resource declares a code with a system, and you're calling
+ * this method to check a binding against a ValueSet that has nothing to do with that system.
+ * @param theValueSet the valueSet
+ * @param theCode the code
+ * @return the system which can be associated with the code
+ */
public static String extractCodeSystemForCode(IBaseResource theValueSet, String theCode) {
if (theValueSet instanceof org.hl7.fhir.dstu3.model.ValueSet) {
return extractCodeSystemForCodeDSTU3((org.hl7.fhir.dstu3.model.ValueSet) theValueSet, theCode);
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java
index f0f3f41e7f9..393d8ce1dc5 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java
@@ -62,6 +62,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import static ca.uhn.fhir.context.support.IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -296,7 +297,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
theResult.getCodeSystemVersion(),
conceptDefinitionComponent,
display,
- getIssuesForCodeValidation(theResult.getCodeValidationIssues()));
+ getIssuesForCodeValidation(theResult.getIssues()));
}
if (retVal == null) {
@@ -307,73 +308,36 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
}
private List getIssuesForCodeValidation(
- List codeValidationIssues) {
- List issues = new ArrayList<>();
+ List theIssues) {
+ List issueComponents = new ArrayList<>();
- for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeValidationIssues) {
+ for (IValidationSupport.CodeValidationIssue issue : theIssues) {
+ OperationOutcome.IssueSeverity severity =
+ OperationOutcome.IssueSeverity.fromCode(issue.getSeverity().getCode());
+ OperationOutcome.IssueType issueType =
+ OperationOutcome.IssueType.fromCode(issue.getType().getCode());
+ String diagnostics = issue.getDiagnostics();
- CodeableConcept codeableConcept = new CodeableConcept().setText(codeValidationIssue.getMessage());
- codeableConcept.addCoding(
- "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
- getIssueCodingFromCodeValidationIssue(codeValidationIssue),
- null);
+ IValidationSupport.CodeValidationIssueDetails details = issue.getDetails();
+ CodeableConcept codeableConcept = new CodeableConcept().setText(details.getText());
+ details.getCodings().forEach(detailCoding -> codeableConcept
+ .addCoding()
+ .setSystem(detailCoding.getSystem())
+ .setCode(detailCoding.getCode()));
- OperationOutcome.OperationOutcomeIssueComponent issue =
+ OperationOutcome.OperationOutcomeIssueComponent issueComponent =
new OperationOutcome.OperationOutcomeIssueComponent()
- .setSeverity(getIssueSeverityFromCodeValidationIssue(codeValidationIssue))
- .setCode(getIssueTypeFromCodeValidationIssue(codeValidationIssue))
- .setDetails(codeableConcept);
- issue.getDetails().setText(codeValidationIssue.getMessage());
- issue.addExtension()
+ .setSeverity(severity)
+ .setCode(issueType)
+ .setDetails(codeableConcept)
+ .setDiagnostics(diagnostics);
+ issueComponent
+ .addExtension()
.setUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id")
.setValue(new StringType("Terminology_PassThrough_TX_Message"));
- issues.add(issue);
+ issueComponents.add(issueComponent);
}
- return issues;
- }
-
- private String getIssueCodingFromCodeValidationIssue(IValidationSupport.CodeValidationIssue codeValidationIssue) {
- switch (codeValidationIssue.getCoding()) {
- case VS_INVALID:
- return "vs-invalid";
- case NOT_FOUND:
- return "not-found";
- case NOT_IN_VS:
- return "not-in-vs";
- case INVALID_CODE:
- return "invalid-code";
- case INVALID_DISPLAY:
- return "invalid-display";
- }
- return null;
- }
-
- private OperationOutcome.IssueType getIssueTypeFromCodeValidationIssue(
- IValidationSupport.CodeValidationIssue codeValidationIssue) {
- switch (codeValidationIssue.getCode()) {
- case NOT_FOUND:
- return OperationOutcome.IssueType.NOTFOUND;
- case CODE_INVALID:
- return OperationOutcome.IssueType.CODEINVALID;
- case INVALID:
- return OperationOutcome.IssueType.INVALID;
- }
- return null;
- }
-
- private OperationOutcome.IssueSeverity getIssueSeverityFromCodeValidationIssue(
- IValidationSupport.CodeValidationIssue codeValidationIssue) {
- switch (codeValidationIssue.getSeverity()) {
- case FATAL:
- return OperationOutcome.IssueSeverity.FATAL;
- case ERROR:
- return OperationOutcome.IssueSeverity.ERROR;
- case WARNING:
- return OperationOutcome.IssueSeverity.WARNING;
- case INFORMATION:
- return OperationOutcome.IssueSeverity.INFORMATION;
- }
- return null;
+ return issueComponents;
}
@Override
@@ -851,25 +815,22 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
.getRootValidationSupport()
.validateCodeInValueSet(
myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, theValueSet);
- if (result != null) {
+ if (result != null && theSystem != null) {
/* We got a value set result, which could be successful, or could contain errors/warnings. The code
might also be invalid in the code system, so we will check that as well and add those issues
to our result.
*/
IValidationSupport.CodeValidationResult codeSystemResult =
validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay);
- final boolean valueSetResultContainsInvalidDisplay = result.getCodeValidationIssues().stream()
- .anyMatch(codeValidationIssue -> codeValidationIssue.getCoding()
- == IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY);
+ final boolean valueSetResultContainsInvalidDisplay = result.getIssues().stream()
+ .anyMatch(VersionSpecificWorkerContextWrapper::hasInvalidDisplayDetailCode);
if (codeSystemResult != null) {
- for (IValidationSupport.CodeValidationIssue codeValidationIssue :
- codeSystemResult.getCodeValidationIssues()) {
+ for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeSystemResult.getIssues()) {
/* Value set validation should already have checked the display name. If we get INVALID_DISPLAY
issues from code system validation, they will only repeat what was already caught.
*/
- if (codeValidationIssue.getCoding() != IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY
- || !valueSetResultContainsInvalidDisplay) {
- result.addCodeValidationIssue(codeValidationIssue);
+ if (!hasInvalidDisplayDetailCode(codeValidationIssue) || !valueSetResultContainsInvalidDisplay) {
+ result.addIssue(codeValidationIssue);
}
}
}
@@ -877,6 +838,10 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
return result;
}
+ private static boolean hasInvalidDisplayDetailCode(IValidationSupport.CodeValidationIssue theIssue) {
+ return theIssue.hasIssueDetailCode(INVALID_DISPLAY.getCode());
+ }
+
private IValidationSupport.CodeValidationResult validateCodeInCodeSystem(
ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) {
return myValidationSupportContext
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java
index bb7eaf1c17b..eac448ad0eb 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java
@@ -9,6 +9,7 @@ import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.junit.jupiter.api.Test;
@@ -21,12 +22,12 @@ import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_GROUP;
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING;
import static java.util.stream.IntStream.range;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_NAME;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.LANGUAGE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_NAME;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.DISPLAY;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.LANGUAGE;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -189,8 +190,6 @@ public interface ILookupCodeTest {
// verify
assertNotNull(outcome);
- assertEquals(theRequest.getCode(), getLookupCodeProvider().getCode());
- assertEquals(theRequest.getSystem(), getLookupCodeProvider().getSystem());
assertEquals(theExpectedResult.isFound(), outcome.isFound());
assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage());
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyLookupCodeTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyLookupCodeTest.java
index 5ba79bd3e6f..95c4fd7d3b9 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyLookupCodeTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyLookupCodeTest.java
@@ -2,6 +2,7 @@ package org.hl7.fhir.common.hapi.validation;
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.context.support.LookupCodeRequest;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.junit.jupiter.api.Test;
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyValidateCodeTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyValidateCodeTest.java
index cb6bb02ac07..21c5d0dc5e7 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyValidateCodeTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IRemoteTerminologyValidateCodeTest.java
@@ -1,17 +1,76 @@
package org.hl7.fhir.common.hapi.validation;
+import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.IValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
+import org.junit.jupiter.api.Test;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createCodeValidationIssues;
+
public interface IRemoteTerminologyValidateCodeTest extends IValidateCodeTest {
default List getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
// this method should be removed once support for issues is fully implemented across all validator types
Optional> issues = RemoteTerminologyServiceValidationSupport.createCodeValidationIssues(theOperationOutcome, getService().getFhirContext().getVersion().getVersion());
return issues.map(theCodeValidationIssues -> theCodeValidationIssues.stream().toList()).orElseGet(List::of);
}
+
+ @Test
+ default void createCodeValidationIssues_withCodeSystemOutcomeForInvalidCode_returnsAsExpected() {
+ IBaseOperationOutcome outcome = getCodeSystemInvalidCodeOutcome();
+ FhirVersionEnum versionEnum = getService().getFhirContext().getVersion().getVersion();
+ Optional> issuesOptional = createCodeValidationIssues(outcome, versionEnum);
+ assertThat(issuesOptional).isPresent();
+ assertThat(issuesOptional.get()).hasSize(1);
+ IValidationSupport.CodeValidationIssue issue = issuesOptional.get().iterator().next();
+ assertThat(issue.getType().getCode()).isEqualTo("code-invalid");
+ assertThat(issue.getSeverity().getCode()).isEqualTo("error");
+ assertThat(issue.getDetails().getCodings()).hasSize(1);
+ IValidationSupport.CodeValidationIssueCoding issueCoding = issue.getDetails().getCodings().get(0);
+ assertThat(issueCoding.getSystem()).isEqualTo("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type");
+ assertThat(issueCoding.getCode()).isEqualTo("invalid-code");
+ assertThat(issue.getDetails().getText()).isEqualTo("Unknown code 'CODE' in the CodeSystem 'http://code.system/url' version '1.0.0'");
+ assertThat(issue.getDiagnostics()).isNull();
+ }
+
+ @Test
+ default void createCodeValidationIssues_withValueSetOutcomeForInvalidCode_returnsAsExpected() {
+ IBaseOperationOutcome outcome = getValueSetInvalidCodeOutcome();
+ FhirVersionEnum versionEnum = getService().getFhirContext().getVersion().getVersion();
+ Optional> issuesOptional = createCodeValidationIssues(outcome, versionEnum);
+ assertThat(issuesOptional).isPresent();
+ assertThat(issuesOptional.get()).hasSize(2);
+ IValidationSupport.CodeValidationIssue issue = issuesOptional.get().iterator().next();
+ assertThat(issue.getType().getCode()).isEqualTo("code-invalid");
+ assertThat(issue.getSeverity().getCode()).isEqualTo("error");
+ assertThat(issue.getDetails().getCodings()).hasSize(1);
+ IValidationSupport.CodeValidationIssueCoding issueCoding = issue.getDetails().getCodings().get(0);
+ assertThat(issueCoding.getSystem()).isEqualTo("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type");
+ assertThat(issueCoding.getCode()).isEqualTo("not-in-vs");
+ assertThat(issue.getDetails().getText()).isEqualTo("The provided code 'http://code.system/url#CODE' was not found in the value set 'http://value.set/url%7C1.0.0'");
+ assertThat(issue.getDiagnostics()).isNull();
+ }
+
+ @Test
+ default void createCodeValidationIssues_withValueSetOutcomeWithCustomDetailCode_returnsAsExpected() {
+ IBaseOperationOutcome outcome = getValueSetCustomDetailCodeOutcome();
+ FhirVersionEnum versionEnum = getService().getFhirContext().getVersion().getVersion();
+ Optional> issuesOptional = createCodeValidationIssues(outcome, versionEnum);
+ assertThat(issuesOptional).isPresent();
+ assertThat(issuesOptional.get()).hasSize(1);
+ IValidationSupport.CodeValidationIssue issue = issuesOptional.get().iterator().next();
+ assertThat(issue.getType().getCode()).isEqualTo("processing");
+ assertThat(issue.getSeverity().getCode()).isEqualTo("information");
+ assertThat(issue.getDetails().getCodings()).hasSize(1);
+ IValidationSupport.CodeValidationIssueCoding issueCoding = issue.getDetails().getCodings().get(0);
+ assertThat(issueCoding.getSystem()).isEqualTo("http://example.com/custom-issue-type");
+ assertThat(issueCoding.getCode()).isEqualTo("valueset-is-draft");
+ assertThat(issue.getDetails().getText()).isNull();
+ assertThat(issue.getDiagnostics()).isEqualTo("The ValueSet status is marked as draft.");
+ }
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidateCodeTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidateCodeTest.java
index 52dbf1177a8..53411b440fe 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidateCodeTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidateCodeTest.java
@@ -4,6 +4,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
+import ca.uhn.fhir.util.ClasspathUtil;
+import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -16,12 +19,13 @@ import java.util.List;
import java.util.stream.Stream;
import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.ERROR_MESSAGE;
-import static org.hl7.fhir.common.hapi.validation.IValidationProviders.VALUE_SET_URL;
+import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.DISPLAY;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.ERROR_MESSAGE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.VALUE_SET_URL;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -31,21 +35,35 @@ import static org.junit.jupiter.api.Assertions.fail;
public interface IValidateCodeTest {
- IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider();
- IValidationProviders.IMyValueSetProvider getValueSetProvider();
+ IValidationProviders.IMyValidationProvider getCodeSystemProvider();
+ IValidationProviders.IMyValidationProvider getValueSetProvider();
IValidationSupport getService();
IBaseParameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource);
String getCodeSystemError();
String getValueSetError();
IBaseOperationOutcome getCodeSystemInvalidCodeOutcome();
IBaseOperationOutcome getValueSetInvalidCodeOutcome();
+ IBaseOperationOutcome getValueSetCustomDetailCodeOutcome();
+
+ default IBaseOperationOutcome getCodeSystemInvalidCodeOutcome(Class extends IBaseOperationOutcome> theResourceClass) {
+ return getOutcome(theResourceClass, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
+ }
+ default IBaseOperationOutcome getValueSetInvalidCodeOutcome(Class extends IBaseOperationOutcome> theResourceClass) {
+ return getOutcome(theResourceClass, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
+ }
+ default IBaseOperationOutcome getValueSetCustomDetailCodeOutcome(Class extends IBaseOperationOutcome> theResourceClass) {
+ return getOutcome(theResourceClass, "/terminology/OperationOutcome-ValueSet-custom-issue-detail.json");
+ }
+ default IBaseOperationOutcome getOutcome(Class extends IBaseOperationOutcome> theResourceClass, String theFile) {
+ return ClasspathUtil.loadResource(getService().getFhirContext(), theResourceClass, theFile);
+ }
default void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
- getCodeSystemProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
+ getCodeSystemProvider().addTerminologyResponse(OPERATION_VALIDATE_CODE, CODE_SYSTEM, CODE, createParameters(theResult, theDisplay, theMessage, theIssuesResource));
}
default void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
- getValueSetProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
+ getValueSetProvider().addTerminologyResponse(OPERATION_VALIDATE_CODE, VALUE_SET_URL, CODE, createParameters(theResult, theDisplay, theMessage, theIssuesResource));
}
@Test
@@ -91,8 +109,8 @@ public interface IValidateCodeTest {
String theValidationMessage,
String theCodeSystem,
String theValueSetUrl) {
- getCodeSystemProvider().setException(theException);
- getValueSetProvider().setException(theException);
+ getCodeSystemProvider().addException(OPERATION_VALIDATE_CODE, theCodeSystem, CODE, theException);
+ getValueSetProvider().addException(OPERATION_VALIDATE_CODE, theValueSetUrl, CODE, theException);
CodeValidationResult outcome = getService().validateCode(null, null, theCodeSystem, CODE, DISPLAY, theValueSetUrl);
verifyErrorResultFromException(outcome, theValidationMessage, theServerMessage);
@@ -105,7 +123,7 @@ public interface IValidateCodeTest {
for (String message : theMessages) {
assertTrue(outcome.getMessage().contains(message));
}
- assertFalse(outcome.getCodeValidationIssues().isEmpty());
+ assertFalse(outcome.getIssues().isEmpty());
}
@Test
@@ -130,11 +148,7 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
- assertTrue(outcome.getCodeValidationIssues().isEmpty());
-
- assertEquals(CODE, getValueSetProvider().getCode());
- assertEquals(DISPLAY, getValueSetProvider().getDisplay());
- assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
+ assertTrue(outcome.getIssues().isEmpty());
}
@Test
@@ -147,9 +161,7 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
- assertTrue(outcome.getCodeValidationIssues().isEmpty());
-
- assertEquals(CODE, getCodeSystemProvider().getCode());
+ assertTrue(outcome.getIssues().isEmpty());
}
@Test
@@ -165,10 +177,7 @@ public interface IValidateCodeTest {
assertNull(outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
- assertTrue(outcome.getCodeValidationIssues().isEmpty());
-
- assertEquals(CODE, getCodeSystemProvider().getCode());
- assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
+ assertTrue(outcome.getIssues().isEmpty());
}
@Test
@@ -184,15 +193,11 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
- assertTrue(outcome.getCodeValidationIssues().isEmpty());
-
- assertEquals(CODE, getCodeSystemProvider().getCode());
- assertEquals(DISPLAY, getCodeSystemProvider().getDisplay());
- assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
+ assertTrue(outcome.getIssues().isEmpty());
}
@Test
- default void validateCode_withCodeSystemError_returnsCorrectly() {
+ default void validateCode_withCodeSystemErrorWithDiagnosticsWithIssues_returnsCorrectly() {
IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome();
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, invalidCodeOutcome);
@@ -204,12 +209,12 @@ public interface IValidateCodeTest {
// assertEquals(CODE, outcome.getCode());
assertEquals(ERROR, outcome.getSeverity());
assertEquals(getCodeSystemError(), outcome.getMessage());
- assertFalse(outcome.getCodeValidationIssues().isEmpty());
+ assertFalse(outcome.getIssues().isEmpty());
verifyIssues(invalidCodeOutcome, outcome);
}
@Test
- default void validateCode_withCodeSystemErrorAndIssues_returnsCorrectly() {
+ default void validateCode_withCodeSystemErrorWithDiagnosticsWithoutIssues_returnsCorrectly() {
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, null);
CodeValidationResult outcome = getService()
@@ -223,10 +228,32 @@ public interface IValidateCodeTest {
assertNull(outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity());
assertEquals(expectedError, outcome.getMessage());
- assertFalse(outcome.getCodeValidationIssues().isEmpty());
- assertEquals(1, outcome.getCodeValidationIssues().size());
- assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
- assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
+ assertFalse(outcome.getIssues().isEmpty());
+ assertEquals(1, outcome.getIssues().size());
+ assertEquals(expectedError, outcome.getIssues().get(0).getDiagnostics());
+ assertEquals(ERROR, outcome.getIssues().get(0).getSeverity());
+ }
+
+ @Test
+ default void validateCode_withCodeSystemErrorWithoutDiagnosticsWithIssues_returnsCorrectly() {
+ IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome();
+ createCodeSystemReturnParameters(false, null, null, invalidCodeOutcome);
+
+ CodeValidationResult outcome = getService()
+ .validateCode(null, null, CODE_SYSTEM, CODE, null, null);
+
+ String expectedError = getCodeSystemError();
+ assertNotNull(outcome);
+ assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
+ assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
+ // assertEquals(CODE, outcome.getCode());
+ assertNull(outcome.getDisplay());
+ assertEquals(ERROR, outcome.getSeverity());
+ assertNull(outcome.getMessage());
+ assertFalse(outcome.getIssues().isEmpty());
+ assertEquals(1, outcome.getIssues().size());
+ assertNull(outcome.getIssues().get(0).getDiagnostics());
+ assertEquals(ERROR, outcome.getIssues().get(0).getSeverity());
}
@Test
@@ -242,10 +269,7 @@ public interface IValidateCodeTest {
assertNull(outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
- assertTrue(outcome.getCodeValidationIssues().isEmpty());
-
- assertEquals(CODE, getValueSetProvider().getCode());
- assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
+ assertTrue(outcome.getIssues().isEmpty());
}
@Test
@@ -261,11 +285,7 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
- assertTrue(outcome.getCodeValidationIssues().isEmpty());
-
- assertEquals(CODE, getValueSetProvider().getCode());
- assertEquals(DISPLAY, getValueSetProvider().getDisplay());
- assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
+ assertTrue(outcome.getIssues().isEmpty());
}
@Test
@@ -283,13 +303,9 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity());
assertEquals(expectedError, outcome.getMessage());
- assertEquals(1, outcome.getCodeValidationIssues().size());
- assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
- assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
-
- assertEquals(CODE, getValueSetProvider().getCode());
- assertEquals(DISPLAY, getValueSetProvider().getDisplay());
- assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
+ assertEquals(1, outcome.getIssues().size());
+ assertEquals(expectedError, outcome.getIssues().get(0).getDiagnostics());
+ assertEquals(ERROR, outcome.getIssues().get(0).getSeverity());
}
@Test
@@ -306,24 +322,28 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity());
assertEquals(getValueSetError(), outcome.getMessage());
- assertFalse(outcome.getCodeValidationIssues().isEmpty());
+ assertFalse(outcome.getIssues().isEmpty());
verifyIssues(invalidCodeOutcome, outcome);
-
- assertEquals(CODE, getValueSetProvider().getCode());
- assertEquals(DISPLAY, getValueSetProvider().getDisplay());
- assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
}
default void verifyIssues(IBaseOperationOutcome theOperationOutcome, CodeValidationResult theResult) {
List issues = getCodeValidationIssues(theOperationOutcome);
- assertEquals(issues.size(), theResult.getCodeValidationIssues().size());
+ assertEquals(issues.size(), theResult.getIssues().size());
for (int i = 0; i < issues.size(); i++) {
IValidationSupport.CodeValidationIssue expectedIssue = issues.get(i);
- IValidationSupport.CodeValidationIssue actualIssue = theResult.getCodeValidationIssues().get(i);
- assertEquals(expectedIssue.getCode(), actualIssue.getCode());
+ IValidationSupport.CodeValidationIssue actualIssue = theResult.getIssues().get(i);
+ assertEquals(expectedIssue.getType().getCode(), actualIssue.getType().getCode());
assertEquals(expectedIssue.getSeverity(), actualIssue.getSeverity());
- assertEquals(expectedIssue.getCoding(), actualIssue.getCoding());
- assertEquals(expectedIssue.getMessage(), actualIssue.getMessage());
+ assertEquals(expectedIssue.getDetails().getText(), actualIssue.getDetails().getText());
+ assertEquals(expectedIssue.getDetails().getCodings().size(), actualIssue.getDetails().getCodings().size());
+ for (int index = 0; index < expectedIssue.getDetails().getCodings().size(); index++) {
+ IValidationSupport.CodeValidationIssueCoding expectedCoding = expectedIssue.getDetails().getCodings().get(index);
+ IValidationSupport.CodeValidationIssueCoding actualCoding = actualIssue.getDetails().getCodings().get(index);
+ assertEquals(expectedCoding.getSystem(), actualCoding.getSystem());
+ assertEquals(expectedCoding.getCode(), actualCoding.getCode());
+ }
+ assertEquals(expectedIssue.getDetails().getText(), actualIssue.getDetails().getText());
+ assertEquals(expectedIssue.getDiagnostics(), actualIssue.getDiagnostics());
}
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidationProviders.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidationProviders.java
deleted file mode 100644
index 1537f8e5c00..00000000000
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/IValidationProviders.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.hl7.fhir.common.hapi.validation;
-
-import ca.uhn.fhir.context.support.IValidationSupport;
-import ca.uhn.fhir.rest.server.IResourceProvider;
-import org.hl7.fhir.instance.model.api.IBaseParameters;
-
-public interface IValidationProviders {
- String CODE_SYSTEM = "http://code.system/url";
- String CODE_SYSTEM_VERSION = "1.0.0";
- String CODE_SYSTEM_NAME = "Test Code System";
- String CODE = "CODE";
- String VALUE_SET_URL = "http://value.set/url";
- String DISPLAY = "Explanation for code TestCode.";
- String LANGUAGE = "en";
- String ERROR_MESSAGE = "This is an error message";
-
- interface IMyCodeSystemProvider extends IResourceProvider {
- String getCode();
- String getSystem();
- String getDisplay();
- void setException(Exception theException);
- void setReturnParams(IBaseParameters theParameters);
- }
-
- interface IMyLookupCodeProvider extends IResourceProvider {
- String getCode();
- String getSystem();
- void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult);
- }
-
- interface IMyValueSetProvider extends IResourceProvider {
- String getCode();
- String getSystem();
- String getDisplay();
- String getValueSet();
- void setException(Exception theException);
- void setReturnParams(IBaseParameters theParameters);
- }
-}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperTest.java
index 57aae8d96f9..cbc79fadc98 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperTest.java
@@ -7,7 +7,6 @@ import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
-
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
@@ -16,17 +15,18 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.junit.jupiter.api.Test;
import org.mockito.quality.Strictness;
+import java.util.List;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
-import java.util.List;
-
public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestWithInlineMocks {
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
@@ -80,7 +80,7 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
}
@Test
- public void validateCode_normally_resolvesCodeSystemFromValueSet() {
+ public void validateCode_codeInValueSet_resolvesCodeSystemFromValueSet() {
// setup
IValidationSupport validationSupport = mockValidationSupport();
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
@@ -90,8 +90,7 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
- when(validationSupport.fetchResource(eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(valueSet);
- when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenReturn(new IValidationSupport.CodeValidationResult());
+ when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenReturn(mock(IValidationSupport.CodeValidationResult.class));
// execute
wrapper.validateCode(new ValidationOptions(), "code0", valueSet);
@@ -101,6 +100,26 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
verify(validationSupport, times(1)).validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), any());
}
+ @Test
+ public void validateCode_codeNotInValueSet_doesNotResolveSystem() {
+ // setup
+ IValidationSupport validationSupport = mockValidationSupport();
+ ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
+ VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(FhirContext.forR5Cached());
+ VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, versionCanonicalizer);
+
+ ValueSet valueSet = new ValueSet();
+ valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
+ valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
+
+ // execute
+ wrapper.validateCode(new ValidationOptions(), "code1", valueSet);
+
+ // verify
+ verify(validationSupport, times(1)).validateCodeInValueSet(any(), any(), eq(null), eq("code1"), any(), any());
+ verify(validationSupport, never()).validateCode(any(), any(), any(), any(), any(), any());
+ }
+
@Test
public void isPrimitive_primitive() {
// setup
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java
index da73c0be800..5a53ba0ac3d 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java
@@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
-import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
@@ -28,10 +27,7 @@ import org.hl7.fhir.dstu2.model.Observation.ObservationStatus;
import org.hl7.fhir.dstu2.model.QuestionnaireResponse;
import org.hl7.fhir.dstu2.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.dstu2.model.StringType;
-import org.hl7.fhir.dstu3.model.CodeSystem;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -41,9 +37,7 @@ import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Optional;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
@@ -100,7 +94,7 @@ public class FhirInstanceValidatorDstu2Test extends BaseValidationTestWithInline
if (myValidConcepts.contains(system + "___" + code)) {
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) {
- return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code");
+ return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage("Unknown code");
} else {
retVal = null;
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java
index ad6276f2761..cfbeca68626 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java
@@ -58,7 +58,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -229,10 +228,10 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')";
- retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
+ retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else if (myValidSystemsNotReturningIssues.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')";
- retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
+ retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message);
} else if (myCodeSystems.containsKey(system)) {
CodeSystem cs = myCodeSystems.get(system);
Optional found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/IValidateCodeProvidersDstu3.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/IValidateCodeProvidersDstu3.java
deleted file mode 100644
index 0c639e310ee..00000000000
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/IValidateCodeProvidersDstu3.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package org.hl7.fhir.dstu3.hapi.validation;
-
-import ca.uhn.fhir.jpa.model.util.JpaConstants;
-import ca.uhn.fhir.rest.annotation.IdParam;
-import ca.uhn.fhir.rest.annotation.Operation;
-import ca.uhn.fhir.rest.annotation.OperationParam;
-import ca.uhn.fhir.rest.api.server.RequestDetails;
-import jakarta.servlet.http.HttpServletRequest;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
-import org.hl7.fhir.dstu3.model.BooleanType;
-import org.hl7.fhir.dstu3.model.CodeSystem;
-import org.hl7.fhir.dstu3.model.CodeType;
-import org.hl7.fhir.dstu3.model.Coding;
-import org.hl7.fhir.dstu3.model.IdType;
-import org.hl7.fhir.dstu3.model.Parameters;
-import org.hl7.fhir.dstu3.model.StringType;
-import org.hl7.fhir.dstu3.model.UriType;
-import org.hl7.fhir.dstu3.model.ValueSet;
-import org.hl7.fhir.instance.model.api.IBaseParameters;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-
-import java.util.List;
-
-public interface IValidateCodeProvidersDstu3 {
- @SuppressWarnings("unused")
- class MyCodeSystemProviderDstu3 implements IValidationProviders.IMyCodeSystemProvider {
- private UriType mySystemUrl;
- private CodeType myCode;
- private StringType myDisplay;
- private Exception myException;
- private Parameters myReturnParams;
-
- @Operation(name = "validate-code", idempotent = true, returnParameters = {
- @OperationParam(name = "result", type = org.hl7.fhir.dstu3.model.BooleanType.class, min = 1),
- @OperationParam(name = "message", type = org.hl7.fhir.dstu3.model.StringType.class),
- @OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class)
- })
- public org.hl7.fhir.dstu3.model.Parameters validateCode(
- HttpServletRequest theServletRequest,
- @IdParam(optional = true) org.hl7.fhir.dstu3.model.IdType theId,
- @OperationParam(name = "url", min = 0, max = 1) org.hl7.fhir.dstu3.model.UriType theCodeSystemUrl,
- @OperationParam(name = "code", min = 0, max = 1) org.hl7.fhir.dstu3.model.CodeType theCode,
- @OperationParam(name = "display", min = 0, max = 1) org.hl7.fhir.dstu3.model.StringType theDisplay
- ) throws Exception {
- mySystemUrl = theCodeSystemUrl;
- myCode = theCode;
- myDisplay = theDisplay;
- if (myException != null) {
- throw myException;
- }
- return myReturnParams;
- }
-
- @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
- @OperationParam(name = "name", type = org.hl7.fhir.dstu3.model.StringType.class, min = 1),
- @OperationParam(name = "version", type = org.hl7.fhir.dstu3.model.StringType.class),
- @OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class, min = 1),
- @OperationParam(name = "abstract", type = org.hl7.fhir.dstu3.model.BooleanType.class, min = 1),
- @OperationParam(name = "property", type = org.hl7.fhir.dstu3.model.StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
- })
- public IBaseParameters lookup(
- HttpServletRequest theServletRequest,
- @OperationParam(name = "code", max = 1) org.hl7.fhir.dstu3.model.CodeType theCode,
- @OperationParam(name = "system",max = 1) org.hl7.fhir.dstu3.model.UriType theSystem,
- @OperationParam(name = "coding", max = 1) Coding theCoding,
- @OperationParam(name = "version", max = 1) org.hl7.fhir.dstu3.model.StringType theVersion,
- @OperationParam(name = "displayLanguage", max = 1) org.hl7.fhir.dstu3.model.CodeType theDisplayLanguage,
- @OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
- RequestDetails theRequestDetails
- ) {
- myCode = theCode;
- return myReturnParams;
- }
-
- @Override
- public Class extends IBaseResource> getResourceType() {
- return CodeSystem.class;
- }
-
- public void setException(Exception theException) {
- myException = theException;
- }
- @Override
- public void setReturnParams(IBaseParameters theParameters) {
- myReturnParams = (Parameters) theParameters;
- }
- @Override
- public String getCode() {
- return myCode != null ? myCode.getValueAsString() : null;
- }
- @Override
- public String getSystem() {
- return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
- }
- public String getDisplay() {
- return myDisplay != null ? myDisplay.getValue() : null;
- }
- }
-
- @SuppressWarnings("unused")
- class MyValueSetProviderDstu3 implements IValidationProviders.IMyValueSetProvider {
- private Exception myException;
- private Parameters myReturnParams;
- private UriType mySystemUrl;
- private UriType myValueSetUrl;
- private CodeType myCode;
- private StringType myDisplay;
-
- @Operation(name = "validate-code", idempotent = true, returnParameters = {
- @OperationParam(name = "result", type = BooleanType.class, min = 1),
- @OperationParam(name = "message", type = org.hl7.fhir.dstu3.model.StringType.class),
- @OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class)
- })
- public Parameters validateCode(
- HttpServletRequest theServletRequest,
- @IdParam(optional = true) IdType theId,
- @OperationParam(name = "url", min = 0, max = 1) org.hl7.fhir.dstu3.model.UriType theValueSetUrl,
- @OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
- @OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
- @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
- @OperationParam(name = "valueSet") org.hl7.fhir.dstu3.model.ValueSet theValueSet
- ) throws Exception {
- mySystemUrl = theSystem;
- myValueSetUrl = theValueSetUrl;
- myCode = theCode;
- myDisplay = theDisplay;
- if (myException != null) {
- throw myException;
- }
- return myReturnParams;
- }
- @Override
- public Class extends IBaseResource> getResourceType() {
- return ValueSet.class;
- }
- public void setException(Exception theException) {
- myException = theException;
- }
- @Override
- public void setReturnParams(IBaseParameters theParameters) {
- myReturnParams = (Parameters) theParameters;
- }
- @Override
- public String getCode() {
- return myCode != null ? myCode.getValueAsString() : null;
- }
- @Override
- public String getSystem() {
- return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
- }
- @Override
- public String getValueSet() {
- return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
- }
- public String getDisplay() {
- return myDisplay != null ? myDisplay.getValue() : null;
- }
- }
-}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java
index 7804b9df10f..57591b31e76 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java
@@ -41,7 +41,6 @@ import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -56,6 +55,8 @@ import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
+import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR;
+import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.WARNING;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.BOOLEAN;
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.CHOICE;
@@ -224,7 +225,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(ValueSet.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(ValueSet.class)))
- .thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"));
+ .thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(ERROR).setMessage("Unknown code"));
CodeSystem codeSystem = new CodeSystem();
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
@@ -246,7 +247,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setCode(CODE_ICC_SCHOOLTYPE_PT));
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))
- .thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode("warning").setMessage("Unknown code: http://codesystems.com/system / code1"));
+ .thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(WARNING).setMessage("Unknown code: http://codesystems.com/system / code1"));
QuestionnaireResponse qa;
@@ -1034,7 +1035,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))
- .thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"));
+ .thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(ERROR).setMessage("Unknown code"));
CodeSystem codeSystem = new CodeSystem();
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeDstu3Test.java
index 6e98c4b31a9..56bc892c3d4 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeDstu3Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeDstu3Test.java
@@ -12,9 +12,9 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem;
@@ -164,8 +164,6 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
@SuppressWarnings("unused")
static class MyLookupCodeProviderDstu3 implements IValidationProviders.IMyLookupCodeProvider {
- private UriType mySystemUrl;
- private CodeType myCode;
private LookupCodeResult myLookupCodeResult;
@Override
@@ -190,8 +188,6 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
RequestDetails theRequestDetails
) {
- myCode = theCode;
- mySystemUrl = theSystem;
if (theSystem == null) {
throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode));
}
@@ -205,15 +201,5 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
public Class extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
-
- @Override
- public String getCode() {
- return myCode != null ? myCode.getValueAsString() : null;
- }
-
- @Override
- public String getSystem() {
- return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
- }
}
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeWithResponseFileDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeWithResponseFileDstu3Test.java
index 48a99f260d0..4817542ef4d 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeWithResponseFileDstu3Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyLookupCodeWithResponseFileDstu3Test.java
@@ -5,13 +5,10 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
-import ca.uhn.fhir.util.ClasspathUtil;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersDstu3;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
-import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseParameters;
-import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -19,13 +16,15 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.List;
+import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_LOOKUP;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
- private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
+ private IValidationProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
@@ -36,7 +35,7 @@ public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
- myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
+ myCodeSystemProvider = new IValidationProvidersDstu3.MyCodeSystemProviderDstu3();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
}
@@ -47,13 +46,10 @@ public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
}
@Test
void lookupCode_withParametersOutput_convertsCorrectly() {
- String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
- IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
- assertTrue(baseResource instanceof Parameters);
- Parameters resultParameters = (Parameters) baseResource;
- myCodeSystemProvider.setReturnParams(resultParameters);
+ String outputFile ="/terminology/CodeSystem-lookup-output-with-subproperties.json";
+ IBaseParameters resultParameters = myCodeSystemProvider.addTerminologyResponse(OPERATION_LOOKUP, CODE_SYSTEM, CODE, ourCtx, outputFile);
- LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
+ LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
// test
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyValidateCodeDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyValidateCodeDstu3Test.java
index af4f39f0926..2f573ad3e5f 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyValidateCodeDstu3Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyValidateCodeDstu3Test.java
@@ -4,16 +4,18 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
-import ca.uhn.fhir.util.ClasspathUtil;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersDstu3;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.dstu3.model.BooleanType;
+import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
+import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.AfterEach;
@@ -22,6 +24,11 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.List;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.ERROR_MESSAGE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.VALUE_SET_URL;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET;
@@ -38,8 +45,8 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
- private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
- private IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 myValueSetProvider;
+ private IValidationProviders.MyValidationProvider myCodeSystemProvider;
+ private IValidationProviders.MyValidationProvider myValueSetProvider;
private RemoteTerminologyServiceValidationSupport mySvc;
private String myCodeSystemError, myValueSetError;
@@ -48,14 +55,14 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
myCodeSystemError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class,
- ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
+ ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, CODE_SYSTEM, CODE, baseUrl, ERROR_MESSAGE);
myValueSetError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class,
- ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
+ ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, CODE_SYSTEM, CODE, VALUE_SET_URL, baseUrl, ERROR_MESSAGE);
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
- myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
- myValueSetProvider = new IValidateCodeProvidersDstu3.MyValueSetProviderDstu3();
+ myCodeSystemProvider = new IValidationProvidersDstu3.MyCodeSystemProviderDstu3();
+ myValueSetProvider = new IValidationProvidersDstu3.MyValueSetProviderDstu3();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
}
@@ -82,45 +89,40 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
}
@Override
- public IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 getCodeSystemProvider() {
+ public IValidationProviders.IMyValidationProvider getCodeSystemProvider() {
return myCodeSystemProvider;
}
@Override
- public IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 getValueSetProvider() {
+ public IValidationProviders.IMyValidationProvider getValueSetProvider() {
return myValueSetProvider;
}
@Override
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
- return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
+ return getCodeSystemInvalidCodeOutcome(OperationOutcome.class);
}
@Override
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
- return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
+ return getValueSetInvalidCodeOutcome(OperationOutcome.class);
+ }
+
+ @Override
+ public IBaseOperationOutcome getValueSetCustomDetailCodeOutcome() {
+ return getValueSetCustomDetailCodeOutcome(OperationOutcome.class);
}
@Override
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
Parameters parameters = new Parameters();
parameters.addParameter().setName("result").setValue(new BooleanType(theResult));
- parameters.addParameter().setName("code").setValue(new StringType(IValidationProviders.CODE));
- parameters.addParameter().setName("system").setValue(new UriType(IValidationProviders.CODE_SYSTEM));
- parameters.addParameter().setName("version").setValue(new StringType(IValidationProviders.CODE_SYSTEM_VERSION));
+ parameters.addParameter().setName("code").setValue(new StringType(CODE));
+ parameters.addParameter().setName("system").setValue(new UriType(CODE_SYSTEM));
+ parameters.addParameter().setName("version").setValue(new StringType(CODE_SYSTEM_VERSION));
parameters.addParameter().setName("display").setValue(new StringType(theDisplay));
parameters.addParameter().setName("message").setValue(new StringType(theMessage));
parameters.addParameter().setName("issues").setResource((Resource) theIssuesResource);
return parameters;
}
-
- @Override
- public void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
- myCodeSystemProvider.setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
- }
-
- @Override
- public void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
- myValueSetProvider.setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
- }
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java
index d03c3aa974d..fb46f7f8008 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java
@@ -307,10 +307,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')";
- retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
+ retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else if (myValidSystemsNotReturningIssues.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')";
- retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
+ retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message);
} else {
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeR4Test.java
index d67966df6d4..382f621f547 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeR4Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeR4Test.java
@@ -11,9 +11,10 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters;
@@ -52,7 +53,7 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
- private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
+ private IValidationProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
private MyLookupCodeProviderR4 myLookupCodeProviderR4;
@BeforeEach
@@ -60,7 +61,7 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true));
- myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
+ myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
myLookupCodeProviderR4 = new MyLookupCodeProviderR4();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myLookupCodeProviderR4);
}
@@ -166,8 +167,6 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
@SuppressWarnings("unused")
static class MyLookupCodeProviderR4 implements IValidationProviders.IMyLookupCodeProvider {
- private UriType mySystemUrl;
- private CodeType myCode;
private LookupCodeResult myLookupCodeResult;
@Override
@@ -192,8 +191,6 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
RequestDetails theRequestDetails
) {
- myCode = theCode;
- mySystemUrl = theSystem;
if (theSystem == null) {
throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode));
}
@@ -206,15 +203,5 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
public Class extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
-
- @Override
- public String getCode() {
- return myCode != null ? myCode.getValueAsString() : null;
- }
-
- @Override
- public String getSystem() {
- return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
- }
}
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeWithResponseFileR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeWithResponseFileR4Test.java
index 37eba91d0ca..a0896bd8c4e 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeWithResponseFileR4Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyLookupCodeWithResponseFileR4Test.java
@@ -5,12 +5,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
-import ca.uhn.fhir.util.ClasspathUtil;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseParameters;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -19,13 +16,15 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.List;
+import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_LOOKUP;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
private static final FhirContext ourCtx = FhirContext.forR4Cached();
- private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
+ private IValidationProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
@@ -36,7 +35,7 @@ public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
- myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
+ myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
}
@@ -48,13 +47,10 @@ public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
@Test
void lookupCode_withParametersOutput_convertsCorrectly() {
- String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
- IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
- assertTrue(baseResource instanceof Parameters);
- Parameters resultParameters = (Parameters) baseResource;
- myCodeSystemProvider.setReturnParams(resultParameters);
+ String outputFile ="/terminology/CodeSystem-lookup-output-with-subproperties.json";
+ IBaseParameters resultParameters = myCodeSystemProvider.addTerminologyResponse(OPERATION_LOOKUP, CODE_SYSTEM, CODE, ourCtx, outputFile);
- LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
+ LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
// test
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyValidateCodeR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyValidateCodeR4Test.java
index 08f6c251869..ffd8045a8a5 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyValidateCodeR4Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyValidateCodeR4Test.java
@@ -2,8 +2,8 @@ package org.hl7.fhir.r4.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
-import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.parser.IJsonLikeParser;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
@@ -13,11 +13,11 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
-import ca.uhn.fhir.util.ClasspathUtil;
+import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
+import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import ca.uhn.fhir.util.ParametersUtil;
import com.google.common.collect.Lists;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest;
-import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -39,6 +39,12 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.DISPLAY;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.ERROR_MESSAGE;
+import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.VALUE_SET_URL;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET;
@@ -61,8 +67,8 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
private static final FhirContext ourCtx = FhirContext.forR4Cached();
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
- private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
- private IValidateCodeProvidersR4.MyValueSetProviderR4 myValueSetProvider;
+ private IValidationProviders.IMyValidationProvider myCodeSystemProvider;
+ private IValidationProviders.IMyValidationProvider myValueSetProvider;
private RemoteTerminologyServiceValidationSupport mySvc;
private String myCodeSystemError, myValueSetError;
@@ -71,14 +77,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
myCodeSystemError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class,
- ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
+ ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, CODE_SYSTEM, CODE, baseUrl, ERROR_MESSAGE);
myValueSetError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class,
- ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
+ ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, CODE_SYSTEM, CODE, VALUE_SET_URL, baseUrl, ERROR_MESSAGE);
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
- myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
- myValueSetProvider = new IValidateCodeProvidersR4.MyValueSetProviderR4();
+ myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
+ myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
}
@@ -95,12 +101,12 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
}
@Override
- public IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider() {
+ public IValidationProviders.IMyValidationProvider getCodeSystemProvider() {
return myCodeSystemProvider;
}
@Override
- public IValidationProviders.IMyValueSetProvider getValueSetProvider() {
+ public IValidationProviders.IMyValidationProvider getValueSetProvider() {
return myValueSetProvider;
}
@@ -116,51 +122,40 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Override
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
- return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
+ return getCodeSystemInvalidCodeOutcome(OperationOutcome.class);
}
@Override
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
- return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
+ return getValueSetInvalidCodeOutcome(OperationOutcome.class);
}
@Override
- public List getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
- return ((OperationOutcome)theOperationOutcome).getIssue().stream()
- .map(issueComponent -> new IValidationSupport.CodeValidationIssue(
- issueComponent.getDetails().getText(),
- IValidationSupport.IssueSeverity.ERROR,
- /* assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match */
- IValidationSupport.CodeValidationIssueCode.INVALID,
- IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))
- .toList();
+ public IBaseOperationOutcome getValueSetCustomDetailCodeOutcome() {
+ return getValueSetCustomDetailCodeOutcome(OperationOutcome.class);
}
@Test
void validateCodeInValueSet_success() {
- createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
+ createValueSetReturnParameters(true, DISPLAY, null, null);
ValueSet valueSet = new ValueSet();
- valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
+ valueSet.setUrl(VALUE_SET_URL);
- CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
+ CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), CODE_SYSTEM, CODE, DISPLAY, valueSet);
assertNotNull(outcome);
- assertEquals(IValidationProviders.CODE, outcome.getCode());
- assertEquals(IValidationProviders.DISPLAY, outcome.getDisplay());
+ assertEquals(CODE, outcome.getCode());
+ assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
-
- assertEquals(IValidationProviders.CODE, myValueSetProvider.getCode());
- assertEquals(IValidationProviders.DISPLAY, myValueSetProvider.getDisplay());
- assertEquals(IValidationProviders.VALUE_SET_URL, myValueSetProvider.getValueSet());
}
@Override
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
Parameters parameters = new Parameters()
- .addParameter("code", IValidationProviders.CODE)
- .addParameter("system", IValidationProviders.CODE_SYSTEM)
- .addParameter("version", IValidationProviders.CODE_SYSTEM_VERSION)
+ .addParameter("code", CODE)
+ .addParameter("system", CODE_SYSTEM)
+ .addParameter("version", CODE_SYSTEM_VERSION)
.addParameter("display", theDisplay)
.addParameter("message", theMessage);
if (theResult != null) {
@@ -181,16 +176,16 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Test
void validateCodeInValueSet_uniqueComposeInclude() {
- createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
+ createValueSetReturnParameters(true, DISPLAY, null, null);
ValueSet valueSet = new ValueSet();
- valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
+ valueSet.setUrl(VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
Collections.singletonList(new ValueSet.ConceptSetComponent().setSystem(systemUrl)) ));
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
- new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
+ new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
// validate service doesn't return error message (as when no code system is present)
assertNotNull(outcome);
@@ -211,16 +206,16 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@ParameterizedTest
@MethodSource(value = "getRemoteTerminologyServerExceptions")
void validateCodeInValueSet_systemNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
- myValueSetProvider.setException(theException);
- createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
+ getValueSetProvider().addException("$validate-code", VALUE_SET_URL, CODE, theException);
+ createValueSetReturnParameters(true, DISPLAY, null, null);
ValueSet valueSet = new ValueSet();
- valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
+ valueSet.setUrl(VALUE_SET_URL);
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
Lists.newArrayList(new ValueSet.ConceptSetComponent(), new ValueSet.ConceptSetComponent())));
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
- new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
+ new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
@@ -230,11 +225,11 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@ParameterizedTest
@MethodSource(value = "getRemoteTerminologyServerExceptions")
void validateCodeInValueSet_systemPresentCodeNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
- myValueSetProvider.setException(theException);
- createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
+ getValueSetProvider().addException(JpaConstants.OPERATION_VALIDATE_CODE, VALUE_SET_URL, CODE, theException);
+ createValueSetReturnParameters(true, DISPLAY, null, null);
ValueSet valueSet = new ValueSet();
- valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
+ valueSet.setUrl(VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
@@ -243,7 +238,7 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
new ValueSet.ConceptSetComponent().setSystem(systemUrl2))));
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
- new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
+ new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
@@ -252,10 +247,10 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Test
void validateCodeInValueSet_systemPresentCodePresentValidatesOKNoVersioned() {
- createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
+ createValueSetReturnParameters(true, DISPLAY, null, null);
ValueSet valueSet = new ValueSet();
- valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
+ valueSet.setUrl(VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
@@ -264,14 +259,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setConcept(
Lists.newArrayList(
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
- new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
+ new ValueSet.ConceptReferenceComponent().setCode(CODE) )
)) ));
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
mySvc.addClientInterceptor(requestInterceptor);
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
- new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
+ new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
assertNotNull(outcome);
assertEquals(systemUrl2, requestInterceptor.getCapturedSystemParameter());
@@ -280,10 +275,10 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Test
void validateCodeInValueSet_systemPresentCodePresentValidatesOKVersioned() {
- createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
+ createValueSetReturnParameters(true, DISPLAY, null, null);
ValueSet valueSet = new ValueSet();
- valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
+ valueSet.setUrl(VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
String systemVersion = "3.0.2";
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
@@ -294,14 +289,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setVersion(system2Version).setConcept(
Lists.newArrayList(
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
- new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
+ new ValueSet.ConceptReferenceComponent().setCode(CODE) )
)) ));
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
mySvc.addClientInterceptor(requestInterceptor);
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
- new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
+ new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
assertNotNull(outcome);
assertEquals(systemUrl2 + "|" + system2Version, requestInterceptor.getCapturedSystemParameter());
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java
index ca55a41bf4c..d5035a8048e 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java
@@ -31,6 +31,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4b.conformance.ProfileUtilities;
import org.hl7.fhir.r4b.context.IWorkerContext;
+import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine;
import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4b.model.AllergyIntolerance;
import org.hl7.fhir.r4b.model.Base;
@@ -61,7 +62,6 @@ import org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4b.model.ValueSet;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4b.terminologies.ValueSetExpander;
-import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine;
import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
@@ -203,7 +203,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')";
- return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
+ return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else {
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java
index f0ea48686e1..abcb0f94704 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java
@@ -48,7 +48,6 @@ import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
@@ -200,10 +199,10 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) {
String theMessage = "Unknown code (for '" + system + "#" + code + "')";
- retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
+ retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else if (myValidSystemsNotReturningIssues.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')";
- retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
+ retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message);
} else {
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
}
diff --git a/hapi-fhir-validation/src/test/resources/terminology/OperationOutcome-ValueSet-custom-issue-detail.json b/hapi-fhir-validation/src/test/resources/terminology/OperationOutcome-ValueSet-custom-issue-detail.json
new file mode 100644
index 00000000000..0823a430cf8
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/terminology/OperationOutcome-ValueSet-custom-issue-detail.json
@@ -0,0 +1,22 @@
+{
+ "resourceType": "OperationOutcome",
+ "issue": [
+ {
+ "severity": "information",
+ "code": "processing",
+ "details": {
+ "coding": [
+ {
+ "system": "http://example.com/custom-issue-type",
+ "code": "valueset-is-draft"
+ }
+ ]
+ },
+ "diagnostics": "The ValueSet status is marked as draft.",
+ "location": [
+ "Bundle",
+ "Line[1] Col[2]"
+ ]
+ }
+ ]
+}
\ No newline at end of file