From cd7a8d39e7376eb6ac3eeddb0d1cd716027f45dd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 3 Mar 2021 13:30:58 +1100 Subject: [PATCH] * Improve terminology validation error messages * add additional validation on profiles around types (for logical models) --- RELEASE_NOTES.md | 5 +++ .../fhir/utilities/i18n/I18nConstants.java | 14 ++++--- .../instance/InstanceValidator.java | 38 +++++++++++-------- .../type/StructureDefinitionValidator.java | 23 +++++++++++ 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index be272a686..593dd4d45 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1 +1,6 @@ * adding session ids to validator service +* fix R5 --> R4 conversion for ConceptMap equivalence default value +* fix issue with snapshot generation for logical models (CCDA) +* Define binding method extension +* Improve terminology validation error messages +* add additional validation on profiles around types (for logical models) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index fb6690293..ed86cdee4 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -361,6 +361,8 @@ public class I18nConstants { public static final String SD_ED_BIND_UNKNOWN_VS = "SD_ED_BIND_UNKNOWN_VS"; public static final String SD_ED_BIND_NOT_VS = "SD_ED_BIND_NOT_VS"; public static final String SD_ED_BIND_NO_BINDABLE = "SD_ED_BIND_NO_BINDABLE"; + public static final String SD_VALUE_TYPE_IILEGAL = "SD_VALUE_TYPE_IILEGAL"; + public static final String SD_NO_TYPES = "SD_NO_TYPES"; public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG"; public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG"; public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND"; @@ -386,9 +388,9 @@ public class I18nConstants { public static final String TERMINOLOGY_TX_CODE_VALUESETMAX = "Terminology_TX_Code_ValueSetMax"; public static final String TERMINOLOGY_TX_CODE_VALUESET_EXT = "Terminology_TX_Code_ValueSet_Ext"; public static final String TERMINOLOGY_TX_CODING_COUNT = "Terminology_TX_Coding_Count"; - public static final String TERMINOLOGY_TX_CONFIRM_1 = "Terminology_TX_Confirm_1"; - public static final String TERMINOLOGY_TX_CONFIRM_2 = "Terminology_TX_Confirm_2"; - public static final String TERMINOLOGY_TX_CONFIRM_3 = "Terminology_TX_Confirm_3"; + public static final String TERMINOLOGY_TX_CONFIRM_1_CC = "Terminology_TX_Confirm_1_CC"; + public static final String TERMINOLOGY_TX_CONFIRM_2_CC = "Terminology_TX_Confirm_2_CC"; + public static final String TERMINOLOGY_TX_CONFIRM_3_CC = "Terminology_TX_Confirm_3_CC"; public static final String TERMINOLOGY_TX_CONFIRM_4a = "Terminology_TX_Confirm_4a"; public static final String TERMINOLOGY_TX_CONFIRM_4b = "Terminology_TX_Confirm_4b"; public static final String TERMINOLOGY_TX_CONFIRM_5 = "Terminology_TX_Confirm_5"; @@ -398,7 +400,7 @@ public class I18nConstants { public static final String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX = "Terminology_TX_Error_CodeableConcept_Max"; public static final String TERMINOLOGY_TX_ERROR_CODING1 = "Terminology_TX_Error_Coding1"; public static final String TERMINOLOGY_TX_ERROR_CODING2 = "Terminology_TX_Error_Coding2"; - public static final String TERMINOLOGY_TX_NOVALID_1 = "Terminology_TX_NoValid_1"; + public static final String TERMINOLOGY_TX_NOVALID_1_CC = "Terminology_TX_NoValid_1_CC"; public static final String TERMINOLOGY_TX_NOVALID_10 = "Terminology_TX_NoValid_10"; public static final String TERMINOLOGY_TX_NOVALID_11 = "Terminology_TX_NoValid_11"; public static final String TERMINOLOGY_TX_NOVALID_12 = "Terminology_TX_NoValid_12"; @@ -408,8 +410,8 @@ public class I18nConstants { public static final String TERMINOLOGY_TX_NOVALID_16 = "Terminology_TX_NoValid_16"; public static final String TERMINOLOGY_TX_NOVALID_17 = "Terminology_TX_NoValid_17"; public static final String TERMINOLOGY_TX_NOVALID_18 = "Terminology_TX_NoValid_18"; - public static final String TERMINOLOGY_TX_NOVALID_2 = "Terminology_TX_NoValid_2"; - public static final String TERMINOLOGY_TX_NOVALID_3 = "Terminology_TX_NoValid_3"; + public static final String TERMINOLOGY_TX_NOVALID_2_CC = "Terminology_TX_NoValid_2_CC"; + public static final String TERMINOLOGY_TX_NOVALID_3_CC = "Terminology_TX_NoValid_3_CC"; public static final String TERMINOLOGY_TX_NOVALID_4 = "Terminology_TX_NoValid_4"; public static final String TERMINOLOGY_TX_NOVALID_5 = "Terminology_TX_NoValid_5"; public static final String TERMINOLOGY_TX_NOVALID_6 = "Terminology_TX_NoValid_6"; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 0a5d6217d..95688103f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -1023,28 +1023,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); else if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } } } else { - if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.getStrength() == BindingStrength.REQUIRED) { + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } } } @@ -1137,28 +1137,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); else if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } } } else { if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } } } @@ -2220,6 +2220,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat * 2. This code doesn't actually decode, which is much easier on memory use for big payloads */ private boolean isValidBase64(String theEncoded) { + if (theEncoded == null) { + return false; + } int charCount = 0; boolean ok = true; for (int i = 0; i < theEncoded.length(); i++) { @@ -4678,8 +4681,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (ed.getMin() > 0) { if (problematicPaths.contains(ed.getPath())) hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin())); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()), Integer.toString(count)); + else { + if (count < ed.getMin()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()), Integer.toString(count)); + } + } } if (ed.hasMax() && !ed.getMax().equals("*")) { if (problematicPaths.contains(ed.getPath())) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index b40f6b82c..9873985b7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -146,6 +146,23 @@ public class StructureDefinitionValidator extends BaseValidator { // String bt = boundType(typeCodes); // hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bt == null, I18nConstants.SD_ED_SHOULD_BIND, element.getNamedChildValue("path"), bt); } + // in a snapshot, we validate that fixedValue, pattern, and defaultValue, if present, are all of the right type + if (snapshot && element.getIdBase().contains(".")) { + if (rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), !typeCodes.isEmpty(), I18nConstants.SD_NO_TYPES, element.getIdBase())) { + Element v = element.getNamedChild("defaultValue"); + if (v != null) { + rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "defaultValue", v.fhirType(), typeCodes); + } + v = element.getNamedChild("fixed"); + if (v != null) { + rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "fixed", v.fhirType(), typeCodes); + } + v = element.getNamedChild("pattern"); + if (v != null) { + rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "pattern", v.fhirType(), typeCodes); + } + } + } } private String boundType(Set typeCodes) { @@ -166,6 +183,12 @@ public class StructureDefinitionValidator extends BaseValidator { if (Utilities.existsInList(tc, "string", "uri", "CodeableConcept", "Quantity", "CodeableReference")) { return tc; } + StructureDefinition sd = context.fetchTypeDefinition(tc); + if (sd != null) { + if (sd.hasExtension(ToolingExtensions.EXT_BINDING_METHOD)) { + return tc; + } + } } return null; }