From cf859c9ed17ed10ea84c9e33e985da088cde89ca Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 14 May 2021 12:21:28 +1000 Subject: [PATCH] add new parameter for whether to include FHIRPath in invariant error messages --- .../hl7/fhir/r5/utils/IResourceValidator.java | 7 +++-- .../fhir/utilities/i18n/I18nConstants.java | 1 + .../src/main/resources/Messages.properties | 1 + .../hl7/fhir/validation/ValidationEngine.java | 25 ++++++++++++++-- .../fhir/validation/cli/model/CliContext.java | 30 ++++++++++++++++++- .../cli/services/ValidationService.java | 4 ++- .../hl7/fhir/validation/cli/utils/Params.java | 6 ++++ .../instance/InstanceValidator.java | 28 +++++++++++++---- pom.xml | 2 +- 9 files changed, 90 insertions(+), 14 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java index af33d377a..7c3852611 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java @@ -193,7 +193,10 @@ public interface IResourceValidator { public boolean isNoInvariantChecks(); public IResourceValidator setNoInvariantChecks(boolean value) ; - + + public boolean isWantInvariantInMessage(); + public IResourceValidator setWantInvariantInMessage(boolean wantInvariantInMessage); + public boolean isNoTerminologyChecks(); public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks); @@ -300,7 +303,7 @@ public interface IResourceValidator { org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List errors, JsonObject object) throws FHIRException; org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List errors, JsonObject object, String profile) throws FHIRException; - org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List errors, JsonObject object, List profile) throws FHIRException; + org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List errors, JsonObject object, List profile) throws FHIRException; } \ No newline at end of file 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 5701932ac..93e0220f2 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 @@ -189,6 +189,7 @@ public class I18nConstants { public static final String INTERNAL_ERROR___TYPE_NOT_KNOWN_ = "Internal_error___type_not_known_"; public static final String INTERNAL_INT_BAD_TYPE = "Internal_INT_Bad_Type"; public static final String INTERNAL_RECURSION_DETECTION_FIND_LOOP_PATH_RECURSION____CHECK_PATHS_ARE_VALID_FOR_PATH_ = "Internal_recursion_detection_find_loop_path_recursion____check_paths_are_valid_for_path_"; + public static final String INV_FAILED = "INV_FAILED"; public static final String INVALID_SLICING__THERE_IS_MORE_THAN_ONE_TYPE_SLICE_AT__BUT_ONE_OF_THEM__HAS_MIN__1_SO_THE_OTHER_SLICES_CANNOT_EXIST = "Invalid_slicing__there_is_more_than_one_type_slice_at__but_one_of_them__has_min__1_so_the_other_slices_cannot_exist"; public static final String LANGUAGE_XHTML_LANG_DIFFERENT1 = "Language_XHTML_Lang_Different1"; public static final String LANGUAGE_XHTML_LANG_DIFFERENT2 = "Language_XHTML_Lang_Different2"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 24971caef..adf551e39 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -652,3 +652,4 @@ BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_MODE = This is not a matching resource t BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME = This is not an OperationOutcome ({0}) BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_NO_MODE = This is not a matching resource type for the specified search (is a search mode needed?) ({0} expecting {1}) BUNDLE_SEARCH_NO_MODE = SearchSet bundles should have search modes on the entries +INV_FAILED = Rule {0} Failed diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 209e67659..d1cab1a84 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -21,6 +21,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.*; +import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.renderers.RendererFactory; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; @@ -127,6 +128,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst @Getter @Setter private Map binaries = new HashMap<>(); @Getter @Setter private boolean doNative; @Getter @Setter private boolean noInvariantChecks; + @Getter @Setter private boolean wantInvariantInMessage; @Getter @Setter private boolean hintAboutNonMustSupport; @Getter @Setter private boolean anyExtensionsAllowed = false; @Getter @Setter private String version; @@ -415,12 +417,25 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return Manager.build(getContext(), structureDefinition); } - public DomainResource generate(String source, String version) throws FHIRException, IOException, EOperationOutcome { + public Resource generate(String source, String version) throws FHIRException, IOException, EOperationOutcome { Content cnt = igLoader.loadContent(source, "validate", false); Resource res = igLoader.loadResourceByVersion(version, cnt.focus, source); RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE); - RendererFactory.factory(res, rc).render((DomainResource) res); - return (DomainResource) res; + genResource(res, rc); + return (Resource) res; + } + + public void genResource(Resource res, RenderingContext rc) throws IOException, EOperationOutcome { + if (res instanceof Bundle) { + Bundle bnd = (Bundle) res; + for (BundleEntryComponent be : bnd.getEntry()) { + if (be.hasResource()) { + genResource(be.getResource(), rc); + } + } + } else { + RendererFactory.factory(res, rc).render((DomainResource) res); + } } public void convert(String source, String output) throws FHIRException, IOException { @@ -463,7 +478,11 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst validator.setHintAboutNonMustSupport(hintAboutNonMustSupport); validator.setAnyExtensionsAllowed(anyExtensionsAllowed); validator.setNoInvariantChecks(isNoInvariantChecks()); + validator.setWantInvariantInMessage(isWantInvariantInMessage()); validator.setValidationLanguage(language); + if (language != null) { + validator.getContext().setValidationMessageLanguage(Locale.forLanguageTag(language)); + } validator.setAssumeValidRestReferences(assumeValidRestReferences); validator.setNoExtensibleWarnings(noExtensibleBindingMessages); validator.setSecurityChecks(securityChecks); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java index 6c85d25c9..bded12724 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java @@ -36,6 +36,10 @@ public class CliContext { private boolean noInternalCaching = false; // internal, for when debugging terminology validation @JsonProperty("noExtensibleBindingMessages") private boolean noExtensibleBindingMessages = false; + @JsonProperty("noInvariants") + private boolean noInvariants = false; + @JsonProperty("wantInvariantsInMessages") + private boolean wantInvariantsInMessages = false; @JsonProperty("map") private String map = null; @@ -441,6 +445,26 @@ public class CliContext { this.noExtensibleBindingMessages = noExtensibleBindingMessages; return this; } + + @JsonProperty("noInvariants") + public boolean isNoInvariants() { + return noInvariants; + } + + @JsonProperty("noInvariants") + public void setNoInvariants(boolean noInvariants) { + this.noInvariants = noInvariants; + } + + @JsonProperty("wantInvariantsInMessages") + public boolean isWantInvariantsInMessages() { + return wantInvariantsInMessages; + } + + @JsonProperty("wantInvariantsInMessages") + public void setWantInvariantsInMessages(boolean wantInvariantsInMessages) { + this.wantInvariantsInMessages = wantInvariantsInMessages; + } @JsonProperty("securityChecks") public boolean isSecurityChecks() { @@ -483,6 +507,8 @@ public class CliContext { canDoNative == that.canDoNative && noInternalCaching == that.noInternalCaching && noExtensibleBindingMessages == that.noExtensibleBindingMessages && + noInvariants == that.noInvariants && + wantInvariantsInMessages == that.wantInvariantsInMessages && Objects.equals(map, that.map) && Objects.equals(output, that.output) && Objects.equals(htmlOutput, that.htmlOutput) && @@ -507,7 +533,7 @@ public class CliContext { @Override public int hashCode() { - return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, htmlOutput, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes); + return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, noInvariants, wantInvariantsInMessages, map, output, htmlOutput, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes); } @Override @@ -522,6 +548,8 @@ public class CliContext { ", canDoNative=" + canDoNative + ", noInternalCaching=" + noInternalCaching + ", noExtensibleBindingMessages=" + noExtensibleBindingMessages + + ", noInvariants=" + noInvariants + + ", wantInvariantsInMessages=" + wantInvariantsInMessages + ", map='" + map + '\'' + ", output='" + output + '\'' + ", htmlOutput='" + htmlOutput + '\'' + diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java index bb7e0156e..ea41a7f6f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java @@ -143,7 +143,7 @@ public class ValidationService { } public void generateNarrative(CliContext cliContext, ValidationEngine validator) throws Exception { - DomainResource r = validator.generate(cliContext.getSources().get(0), cliContext.getSv()); + Resource r = validator.generate(cliContext.getSources().get(0), cliContext.getSv()); System.out.println(" ...generated narrative successfully"); if (cliContext.getOutput() != null) { validator.handleOutput(r, cliContext.getOutput(), cliContext.getSv()); @@ -243,6 +243,8 @@ public class ValidationService { validator.setSnomedExtension(cliContext.getSnomedCTCode()); validator.setAssumeValidRestReferences(cliContext.isAssumeValidRestReferences()); validator.setNoExtensibleBindingMessages(cliContext.isNoExtensibleBindingMessages()); + validator.setNoInvariantChecks(cliContext.isNoInvariants()); + validator.setWantInvariantInMessage(cliContext.isWantInvariantsInMessages()); validator.setSecurityChecks(cliContext.isSecurityChecks()); validator.setCrumbTrails(cliContext.isCrumbTrails()); validator.setShowTimes(cliContext.isShowTimes()); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java index a2be458e8..5fb3c0ee0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java @@ -52,6 +52,8 @@ public class Params { public static final String RIGHT = "-right"; public static final String NO_INTERNAL_CACHING = "-no-internal-caching"; public static final String NO_EXTENSIBLE_BINDING_WARNINGS = "-no-extensible-binding-warnings"; + public static final String NO_INVARIANTS = "-no-invariants"; + public static final String WANT_INVARIANTS_IN_MESSAGES = "-want-invariants-in-messages"; public static final String SECURITY_CHECKS = "-security-checks"; public static final String CRUMB_TRAIL = "-crumb-trails"; public static final String SHOW_TIMES = "-show-times"; @@ -156,6 +158,10 @@ public class Params { cliContext.setNoInternalCaching(true); } else if (args[i].equals(NO_EXTENSIBLE_BINDING_WARNINGS)) { cliContext.setNoExtensibleBindingMessages(true); + } else if (args[i].equals(NO_INVARIANTS)) { + cliContext.setNoInvariants(true); + } else if (args[i].equals(WANT_INVARIANTS_IN_MESSAGES)) { + cliContext.setWantInvariantsInMessages(true); } else if (args[i].equals(HINT_ABOUT_NON_MUST_SUPPORT)) { cliContext.setHintAboutNonMustSupport(true); } else if (args[i].equals(TO_VERSION)) { 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 36e47f7d6..7dc6b0e13 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 @@ -341,6 +341,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean anyExtensionsAllowed; private boolean errorForUnknownProfiles; private boolean noInvariantChecks; + private boolean wantInvariantInMessage; private boolean noTerminologyChecks; private boolean hintAboutNonMustSupport; private boolean showMessagesFromReferences; @@ -427,6 +428,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return this; } + @Override + public boolean isWantInvariantInMessage() { + return wantInvariantInMessage; + } + + @Override + public IResourceValidator setWantInvariantInMessage(boolean wantInvariantInMessage) { + this.wantInvariantInMessage = wantInvariantInMessage; + return this; + } + public IValidatorResourceFetcher getFetcher() { return this.fetcher; } @@ -5015,20 +5027,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!ok) { if (!Utilities.noString(msg)) { - msg = " (" + msg + ")"; + msg = "'" + inv.getHuman()+"' (" + msg + ")"; + } else if (wantInvariantInMessage) { + msg = "'" + inv.getHuman()+"' [" + n.toString() + "]"; + } else { + msg = context.formatMessage(I18nConstants.INV_FAILED, "'" + inv.getHuman()+"'"); } if (inv.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice") && ToolingExtensions.readBooleanExtension(inv, "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice")) { if (bpWarnings == BestPracticeWarningLevel.Hint) - hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); + hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + msg); else if (bpWarnings == BestPracticeWarningLevel.Warning) - warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); + warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg); else if (bpWarnings == BestPracticeWarningLevel.Error) - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg); } else if (inv.getSeverity() == ConstraintSeverity.ERROR) { - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg); } else if (inv.getSeverity() == ConstraintSeverity.WARNING) { - warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg)); + warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg); } } } diff --git a/pom.xml b/pom.xml index 41180b8b1..f2d0533ec 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.1.0 - 1.1.63 + 1.1.64-SNAPSHOT 5.7.1 1.7.1 3.0.0-M4