diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6697-missing-search-params-with-hsearch-enabled.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6697-missing-search-params-with-hsearch-enabled.yaml new file mode 100644 index 000000000000..6275acddcb4a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6697-missing-search-params-with-hsearch-enabled.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 6697 +title: "Previously, operation $apply-codesystem-delta-add issued with Hibernate Search enabled and default search params option turned off resulted in an invalid sort specification error. This has been fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImpl.java index 4fbcd65c927c..232b8ab1ccc1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImpl.java @@ -22,6 +22,7 @@ import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; @@ -46,12 +47,19 @@ import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.QTY_VALUE_NORM; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.SEARCH_PARAM_ROOT; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.URI_VALUE; +import static java.util.Objects.isNull; /** * Used to build HSearch sort clauses. */ public class HSearchSortHelperImpl implements IHSearchSortHelper { private static final Logger ourLog = LoggerFactory.getLogger(HSearchSortHelperImpl.class); + public static final Map ourSortingParamNameToParamType = Map.of( + Constants.PARAM_LASTUPDATED, RestSearchParameterTypeEnum.DATE, + Constants.PARAM_ID, RestSearchParameterTypeEnum.TOKEN, + Constants.PARAM_TAG, RestSearchParameterTypeEnum.TOKEN, + Constants.PARAM_SECURITY, RestSearchParameterTypeEnum.TOKEN, + Constants.PARAM_SOURCE, RestSearchParameterTypeEnum.TOKEN); /** Indicates which HSearch properties must be sorted for each RestSearchParameterTypeEnum **/ private Map> mySortPropertyListMap = Map.of( @@ -151,14 +159,20 @@ Optional getSortClause(SearchSortFactory theF, SortSpec theSortSp */ @VisibleForTesting Optional getParamType(String theResourceTypeName, String theParamName) { + RestSearchParameterTypeEnum value; + ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams( theResourceTypeName, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH); RuntimeSearchParam searchParam = activeSearchParams.get(theParamName); + if (searchParam == null) { - return Optional.empty(); + value = isNull(theParamName) ? null : ourSortingParamNameToParamType.get(theParamName); + + } else { + value = searchParam.getParamType(); } - return Optional.of(searchParam.getParamType()); + return Optional.ofNullable(value); } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 567d92e324a1..c0e46faa16c2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -165,7 +165,7 @@ public UploadStatistics applyDeltaCodeSystemsAdd(String theSystem, CustomTermino Validate.notNull(csv); CodeSystem codeSystem = myTerminologySvc.fetchCanonicalCodeSystemFromCompleteContext(theSystem); - if (codeSystem.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) { + if (codeSystem != null && codeSystem.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) { throw new InvalidRequestException( Msg.code(844) + "CodeSystem with url[" + Constants.codeSystemWithDefaultDescription(theSystem) + "] can not apply a delta - wrong content mode: " + codeSystem.getContent()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImplTest.java index 313381c6be5f..55a19dbd55fe 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/search/HSearchSortHelperImplTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.dao.search; import ca.uhn.fhir.context.RuntimeSearchParam; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; @@ -13,6 +14,9 @@ import org.hibernate.search.engine.search.sort.dsl.SortFinalStep; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; @@ -20,6 +24,7 @@ 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.assertFalse; @@ -81,6 +86,36 @@ void testGetParamType() { assertFalse(paramType.isEmpty()); } + private static Stream provideArgumentsForGetParamType() { + Stream.Builder retVal = Stream.builder(); + HSearchSortHelperImpl.ourSortingParamNameToParamType.forEach((theSortSpecName, theRestSearchParameterTypeEnum) -> + { + SortSpec sortSpec = new SortSpec(theSortSpecName); + retVal.add(Arguments.of(sortSpec, Optional.of(theRestSearchParameterTypeEnum))); + }); + + return retVal.build(); + } + /** + * Validates that getParamType() returns a param type when _id, _lastUpdated, _tag, _security and _source are absent from + * the search param registry. + */ + @ParameterizedTest + @MethodSource("provideArgumentsForGetParamType") + void testGetParamTypeWhenParamNameIsNotInSearchParamRegistry(SortSpec sortSpec, Optional expectedSearchParamType) { + //Given that we have params absent from the SearchParamsRegistry + String resourceType = "CodeSystem"; + String absentSearchParam = sortSpec.getParamName(); + when(mockSearchParamRegistry.getActiveSearchParams(eq(resourceType), any())).thenReturn(mockResourceSearchParams); + when(mockResourceSearchParams.get(absentSearchParam)).thenReturn(null); + + //Execute + Optional paramType = tested.getParamType(resourceType, absentSearchParam); + + //Validate + assertThat(paramType).isEqualTo(expectedSearchParamType); + } + @Test void testGetSortClause() { SortSpec sortSpec = new SortSpec(); 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 0662fbc1dc83..4f4249d6912e 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 @@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.rp.r4.PatientResourceProvider; import ca.uhn.fhir.jpa.search.BaseSourceSearchParameterTestCases; import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases; +import ca.uhn.fhir.jpa.search.IIdSearchTestTemplate; import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases; import ca.uhn.fhir.jpa.search.builder.SearchBuilder; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory; @@ -2616,5 +2617,16 @@ protected void beforeOrAfterTestClass(TestContext testContext, DirtiesContext.Cl } } + @Nested + class IdTestCases implements IIdSearchTestTemplate { + @Override + public TestDaoSearch getSearch() { + return myTestDaoSearch; + } + @Override + public ITestDataBuilder getBuilder() { + return myTestDataBuilder; + } + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index 99180105bde9..36a122d3bc64 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -356,6 +356,37 @@ public void testApplyDeltaAdd_UsingCodeSystem() { ); } + @Test + public void testApplyDeltaAdd_UsingCodeSystemWithElasticSearch() { + //Given: Advance HSearch indexing is enabled + myStorageSettings.setHibernateSearchIndexFullText(true); + myStorageSettings.setHibernateSearchIndexSearchParams(true); + myStorageSettings.setStoreResourceInHSearchIndex(true); + + //Given: We have a non-existent code system + CodeSystem codeSystem = new CodeSystem(); + myClient.create().resource(codeSystem).execute(); + CodeSystem.ConceptDefinitionComponent chem = codeSystem.addConcept().setCode("CHEM").setDisplay("Chemistry"); + chem.addConcept().setCode("HB").setDisplay("Hemoglobin"); + chem.addConcept().setCode("NEUT").setDisplay("Neutrophils"); + CodeSystem.ConceptDefinitionComponent micro = codeSystem.addConcept().setCode("MICRO").setDisplay("Microbiology"); + micro.addConcept().setCode("C&S").setDisplay("Culture And Sensitivity"); + + //Execute + Parameters outcome = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) + .withParameter(Parameters.class, TerminologyUploaderProvider.PARAM_SYSTEM, new UriType("http://example.com/cs")) + .andParameter(TerminologyUploaderProvider.PARAM_CODESYSTEM, codeSystem) + .prettyPrint() + .execute(); + + //Validate + IntegerType conceptCount = (IntegerType) outcome.getParameter("conceptCount").getValue(); + assertThat(conceptCount.getValue()).isEqualTo(5); + } + @Test public void testApplyDeltaAdd_UsingCodeSystemWithConceptProprieties() { CodeSystem codeSystem = new CodeSystem();