Fix handling of Coding Validation when code system is unknown
This commit is contained in:
parent
e74c4c98fa
commit
21848fc9af
|
@ -93,4 +93,9 @@ public boolean hasCoding(String system, String code) {
|
||||||
public void addCoding(String system, String code, String display) {
|
public void addCoding(String system, String code, String display) {
|
||||||
getCoding().add(new Coding(system, code, display));
|
getCoding().add(new Coding(system, code, display));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return hasCoding() ? getCoding().toString() : "["+getText()+"]";
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,6 +404,11 @@ public boolean hasCoding(String system, String code) {
|
||||||
public void addCoding(String system, String code, String display) {
|
public void addCoding(String system, String code, String display) {
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue