Cleanup and utilities from composite work (#4066)
Utilities and cleanup from composite work #4066
This commit is contained in:
parent
6f80206776
commit
ee1cb4e392
|
@ -3,6 +3,9 @@ package ca.uhn.fhir.util;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
|
@ -25,14 +28,12 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class ObjectUtil {
|
public class ObjectUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Just use Objects.equals() instead;
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public static boolean equals(Object object1, Object object2) {
|
public static boolean equals(Object object1, Object object2) {
|
||||||
if (object1 == object2) {
|
return Objects.equals(object1, object2);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ((object1 == null) || (object2 == null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return object1.equals(object2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T requireNonNull(T obj, String message) {
|
public static <T> T requireNonNull(T obj, String message) {
|
||||||
|
@ -46,5 +47,21 @@ public class ObjectUtil {
|
||||||
throw new IllegalArgumentException(Msg.code(1777) + message);
|
throw new IllegalArgumentException(Msg.code(1777) + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast the object to the type using Optional.
|
||||||
|
* Useful for streaming with flatMap.
|
||||||
|
* @param theObject any object
|
||||||
|
* @param theClass the class to check instanceof
|
||||||
|
* @return Optional present if theObject is of type theClass
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Optional<T> castIfInstanceof(Object theObject, Class<T> theClass) {
|
||||||
|
if (theClass.isInstance(theObject)) {
|
||||||
|
return Optional.of((T) theObject);
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
package ca.uhn.fhir.util;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class ObjectUtilTest {
|
import java.util.Optional;
|
||||||
|
|
||||||
|
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.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
class ObjectUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEquals() {
|
void testEquals() {
|
||||||
String a = new String("a");
|
String a = "a";
|
||||||
String b = new String("b");
|
String b = "b";
|
||||||
assertFalse(ObjectUtil.equals(b, a));
|
assertFalse(ObjectUtil.equals(b, a));
|
||||||
assertFalse(ObjectUtil.equals(a, b));
|
assertFalse(ObjectUtil.equals(a, b));
|
||||||
assertFalse(ObjectUtil.equals(a, null));
|
assertFalse(ObjectUtil.equals(a, null));
|
||||||
|
@ -20,7 +26,7 @@ public class ObjectUtilTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequireNonNull() {
|
void testRequireNonNull() {
|
||||||
String message = "Must not be null in test";
|
String message = "Must not be null in test";
|
||||||
try {
|
try {
|
||||||
ObjectUtil.requireNonNull(null, message);
|
ObjectUtil.requireNonNull(null, message);
|
||||||
|
@ -32,7 +38,7 @@ public class ObjectUtilTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequireNotEmpty() {
|
void testRequireNotEmpty() {
|
||||||
//All these are empty, null or whitespace strings.
|
//All these are empty, null or whitespace strings.
|
||||||
testRequireNotEmptyErrorScenario(null);
|
testRequireNotEmptyErrorScenario(null);
|
||||||
testRequireNotEmptyErrorScenario("");
|
testRequireNotEmptyErrorScenario("");
|
||||||
|
@ -54,5 +60,22 @@ public class ObjectUtilTest {
|
||||||
assertEquals(Msg.code(1777) + message, e.getMessage());
|
assertEquals(Msg.code(1777) + message, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCast_isInstance_present() {
|
||||||
|
Boolean value = Boolean.FALSE;
|
||||||
|
|
||||||
|
Optional<Boolean> result = ObjectUtil.castIfInstanceof(value, Boolean.class);
|
||||||
|
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCast_isNotInstance_empty() {
|
||||||
|
Boolean value = Boolean.FALSE;
|
||||||
|
|
||||||
|
Optional<Integer> result = ObjectUtil.castIfInstanceof(value, Integer.class);
|
||||||
|
|
||||||
|
assertTrue(result.isEmpty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,14 +284,13 @@ public class TestUtil {
|
||||||
} else {
|
} else {
|
||||||
Validate.notNull(fk);
|
Validate.notNull(fk);
|
||||||
Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + theAnnotatedElement + " has no name()");
|
Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + theAnnotatedElement + " has no name()");
|
||||||
|
|
||||||
|
// temporarily allow the hibernate legacy sp fk names
|
||||||
List<String> legacySPHibernateFKNames = Arrays.asList(
|
List<String> legacySPHibernateFKNames = Arrays.asList(
|
||||||
"FKC97MPK37OKWU8QVTCEG2NH9VN", "FKGXSREUTYMMFJUWDSWV3Y887DO");
|
"FKC97MPK37OKWU8QVTCEG2NH9VN", "FKGXSREUTYMMFJUWDSWV3Y887DO");
|
||||||
if (legacySPHibernateFKNames.contains(fk.name())) {
|
Validate.isTrue(fk.name().startsWith("FK_") || legacySPHibernateFKNames.contains(fk.name()),
|
||||||
// wipmb temporarily allow the hibernate legacy sp fk names
|
"Foreign key " + fk.name() + " on " + theAnnotatedElement + " must start with FK");
|
||||||
} else {
|
|
||||||
Validate.isTrue(fk.name().startsWith("FK_"),
|
|
||||||
"Foreign key " + fk.name() + " on " + theAnnotatedElement + " must start with FK");
|
|
||||||
}
|
|
||||||
if ( ! duplicateNameValidationExceptionList.contains(fk.name())) {
|
if ( ! duplicateNameValidationExceptionList.contains(fk.name())) {
|
||||||
assertNotADuplicateName(fk.name(), theNames);
|
assertNotADuplicateName(fk.name(), theNames);
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,7 +368,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theSearchRuntimeDetails != null) {
|
if (theSearchRuntimeDetails != null) {
|
||||||
// wipmb we no longer have full size.
|
|
||||||
theSearchRuntimeDetails.setFoundIndexMatchesCount(resultCount);
|
theSearchRuntimeDetails.setFoundIndexMatchesCount(resultCount);
|
||||||
HookParams params = new HookParams()
|
HookParams params = new HookParams()
|
||||||
.add(RequestDetails.class, theRequest)
|
.add(RequestDetails.class, theRequest)
|
||||||
|
@ -377,7 +376,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
|
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wipmb extract
|
// todo MB extract this and move to FullText svc
|
||||||
// can we skip the database entirely and return the pid list from here?
|
// can we skip the database entirely and return the pid list from here?
|
||||||
boolean canSkipDatabase =
|
boolean canSkipDatabase =
|
||||||
// if we processed an AND clause, and it returned nothing, then nothing can match.
|
// if we processed an AND clause, and it returned nothing, then nothing can match.
|
||||||
|
@ -388,12 +387,10 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
theParams.isEmpty() &&
|
theParams.isEmpty() &&
|
||||||
// not every param is a param. :-(
|
// not every param is a param. :-(
|
||||||
theParams.getNearDistanceParam() == null &&
|
theParams.getNearDistanceParam() == null &&
|
||||||
|
// todo MB don't we support _lastUpdated and _offset now?
|
||||||
theParams.getLastUpdated() == null &&
|
theParams.getLastUpdated() == null &&
|
||||||
theParams.getEverythingMode() == null &&
|
theParams.getEverythingMode() == null &&
|
||||||
theParams.getOffset() == null
|
theParams.getOffset() == null
|
||||||
// &&
|
|
||||||
// // or sorting?
|
|
||||||
// theParams.getSort() == null
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (canSkipDatabase) {
|
if (canSkipDatabase) {
|
||||||
|
@ -406,7 +403,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
ourLog.trace("Query needs db after HSearch. Chunking.");
|
ourLog.trace("Query needs db after HSearch. Chunking.");
|
||||||
// Finish the query in the database for the rest of the search parameters, sorting, partitioning, etc.
|
// Finish the query in the database for the rest of the search parameters, sorting, partitioning, etc.
|
||||||
// We break the pids into chunks that fit in the 1k limit for jdbc bind params.
|
// We break the pids into chunks that fit in the 1k limit for jdbc bind params.
|
||||||
// wipmb change chunk to take iterator
|
|
||||||
new QueryChunker<Long>()
|
new QueryChunker<Long>()
|
||||||
.chunk(Streams.stream(fulltextExecutor).collect(Collectors.toList()), t -> doCreateChunkedQueries(theParams, t, theOffset, sort, theCountOnlyFlag, theRequest, queries));
|
.chunk(Streams.stream(fulltextExecutor).collect(Collectors.toList()), t -> doCreateChunkedQueries(theParams, t, theOffset, sort, theCountOnlyFlag, theRequest, queries));
|
||||||
}
|
}
|
||||||
|
@ -1016,7 +1012,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
if (myDaoConfig.isAdvancedHSearchIndexing() && myDaoConfig.isStoreResourceInHSearchIndex()) {
|
if (myDaoConfig.isAdvancedHSearchIndexing() && myDaoConfig.isStoreResourceInHSearchIndex()) {
|
||||||
List<Long> pidList = thePids.stream().map(ResourcePersistentId::getIdAsLong).collect(Collectors.toList());
|
List<Long> pidList = thePids.stream().map(ResourcePersistentId::getIdAsLong).collect(Collectors.toList());
|
||||||
|
|
||||||
// wipmb standardize on ResourcePersistentId
|
|
||||||
List<IBaseResource> resources = myFulltextSearchSvc.getResources(pidList);
|
List<IBaseResource> resources = myFulltextSearchSvc.getResources(pidList);
|
||||||
return resources;
|
return resources;
|
||||||
} else if (!Objects.isNull(myParams) && myParams.isLastN()) {
|
} else if (!Objects.isNull(myParams) && myParams.isLastN()) {
|
||||||
|
@ -1693,7 +1688,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) {
|
private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) {
|
||||||
if (myQueryList.isEmpty()) {
|
if (myQueryList.isEmpty()) {
|
||||||
// wipmb what is this?
|
|
||||||
// Capture times for Lucene/Elasticsearch queries as well
|
// Capture times for Lucene/Elasticsearch queries as well
|
||||||
mySearchRuntimeDetails.setQueryStopwatch(new StopWatch());
|
mySearchRuntimeDetails.setQueryStopwatch(new StopWatch());
|
||||||
myQueryList = createQuery(myParams, mySort, theOffset, theMaxResultsToFetch, false, myRequest, mySearchRuntimeDetails);
|
myQueryList = createQuery(myParams, mySort, theOffset, theMaxResultsToFetch, false, myRequest, mySearchRuntimeDetails);
|
||||||
|
|
|
@ -65,7 +65,7 @@ abstract public class BaseR4SearchLastN extends BaseJpaTest {
|
||||||
private static final Map<String, String> observationCategoryMap = new HashMap<>();
|
private static final Map<String, String> observationCategoryMap = new HashMap<>();
|
||||||
private static final Map<String, String> observationCodeMap = new HashMap<>();
|
private static final Map<String, String> observationCodeMap = new HashMap<>();
|
||||||
private static final Map<String, Date> observationEffectiveMap = new HashMap<>();
|
private static final Map<String, Date> observationEffectiveMap = new HashMap<>();
|
||||||
// wipmb make thise normal fields. This static setup wasn't working
|
// todo mb make thise normal fields. This static setup wasn't working
|
||||||
protected static IIdType patient0Id = null;
|
protected static IIdType patient0Id = null;
|
||||||
protected static IIdType patient1Id = null;
|
protected static IIdType patient1Id = null;
|
||||||
protected static IIdType patient2Id = null;
|
protected static IIdType patient2Id = null;
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<configuration>
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!--<logger name="ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber" additivity="false" level="trace">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>-->
|
|
||||||
|
|
||||||
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="org.eclipse.jetty.websocket" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" additivity="true" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="org.eclipse" additivity="false" level="error">
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.jpa.dao" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!-- set to debug to enable term expansion logs -->
|
|
||||||
<logger name="ca.uhn.fhir.jpa.term" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!-- Set to 'trace' to enable SQL logging -->
|
|
||||||
<logger name="org.hibernate.SQL" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
<!-- Set to 'trace' to enable SQL Value logging -->
|
|
||||||
<logger name="org.hibernate.type" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!-- <logger name="ca.uhn.fhir.jpa.model.search" additivity="false" level="debug"/>-->
|
|
||||||
<!-- <logger name="org.elasticsearch.client" additivity="true" level="trace"/>-->
|
|
||||||
<!-- <logger name="org.hibernate.search.elasticsearch.request" additivity="false" level="trace"/>-->
|
|
||||||
<!-- <logger name="org.hibernate.search" level="TRACE"/>-->
|
|
||||||
<!-- <logger name="org.hibernate.search.query" level="TRACE"/>-->
|
|
||||||
<!-- <logger name="org.hibernate.search.elasticsearch.request" level="TRACE"/>-->
|
|
||||||
<!-- See https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#backend-lucene-io-writer-infostream for lucene logging
|
|
||||||
<logger name="org.hibernate.search.backend.lucene.infostream" level="TRACE"/> -->
|
|
||||||
<logger name="org.springframework.test.context.cache" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<root level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</configuration>
|
|
|
@ -98,8 +98,10 @@ public class TestHSearchAddInConfig {
|
||||||
luceneProperties.put(BackendSettings.backendKey(LuceneIndexSettings.IO_WRITER_INFOSTREAM), "true");
|
luceneProperties.put(BackendSettings.backendKey(LuceneIndexSettings.IO_WRITER_INFOSTREAM), "true");
|
||||||
luceneProperties.put(HibernateOrmMapperSettings.ENABLED, "true");
|
luceneProperties.put(HibernateOrmMapperSettings.ENABLED, "true");
|
||||||
|
|
||||||
return (theProperties) ->
|
return (theProperties) -> {
|
||||||
|
ourLog.debug("Configuring Hibernate Search - {}", luceneProperties);
|
||||||
theProperties.putAll(luceneProperties);
|
theProperties.putAll(luceneProperties);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,71 +5,42 @@
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!--<logger name="ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber" additivity="false" level="trace">
|
<!-- define the root first, so the rest can inherit our logger -->
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>-->
|
|
||||||
|
|
||||||
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="org.eclipse.jetty.websocket" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" additivity="true" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="org.eclipse" additivity="false" level="error">
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.jpa.dao" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!-- set to debug to enable term expansion logs -->
|
|
||||||
<logger name="ca.uhn.fhir.jpa.term" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!-- Set to 'trace' to enable SQL logging -->
|
|
||||||
<logger name="org.hibernate.SQL" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
<!-- Set to 'trace' to enable SQL Value logging -->
|
|
||||||
<logger name="org.hibernate.type" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
|
|
||||||
<logger name="org.elasticsearch.client" additivity="true" level="trace"/>
|
|
||||||
<!--
|
|
||||||
<logger name="ca.uhn.fhir.jpa.model.search" additivity="false" level="debug"/>
|
|
||||||
<logger name="org.elasticsearch.client" additivity="true" level="trace"/>
|
|
||||||
<logger name="org.hibernate.search" additivity="false" level="TRACE"/>
|
|
||||||
<logger name="org.hibernate.search.query" additivity="false" level="TRACE"/>
|
|
||||||
<logger name="org.hibernate.search.elasticsearch.request" additivity="false" level="TRACE"/>
|
|
||||||
-->
|
|
||||||
<!-- See https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#backend-lucene-io-writer-infostream for lucene logging
|
|
||||||
<logger name="org.hibernate.search.backend.lucene.infostream" level="TRACE"/> -->
|
|
||||||
<logger name="org.springframework.test.context.cache" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.jpa.bulk" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<root level="info">
|
<root level="info">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
|
<logger name="ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber" level="info"/>
|
||||||
|
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" level="info"/>
|
||||||
|
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" level="info"/>
|
||||||
|
<logger name="org.eclipse.jetty.websocket" level="info"/>
|
||||||
|
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" level="info"/>
|
||||||
|
<logger name="org.eclipse" level="error"/>
|
||||||
|
<logger name="ca.uhn.fhir.rest.client" level="info"/>
|
||||||
|
<logger name="ca.uhn.fhir.jpa.dao" level="info"/>
|
||||||
|
|
||||||
|
<!-- set to debug to enable term expansion logs -->
|
||||||
|
<logger name="ca.uhn.fhir.jpa.term" level="info"/>
|
||||||
|
|
||||||
|
<!-- Set to 'trace' to enable SQL logging -->
|
||||||
|
<logger name="org.hibernate.SQL" level="info"/>
|
||||||
|
<!-- Set to 'trace' to enable SQL Value logging -->
|
||||||
|
<logger name="org.hibernate.type" level="info"/>
|
||||||
|
|
||||||
|
<logger name="org.springframework.test.context.cache" level="info"/>
|
||||||
|
<logger name="ca.uhn.fhir.jpa.bulk" level="info"/>
|
||||||
|
|
||||||
|
<!-- debugging -->
|
||||||
|
<!--
|
||||||
|
<logger name="org.elasticsearch.client" level="trace"/>
|
||||||
|
<logger name="org.hibernate.search.elasticsearch.request" level="TRACE"/>
|
||||||
|
<logger name="ca.uhn.fhir.jpa.model.search" level="debug"/>
|
||||||
|
<logger name="org.elasticsearch.client" level="trace"/>
|
||||||
|
<logger name="org.hibernate.search" level="debug"/>
|
||||||
|
<logger name="org.hibernate.search.query" level="TRACE"/>
|
||||||
|
<logger name="org.hibernate.search.elasticsearch.request" level="TRACE"/>
|
||||||
|
-->
|
||||||
|
<!-- See https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#backend-lucene-io-writer-infostream for lucene logging
|
||||||
|
<logger name="org.hibernate.search.backend.lucene.infostream" level="TRACE"/> -->
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
package ca.uhn.fhir.test.utilities;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
|
import org.hl7.fhir.r4.model.Quantity;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class ITestDataBuilderTest {
|
||||||
|
|
||||||
|
FhirContext myFhirContext = FhirContext.forR4Cached();
|
||||||
|
|
||||||
|
List<IBaseResource> myCreatedList = new ArrayList<>();
|
||||||
|
List<IBaseResource> myUpdatedList = new ArrayList<>();
|
||||||
|
|
||||||
|
ITestDataBuilder myTDB = new ITestDataBuilder() {
|
||||||
|
@Override
|
||||||
|
public IIdType doCreateResource(IBaseResource theResource) {
|
||||||
|
myCreatedList.add(theResource);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIdType doUpdateResource(IBaseResource theResource) {
|
||||||
|
myUpdatedList.add(theResource);
|
||||||
|
return theResource.getIdElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return myFhirContext;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ObservationCreation {
|
||||||
|
@Test
|
||||||
|
void createObservation_withEffective_setsDate() {
|
||||||
|
myTDB.createObservation(
|
||||||
|
myTDB.withEffectiveDate("2020-01-01T12:34:56"));
|
||||||
|
|
||||||
|
assertEquals(1, myCreatedList.size());
|
||||||
|
Observation o = (Observation) myCreatedList.get(0);
|
||||||
|
|
||||||
|
assertEquals("2020-01-01T12:34:56", o.getEffectiveDateTimeType().getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createObservation_withObservationCode_setsCode() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
myTDB.createObservation(
|
||||||
|
myTDB.withObservationCode("http://example.com", "a-code-value", "a code description")
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals(1, myCreatedList.size());
|
||||||
|
Observation o = (Observation) myCreatedList.get(0);
|
||||||
|
|
||||||
|
CodeableConcept codeable = o.getCode();
|
||||||
|
assertNotNull(codeable);
|
||||||
|
assertEquals(1,codeable.getCoding().size(), "has one coding");
|
||||||
|
Coding coding = codeable.getCoding().get(0);
|
||||||
|
|
||||||
|
assertEquals("http://example.com", coding.getSystem());
|
||||||
|
assertEquals("a-code-value", coding.getCode());
|
||||||
|
assertEquals("a code description", coding.getDisplay());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createObservation_withValueQuantity_createsQuantity() {
|
||||||
|
myTDB.createObservation(
|
||||||
|
myTDB.withQuantityAtPath("valueQuantity", 200, "hulla", "bpm"));
|
||||||
|
|
||||||
|
assertEquals(1, myCreatedList.size());
|
||||||
|
Observation o = (Observation) myCreatedList.get(0);
|
||||||
|
|
||||||
|
Quantity valueQuantity = o.getValueQuantity();
|
||||||
|
assertNotNull(valueQuantity);
|
||||||
|
|
||||||
|
assertEquals(200, valueQuantity.getValue().doubleValue());
|
||||||
|
assertEquals("hulla", valueQuantity.getSystem());
|
||||||
|
assertEquals("bpm", valueQuantity.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createObservation_withComponents_createsComponents() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
myTDB.createObservation(
|
||||||
|
myTDB.withObservationCode("http://example.com", "a-code-value", "a code description"),
|
||||||
|
myTDB.withEffectiveDate("2020-01-01T12:34:56"),
|
||||||
|
myTDB.withObservationComponent(
|
||||||
|
myTDB.withObservationCode("http://example.com", "another-code-value"),
|
||||||
|
myTDB.withQuantityAtPath("valueQuantity", 200, "hulla", "bpm")),
|
||||||
|
myTDB.withObservationComponent(
|
||||||
|
myTDB.withObservationCode("http://example.com", "yet-another-code-value"),
|
||||||
|
myTDB.withQuantityAtPath("valueQuantity", 1000000, "hulla", "sik"))
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals(1, myCreatedList.size());
|
||||||
|
Observation o = (Observation) myCreatedList.get(0);
|
||||||
|
|
||||||
|
assertEquals(2, o.getComponent().size());
|
||||||
|
Observation.ObservationComponentComponent secondComponent = o.getComponent().get(1);
|
||||||
|
|
||||||
|
assertEquals("yet-another-code-value", secondComponent.getCode().getCoding().get(0).getCode());
|
||||||
|
assertEquals(1000000.0, secondComponent.getValueQuantity().getValue().doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* Some test for our utilities that need a FhirContext
|
||||||
|
*/
|
||||||
|
package ca.uhn.fhir.test.utilities;
|
|
@ -84,7 +84,12 @@ public class FhirContextSearchParamRegistry implements ISearchParamRegistry {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public RuntimeSearchParam getActiveSearchParamByUrl(String theUrl) {
|
public RuntimeSearchParam getActiveSearchParamByUrl(String theUrl) {
|
||||||
throw new UnsupportedOperationException(Msg.code(2067));
|
// simple implementation for test support
|
||||||
|
return myCtx.getResourceTypes().stream()
|
||||||
|
.flatMap(type->getActiveSearchParams(type).values().stream())
|
||||||
|
.filter(rsp->theUrl.equals(rsp.getUri()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,7 +26,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.MetaUtil;
|
import ca.uhn.fhir.util.MetaUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -34,6 +34,8 @@ import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.InstantType;
|
import org.hl7.fhir.r4.model.InstantType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -49,6 +51,7 @@ import static org.hamcrest.Matchers.matchesPattern;
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||||
public interface ITestDataBuilder {
|
public interface ITestDataBuilder {
|
||||||
|
Logger ourLog = LoggerFactory.getLogger(ITestDataBuilder.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set Patient.active = true
|
* Set Patient.active = true
|
||||||
|
@ -106,6 +109,13 @@ public interface ITestDataBuilder {
|
||||||
return t -> __setPrimitiveChild(getFhirContext(), t, "effectiveDateTime", "dateTime", theDate);
|
return t -> __setPrimitiveChild(getFhirContext(), t, "effectiveDateTime", "dateTime", theDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Observation.effectiveDate
|
||||||
|
*/
|
||||||
|
default Consumer<IBaseResource> withDateTimeAt(String thePath, String theDate) {
|
||||||
|
return t -> __setPrimitiveChild(getFhirContext(), t, thePath, "dateTime", theDate);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set [Resource].identifier.system and [Resource].identifier.value
|
* Set [Resource].identifier.system and [Resource].identifier.value
|
||||||
*/
|
*/
|
||||||
|
@ -192,6 +202,10 @@ public interface ITestDataBuilder {
|
||||||
default IIdType createResource(String theResourceType, Consumer<IBaseResource>... theModifiers) {
|
default IIdType createResource(String theResourceType, Consumer<IBaseResource>... theModifiers) {
|
||||||
IBaseResource resource = buildResource(theResourceType, theModifiers);
|
IBaseResource resource = buildResource(theResourceType, theModifiers);
|
||||||
|
|
||||||
|
if (ourLog.isDebugEnabled()) {
|
||||||
|
ourLog.debug("Creating {}", getFhirContext().newJsonParser().encodeResourceToString(resource));
|
||||||
|
}
|
||||||
|
|
||||||
if (isNotBlank(resource.getIdElement().getValue())) {
|
if (isNotBlank(resource.getIdElement().getValue())) {
|
||||||
return doUpdateResource(resource);
|
return doUpdateResource(resource);
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,15 +241,15 @@ public interface ITestDataBuilder {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
default <T extends IBase> Consumer<T> withElementAt(String thePath, Consumer<IBase>... theModifiers) {
|
default <T extends IBase, E extends IBase> Consumer<T> withElementAt(String thePath, Consumer<E>... theModifiers) {
|
||||||
return t->{
|
return t->{
|
||||||
FhirTerser terser = getFhirContext().newTerser();
|
FhirTerser terser = getFhirContext().newTerser();
|
||||||
IBase element = terser.addElement(t, thePath);
|
E element = terser.addElement(t, thePath);
|
||||||
applyElementModifiers(element, theModifiers);
|
applyElementModifiers(element, theModifiers);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
default Consumer<IBaseResource> withQuantityAtPath(String thePath, double theValue, String theSystem, String theCode) {
|
default <T extends IBase> Consumer<T> withQuantityAtPath(String thePath, Number theValue, String theSystem, String theCode) {
|
||||||
return withElementAt(thePath,
|
return withElementAt(thePath,
|
||||||
withPrimitiveAttribute("value", theValue),
|
withPrimitiveAttribute("value", theValue),
|
||||||
withPrimitiveAttribute("system", theSystem),
|
withPrimitiveAttribute("system", theSystem),
|
||||||
|
@ -256,8 +270,8 @@ public interface ITestDataBuilder {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void applyElementModifiers(IBase element, Consumer<IBase>[] theModifiers) {
|
default <E extends IBase> void applyElementModifiers(E element, Consumer<E>[] theModifiers) {
|
||||||
for (Consumer<IBase> nextModifier : theModifiers) {
|
for (Consumer<E> nextModifier : theModifiers) {
|
||||||
nextModifier.accept(element);
|
nextModifier.accept(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,16 +280,24 @@ public interface ITestDataBuilder {
|
||||||
return withObservationCode(theSystem, theCode, null);
|
return withObservationCode(theSystem, theCode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
default Consumer<IBaseResource> withObservationCode(@Nullable String theSystem, @Nullable String theCode, String theDisplay) {
|
default Consumer<IBaseResource> withObservationCode(@Nullable String theSystem, @Nullable String theCode, @Nullable String theDisplay) {
|
||||||
return t -> {
|
return withCodingAt("code.coding", theSystem, theCode, theDisplay);
|
||||||
FhirTerser terser = getFhirContext().newTerser();
|
}
|
||||||
IBase coding = terser.addElement(t, "code.coding");
|
|
||||||
terser.addElement(coding, "system", theSystem);
|
default <T extends IBase> Consumer<T> withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue) {
|
||||||
terser.addElement(coding, "code", theCode);
|
return withCodingAt(thePath, theSystem, theValue, null);
|
||||||
if (StringUtils.isNotEmpty(theDisplay)) {
|
}
|
||||||
terser.addElement(coding, "display", theDisplay);
|
|
||||||
}
|
default <T extends IBase> Consumer<T> withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue, @Nullable String theDisplay) {
|
||||||
};
|
return withElementAt(thePath,
|
||||||
|
withPrimitiveAttribute("system", theSystem),
|
||||||
|
withPrimitiveAttribute("code", theValue),
|
||||||
|
withPrimitiveAttribute("display", theDisplay)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T extends IBaseResource, E extends IBase> Consumer<T> withObservationComponent(Consumer<E>... theModifiers) {
|
||||||
|
return withElementAt("component", theModifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
default Consumer<IBaseResource> withObservationHasMember(@Nullable IIdType theHasMember) {
|
default Consumer<IBaseResource> withObservationHasMember(@Nullable IIdType theHasMember) {
|
||||||
|
@ -302,6 +324,7 @@ public interface ITestDataBuilder {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo mb extract these to something like TestDataBuilderBacking. Maybe split out create* into child interface since people skip it.
|
||||||
/**
|
/**
|
||||||
* Users of this API must implement this method
|
* Users of this API must implement this method
|
||||||
*/
|
*/
|
||||||
|
@ -328,4 +351,58 @@ public interface ITestDataBuilder {
|
||||||
booleanType.setValueAsString(theValue);
|
booleanType.setValueAsString(theValue);
|
||||||
activeChild.getMutator().addValue(theTarget, booleanType);
|
activeChild.getMutator().addValue(theTarget, booleanType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Support {
|
||||||
|
FhirContext getFhirContext();
|
||||||
|
IIdType createResource(IBaseResource theResource);
|
||||||
|
IIdType updateResource(IBaseResource theResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WithSupport extends ITestDataBuilder {
|
||||||
|
Support getSupport();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default FhirContext getFhirContext() {
|
||||||
|
return getSupport().getFhirContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default IIdType doCreateResource(IBaseResource theResource) {
|
||||||
|
return getSupport().createResource(theResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default IIdType doUpdateResource(IBaseResource theResource) {
|
||||||
|
return getSupport().updateResource(theResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy support to use ITestDataBuilder as just a builder, not a DAO
|
||||||
|
* todo mb Maybe we should split out the builder into a super-interface and drop this?
|
||||||
|
*/
|
||||||
|
class SupportNoDao implements Support {
|
||||||
|
final FhirContext myFhirContext;
|
||||||
|
|
||||||
|
public SupportNoDao(FhirContext theFhirContext) {
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return myFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIdType createResource(IBaseResource theResource) {
|
||||||
|
Validate.isTrue(false, "Create not supported");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIdType updateResource(IBaseResource theResource) {
|
||||||
|
Validate.isTrue(false, "Update not supported");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue