Skip to content

Commit

Permalink
MAT-7941: Demote Terminology errors to warnings during test case vali…
Browse files Browse the repository at this point in the history
…dation for QI-Core 6 only by replacing the In Memory Terminology validator with our custom lenient validator, composed of the In Memory validator.

The In Memory validator is unable to expand the internal US-Core value sets (because they now point to VSAC instead of being a static list of codes) and concludes codes used in test cases are invalid.

Accept the result of the In Memory Validator and demote any Error findings to Warning.
  • Loading branch information
jkotanchik-SB committed Nov 26, 2024
1 parent d90eeed commit 118da42
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidatorModule;
import gov.cms.madie.madiefhirservice.utils.QiCoreLenientTerminologyValidator;
import gov.cms.madie.madiefhirservice.utils.ResourceUtils;
import lombok.extern.slf4j.Slf4j;
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.NpmPackageValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.common.hapi.validation.support.*;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.utils.LiquidEngine;
Expand Down Expand Up @@ -85,7 +82,7 @@ public IValidationSupport validationSupportChainQiCore600(
return new ValidationSupportChain(
npmPackageSupport,
new DefaultProfileValidationSupport(qicore6FhirContext),
new InMemoryTerminologyServerValidationSupport(qicore6FhirContext),
new QiCoreLenientTerminologyValidator(qicore6FhirContext),
new CommonCodeSystemsTerminologyService(qicore6FhirContext),
unknownCodeSystemWarningValidationSupport);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package gov.cms.madie.madiefhirservice.utils;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.*;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseResource;

import java.util.List;

class LenientTerminologyValidator implements IValidationSupport {
private final InMemoryTerminologyServerValidationSupport
inMemoryTerminologyServerValidationSupport;

public LenientTerminologyValidator(FhirContext theCtx) {
inMemoryTerminologyServerValidationSupport =
new InMemoryTerminologyServerValidationSupport(theCtx);
}

@Override
public FhirContext getFhirContext() {
return inMemoryTerminologyServerValidationSupport.getFhirContext();
}

@Nullable
@Override
public ValueSetExpansionOutcome expandValueSet(
ValidationSupportContext theValidationSupportContext,
@Nullable ValueSetExpansionOptions theExpansionOptions,
@Nonnull IBaseResource theValueSetToExpand) {
return inMemoryTerminologyServerValidationSupport.expandValueSet(
theValidationSupportContext, theExpansionOptions, theValueSetToExpand);
}

@Nullable
@Override
public ValueSetExpansionOutcome expandValueSet(
ValidationSupportContext theValidationSupportContext,
@Nullable ValueSetExpansionOptions theExpansionOptions,
@Nonnull String theValueSetUrlToExpand)
throws ResourceNotFoundException {
return inMemoryTerminologyServerValidationSupport.expandValueSet(
theValidationSupportContext, theExpansionOptions, theValueSetUrlToExpand);
}

@Nullable
@Override
public List<IBaseResource> fetchAllConformanceResources() {
return inMemoryTerminologyServerValidationSupport.fetchAllConformanceResources();
}

@Nullable
@Override
public <T extends IBaseResource> List<T> fetchAllSearchParameters() {
return inMemoryTerminologyServerValidationSupport.fetchAllSearchParameters();
}

@Nullable
@Override
public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
return inMemoryTerminologyServerValidationSupport.fetchAllStructureDefinitions();
}

@Nullable
@Override
public <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
return inMemoryTerminologyServerValidationSupport.fetchAllNonBaseStructureDefinitions();
}

@Nullable
@Override
public IBaseResource fetchCodeSystem(String theSystem) {
return inMemoryTerminologyServerValidationSupport.fetchCodeSystem(theSystem);
}

@Nullable
@Override
public <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) {
return inMemoryTerminologyServerValidationSupport.fetchResource(theClass, theUri);
}

@Nullable
@Override
public IBaseResource fetchStructureDefinition(String theUrl) {
return inMemoryTerminologyServerValidationSupport.fetchStructureDefinition(theUrl);
}

@Override
public boolean isCodeSystemSupported(
ValidationSupportContext theValidationSupportContext, String theSystem) {
return inMemoryTerminologyServerValidationSupport.isCodeSystemSupported(
theValidationSupportContext, theSystem);
}

@Override
public boolean isRemoteTerminologyServiceConfigured() {
return inMemoryTerminologyServerValidationSupport.isRemoteTerminologyServiceConfigured();
}

@Nullable
@Override
public IBaseResource fetchValueSet(String theValueSetUrl) {
return inMemoryTerminologyServerValidationSupport.fetchValueSet(theValueSetUrl);
}

@Override
public byte[] fetchBinary(String binaryKey) {
return inMemoryTerminologyServerValidationSupport.fetchBinary(binaryKey);
}

@Nullable
@Override
public CodeValidationResult validateCode(
ValidationSupportContext theValidationSupportContext,
ConceptValidationOptions theOptions,
String theCodeSystem,
String theCode,
String theDisplay,
String theValueSetUrl) {
return inMemoryTerminologyServerValidationSupport.validateCode(
theValidationSupportContext,
theOptions,
theCodeSystem,
theCode,
theDisplay,
theValueSetUrl);
}

@Nullable
@Override
public CodeValidationResult validateCodeInValueSet(
ValidationSupportContext theValidationSupportContext,
ConceptValidationOptions theOptions,
String theCodeSystem,
String theCode,
String theDisplay,
@Nonnull IBaseResource theValueSet) {
return inMemoryTerminologyServerValidationSupport.validateCodeInValueSet(
theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSet);
}

@Nullable
@Override
public LookupCodeResult lookupCode(
ValidationSupportContext theValidationSupportContext,
String theSystem,
String theCode,
String theDisplayLanguage) {
return inMemoryTerminologyServerValidationSupport.lookupCode(
theValidationSupportContext, theSystem, theCode, theDisplayLanguage);
}

@Nullable
@Override
public LookupCodeResult lookupCode(
ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
return inMemoryTerminologyServerValidationSupport.lookupCode(
theValidationSupportContext, theSystem, theCode);
}

@Nullable
@Override
public LookupCodeResult lookupCode(
ValidationSupportContext theValidationSupportContext,
@Nonnull LookupCodeRequest theLookupCodeRequest) {
return inMemoryTerminologyServerValidationSupport.lookupCode(
theValidationSupportContext, theLookupCodeRequest);
}

@Override
public boolean isValueSetSupported(
ValidationSupportContext theValidationSupportContext, String theValueSetUrl) {
return inMemoryTerminologyServerValidationSupport.isValueSetSupported(
theValidationSupportContext, theValueSetUrl);
}

@Nullable
@Override
public IBaseResource generateSnapshot(
ValidationSupportContext theValidationSupportContext,
IBaseResource theInput,
String theUrl,
String theWebUrl,
String theProfileName) {
return inMemoryTerminologyServerValidationSupport.generateSnapshot(
theValidationSupportContext, theInput, theUrl, theWebUrl, theProfileName);
}

@Override
public void invalidateCaches() {
inMemoryTerminologyServerValidationSupport.invalidateCaches();
}

@Nullable
@Override
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
return inMemoryTerminologyServerValidationSupport.translateConcept(theRequest);
}

@Override
public String getName() {
return inMemoryTerminologyServerValidationSupport.getName();
}

@Override
public boolean isEnabledValidationForCodingsLogicalAnd() {
return inMemoryTerminologyServerValidationSupport.isEnabledValidationForCodingsLogicalAnd();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package gov.cms.madie.madiefhirservice.utils;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.*;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;

import java.util.Optional;

@Slf4j
public class QiCoreLenientTerminologyValidator extends LenientTerminologyValidator {

public QiCoreLenientTerminologyValidator(FhirContext qicore6FhirContext) {
super(qicore6FhirContext);
}

@Override
public CodeValidationResult validateCodeInValueSet(
ValidationSupportContext theValidationSupportContext,
ConceptValidationOptions theOptions,
String theCodeSystemUrlAndVersion,
String theCode,
String theDisplay,
@Nonnull IBaseResource theValueSet) {

CodeValidationResult codeValidationResult =
super.validateCodeInValueSet(
theValidationSupportContext,
theOptions,
theCodeSystemUrlAndVersion,
theCode,
theDisplay,
theValueSet);

Optional.ofNullable(codeValidationResult)
.filter(
result ->
StringUtils.isNotBlank(result.getSeverityCode())
&& result.getSeverityCode().equalsIgnoreCase(IssueSeverity.ERROR.getCode()))
.ifPresent(
result -> {
log.debug("codeValidationResult:{}", result.getMessage());
result.setSeverity(IssueSeverity.WARNING);
});
return codeValidationResult;
}
}

0 comments on commit 118da42

Please sign in to comment.