Fix handling of Coding Validation when code system is unknown

This commit is contained in:
Grahame Grieve 2022-10-05 21:16:57 +11:00
parent e74c4c98fa
commit 21848fc9af
5 changed files with 54 additions and 21 deletions

View File

@ -94,3 +94,8 @@ public boolean hasCoding(String system, String code) {
getCoding().add(new Coding(system, code, display)); getCoding().add(new Coding(system, code, display));
} }
@Override
public String toString() {
return hasCoding() ? getCoding().toString() : "["+getText()+"]";
}

View File

@ -365,8 +365,9 @@ public class XmlParser extends ParserBase {
} else } else
ok = ok || (attr.getLocalName().equals("schemaLocation")); // xsi:schemalocation allowed for non FHIR content ok = ok || (attr.getLocalName().equals("schemaLocation")); // xsi:schemalocation allowed for non FHIR content
ok = ok || (hasTypeAttr(element) && attr.getLocalName().equals("type") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())); // xsi:type allowed if element says so ok = ok || (hasTypeAttr(element) && attr.getLocalName().equals("type") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())); // xsi:type allowed if element says so
if (!ok) if (!ok) {
logError(line(node, false), col(node, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNDEFINED_ATTRIBUTE__ON__FOR_TYPE__PROPERTIES__, attr.getNodeName(), node.getNodeName(), element.fhirType(), properties), IssueSeverity.ERROR); logError(line(node, false), col(node, false), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNDEFINED_ATTRIBUTE__ON__FOR_TYPE__PROPERTIES__, attr.getNodeName(), node.getNodeName(), element.fhirType(), properties), IssueSeverity.ERROR);
}
} }
} }
} }

View File

@ -405,6 +405,11 @@ public boolean hasCoding(String system, String code) {
getCoding().add(new Coding(system, code, display)); getCoding().add(new Coding(system, code, display));
} }
@Override
public String toString() {
return hasCoding() ? getCoding().toString() : "["+getText()+"]";
}
// end addition // end addition
} }

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.r5.terminologies; package org.hl7.fhir.r5.terminologies;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/* /*
@ -34,10 +35,24 @@ import java.util.List;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ETooCostly; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ETooCostly;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.EOperationOutcome;
public interface ValueSetChecker { public interface ValueSetChecker {
Boolean codeInValueSet(String system, String code, List<String> warnings) throws ETooCostly, EOperationOutcome, Exception; public static class ValidationProcessInfo {
private TerminologyServiceErrorClass err;
private List<String> warnings = new ArrayList<>();
public TerminologyServiceErrorClass getErr() {
return err;
}
public void setErr(TerminologyServiceErrorClass err) {
this.err = err;
}
public List<String> getWarnings() {
return warnings;
}
}
Boolean codeInValueSet(String system, String code, ValidationProcessInfo info) throws ETooCostly, EOperationOutcome, Exception;
} }

View File

@ -61,6 +61,7 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetChecker.ValidationProcessInfo;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
@ -129,11 +130,11 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
public ValidationResult validateCode(CodeableConcept code) throws FHIRException { public ValidationResult validateCode(CodeableConcept code) throws FHIRException {
// first, we validate the codings themselves // first, we validate the codings themselves
List<String> errors = new ArrayList<String>(); List<String> errors = new ArrayList<String>();
List<String> warnings = new ArrayList<String>(); ValidationProcessInfo info = new ValidationProcessInfo();
if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) { if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
for (Coding c : code.getCoding()) { for (Coding c : code.getCoding()) {
if (!c.hasSystem()) { if (!c.hasSystem()) {
warnings.add(context.formatMessage(I18nConstants.CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE)); info.getWarnings().add(context.formatMessage(I18nConstants.CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE));
} }
CodeSystem cs = resolveCodeSystem(c.getSystem()); CodeSystem cs = resolveCodeSystem(c.getSystem());
ValidationResult res = null; ValidationResult res = null;
@ -145,7 +146,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (!res.isOk()) { if (!res.isOk()) {
errors.add(res.getMessage()); errors.add(res.getMessage());
} else if (res.getMessage() != null) { } else if (res.getMessage() != null) {
warnings.add(res.getMessage()); info.getWarnings().add(res.getMessage());
} }
} }
} }
@ -153,7 +154,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) { if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
Boolean result = false; Boolean result = false;
for (Coding c : code.getCoding()) { for (Coding c : code.getCoding()) {
Boolean ok = codeInValueSet(c.getSystem(), c.getCode(), warnings); Boolean ok = codeInValueSet(c.getSystem(), c.getCode(), info);
if (ok == null && result == false) { if (ok == null && result == false) {
result = null; result = null;
} else if (ok) { } else if (ok) {
@ -162,15 +163,15 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
} }
} }
if (result == null) { if (result == null) {
warnings.add(0, context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl())); info.getWarnings().add(0, context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl()));
} else if (!result) { } else if (!result) {
errors.add(0, context.formatMessage(I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl())); errors.add(0, context.formatMessage(I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl()));
} }
} }
if (errors.size() > 0) { if (errors.size() > 0) {
return new ValidationResult(IssueSeverity.ERROR, errors.toString()); return new ValidationResult(IssueSeverity.ERROR, errors.toString());
} else if (warnings.size() > 0) { } else if (info.getWarnings().size() > 0) {
return new ValidationResult(IssueSeverity.WARNING, warnings.toString()); return new ValidationResult(IssueSeverity.WARNING, info.getWarnings().toString());
} else { } else {
ConceptDefinitionComponent cd = new ConceptDefinitionComponent(foundCoding.getCode()); ConceptDefinitionComponent cd = new ConceptDefinitionComponent(foundCoding.getCode());
cd.setDisplay(foundCoding.getDisplay()); cd.setDisplay(foundCoding.getDisplay());
@ -251,19 +252,24 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
inInclude = checkInclude(code); inInclude = checkInclude(code);
} }
List<String> warnings = new ArrayList<>(); ValidationProcessInfo info = new ValidationProcessInfo();
// then, if we have a value set, we check it's in the value set // then, if we have a value set, we check it's in the value set
if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) { if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
if ((res==null || res.isOk())) { if ((res==null || res.isOk())) {
Boolean ok = codeInValueSet(system, code.getCode(), warnings); Boolean ok = codeInValueSet(system, code.getCode(), info);
if (ok == null || !ok) { if (ok == null || !ok) {
if (res == null) { if (res == null) {
res = new ValidationResult((IssueSeverity) null, null); res = new ValidationResult((IssueSeverity) null, null);
} }
if (!inExpansion && !inInclude) { if (info.getErr() != null) {
if (warnings != null) { res.setErrorClass(info.getErr());
res.setMessage("Not in value set "+valueset.getUrl()+" ("+warnings+")").setSeverity(IssueSeverity.ERROR); }
if (ok == null) {
res.setMessage("Unable to check whether code is in value set "+valueset.getUrl()+": "+info.getWarnings()).setSeverity(IssueSeverity.WARNING);
} else if (!inExpansion && !inInclude) {
if (!info.getWarnings().isEmpty()) {
res.setMessage("Not in value set "+valueset.getUrl()+": "+info.getWarnings()).setSeverity(IssueSeverity.ERROR);
} else { } else {
res.setMessage("Not in value set "+valueset.getUrl()).setSeverity(IssueSeverity.ERROR); res.setMessage("Not in value set "+valueset.getUrl()).setSeverity(IssueSeverity.ERROR);
} }
@ -640,7 +646,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
} }
@Override @Override
public Boolean codeInValueSet(String system, String code, List<String> warnings) throws FHIRException { public Boolean codeInValueSet(String system, String code, ValidationProcessInfo info) throws FHIRException {
if (valueset == null) { if (valueset == null) {
return false; return false;
} }
@ -651,7 +657,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
} else if (valueset.hasCompose()) { } else if (valueset.hasCompose()) {
int i = 0; int i = 0;
for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) { for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
Boolean ok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, warnings); Boolean ok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, info);
i++; i++;
if (ok == null && result == false) { if (ok == null && result == false) {
result = null; result = null;
@ -662,7 +668,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
} }
i = valueset.getCompose().getInclude().size(); i = valueset.getCompose().getInclude().size();
for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) { for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) {
Boolean nok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, warnings); Boolean nok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, info);
i++; i++;
if (nok == null && result == false) { if (nok == null && result == false) {
result = null; result = null;
@ -675,7 +681,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return result; return result;
} }
private Boolean inComponent(ConceptSetComponent vsi, int vsiIndex, String system, String code, boolean only, List<String> warnings) throws FHIRException { private Boolean inComponent(ConceptSetComponent vsi, int vsiIndex, String system, String code, boolean only, ValidationProcessInfo info) throws FHIRException {
boolean ok = true; boolean ok = true;
if (vsi.hasValueSet()) { if (vsi.hasValueSet()) {
@ -721,8 +727,9 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
vs.getCompose().addInclude(vsi); vs.getCompose().addInclude(vsi);
ValidationResult res = context.validateCode(options.noClient(), new Coding(system, code, null), vs); ValidationResult res = context.validateCode(options.noClient(), new Coding(system, code, null), vs);
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) { if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {
if (warnings != null && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) { if (info != null && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
warnings.add(context.formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system)); info.getWarnings().add(context.formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system));
info.setErr(TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED);
} }
return null; return null;
} }