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 org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -25,14 +28,12 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
public class ObjectUtil {
|
||||
|
||||
/**
|
||||
* @deprecated Just use Objects.equals() instead;
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean equals(Object object1, Object object2) {
|
||||
if (object1 == object2) {
|
||||
return true;
|
||||
}
|
||||
if ((object1 == null) || (object2 == null)) {
|
||||
return false;
|
||||
}
|
||||
return object1.equals(object2);
|
||||
return Objects.equals(object1, object2);
|
||||
}
|
||||
|
||||
public static <T> T requireNonNull(T obj, String message) {
|
||||
|
@ -47,4 +48,20 @@ public class ObjectUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
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
|
||||
public void testEquals() {
|
||||
String a = new String("a");
|
||||
String b = new String("b");
|
||||
void testEquals() {
|
||||
String a = "a";
|
||||
String b = "b";
|
||||
assertFalse(ObjectUtil.equals(b, a));
|
||||
assertFalse(ObjectUtil.equals(a, b));
|
||||
assertFalse(ObjectUtil.equals(a, null));
|
||||
|
@ -20,7 +26,7 @@ public class ObjectUtilTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequireNonNull() {
|
||||
void testRequireNonNull() {
|
||||
String message = "Must not be null in test";
|
||||
try {
|
||||
ObjectUtil.requireNonNull(null, message);
|
||||
|
@ -32,7 +38,7 @@ public class ObjectUtilTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequireNotEmpty() {
|
||||
void testRequireNotEmpty() {
|
||||
//All these are empty, null or whitespace strings.
|
||||
testRequireNotEmptyErrorScenario(null);
|
||||
testRequireNotEmptyErrorScenario("");
|
||||
|
@ -55,4 +61,21 @@ public class ObjectUtilTest {
|
|||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
Validate.notNull(fk);
|
||||
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(
|
||||
"FKC97MPK37OKWU8QVTCEG2NH9VN", "FKGXSREUTYMMFJUWDSWV3Y887DO");
|
||||
if (legacySPHibernateFKNames.contains(fk.name())) {
|
||||
// wipmb temporarily allow the hibernate legacy sp fk names
|
||||
} else {
|
||||
Validate.isTrue(fk.name().startsWith("FK_"),
|
||||
Validate.isTrue(fk.name().startsWith("FK_") || legacySPHibernateFKNames.contains(fk.name()),
|
||||
"Foreign key " + fk.name() + " on " + theAnnotatedElement + " must start with FK");
|
||||
}
|
||||
|
||||
if ( ! duplicateNameValidationExceptionList.contains(fk.name())) {
|
||||
assertNotADuplicateName(fk.name(), theNames);
|
||||
}
|
||||
|
|
|
@ -368,7 +368,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
if (theSearchRuntimeDetails != null) {
|
||||
// wipmb we no longer have full size.
|
||||
theSearchRuntimeDetails.setFoundIndexMatchesCount(resultCount);
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequest)
|
||||
|
@ -377,7 +376,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
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?
|
||||
boolean canSkipDatabase =
|
||||
// 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() &&
|
||||
// not every param is a param. :-(
|
||||
theParams.getNearDistanceParam() == null &&
|
||||
// todo MB don't we support _lastUpdated and _offset now?
|
||||
theParams.getLastUpdated() == null &&
|
||||
theParams.getEverythingMode() == null &&
|
||||
theParams.getOffset() == null
|
||||
// &&
|
||||
// // or sorting?
|
||||
// theParams.getSort() == null
|
||||
);
|
||||
|
||||
if (canSkipDatabase) {
|
||||
|
@ -406,7 +403,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
ourLog.trace("Query needs db after HSearch. Chunking.");
|
||||
// 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.
|
||||
// wipmb change chunk to take iterator
|
||||
new QueryChunker<Long>()
|
||||
.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()) {
|
||||
List<Long> pidList = thePids.stream().map(ResourcePersistentId::getIdAsLong).collect(Collectors.toList());
|
||||
|
||||
// wipmb standardize on ResourcePersistentId
|
||||
List<IBaseResource> resources = myFulltextSearchSvc.getResources(pidList);
|
||||
return resources;
|
||||
} else if (!Objects.isNull(myParams) && myParams.isLastN()) {
|
||||
|
@ -1693,7 +1688,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) {
|
||||
if (myQueryList.isEmpty()) {
|
||||
// wipmb what is this?
|
||||
// Capture times for Lucene/Elasticsearch queries as well
|
||||
mySearchRuntimeDetails.setQueryStopwatch(new StopWatch());
|
||||
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> observationCodeMap = 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 patient1Id = 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(HibernateOrmMapperSettings.ENABLED, "true");
|
||||
|
||||
return (theProperties) ->
|
||||
return (theProperties) -> {
|
||||
ourLog.debug("Configuring Hibernate Search - {}", luceneProperties);
|
||||
theProperties.putAll(luceneProperties);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,71 +5,42 @@
|
|||
</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="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>
|
||||
|
||||
<!-- define the root first, so the rest can inherit our logger -->
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</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>
|
||||
|
|
|
@ -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
|
||||
@Override
|
||||
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
|
||||
|
|
|
@ -26,7 +26,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
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.IBaseReference;
|
||||
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.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
@ -49,6 +51,7 @@ import static org.hamcrest.Matchers.matchesPattern;
|
|||
*/
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
public interface ITestDataBuilder {
|
||||
Logger ourLog = LoggerFactory.getLogger(ITestDataBuilder.class);
|
||||
|
||||
/**
|
||||
* Set Patient.active = true
|
||||
|
@ -106,6 +109,13 @@ public interface ITestDataBuilder {
|
|||
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
|
||||
*/
|
||||
|
@ -192,6 +202,10 @@ public interface ITestDataBuilder {
|
|||
default IIdType createResource(String theResourceType, Consumer<IBaseResource>... theModifiers) {
|
||||
IBaseResource resource = buildResource(theResourceType, theModifiers);
|
||||
|
||||
if (ourLog.isDebugEnabled()) {
|
||||
ourLog.debug("Creating {}", getFhirContext().newJsonParser().encodeResourceToString(resource));
|
||||
}
|
||||
|
||||
if (isNotBlank(resource.getIdElement().getValue())) {
|
||||
return doUpdateResource(resource);
|
||||
} 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->{
|
||||
FhirTerser terser = getFhirContext().newTerser();
|
||||
IBase element = terser.addElement(t, thePath);
|
||||
E element = terser.addElement(t, thePath);
|
||||
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,
|
||||
withPrimitiveAttribute("value", theValue),
|
||||
withPrimitiveAttribute("system", theSystem),
|
||||
|
@ -256,8 +270,8 @@ public interface ITestDataBuilder {
|
|||
return element;
|
||||
}
|
||||
|
||||
default void applyElementModifiers(IBase element, Consumer<IBase>[] theModifiers) {
|
||||
for (Consumer<IBase> nextModifier : theModifiers) {
|
||||
default <E extends IBase> void applyElementModifiers(E element, Consumer<E>[] theModifiers) {
|
||||
for (Consumer<E> nextModifier : theModifiers) {
|
||||
nextModifier.accept(element);
|
||||
}
|
||||
}
|
||||
|
@ -266,16 +280,24 @@ public interface ITestDataBuilder {
|
|||
return withObservationCode(theSystem, theCode, null);
|
||||
}
|
||||
|
||||
default Consumer<IBaseResource> withObservationCode(@Nullable String theSystem, @Nullable String theCode, String theDisplay) {
|
||||
return t -> {
|
||||
FhirTerser terser = getFhirContext().newTerser();
|
||||
IBase coding = terser.addElement(t, "code.coding");
|
||||
terser.addElement(coding, "system", theSystem);
|
||||
terser.addElement(coding, "code", theCode);
|
||||
if (StringUtils.isNotEmpty(theDisplay)) {
|
||||
terser.addElement(coding, "display", theDisplay);
|
||||
default Consumer<IBaseResource> withObservationCode(@Nullable String theSystem, @Nullable String theCode, @Nullable String theDisplay) {
|
||||
return withCodingAt("code.coding", theSystem, theCode, theDisplay);
|
||||
}
|
||||
};
|
||||
|
||||
default <T extends IBase> Consumer<T> withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue) {
|
||||
return withCodingAt(thePath, theSystem, theValue, null);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -328,4 +351,58 @@ public interface ITestDataBuilder {
|
|||
booleanType.setValueAsString(theValue);
|
||||
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