Resolve validation errors to ValueSet with UCUM (#1948)
* Add tests for validation errors * Work on validation errors * Bump core version * Fix validation errors * Test fixes * Add changelog * Test fix * Test fix * Test fix
This commit is contained in:
parent
9e19b87e67
commit
6fb6675b1e
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 1948
|
||||
title: "When validating resources containing codes in a ValueSet that included UCUM codes, the validator would
|
||||
incorrectly report that the code was valid even if it was not in the ValueSet. This has been corrected."
|
|
@ -89,7 +89,6 @@ import com.github.benmanes.caffeine.cache.Cache;
|
|||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import net.bytebuddy.implementation.bytecode.Throw;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
@ -625,128 +624,55 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
|
||||
if (cs != null) {
|
||||
|
||||
TermCodeSystemVersion csv = cs.getCurrentVersion();
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
|
||||
/*
|
||||
* If FullText searching is not enabled, we can handle only basic expansions
|
||||
* since we're going to do it without the database.
|
||||
*/
|
||||
if (myFulltextSearchSvc == null) {
|
||||
expandWithoutHibernateSearch(theValueSetCodeAccumulator, csv, theAddedCodes, theIncludeOrExclude, system, theAdd, theCodeCounter);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, let's use hibernate search to build the expansion
|
||||
*/
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
if (theWantConceptOrNull != null) {
|
||||
bool.must(qb.keyword().onField("myCode").matching(theWantConceptOrNull.getCode()).createQuery());
|
||||
}
|
||||
|
||||
/*
|
||||
* Filters
|
||||
*/
|
||||
handleFilters(bool, system, qb, theIncludeOrExclude);
|
||||
|
||||
Query luceneQuery = bool.createQuery();
|
||||
|
||||
/*
|
||||
* Include/Exclude Concepts
|
||||
*/
|
||||
List<Term> codes = theIncludeOrExclude
|
||||
.getConcept()
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(ValueSet.ConceptReferenceComponent::getCode)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(t -> new Term("myCode", t))
|
||||
.collect(Collectors.toList());
|
||||
if (codes.size() > 0) {
|
||||
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
builder.setMinimumNumberShouldMatch(1);
|
||||
for (Term nextCode : codes) {
|
||||
builder.add(new TermQuery(nextCode), BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
|
||||
luceneQuery = new BooleanQuery.Builder()
|
||||
.add(luceneQuery, BooleanClause.Occur.MUST)
|
||||
.add(builder.build(), BooleanClause.Occur.MUST)
|
||||
.build();
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the query
|
||||
*/
|
||||
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
|
||||
|
||||
/*
|
||||
* DM 2019-08-21 - Processing slows after any ValueSets with many codes explicitly identified. This might
|
||||
* be due to the dark arts that is memory management. Will monitor but not do anything about this right now.
|
||||
*/
|
||||
BooleanQuery.setMaxClauseCount(10000);
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
int maxResultsPerBatch = 10000;
|
||||
|
||||
/*
|
||||
* If the accumulator is bounded, we may reduce the size of the query to
|
||||
* Lucene in order to be more efficient.
|
||||
*/
|
||||
if (theAdd) {
|
||||
Integer accumulatorCapacityRemaining = theValueSetCodeAccumulator.getCapacityRemaining();
|
||||
if (accumulatorCapacityRemaining != null) {
|
||||
maxResultsPerBatch = Math.min(maxResultsPerBatch, accumulatorCapacityRemaining + 1);
|
||||
}
|
||||
if (maxResultsPerBatch <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
jpaQuery.setMaxResults(maxResultsPerBatch);
|
||||
jpaQuery.setFirstResult(theQueryIndex * maxResultsPerBatch);
|
||||
|
||||
ourLog.debug("Beginning batch expansion for {} with max results per batch: {}", (theAdd ? "inclusion" : "exclusion"), maxResultsPerBatch);
|
||||
|
||||
StopWatch swForBatch = new StopWatch();
|
||||
AtomicInteger countForBatch = new AtomicInteger(0);
|
||||
|
||||
List resultList = jpaQuery.getResultList();
|
||||
int resultsInBatch = resultList.size();
|
||||
int firstResult = jpaQuery.getFirstResult();
|
||||
for (Object next : resultList) {
|
||||
count.incrementAndGet();
|
||||
countForBatch.incrementAndGet();
|
||||
TermConcept concept = (TermConcept) next;
|
||||
try {
|
||||
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, concept, theAdd, theCodeCounter);
|
||||
} catch (ExpansionTooCostlyException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.debug("Batch expansion for {} with starting index of {} produced {} results in {}ms", (theAdd ? "inclusion" : "exclusion"), firstResult, countForBatch, swForBatch.getMillis());
|
||||
|
||||
if (resultsInBatch < maxResultsPerBatch) {
|
||||
ourLog.debug("Expansion for {} produced {} results in {}ms", (theAdd ? "inclusion" : "exclusion"), count, sw.getMillis());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs);
|
||||
|
||||
} else {
|
||||
// No CodeSystem matching the URL found in the database.
|
||||
|
||||
if (theIncludeOrExclude.getConcept().size() > 0 && theWantConceptOrNull != null) {
|
||||
if (defaultString(theIncludeOrExclude.getSystem()).equals(theWantConceptOrNull.getSystem())) {
|
||||
if (theIncludeOrExclude.getConcept().stream().noneMatch(t -> t.getCode().equals(theWantConceptOrNull.getCode()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No CodeSystem matching the URL found in the database.
|
||||
CodeSystem codeSystemFromContext = fetchCanonicalCodeSystemFromCompleteContext(system);
|
||||
if (codeSystemFromContext == null) {
|
||||
|
||||
// This is a last ditch effort.. We don't have a CodeSystem resource for the desired CS, and we don't have
|
||||
// anything at all in the database that matches it. So let's try asking the validation support context
|
||||
// just in case there is a registered service that knows how to handle this. This can happen, for example,
|
||||
// if someone creates a valueset that includes UCUM codes, since we don't have a CodeSystem resource for those
|
||||
// but CommonCodeSystemsTerminologyService can validate individual codes.
|
||||
List<VersionIndependentConcept> includedConcepts = null;
|
||||
if (theWantConceptOrNull != null) {
|
||||
includedConcepts = new ArrayList<>();
|
||||
includedConcepts.add(theWantConceptOrNull);
|
||||
} else if (!theIncludeOrExclude.getConcept().isEmpty()) {
|
||||
includedConcepts = theIncludeOrExclude
|
||||
.getConcept()
|
||||
.stream()
|
||||
.map(t->new VersionIndependentConcept(theIncludeOrExclude.getSystem(), t.getCode()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (includedConcepts != null) {
|
||||
int foundCount = 0;
|
||||
for (VersionIndependentConcept next : includedConcepts) {
|
||||
LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), next.getSystem(), next.getCode());
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, next.getSystem(), next.getCode(), lookup.getCodeDisplay());
|
||||
foundCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundCount == includedConcepts.size()) {
|
||||
return false;
|
||||
// ELSE, we'll continue below and throw an exception
|
||||
}
|
||||
}
|
||||
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", system);
|
||||
if (provideExpansionOptions(theExpansionOptions).isFailOnMissingCodeSystem()) {
|
||||
throw new PreconditionFailedException(msg);
|
||||
|
@ -755,6 +681,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
theValueSetCodeAccumulator.addMessage(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!theIncludeOrExclude.getConcept().isEmpty()) {
|
||||
|
@ -779,6 +706,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (hasValueSet) {
|
||||
|
||||
for (CanonicalType nextValueSet : theIncludeOrExclude.getValueSet()) {
|
||||
|
@ -825,6 +753,126 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs) {
|
||||
TermCodeSystemVersion csv = theCs.getCurrentVersion();
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
|
||||
/*
|
||||
* If FullText searching is not enabled, we can handle only basic expansions
|
||||
* since we're going to do it without the database.
|
||||
*/
|
||||
if (myFulltextSearchSvc == null) {
|
||||
expandWithoutHibernateSearch(theValueSetCodeAccumulator, csv, theAddedCodes, theIncludeOrExclude, theSystem, theAdd, theCodeCounter);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, let's use hibernate search to build the expansion
|
||||
*/
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
if (theWantConceptOrNull != null) {
|
||||
bool.must(qb.keyword().onField("myCode").matching(theWantConceptOrNull.getCode()).createQuery());
|
||||
}
|
||||
|
||||
/*
|
||||
* Filters
|
||||
*/
|
||||
handleFilters(bool, theSystem, qb, theIncludeOrExclude);
|
||||
|
||||
Query luceneQuery = bool.createQuery();
|
||||
|
||||
/*
|
||||
* Include/Exclude Concepts
|
||||
*/
|
||||
List<Term> codes = theIncludeOrExclude
|
||||
.getConcept()
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(ValueSet.ConceptReferenceComponent::getCode)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(t -> new Term("myCode", t))
|
||||
.collect(Collectors.toList());
|
||||
if (codes.size() > 0) {
|
||||
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
builder.setMinimumNumberShouldMatch(1);
|
||||
for (Term nextCode : codes) {
|
||||
builder.add(new TermQuery(nextCode), BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
|
||||
luceneQuery = new BooleanQuery.Builder()
|
||||
.add(luceneQuery, BooleanClause.Occur.MUST)
|
||||
.add(builder.build(), BooleanClause.Occur.MUST)
|
||||
.build();
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the query
|
||||
*/
|
||||
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
|
||||
|
||||
/*
|
||||
* DM 2019-08-21 - Processing slows after any ValueSets with many codes explicitly identified. This might
|
||||
* be due to the dark arts that is memory management. Will monitor but not do anything about this right now.
|
||||
*/
|
||||
BooleanQuery.setMaxClauseCount(10000);
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
int maxResultsPerBatch = 10000;
|
||||
|
||||
/*
|
||||
* If the accumulator is bounded, we may reduce the size of the query to
|
||||
* Lucene in order to be more efficient.
|
||||
*/
|
||||
if (theAdd) {
|
||||
Integer accumulatorCapacityRemaining = theValueSetCodeAccumulator.getCapacityRemaining();
|
||||
if (accumulatorCapacityRemaining != null) {
|
||||
maxResultsPerBatch = Math.min(maxResultsPerBatch, accumulatorCapacityRemaining + 1);
|
||||
}
|
||||
if (maxResultsPerBatch <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
jpaQuery.setMaxResults(maxResultsPerBatch);
|
||||
jpaQuery.setFirstResult(theQueryIndex * maxResultsPerBatch);
|
||||
|
||||
ourLog.debug("Beginning batch expansion for {} with max results per batch: {}", (theAdd ? "inclusion" : "exclusion"), maxResultsPerBatch);
|
||||
|
||||
StopWatch swForBatch = new StopWatch();
|
||||
AtomicInteger countForBatch = new AtomicInteger(0);
|
||||
|
||||
List resultList = jpaQuery.getResultList();
|
||||
int resultsInBatch = resultList.size();
|
||||
int firstResult = jpaQuery.getFirstResult();
|
||||
for (Object next : resultList) {
|
||||
count.incrementAndGet();
|
||||
countForBatch.incrementAndGet();
|
||||
TermConcept concept = (TermConcept) next;
|
||||
try {
|
||||
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, concept, theAdd, theCodeCounter);
|
||||
} catch (ExpansionTooCostlyException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.debug("Batch expansion for {} with starting index of {} produced {} results in {}ms", (theAdd ? "inclusion" : "exclusion"), firstResult, countForBatch, swForBatch.getMillis());
|
||||
|
||||
if (resultsInBatch < maxResultsPerBatch) {
|
||||
ourLog.debug("Expansion for {} produced {} results in {}ms", (theAdd ? "inclusion" : "exclusion"), count, sw.getMillis());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nonnull
|
||||
ValueSetExpansionOptions provideExpansionOptions(@Nullable ValueSetExpansionOptions theExpansionOptions) {
|
||||
if (theExpansionOptions != null) {
|
||||
|
@ -1939,12 +1987,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
@Override
|
||||
@Transactional
|
||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
|
||||
if (myInvokeOnNextCallForUnitTest != null) {
|
||||
Runnable invokeOnNextCallForUnitTest = myInvokeOnNextCallForUnitTest;
|
||||
myInvokeOnNextCallForUnitTest = null;
|
||||
invokeOnNextCallForUnitTest.run();
|
||||
}
|
||||
invokeRunnableForUnitTest();
|
||||
|
||||
IPrimitiveType<?> urlPrimitive = myContext.newTerser().getSingleValueOrNull(theValueSet, "url", IPrimitiveType.class);
|
||||
String url = urlPrimitive.getValueAsString();
|
||||
|
@ -2115,6 +2158,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only used for unit tests to test failure conditions
|
||||
*/
|
||||
static void invokeRunnableForUnitTest() {
|
||||
if (myInvokeOnNextCallForUnitTest != null) {
|
||||
Runnable invokeOnNextCallForUnitTest = myInvokeOnNextCallForUnitTest;
|
||||
myInvokeOnNextCallForUnitTest = null;
|
||||
invokeOnNextCallForUnitTest.run();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setInvokeOnNextCallForUnitTest(Runnable theInvokeOnNextCallForUnitTest) {
|
||||
myInvokeOnNextCallForUnitTest = theInvokeOnNextCallForUnitTest;
|
||||
|
|
|
@ -101,6 +101,8 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
|||
@CoverageIgnore
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
invokeRunnableForUnitTest();
|
||||
|
||||
Optional<VersionIndependentConcept> codeOpt = Optional.empty();
|
||||
boolean haveValidated = false;
|
||||
|
||||
|
|
|
@ -145,7 +145,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
|||
// Validate once
|
||||
myCaptureQueriesListener.clear();
|
||||
myObservationDao.validate(obs, null, null, null, null, null, null);
|
||||
assertEquals(9, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(10, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size());
|
||||
assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size());
|
||||
assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size());
|
||||
|
|
|
@ -5,6 +5,8 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermValueSet;
|
||||
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
|
@ -12,6 +14,8 @@ import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
|||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
|
||||
import ca.uhn.fhir.jpa.validation.ValidationSettings;
|
||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
|
@ -91,6 +95,122 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
@Autowired
|
||||
private ValidationSettings myValidationSettings;
|
||||
|
||||
/**
|
||||
* Use a valueset that explicitly brings in some UCUM codes
|
||||
*/
|
||||
@Test
|
||||
public void testValidateCodeInValueSetWithBuiltInCodeSystem() throws IOException {
|
||||
myValueSetDao.create(loadResourceFromClasspath(ValueSet.class, "/r4/bl/bb-vs.json"));
|
||||
myStructureDefinitionDao.create(loadResourceFromClasspath(StructureDefinition.class, "/r4/bl/bb-sd.json"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus());
|
||||
});
|
||||
|
||||
OperationOutcome outcome;
|
||||
|
||||
// Use a code that's in the ValueSet
|
||||
{
|
||||
outcome = (OperationOutcome) myObservationDao.validate(loadResourceFromClasspath(Observation.class, "/r4/bl/bb-obs-code-in-valueset.json"), null, null, null, null, null, mySrd).getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr, not(containsString("\"error\"")));
|
||||
}
|
||||
|
||||
// Use a code that's not in the ValueSet
|
||||
try {
|
||||
outcome = (OperationOutcome) myObservationDao.validate(loadResourceFromClasspath(Observation.class, "/r4/bl/bb-obs-code-not-in-valueset.json"), null, null, null, null, null, mySrd).getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
outcome = (OperationOutcome) e.getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr, containsString("Could not confirm that the codes provided are in the value set https://bb/ValueSet/BBDemographicAgeUnit, and a code from this value set is required"));
|
||||
}
|
||||
|
||||
// Before, the VS wasn't pre-expanded. Try again with it pre-expanded
|
||||
runInTransaction(() -> {
|
||||
TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus());
|
||||
});
|
||||
|
||||
myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
runInTransaction(() -> {
|
||||
TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vs.getExpansionStatus());
|
||||
});
|
||||
|
||||
// Use a code that's in the ValueSet
|
||||
{
|
||||
outcome = (OperationOutcome) myObservationDao.validate(loadResourceFromClasspath(Observation.class, "/r4/bl/bb-obs-code-in-valueset.json"), null, null, null, null, null, mySrd).getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr, not(containsString("\"error\"")));
|
||||
}
|
||||
|
||||
// Use a code that's not in the ValueSet
|
||||
try {
|
||||
outcome = (OperationOutcome) myObservationDao.validate(loadResourceFromClasspath(Observation.class, "/r4/bl/bb-obs-code-not-in-valueset.json"), null, null, null, null, null, mySrd).getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
outcome = (OperationOutcome) e.getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr, containsString("Could not confirm that the codes provided are in the value set https://bb/ValueSet/BBDemographicAgeUnit, and a code from this value set is required"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateCodeUsingQuantityBinding() throws IOException {
|
||||
myValueSetDao.create(loadResourceFromClasspath(ValueSet.class, "/r4/bl/bb-vs.json"));
|
||||
myStructureDefinitionDao.create(loadResourceFromClasspath(StructureDefinition.class, "/r4/bl/bb-sd.json"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus());
|
||||
});
|
||||
|
||||
OperationOutcome outcome;
|
||||
|
||||
// Use the wrong datatype
|
||||
try {
|
||||
myFhirCtx.setParserErrorHandler(new LenientErrorHandler());
|
||||
Observation resource = loadResourceFromClasspath(Observation.class, "/r4/bl/bb-obs-value-is-not-quantity2.json");
|
||||
outcome = (OperationOutcome) myObservationDao.validate(resource, null, null, null, null, null, mySrd).getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
outcome = (OperationOutcome) e.getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr, containsString("\"error\""));
|
||||
}
|
||||
|
||||
// Use the wrong datatype
|
||||
try {
|
||||
myFhirCtx.setParserErrorHandler(new LenientErrorHandler());
|
||||
Observation resource = loadResourceFromClasspath(Observation.class, "/r4/bl/bb-obs-value-is-not-quantity.json");
|
||||
outcome = (OperationOutcome) myObservationDao.validate(resource, null, null, null, null, null, mySrd).getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
outcome = (OperationOutcome) e.getOperationOutcome();
|
||||
String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr, containsString("The Profile \\\"https://bb/StructureDefinition/BBDemographicAge\\\" definition allows for the type Quantity but found type string"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a loinc valueset that expands to more results than the expander is willing to do
|
||||
* in memory, and make sure we can still validate correctly, even if we're using
|
||||
|
@ -413,10 +533,16 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.getCode().getCodingFirstRep().setSystem("http://example.com/codesystem");
|
||||
obs.getCode().getCodingFirstRep().setCode("foo-foo");
|
||||
obs.getCode().getCodingFirstRep().setDisplay("Some Code");
|
||||
outcome = (OperationOutcome) myObservationDao.validate(obs, null, null, null, ValidationModeEnum.CREATE, "http://example.com/structuredefinition", mySrd).getOperationOutcome();
|
||||
ourLog.info("Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
assertEquals("Unknown code in fragment CodeSystem 'http://example.com/codesystem#foo-foo'", outcome.getIssueFirstRep().getDiagnostics());
|
||||
assertEquals(OperationOutcome.IssueSeverity.WARNING, outcome.getIssueFirstRep().getSeverity());
|
||||
try {
|
||||
outcome = (OperationOutcome) myObservationDao.validate(obs, null, null, null, ValidationModeEnum.CREATE, "http://example.com/structuredefinition", mySrd).getOperationOutcome();
|
||||
ourLog.info("Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||
assertEquals("None of the codes provided are in the value set http://example.com/valueset (http://example.com/valueset), and a code from this value set is required) (codes = http://example.com/codesystem#foo-foo)", oo.getIssueFirstRep().getDiagnostics());
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
||||
}
|
||||
|
||||
// Correct codesystem, Code in codesystem
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://example.com/codesystem");
|
||||
|
@ -610,6 +736,12 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
*/
|
||||
@Test
|
||||
public void testValidate_TermSvcHasNpe() {
|
||||
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setUrl("http://FOO");
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
myCodeSystemDao.create(cs);
|
||||
|
||||
BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> {
|
||||
throw new NullPointerException("MY ERROR");
|
||||
});
|
||||
|
@ -623,18 +755,15 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.setStatus(ObservationStatus.FINAL);
|
||||
obs.setValue(new StringType("This is the value"));
|
||||
|
||||
OperationOutcome oo;
|
||||
|
||||
// Valid code
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE99999").setDisplay("Display 3");
|
||||
try {
|
||||
validateAndReturnOutcome(obs);
|
||||
fail();
|
||||
} catch (NullPointerException e) {
|
||||
assertEquals("MY ERROR", e.getMessage());
|
||||
}
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://FOO").setCode("CODE99999").setDisplay("Display 3");
|
||||
|
||||
OperationOutcome oo = validateAndReturnOutcome(obs);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||
assertEquals("Error MY ERROR validating Coding: java.lang.NullPointerException: MY ERROR", oo.getIssueFirstRep().getDiagnostics());
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -943,6 +1072,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(null);
|
||||
|
||||
myValidationSettings.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.IGNORE);
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -198,6 +198,25 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInstallR4PackageWithNoDescription() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
byte[] bytes = loadClasspathBytes("/packages/UK.Core.r4-1.1.0.tgz");
|
||||
myFakeNpmServlet.myResponses.put("/UK.Core.r4/1.1.0", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("UK.Core.r4").setVersion("1.1.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
||||
// Make sure we can fetch the package by ID and Version
|
||||
NpmPackage pkg = myPackageCacheManager.loadPackage("UK.Core.r4", "1.1.0");
|
||||
assertEquals(null, pkg.description());
|
||||
assertEquals("UK.Core.r4", pkg.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadPackageMetadata() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"code": "L7b8daLEuY",
|
||||
"system": "https://bbl.health"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"profile": [
|
||||
"https://bb/StructureDefinition/BBDemographicAge"
|
||||
]
|
||||
},
|
||||
"resourceType": "Observation",
|
||||
"status": "final",
|
||||
"valueQuantity" : {
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"code" : "mo",
|
||||
"value" : 8
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"code": "L7b8daLEuY",
|
||||
"system": "https://bbl.health"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"profile": [
|
||||
"https://bb/StructureDefinition/BBDemographicAge"
|
||||
]
|
||||
},
|
||||
"resourceType": "Observation",
|
||||
"status": "final",
|
||||
"valueQuantity" : {
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"code" : "cm",
|
||||
"value" : 8
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"meta": {
|
||||
"profile": [
|
||||
"https://bb/StructureDefinition/BBDemographicAge"
|
||||
]
|
||||
},
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"code": "L7b8daLEuY",
|
||||
"system": "https://bbl.health"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resourceType": "Observation",
|
||||
"status": "final",
|
||||
"valueString" : "test"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"meta": {
|
||||
"profile": [
|
||||
"https://bb/StructureDefinition/BBDemographicAge"
|
||||
]
|
||||
},
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"code": "L7b8daLEuY",
|
||||
"system": "https://bbl.health"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resourceType": "Observation",
|
||||
"status": "final",
|
||||
"valueDecimal" : 1.23
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"resourceType": "ValueSet",
|
||||
"id": "BBDemographicAgeUnit",
|
||||
"text": {
|
||||
"status": "generated",
|
||||
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"></div>"
|
||||
},
|
||||
"url": "https://bb/ValueSet/BBDemographicAgeUnit",
|
||||
"name": "BBDemographicAgeUnit",
|
||||
"title": "Babylon Demographic Age Unit",
|
||||
"status": "draft",
|
||||
"version": "20190731",
|
||||
"experimental": false,
|
||||
"description": "Age Unit",
|
||||
"publisher": "Babylon Partners, Ltd.",
|
||||
"immutable": false,
|
||||
"compose": {
|
||||
"include": [
|
||||
{
|
||||
"system": "http://unitsofmeasure.org",
|
||||
"concept": [
|
||||
{
|
||||
"code": "a",
|
||||
"display": "years"
|
||||
},
|
||||
{
|
||||
"code": "mo",
|
||||
"display": "months"
|
||||
},
|
||||
{
|
||||
"code": "wk",
|
||||
"display": "weeks"
|
||||
},
|
||||
{
|
||||
"code": "d",
|
||||
"display": "days"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
|||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
import ca.uhn.fhir.util.VersionEnum;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@ -121,6 +122,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
pkgVerRes.addForeignKey("20200610.12", "FK_NPM_PKVR_RESID").toColumn("BINARY_RES_ID").references("HFJ_RESOURCE", "RES_ID");
|
||||
pkgVerRes.addIndex("20200610.13", "IDX_PACKVERRES_URL").unique(false).withColumns("CANONICAL_URL");
|
||||
|
||||
pkgVerRes.modifyColumn("20200629.1", "PKG_DESC").nullable().withType(ColumnTypeEnum.STRING, 200);
|
||||
pkgVerRes.modifyColumn("20200629.2", "DESC_UPPER").nullable().withType(ColumnTypeEnum.STRING, 200);
|
||||
|
||||
}
|
||||
|
||||
private void init501() { //20200514 - present
|
||||
|
|
|
@ -74,9 +74,9 @@ public class NpmPackageVersionEntity {
|
|||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "SAVED_TIME", nullable = false)
|
||||
private Date mySavedTime;
|
||||
@Column(name = "PKG_DESC", nullable = false, length = 200)
|
||||
@Column(name = "PKG_DESC", nullable = true, length = 200)
|
||||
private String myDescription;
|
||||
@Column(name = "DESC_UPPER", nullable = false, length = 200)
|
||||
@Column(name = "DESC_UPPER", nullable = true, length = 200)
|
||||
private String myDescriptionUpper;
|
||||
@Column(name = "CURRENT_VERSION", nullable = false)
|
||||
private boolean myCurrentVersion;
|
||||
|
|
|
@ -415,6 +415,16 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClientRetryCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkerContext setClientRetryCount(int value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
|
||||
ConceptValidationOptions retVal = new ConceptValidationOptions();
|
||||
if (theOptions.isGuessSystem()) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* This {@link IValidationSupport validation support module} can be used to validate codes against common
|
||||
|
@ -36,8 +37,10 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
||||
public static final String LANGUAGES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/languages";
|
||||
public static final String MIMETYPES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/mimetypes";
|
||||
public static final String MIMETYPES_CODESYSTEM_URL = "urn:ietf:bcp:13";
|
||||
public static final String CURRENCIES_CODESYSTEM_URL = "urn:iso:std:iso:4217";
|
||||
public static final String CURRENCIES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/currencies";
|
||||
public static final String COUNTRIES_CODESYSTEM_URL = "urn:iso:std:iso:3166";
|
||||
public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org";
|
||||
public static final String UCUM_VALUESET_URL = "http://hl7.org/fhir/ValueSet/ucum-units";
|
||||
private static final String USPS_CODESYSTEM_URL = "https://www.usps.com/";
|
||||
|
@ -45,6 +48,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
private static final Logger ourLog = LoggerFactory.getLogger(CommonCodeSystemsTerminologyService.class);
|
||||
private static Map<String, String> USPS_CODES = Collections.unmodifiableMap(buildUspsCodes());
|
||||
private static Map<String, String> ISO_4217_CODES = Collections.unmodifiableMap(buildIso4217Codes());
|
||||
private static Map<String, String> ISO_3166_CODES = Collections.unmodifiableMap(buildIso3166Codes());
|
||||
private final FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
|
@ -144,49 +148,104 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
|
||||
if (UCUM_CODESYSTEM_URL.equals(theSystem) && theValidationSupportContext.getRootValidationSupport().getFhirContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
switch (theSystem) {
|
||||
case UCUM_CODESYSTEM_URL:
|
||||
|
||||
InputStream input = ClasspathUtil.loadResourceAsStream("/ucum-essence.xml");
|
||||
try {
|
||||
UcumEssenceService svc = new UcumEssenceService(input);
|
||||
String outcome = svc.analyse(theCode);
|
||||
if (outcome != null) {
|
||||
InputStream input = ClasspathUtil.loadResourceAsStream("/ucum-essence.xml");
|
||||
try {
|
||||
UcumEssenceService svc = new UcumEssenceService(input);
|
||||
String outcome = svc.analyse(theCode);
|
||||
if (outcome != null) {
|
||||
|
||||
LookupCodeResult retVal = new LookupCodeResult();
|
||||
retVal.setSearchedForCode(theCode);
|
||||
retVal.setSearchedForSystem(theSystem);
|
||||
retVal.setFound(true);
|
||||
retVal.setCodeDisplay(outcome);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
} catch (UcumException e) {
|
||||
ourLog.debug("Failed parse UCUM code: {}", theCode, e);
|
||||
return null;
|
||||
} finally {
|
||||
ClasspathUtil.close(input);
|
||||
}
|
||||
break;
|
||||
|
||||
case COUNTRIES_CODESYSTEM_URL:
|
||||
|
||||
String display = ISO_3166_CODES.get(theCode);
|
||||
if (isNotBlank(display)) {
|
||||
LookupCodeResult retVal = new LookupCodeResult();
|
||||
retVal.setSearchedForCode(theCode);
|
||||
retVal.setSearchedForSystem(theSystem);
|
||||
retVal.setFound(true);
|
||||
retVal.setCodeDisplay(outcome);
|
||||
retVal.setCodeDisplay(display);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
} catch (UcumException e) {
|
||||
ourLog.debug("Failed parse UCUM code: {}", theCode, e);
|
||||
break;
|
||||
|
||||
case MIMETYPES_CODESYSTEM_URL:
|
||||
|
||||
// This is a pretty naive implementation - Should be enhanced in future
|
||||
LookupCodeResult retVal = new LookupCodeResult();
|
||||
retVal.setSearchedForCode(theCode);
|
||||
retVal.setSearchedForSystem(theSystem);
|
||||
retVal.setFound(true);
|
||||
return retVal;
|
||||
|
||||
default:
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
ClasspathUtil.close(input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
// If we get here it means we know the codesystem but the code was bad
|
||||
LookupCodeResult retVal = new LookupCodeResult();
|
||||
retVal.setSearchedForCode(theCode);
|
||||
retVal.setSearchedForSystem(theSystem);
|
||||
retVal.setFound(false);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
|
||||
switch (theSystem) {
|
||||
case COUNTRIES_CODESYSTEM_URL:
|
||||
case UCUM_CODESYSTEM_URL:
|
||||
case MIMETYPES_CODESYSTEM_URL:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) {
|
||||
|
||||
public String getValueSetUrl(@Nonnull IBaseResource theValueSet) {
|
||||
switch (theValueSetUrl) {
|
||||
case CURRENCIES_VALUESET_URL:
|
||||
case LANGUAGES_VALUESET_URL:
|
||||
case MIMETYPES_VALUESET_URL:
|
||||
case UCUM_VALUESET_URL:
|
||||
case USPS_VALUESET_URL:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
public static String getValueSetUrl(@Nonnull IBaseResource theValueSet) {
|
||||
String url;
|
||||
switch (getFhirContext().getVersion().getVersion()) {
|
||||
switch (theValueSet.getStructureFhirVersionEnum()) {
|
||||
case DSTU2: {
|
||||
url = ((ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSet).getUrl();
|
||||
break;
|
||||
|
@ -209,16 +268,11 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
}
|
||||
case DSTU2_1:
|
||||
default:
|
||||
throw new IllegalArgumentException("Can not handle version: " + getFhirContext().getVersion().getVersion());
|
||||
throw new IllegalArgumentException("Can not handle version: " + theValueSet.getStructureFhirVersionEnum());
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
private static HashMap<String, String> buildUspsCodes() {
|
||||
HashMap<String, String> uspsCodes = new HashMap<>();
|
||||
uspsCodes.put("AK", "Alaska");
|
||||
|
@ -471,4 +525,259 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
return iso4217Codes;
|
||||
}
|
||||
|
||||
|
||||
private static HashMap<String, String> buildIso3166Codes() {
|
||||
HashMap<String, String> codes = new HashMap<>();
|
||||
codes.put("AF", "Afghanistan");
|
||||
codes.put("AX", "Åland Islands");
|
||||
codes.put("AL", "Albania");
|
||||
codes.put("DZ", "Algeria");
|
||||
codes.put("AS", "American Samoa");
|
||||
codes.put("AD", "Andorra");
|
||||
codes.put("AO", "Angola");
|
||||
codes.put("AI", "Anguilla");
|
||||
codes.put("AQ", "Antarctica");
|
||||
codes.put("AG", "Antigua & Barbuda");
|
||||
codes.put("AR", "Argentina");
|
||||
codes.put("AM", "Armenia");
|
||||
codes.put("AW", "Aruba");
|
||||
codes.put("AU", "Australia");
|
||||
codes.put("AT", "Austria");
|
||||
codes.put("AZ", "Azerbaijan");
|
||||
codes.put("BS", "Bahamas");
|
||||
codes.put("BH", "Bahrain");
|
||||
codes.put("BD", "Bangladesh");
|
||||
codes.put("BB", "Barbados");
|
||||
codes.put("BY", "Belarus");
|
||||
codes.put("BE", "Belgium");
|
||||
codes.put("BZ", "Belize");
|
||||
codes.put("BJ", "Benin");
|
||||
codes.put("BM", "Bermuda");
|
||||
codes.put("BT", "Bhutan");
|
||||
codes.put("BO", "Bolivia");
|
||||
codes.put("BA", "Bosnia & Herzegovina");
|
||||
codes.put("BW", "Botswana");
|
||||
codes.put("BV", "Bouvet Island");
|
||||
codes.put("BR", "Brazil");
|
||||
codes.put("IO", "British Indian Ocean Territory");
|
||||
codes.put("VG", "British Virgin Islands");
|
||||
codes.put("BN", "Brunei");
|
||||
codes.put("BG", "Bulgaria");
|
||||
codes.put("BF", "Burkina Faso");
|
||||
codes.put("BI", "Burundi");
|
||||
codes.put("KH", "Cambodia");
|
||||
codes.put("CM", "Cameroon");
|
||||
codes.put("CA", "Canada");
|
||||
codes.put("CV", "Cape Verde");
|
||||
codes.put("BQ", "Caribbean Netherlands");
|
||||
codes.put("KY", "Cayman Islands");
|
||||
codes.put("CF", "Central African Republic");
|
||||
codes.put("TD", "Chad");
|
||||
codes.put("CL", "Chile");
|
||||
codes.put("CN", "China");
|
||||
codes.put("CX", "Christmas Island");
|
||||
codes.put("CC", "Cocos (Keeling) Islands");
|
||||
codes.put("CO", "Colombia");
|
||||
codes.put("KM", "Comoros");
|
||||
codes.put("CG", "Congo - Brazzaville");
|
||||
codes.put("CD", "Congo - Kinshasa");
|
||||
codes.put("CK", "Cook Islands");
|
||||
codes.put("CR", "Costa Rica");
|
||||
codes.put("CI", "Côte d’Ivoire");
|
||||
codes.put("HR", "Croatia");
|
||||
codes.put("CU", "Cuba");
|
||||
codes.put("CW", "Curaçao");
|
||||
codes.put("CY", "Cyprus");
|
||||
codes.put("CZ", "Czechia");
|
||||
codes.put("DK", "Denmark");
|
||||
codes.put("DJ", "Djibouti");
|
||||
codes.put("DM", "Dominica");
|
||||
codes.put("DO", "Dominican Republic");
|
||||
codes.put("EC", "Ecuador");
|
||||
codes.put("EG", "Egypt");
|
||||
codes.put("SV", "El Salvador");
|
||||
codes.put("GQ", "Equatorial Guinea");
|
||||
codes.put("ER", "Eritrea");
|
||||
codes.put("EE", "Estonia");
|
||||
codes.put("SZ", "Eswatini");
|
||||
codes.put("ET", "Ethiopia");
|
||||
codes.put("FK", "Falkland Islands");
|
||||
codes.put("FO", "Faroe Islands");
|
||||
codes.put("FJ", "Fiji");
|
||||
codes.put("FI", "Finland");
|
||||
codes.put("FR", "France");
|
||||
codes.put("GF", "French Guiana");
|
||||
codes.put("PF", "French Polynesia");
|
||||
codes.put("TF", "French Southern Territories");
|
||||
codes.put("GA", "Gabon");
|
||||
codes.put("GM", "Gambia");
|
||||
codes.put("GE", "Georgia");
|
||||
codes.put("DE", "Germany");
|
||||
codes.put("GH", "Ghana");
|
||||
codes.put("GI", "Gibraltar");
|
||||
codes.put("GR", "Greece");
|
||||
codes.put("GL", "Greenland");
|
||||
codes.put("GD", "Grenada");
|
||||
codes.put("GP", "Guadeloupe");
|
||||
codes.put("GU", "Guam");
|
||||
codes.put("GT", "Guatemala");
|
||||
codes.put("GG", "Guernsey");
|
||||
codes.put("GN", "Guinea");
|
||||
codes.put("GW", "Guinea-Bissau");
|
||||
codes.put("GY", "Guyana");
|
||||
codes.put("HT", "Haiti");
|
||||
codes.put("HM", "Heard & McDonald Islands");
|
||||
codes.put("HN", "Honduras");
|
||||
codes.put("HK", "Hong Kong SAR China");
|
||||
codes.put("HU", "Hungary");
|
||||
codes.put("IS", "Iceland");
|
||||
codes.put("IN", "India");
|
||||
codes.put("ID", "Indonesia");
|
||||
codes.put("IR", "Iran");
|
||||
codes.put("IQ", "Iraq");
|
||||
codes.put("IE", "Ireland");
|
||||
codes.put("IM", "Isle of Man");
|
||||
codes.put("IL", "Israel");
|
||||
codes.put("IT", "Italy");
|
||||
codes.put("JM", "Jamaica");
|
||||
codes.put("JP", "Japan");
|
||||
codes.put("JE", "Jersey");
|
||||
codes.put("JO", "Jordan");
|
||||
codes.put("KZ", "Kazakhstan");
|
||||
codes.put("KE", "Kenya");
|
||||
codes.put("KI", "Kiribati");
|
||||
codes.put("KW", "Kuwait");
|
||||
codes.put("KG", "Kyrgyzstan");
|
||||
codes.put("LA", "Laos");
|
||||
codes.put("LV", "Latvia");
|
||||
codes.put("LB", "Lebanon");
|
||||
codes.put("LS", "Lesotho");
|
||||
codes.put("LR", "Liberia");
|
||||
codes.put("LY", "Libya");
|
||||
codes.put("LI", "Liechtenstein");
|
||||
codes.put("LT", "Lithuania");
|
||||
codes.put("LU", "Luxembourg");
|
||||
codes.put("MO", "Macao SAR China");
|
||||
codes.put("MG", "Madagascar");
|
||||
codes.put("MW", "Malawi");
|
||||
codes.put("MY", "Malaysia");
|
||||
codes.put("MV", "Maldives");
|
||||
codes.put("ML", "Mali");
|
||||
codes.put("MT", "Malta");
|
||||
codes.put("MH", "Marshall Islands");
|
||||
codes.put("MQ", "Martinique");
|
||||
codes.put("MR", "Mauritania");
|
||||
codes.put("MU", "Mauritius");
|
||||
codes.put("YT", "Mayotte");
|
||||
codes.put("MX", "Mexico");
|
||||
codes.put("FM", "Micronesia");
|
||||
codes.put("MD", "Moldova");
|
||||
codes.put("MC", "Monaco");
|
||||
codes.put("MN", "Mongolia");
|
||||
codes.put("ME", "Montenegro");
|
||||
codes.put("MS", "Montserrat");
|
||||
codes.put("MA", "Morocco");
|
||||
codes.put("MZ", "Mozambique");
|
||||
codes.put("MM", "Myanmar (Burma)");
|
||||
codes.put("NA", "Namibia");
|
||||
codes.put("NR", "Nauru");
|
||||
codes.put("NP", "Nepal");
|
||||
codes.put("NL", "Netherlands");
|
||||
codes.put("NC", "New Caledonia");
|
||||
codes.put("NZ", "New Zealand");
|
||||
codes.put("NI", "Nicaragua");
|
||||
codes.put("NE", "Niger");
|
||||
codes.put("NG", "Nigeria");
|
||||
codes.put("NU", "Niue");
|
||||
codes.put("NF", "Norfolk Island");
|
||||
codes.put("KP", "North Korea");
|
||||
codes.put("MK", "North Macedonia");
|
||||
codes.put("MP", "Northern Mariana Islands");
|
||||
codes.put("NO", "Norway");
|
||||
codes.put("OM", "Oman");
|
||||
codes.put("PK", "Pakistan");
|
||||
codes.put("PW", "Palau");
|
||||
codes.put("PS", "Palestinian Territories");
|
||||
codes.put("PA", "Panama");
|
||||
codes.put("PG", "Papua New Guinea");
|
||||
codes.put("PY", "Paraguay");
|
||||
codes.put("PE", "Peru");
|
||||
codes.put("PH", "Philippines");
|
||||
codes.put("PN", "Pitcairn Islands");
|
||||
codes.put("PL", "Poland");
|
||||
codes.put("PT", "Portugal");
|
||||
codes.put("PR", "Puerto Rico");
|
||||
codes.put("QA", "Qatar");
|
||||
codes.put("RE", "Réunion");
|
||||
codes.put("RO", "Romania");
|
||||
codes.put("RU", "Russia");
|
||||
codes.put("RW", "Rwanda");
|
||||
codes.put("WS", "Samoa");
|
||||
codes.put("SM", "San Marino");
|
||||
codes.put("ST", "São Tomé & Príncipe");
|
||||
codes.put("SA", "Saudi Arabia");
|
||||
codes.put("SN", "Senegal");
|
||||
codes.put("RS", "Serbia");
|
||||
codes.put("SC", "Seychelles");
|
||||
codes.put("SL", "Sierra Leone");
|
||||
codes.put("SG", "Singapore");
|
||||
codes.put("SX", "Sint Maarten");
|
||||
codes.put("SK", "Slovakia");
|
||||
codes.put("SI", "Slovenia");
|
||||
codes.put("SB", "Solomon Islands");
|
||||
codes.put("SO", "Somalia");
|
||||
codes.put("ZA", "South Africa");
|
||||
codes.put("GS", "South Georgia & South Sandwich Islands");
|
||||
codes.put("KR", "South Korea");
|
||||
codes.put("SS", "South Sudan");
|
||||
codes.put("ES", "Spain");
|
||||
codes.put("LK", "Sri Lanka");
|
||||
codes.put("BL", "St. Barthélemy");
|
||||
codes.put("SH", "St. Helena");
|
||||
codes.put("KN", "St. Kitts & Nevis");
|
||||
codes.put("LC", "St. Lucia");
|
||||
codes.put("MF", "St. Martin");
|
||||
codes.put("PM", "St. Pierre & Miquelon");
|
||||
codes.put("VC", "St. Vincent & Grenadines");
|
||||
codes.put("SD", "Sudan");
|
||||
codes.put("SR", "Suriname");
|
||||
codes.put("SJ", "Svalbard & Jan Mayen");
|
||||
codes.put("SE", "Sweden");
|
||||
codes.put("CH", "Switzerland");
|
||||
codes.put("SY", "Syria");
|
||||
codes.put("TW", "Taiwan");
|
||||
codes.put("TJ", "Tajikistan");
|
||||
codes.put("TZ", "Tanzania");
|
||||
codes.put("TH", "Thailand");
|
||||
codes.put("TL", "Timor-Leste");
|
||||
codes.put("TG", "Togo");
|
||||
codes.put("TK", "Tokelau");
|
||||
codes.put("TO", "Tonga");
|
||||
codes.put("TT", "Trinidad & Tobago");
|
||||
codes.put("TN", "Tunisia");
|
||||
codes.put("TR", "Turkey");
|
||||
codes.put("TM", "Turkmenistan");
|
||||
codes.put("TC", "Turks & Caicos Islands");
|
||||
codes.put("TV", "Tuvalu");
|
||||
codes.put("UM", "U.S. Outlying Islands");
|
||||
codes.put("VI", "U.S. Virgin Islands");
|
||||
codes.put("UG", "Uganda");
|
||||
codes.put("UA", "Ukraine");
|
||||
codes.put("AE", "United Arab Emirates");
|
||||
codes.put("GB", "United Kingdom");
|
||||
codes.put("US", "United States");
|
||||
codes.put("UY", "Uruguay");
|
||||
codes.put("UZ", "Uzbekistan");
|
||||
codes.put("VU", "Vanuatu");
|
||||
codes.put("VA", "Vatican City");
|
||||
codes.put("VE", "Venezuela");
|
||||
codes.put("VN", "Vietnam");
|
||||
codes.put("WF", "Wallis & Futuna");
|
||||
codes.put("EH", "Western Sahara");
|
||||
codes.put("YE", "Yemen");
|
||||
codes.put("ZM", "Zambia");
|
||||
codes.put("ZW", "Zimbabwe");
|
||||
return codes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -119,7 +119,8 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
public CodeValidationResult
|
||||
validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystem, theCode);
|
||||
if (expansion == null) {
|
||||
return null;
|
||||
|
@ -439,39 +440,33 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
boolean ableToHandleCode = false;
|
||||
if (codeSystem == null) {
|
||||
if (codeSystem == null || codeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
|
||||
if (theWantCode != null) {
|
||||
LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode);
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
.setCode(theWantCode)
|
||||
.setDisplay(lookup.getCodeDisplay());
|
||||
List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
|
||||
addCodes(system, codesList, nextCodeList, wantCodes);
|
||||
ableToHandleCode = true;
|
||||
if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, system)) {
|
||||
LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode);
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
.setCode(theWantCode)
|
||||
.setDisplay(lookup.getCodeDisplay());
|
||||
List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
|
||||
addCodes(system, codesList, nextCodeList, wantCodes);
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
ableToHandleCode = true;
|
||||
|
||||
}
|
||||
|
||||
if (!ableToHandleCode) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
||||
if (codeSystem != null) {
|
||||
|
||||
if (codeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
||||
if (codeSystem != null && codeSystem.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
addCodes(system, codeSystem.getConcept(), nextCodeList, wantCodes);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -231,10 +231,12 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
@Override
|
||||
public CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (theOptions.isInferSystem() || (theCodeSystem != null && next.isCodeSystemSupported(theValidationSupportContext, theCodeSystem))) {
|
||||
CodeValidationResult retVal = next.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
if (isBlank(theValueSetUrl) || next.isValueSetSupported(theValidationSupportContext, theValueSetUrl)) {
|
||||
if (theOptions.isInferSystem() || (theCodeSystem != null && next.isCodeSystemSupported(theValidationSupportContext, theCodeSystem))) {
|
||||
CodeValidationResult retVal = next.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +246,8 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
@Override
|
||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (theOptions.isInferSystem() || (theCodeSystem != null && next.isCodeSystemSupported(theValidationSupportContext, theCodeSystem))) {
|
||||
String url = CommonCodeSystemsTerminologyService.getValueSetUrl(theValueSet);
|
||||
if (isBlank(url) || next.isValueSetSupported(theValidationSupportContext, url)) {
|
||||
CodeValidationResult retVal = next.validateCodeInValueSet(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSet);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
|
|
|
@ -131,6 +131,16 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClientRetryCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkerContext setClientRetryCount(int value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateSnapshot(StructureDefinition input) throws FHIRException {
|
||||
if (input.hasSnapshot()) {
|
||||
|
|
|
@ -167,6 +167,18 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
// when(mockSupport.isValueSetSupported(any(), nullable(String.class))).thenAnswer(new Answer<Boolean>() {
|
||||
// @Override
|
||||
// public Boolean answer(InvocationOnMock theInvocation) {
|
||||
// String url = (String) theInvocation.getArguments()[1];
|
||||
// boolean retVal = myValueSets.containsKey(url);
|
||||
// return retVal;
|
||||
// }
|
||||
// });
|
||||
when(mockSupport.fetchValueSet(any())).thenAnswer(t->{
|
||||
String url = t.getArgument(0, String.class);
|
||||
return myValueSets.get(url);
|
||||
});
|
||||
when(mockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
|
||||
@Override
|
||||
public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
|
@ -212,7 +224,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.WARNING.toCode()).setMessage("Unknown code: " + system + " / " + code);
|
||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code");
|
||||
} else if (myCodeSystems.containsKey(system)) {
|
||||
CodeSystem cs = myCodeSystems.get(system);
|
||||
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
|
||||
|
@ -1035,8 +1047,8 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
|
||||
assertThat(errors.toString(), containsString("warning"));
|
||||
assertThat(errors.toString(), containsString("Unknown code: http://loinc.org / 12345"));
|
||||
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
||||
assertEquals("Unknown code for \"http://loinc.org#12345\"", errors.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1058,7 +1070,6 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
assertThat(errors.toString(), containsString("Element 'Observation.subject': minimum required = 1, but only found 0"));
|
||||
assertThat(errors.toString(), containsString("Element 'Observation.context': max allowed = 0, but found 1"));
|
||||
assertThat(errors.toString(), containsString("Element 'Observation.device': minimum required = 1, but only found 0"));
|
||||
assertThat(errors.toString(), containsString(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1150,7 +1161,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
assertThat(errors.toString(), errors.size(), greaterThan(0));
|
||||
assertEquals("Unknown code: http://acme.org / 9988877", errors.get(0).getMessage());
|
||||
assertEquals("Unknown code for \"http://acme.org#9988877\"", errors.get(0).getMessage());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1197,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(1, errors.size());
|
||||
assertEquals("Unknown code: http://loinc.org / 1234", errors.get(0).getMessage());
|
||||
assertEquals("Unknown code for \"http://loinc.org#1234\"", errors.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1038,6 +1038,8 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
options.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
|
||||
when(myValSupport.fetchResource(eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
when(myValSupport.isValueSetSupported(any(), eq("http://somevalueset"))).thenReturn(true);
|
||||
|
||||
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), any(IBaseResource.class))).thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
|
|
|
@ -206,7 +206,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.WARNING.toCode()).setMessage("Unknown code: " + system + " / " + code);
|
||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code");
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
@ -1110,8 +1110,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
|
||||
assertThat(errors.toString(), containsString("warning"));
|
||||
assertThat(errors.toString(), containsString("Unknown code: http://loinc.org / 12345"));
|
||||
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
||||
assertEquals("Unknown code for \"http://loinc.org#12345\"", errors.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1266,7 +1266,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
assertThat(errors.toString(), errors.size(), greaterThan(0));
|
||||
assertEquals("Unknown code: http://acme.org / 9988877", errors.get(0).getMessage());
|
||||
assertEquals("Unknown code for \"http://acme.org#9988877\"", errors.get(0).getMessage());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1304,7 +1304,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(1, errors.size());
|
||||
assertEquals("Unknown code: http://loinc.org / 1234", errors.get(0).getMessage());
|
||||
assertEquals("Unknown code for \"http://loinc.org#1234\"", errors.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.validation.FhirValidator;
|
|||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
|
@ -61,7 +62,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport, new InMemoryTerminologyServerValidationSupport(ourCtx));
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx));
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
|
|
@ -163,7 +163,7 @@ public class FhirInstanceValidatorR5Test {
|
|||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.WARNING).setMessage("Unknown code: " + system + " / " + code);
|
||||
return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage("Unknown code");
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
@ -754,8 +754,8 @@ public class FhirInstanceValidatorR5Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
|
||||
assertThat(errors.toString(), containsString("warning"));
|
||||
assertThat(errors.toString(), containsString("Unknown code: http://loinc.org / 12345"));
|
||||
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
||||
assertEquals("Unknown code for \"http://loinc.org#12345\"", errors.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -877,7 +877,7 @@ public class FhirInstanceValidatorR5Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
assertThat(errors.toString(), errors.size(), greaterThan(0));
|
||||
assertEquals("Unknown code: http://acme.org / 9988877", errors.get(0).getMessage());
|
||||
assertEquals("Unknown code for \"http://acme.org#9988877\"", errors.get(0).getMessage());
|
||||
|
||||
}
|
||||
|
||||
|
@ -915,7 +915,7 @@ public class FhirInstanceValidatorR5Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(1, errors.size());
|
||||
assertEquals("Unknown code: http://loinc.org / 1234", errors.get(0).getMessage());
|
||||
assertEquals("Unknown code for \"http://loinc.org#1234\"", errors.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.validation.FhirValidator;
|
|||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
|
@ -65,7 +66,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport, new InMemoryTerminologyServerValidationSupport(ourCtx));
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx));
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
|
Loading…
Reference in New Issue