Fixing ValueSet expansion not supporting canonical url. (#3501)
* 2991 - WIP, starting to implement test to reproduce the issue. * Adding capability to extract url & version from the valueSet.include.url * parsing codeSystem canonical URL to extra version if required. * WIP * Cleaning up the test. * Replacing hard code value with variable. * Removing non required test. * Modifications following first code review. * Further modifications following first code review. * Refining the test for better code coverage. * Prefixing the change log file name with the hapi-fhir issue number. Co-authored-by: Etienne Poirier <etienne.poirier@smilecdr.com>
This commit is contained in:
parent
9e0c364fb4
commit
26784ba7c8
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 3515
|
||||||
|
jira: SMILE-2991
|
||||||
|
title: "Supporting expansion of ValueSet include/exclude system URI expressed in canonical format during validation."
|
|
@ -1,12 +1,12 @@
|
||||||
package org.hl7.fhir.common.hapi.validation.support;
|
package org.hl7.fhir.common.hapi.validation.support;
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.util.FhirVersionIndependentConcept;
|
import ca.uhn.fhir.util.FhirVersionIndependentConcept;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
@ -36,9 +36,12 @@ import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.contains;
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.substringAfter;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.substringBefore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a basic in-memory terminology service, designed to expand ValueSets and validate codes
|
* This class is a basic in-memory terminology service, designed to expand ValueSets and validate codes
|
||||||
|
@ -47,6 +50,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
* external term service API)
|
* external term service API)
|
||||||
*/
|
*/
|
||||||
public class InMemoryTerminologyServerValidationSupport implements IValidationSupport {
|
public class InMemoryTerminologyServerValidationSupport implements IValidationSupport {
|
||||||
|
private static final String OUR_PIPE_CHARACTER = "|";
|
||||||
|
|
||||||
private final FhirContext myCtx;
|
private final FhirContext myCtx;
|
||||||
|
|
||||||
public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) {
|
public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) {
|
||||||
|
@ -539,13 +544,15 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <code>true</code> if at least one code was addded
|
* Returns <code>true</code> if at least one code was added
|
||||||
*/
|
*/
|
||||||
private boolean expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theInclude) throws ExpansionCouldNotBeCompletedInternallyException {
|
private boolean expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theInclude) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||||
|
|
||||||
String wantSystemUrl = null;
|
String wantSystemUrl = null;
|
||||||
String wantSystemVersion = null;
|
String wantSystemVersion = null;
|
||||||
|
|
||||||
if (theWantSystemUrlAndVersion != null) {
|
if (theWantSystemUrlAndVersion != null) {
|
||||||
int versionIndex = theWantSystemUrlAndVersion.indexOf("|");
|
int versionIndex = theWantSystemUrlAndVersion.indexOf(OUR_PIPE_CHARACTER);
|
||||||
if (versionIndex > -1) {
|
if (versionIndex > -1) {
|
||||||
wantSystemUrl = theWantSystemUrlAndVersion.substring(0, versionIndex);
|
wantSystemUrl = theWantSystemUrlAndVersion.substring(0, versionIndex);
|
||||||
wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex + 1);
|
wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex + 1);
|
||||||
|
@ -554,15 +561,20 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String includeOrExcludeConceptSystemUrl = theInclude.getSystem();
|
||||||
|
String includeOrExcludeConceptSystemVersion = theInclude.getVersion();
|
||||||
|
|
||||||
Function<String, CodeSystem> codeSystemLoader = newCodeSystemLoader(theValidationSupportContext);
|
Function<String, CodeSystem> codeSystemLoader = newCodeSystemLoader(theValidationSupportContext);
|
||||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = newValueSetLoader(theValidationSupportContext);
|
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = newValueSetLoader(theValidationSupportContext);
|
||||||
|
|
||||||
List<FhirVersionIndependentConcept> nextCodeList = new ArrayList<>();
|
List<FhirVersionIndependentConcept> nextCodeList = new ArrayList<>();
|
||||||
String includeOrExcludeConceptSystemUrl = theInclude.getSystem();
|
|
||||||
String includeOrExcludeConceptSystemVersion = theInclude.getVersion();
|
|
||||||
CodeSystem includeOrExcludeSystemResource = null;
|
CodeSystem includeOrExcludeSystemResource = null;
|
||||||
|
|
||||||
if (isNotBlank(includeOrExcludeConceptSystemUrl)) {
|
if (isNotBlank(includeOrExcludeConceptSystemUrl)) {
|
||||||
|
|
||||||
|
includeOrExcludeConceptSystemVersion = optionallyPopulateVersionFromUrl(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion);
|
||||||
|
includeOrExcludeConceptSystemUrl = substringBefore(includeOrExcludeConceptSystemUrl, OUR_PIPE_CHARACTER);
|
||||||
|
|
||||||
if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) {
|
if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -573,7 +585,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
|
|
||||||
String loadedCodeSystemUrl;
|
String loadedCodeSystemUrl;
|
||||||
if (includeOrExcludeConceptSystemVersion != null) {
|
if (includeOrExcludeConceptSystemVersion != null) {
|
||||||
loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl + "|" + includeOrExcludeConceptSystemVersion;
|
loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl + OUR_PIPE_CHARACTER + includeOrExcludeConceptSystemVersion;
|
||||||
} else {
|
} else {
|
||||||
loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl;
|
loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl;
|
||||||
}
|
}
|
||||||
|
@ -810,6 +822,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String optionallyPopulateVersionFromUrl(String theSystemUrl, String theVersion) {
|
||||||
|
if(contains(theSystemUrl, OUR_PIPE_CHARACTER) && isBlank(theVersion)){
|
||||||
|
theVersion = substringAfter(theSystemUrl, OUR_PIPE_CHARACTER);
|
||||||
|
}
|
||||||
|
return theVersion;
|
||||||
|
}
|
||||||
|
|
||||||
public enum FailureType {
|
public enum FailureType {
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ import org.hl7.fhir.r4.model.Enumerations;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -423,6 +425,40 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
||||||
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {"http://terminology.hl7.org/CodeSystem/v2-0360|2.7","http://terminology.hl7.org/CodeSystem/v2-0360"})
|
||||||
|
void testValidateCodeInValueSet_VsExpandedWithIncludes(String theCodeSystemUri) {
|
||||||
|
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||||
|
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||||
|
String codeMD = "MD";
|
||||||
|
|
||||||
|
CodeSystem cs = new CodeSystem();
|
||||||
|
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||||
|
cs.setUrl(theCodeSystemUri);
|
||||||
|
cs.addConcept()
|
||||||
|
.setCode(codeMD)
|
||||||
|
.setDisplay("Doctor of Medicine");
|
||||||
|
myPrePopulated.addCodeSystem(cs);
|
||||||
|
|
||||||
|
ValueSet theValueSet = new ValueSet();
|
||||||
|
theValueSet.setUrl("http://someValueSetURL");
|
||||||
|
theValueSet.setVersion("0360");
|
||||||
|
theValueSet.getCompose().addInclude().setSystem(theCodeSystemUri);
|
||||||
|
|
||||||
|
String theCodeToValidateCodeSystemUrl = theCodeSystemUri;
|
||||||
|
String theCodeToValidate = codeMD;
|
||||||
|
|
||||||
|
IValidationSupport.CodeValidationResult codeValidationResult = mySvc.validateCodeInValueSet(
|
||||||
|
valCtx,
|
||||||
|
options,
|
||||||
|
theCodeToValidateCodeSystemUrl,
|
||||||
|
theCodeToValidate,
|
||||||
|
null,
|
||||||
|
theValueSet);
|
||||||
|
|
||||||
|
assertTrue(codeValidationResult.isOk());
|
||||||
|
}
|
||||||
|
|
||||||
private static class PrePopulatedValidationSupportDstu2 extends PrePopulatedValidationSupport {
|
private static class PrePopulatedValidationSupportDstu2 extends PrePopulatedValidationSupport {
|
||||||
private final Map<String, IBaseResource> myDstu2ValueSets;
|
private final Map<String, IBaseResource> myDstu2ValueSets;
|
||||||
|
|
Loading…
Reference in New Issue