Don't error out on missing CS (#1570)

* Don't error out on mi9ssing CS

* Add tests

* Add a bit of test coverage

* One more test
This commit is contained in:
James Agnew 2019-10-30 08:38:39 -04:00 committed by GitHub
parent 9d4df3e470
commit 007cfaf00e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 181 additions and 4 deletions

View File

@ -1,4 +1,5 @@
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet
# Core Library Messages
ca.uhn.fhir.context.FhirContext.unknownResourceName=Unknown resource name "{0}" (this name is not known in FHIR version "{1}")

View File

@ -675,7 +675,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
CodeSystem codeSystemFromContext = getCodeSystemFromContext(system);
if (codeSystemFromContext == null) {
throw new InvalidRequestException("Unknown code system: " + system);
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", system);
ourLog.warn(msg);
theValueSetCodeAccumulator.addMessage(msg);
return false;
}
if (!theIncludeOrExclude.getConcept().isEmpty()) {

View File

@ -27,6 +27,8 @@ import java.util.Collection;
public interface IValueSetConceptAccumulator {
void addMessage(String theMessage);
void includeConcept(String theSystem, String theCode, String theDisplay);
void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations);

View File

@ -57,6 +57,12 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
myConceptsExcluded = 0;
}
@Override
public void addMessage(String theMessage) {
// ignore for now
}
@Override
public void includeConcept(String theSystem, String theCode, String theDisplay) {
saveConcept(theSystem, theCode, theDisplay);
@ -82,7 +88,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
if (optionalConcept.isPresent()) {
TermValueSetConcept concept = optionalConcept.get();
ourLog.info("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl());
ourLog.debug("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl());
for (TermValueSetConceptDesignation designation : concept.getDesignations()) {
myValueSetConceptDesignationDao.deleteById(designation.getId());
myTermValueSet.decrementTotalConceptDesignations();
@ -90,7 +96,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
myValueSetConceptDao.deleteById(concept.getId());
myTermValueSet.decrementTotalConcepts();
myValueSetDao.save(myTermValueSet);
ourLog.info("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl());
ourLog.debug("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl());
if (++myConceptsExcluded % 250 == 0) {
ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSet.getUrl());

View File

@ -22,9 +22,11 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet;
import javax.annotation.Nullable;
@ -54,6 +56,13 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
return myMaxCapacity - myConceptsCount;
}
@Override
public void addMessage(String theMessage) {
addExtension()
.setUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE)
.setValue(new StringType(theMessage));
}
@Override
public void includeConcept(String theSystem, String theCode, String theDisplay) {
incrementConceptsCount();

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.rest.api.Constants;
@ -9,11 +11,14 @@ import ca.uhn.fhir.rest.param.HasOrListParam;
import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.StringParam;
import org.hl7.fhir.r5.model.Organization;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.Practitioner;
import org.hl7.fhir.r5.model.PractitionerRole;
import org.junit.AfterClass;
import org.junit.Test;
import java.util.Date;
import static org.junit.Assert.assertEquals;
@SuppressWarnings({"unchecked", "Duplicates"})
@ -98,6 +103,40 @@ public class FhirResourceDaoR5SearchNoFtTest extends BaseJpaR5Test {
assertEquals(1, outcome.getResources(0, 1).size());
}
@Test
public void testSearchDoesntFailIfResourcesAreDeleted() {
Patient p = new Patient();
p.addIdentifier().setValue("1");
myPatientDao.create(p);
p = new Patient();
p.addIdentifier().setValue("2");
myPatientDao.create(p);
p = new Patient();
p.addIdentifier().setValue("3");
Long id = myPatientDao.create(p).getId().getIdPartAsLong();
IBundleProvider outcome = myPatientDao.search(new SearchParameterMap());
assertEquals(3, outcome.size().intValue());
runInTransaction(()->{
ResourceTable table = myResourceTableDao.findById(id).orElseThrow(() -> new IllegalArgumentException());
table.setDeleted(new Date());
myResourceTableDao.save(table);
});
assertEquals(2, outcome.getResources(0, 3).size());
runInTransaction(()->{
myResourceHistoryTableDao.deleteAll();
});
assertEquals(0, outcome.getResources(0, 3).size());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -0,0 +1,75 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao;
import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class ValueSetConceptAccumulatorTest {
private ValueSetConceptAccumulator myAccumulator;
private TermValueSet myValueSet;
@Mock
private ITermValueSetDao myValueSetDao;
@Mock
private ITermValueSetConceptDesignationDao myValueSetDesignationDao;
@Mock
private ITermValueSetConceptDao myValueSetConceptDao;
@Before
public void before() {
myValueSet = new TermValueSet();
myAccumulator = new ValueSetConceptAccumulator(myValueSet, myValueSetDao, myValueSetConceptDao, myValueSetDesignationDao);
}
@Test
public void testIncludeConcept() {
for (int i = 0; i < 1000; i++) {
myAccumulator.includeConcept("sys", "code", "display");
}
verify(myValueSetConceptDao, times(1000)).save(any());
}
@Test
public void testExcludeBlankConcept() {
myAccumulator.excludeConcept("", "");
verifyNoInteractions(myValueSetConceptDao);
}
@Test
public void testAddMessage() {
myAccumulator.addMessage("foo");
verifyNoInteractions(myValueSetConceptDao);
}
@Test
public void testExcludeConceptWithDesignations() {
for (int i = 0; i <1000; i++) {
TermValueSetConcept value = new TermValueSetConcept();
value.setCode("code");
value.getDesignations().add(new TermValueSetConceptDesignation().setValue("foo"));
when(myValueSetConceptDao.findByTermValueSetIdSystemAndCode(any(), eq("sys"), eq("code"+i))).thenReturn(Optional.of(value));
myAccumulator.excludeConcept("sys", "code"+i);
}
}
}

View File

@ -2,11 +2,13 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
import org.junit.Test;
@ -688,6 +690,21 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
assertEquals("Systolische bloeddruk minimaal 1 uur", designationComponent.getValue());
}
@Test
public void testExpandValueSetWithUnknownCodeSystem() {
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem("http://unknown-system");
ValueSet outcome = myTermSvc.expandValueSetInMemory(vs, null);
assertEquals(0, outcome.getExpansion().getContains().size());
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(encoded);
Extension extensionByUrl = outcome.getExpansion().getExtensionByUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE);
assertEquals("Unknown CodeSystem URI \"http://unknown-system\" referenced from ValueSet", extensionByUrl.getValueAsPrimitive().getValueAsString());
}
@Test
public void testExpandTermValueSetAndChildrenWithOffsetAndCountWithClientAssignedId() throws Exception {
myDaoConfig.setPreExpandValueSets(true);

View File

@ -24,6 +24,13 @@ import ca.uhn.fhir.rest.api.Constants;
public class JpaConstants {
/**
* Non-instantiable
*/
private JpaConstants() {
// nothing
}
/**
* Operation name for the $apply-codesystem-delta-add operation
*/
@ -243,6 +250,12 @@ public class JpaConstants {
*/
public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED";
/**
* Message added to expansion valueset
*/
public static final String EXT_VALUESET_EXPANSION_MESSAGE = "http://hapifhir.io/fhir/StructureDefinition/valueset-expansion-message";
/**
* Parameter for the $export operation
*/

View File

@ -11,6 +11,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
@ -50,7 +52,17 @@ public class SubscriptionLoaderTest extends BaseBlockingQueueSubscribableChannel
@Test
public void testMultipleThreadsDontBlock() throws InterruptedException {
SubscriptionLoader svc = new SubscriptionLoader();
svc.acquireSemaphoreForUnitTest();
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
try {
svc.acquireSemaphoreForUnitTest();
latch.countDown();
} catch (InterruptedException theE) {
// ignore
}
}).start();
latch.await(10, TimeUnit.SECONDS);
svc.syncSubscriptions();
}