Re-enable contains searches on the public HAPI FHIR server and improve
message formatting in HapiLocalizer
This commit is contained in:
parent
ed4da7c414
commit
a8c76450e5
|
@ -101,13 +101,36 @@ public class HapiLocalizer {
|
||||||
|
|
||||||
String formatString = getFormatString(theQualifiedKey);
|
String formatString = getFormatString(theQualifiedKey);
|
||||||
|
|
||||||
format = new MessageFormat(formatString.trim());
|
format = newMessageFormat(formatString);
|
||||||
myKeyToMessageFormat.put(theQualifiedKey, format);
|
myKeyToMessageFormat.put(theQualifiedKey, format);
|
||||||
return format.format(theParameters);
|
return format.format(theParameters);
|
||||||
}
|
}
|
||||||
return getFormatString(theQualifiedKey);
|
return getFormatString(theQualifiedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageFormat newMessageFormat(String theFormatString) {
|
||||||
|
StringBuilder pattern = new StringBuilder(theFormatString.trim());
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < (pattern.length()-1); i++) {
|
||||||
|
if (pattern.charAt(i) == '{') {
|
||||||
|
char nextChar = pattern.charAt(i+1);
|
||||||
|
if (nextChar >= '0' && nextChar <= '9') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern.replace(i, i+1, "'{'");
|
||||||
|
int closeBraceIndex = pattern.indexOf("}", i);
|
||||||
|
if (closeBraceIndex > 0) {
|
||||||
|
i = closeBraceIndex;
|
||||||
|
pattern.replace(i, i+1, "'}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MessageFormat(pattern.toString());
|
||||||
|
}
|
||||||
|
|
||||||
protected void init() {
|
protected void init() {
|
||||||
for (String nextName : myBundleNames) {
|
for (String nextName : myBundleNames) {
|
||||||
myBundle.add(ResourceBundle.getBundle(nextName));
|
myBundle.add(ResourceBundle.getBundle(nextName));
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.i18n;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -10,6 +11,15 @@ import org.junit.Test;
|
||||||
|
|
||||||
public class HapiLocalizerTest {
|
public class HapiLocalizerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEscapePatterns() {
|
||||||
|
HapiLocalizer loc = new HapiLocalizer();
|
||||||
|
|
||||||
|
assertEquals("some message", loc.newMessageFormat("some message").format(new Object[]{}));
|
||||||
|
assertEquals("var1 {var2} var3 {var4}", loc.newMessageFormat("var1 {var2} var3 {var4}").format(new Object[]{}));
|
||||||
|
assertEquals("var1 A var3 B", loc.newMessageFormat("var1 {0} var3 {1}").format(new Object[]{ "A", "B"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllKeys() {
|
public void testAllKeys() {
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
||||||
toDelete.add(nextEntry.getValue());
|
toDelete.add(nextEntry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mySearchParamPresentDao.deleteInBatch(toDelete);
|
mySearchParamPresentDao.deleteAll(toDelete);
|
||||||
|
|
||||||
// Add any that should be added
|
// Add any that should be added
|
||||||
List<SearchParamPresent> toAdd = new ArrayList<>();
|
List<SearchParamPresent> toAdd = new ArrayList<>();
|
||||||
|
|
|
@ -398,7 +398,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||||
txTemplate.execute(t -> {
|
txTemplate.execute(t -> {
|
||||||
theDao.deleteInBatch(link);
|
theDao.deleteAll(link);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2935,6 +2935,52 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
assertThat(ids, containsInAnyOrder(pt2id));
|
assertThat(ids, containsInAnyOrder(pt2id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithContainsLowerCase() {
|
||||||
|
myDaoConfig.setAllowContainsSearches(true);
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.addName().setFamily("abcdefghijk");
|
||||||
|
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.addName().setFamily("fghijk");
|
||||||
|
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
Patient pt3 = new Patient();
|
||||||
|
pt3.addName().setFamily("zzzzz");
|
||||||
|
myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
|
||||||
|
List<String> ids;
|
||||||
|
SearchParameterMap map;
|
||||||
|
IBundleProvider results;
|
||||||
|
|
||||||
|
// Contains = true
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_NAME, new StringParam("FGHIJK").setContains(true));
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
results = myPatientDao.search(map);
|
||||||
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
assertThat(ids, containsInAnyOrder(pt1id, pt2id));
|
||||||
|
|
||||||
|
// Contains = false
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_NAME, new StringParam("FGHIJK").setContains(false));
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
results = myPatientDao.search(map);
|
||||||
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
assertThat(ids, containsInAnyOrder(pt2id));
|
||||||
|
|
||||||
|
// No contains
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_NAME, new StringParam("FGHIJK"));
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
results = myPatientDao.search(map);
|
||||||
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
assertThat(ids, containsInAnyOrder(pt2id));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithContainsDisabled() {
|
public void testSearchWithContainsDisabled() {
|
||||||
myDaoConfig.setAllowContainsSearches(false);
|
myDaoConfig.setAllowContainsSearches(false);
|
||||||
|
|
|
@ -37,8 +37,10 @@ import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
@ -137,6 +139,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
||||||
myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo());
|
myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo());
|
||||||
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
|
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
|
||||||
|
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
|
||||||
|
|
||||||
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
|
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
|
||||||
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
|
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
|
||||||
|
@ -156,7 +159,42 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
|
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithContainsLowerCase() {
|
||||||
|
myDaoConfig.setAllowContainsSearches(true);
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.addName().setFamily("Elizabeth");
|
||||||
|
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.addName().setFamily("fghijk");
|
||||||
|
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
Patient pt3 = new Patient();
|
||||||
|
pt3.addName().setFamily("zzzzz");
|
||||||
|
myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
|
||||||
|
Bundle output = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource("Patient")
|
||||||
|
.where(Patient.NAME.contains().value("ZAB"))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
List<String> ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
|
||||||
|
assertThat(ids, containsInAnyOrder(pt1id));
|
||||||
|
|
||||||
|
output = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource("Patient")
|
||||||
|
.where(Patient.NAME.contains().value("zab"))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
|
||||||
|
assertThat(ids, containsInAnyOrder(pt1id));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testManualPagingLinkOffsetDoesntReturnBeyondEnd() {
|
public void testManualPagingLinkOffsetDoesntReturnBeyondEnd() {
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class WebsocketWithCriteriaDstu3Test extends BaseResourceProviderDstu3Tes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createObservation() throws Exception {
|
public void createObservation() {
|
||||||
Observation observation = new Observation();
|
Observation observation = new Observation();
|
||||||
CodeableConcept codeableConcept = new CodeableConcept();
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
observation.setCode(codeableConcept);
|
observation.setCode(codeableConcept);
|
||||||
|
|
|
@ -46,7 +46,7 @@ import static org.apache.commons.lang3.StringUtils.left;
|
||||||
* IDX_SP_STRING
|
* IDX_SP_STRING
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This one us used only for sorting
|
// This is used for sorting, and for :contains queries currently
|
||||||
@Index(name = "IDX_SP_STRING_HASH_IDENT", columnList = "HASH_IDENTITY"),
|
@Index(name = "IDX_SP_STRING_HASH_IDENT", columnList = "HASH_IDENTITY"),
|
||||||
|
|
||||||
@Index(name = "IDX_SP_STRING_HASH_NRM", columnList = "HASH_NORM_PREFIX,SP_VALUE_NORMALIZED"),
|
@Index(name = "IDX_SP_STRING_HASH_NRM", columnList = "HASH_NORM_PREFIX,SP_VALUE_NORMALIZED"),
|
||||||
|
|
|
@ -61,6 +61,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
retVal.setSubscriptionEnabled(true);
|
retVal.setSubscriptionEnabled(true);
|
||||||
retVal.setSubscriptionPollDelay(5000);
|
retVal.setSubscriptionPollDelay(5000);
|
||||||
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||||
|
retVal.setAllowContainsSearches(true);
|
||||||
retVal.setAllowMultipleDelete(true);
|
retVal.setAllowMultipleDelete(true);
|
||||||
retVal.setAllowInlineMatchUrlReferences(true);
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
retVal.setAllowExternalReferences(true);
|
retVal.setAllowExternalReferences(true);
|
||||||
|
|
|
@ -52,6 +52,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
retVal.setSubscriptionEnabled(true);
|
retVal.setSubscriptionEnabled(true);
|
||||||
retVal.setSubscriptionPollDelay(5000);
|
retVal.setSubscriptionPollDelay(5000);
|
||||||
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||||
|
retVal.setAllowContainsSearches(true);
|
||||||
retVal.setAllowMultipleDelete(true);
|
retVal.setAllowMultipleDelete(true);
|
||||||
retVal.setAllowInlineMatchUrlReferences(true);
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
retVal.setAllowExternalReferences(true);
|
retVal.setAllowExternalReferences(true);
|
||||||
|
|
|
@ -52,6 +52,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
retVal.setSubscriptionEnabled(true);
|
retVal.setSubscriptionEnabled(true);
|
||||||
retVal.setSubscriptionPollDelay(5000);
|
retVal.setSubscriptionPollDelay(5000);
|
||||||
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||||
|
retVal.setAllowContainsSearches(true);
|
||||||
retVal.setAllowMultipleDelete(true);
|
retVal.setAllowMultipleDelete(true);
|
||||||
retVal.setAllowInlineMatchUrlReferences(true);
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
retVal.setAllowExternalReferences(true);
|
retVal.setAllowExternalReferences(true);
|
||||||
|
|
|
@ -77,6 +77,10 @@
|
||||||
A new config setting has been added to the JPA DaoConfig that disables validation
|
A new config setting has been added to the JPA DaoConfig that disables validation
|
||||||
of the resource type for target resources in references.
|
of the resource type for target resources in references.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
HapiLocalizer can now handle message patterns with braces that aren't a part of a
|
||||||
|
message format expression. E.g. "Here is an {example}".
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.7.0" date="2019-02-06" description="Gale">
|
<release version="3.7.0" date="2019-02-06" description="Gale">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue