From 4e173f4715cc4228e2c0084469be7e56e9c3b918 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 7 May 2021 18:29:21 +1000 Subject: [PATCH] * Validator: Load code systems from known packages on the fly * Validator: better handle invalid v3 dates * Renderer: Render OperationDefinition.InputProfile and OutputProfile * Important: Allow more valid schemas for Utilities.isAbsoluteUrl * Validator: remove notes about extensible bindings if profile extensible binding is valid --- RELEASE_NOTES.md | 7 ++++++- .../conv30_50/StructureMap30_50.java | 2 +- .../fhir/r5/context/BaseWorkerContext.java | 19 +++++++++++++++++- .../hl7/fhir/r5/context/IWorkerContext.java | 4 ++++ .../hl7/fhir/r5/elementmodel/XmlParser.java | 8 ++++++-- .../OperationDefinitionRenderer.java | 20 +++++++++++++++++++ .../org/hl7/fhir/utilities/Utilities.java | 8 +++++--- .../hl7/fhir/utilities/npm/NpmPackage.java | 2 +- .../hl7/fhir/validation/BaseValidator.java | 2 +- .../hl7/fhir/validation/ValidationEngine.java | 2 ++ .../services/StandAloneValidatorFetcher.java | 14 +++++++++++-- .../cli/services/ValidationService.java | 4 +++- .../instance/InstanceValidator.java | 3 +++ pom.xml | 2 +- 14 files changed, 83 insertions(+), 14 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b47a784c6..e129244f7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,4 +2,9 @@ * Fix compartment definitions of ListResource.source and subject for R3 and R4 * Snapshot generator: fix problem checking types on logical models * Do not flag internal references as suspicious -* XMLParser allows passing a schema location \ No newline at end of file +* XMLParser allows passing a schema location +* Validator: Load code systems from known packages on the fly +* Validator: better handle invalid v3 dates +* Renderer: Render OperationDefinition.InputProfile and OutputProfile +* Important: Allow more valid schemas for Utilities.isAbsoluteUrl +* Validator: remove notes about extensible bindings if profile extensible binding is valid diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureMap30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureMap30_50.java index 66ded6531..624c845e6 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureMap30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/StructureMap30_50.java @@ -267,7 +267,7 @@ public class StructureMap30_50 { VersionConvertor_30_50.copyElement(src, tgt); if (src.hasContext()) tgt.setContextElement(VersionConvertor_30_50.convertId(src.getContextElement())); - if (src.hasContextType() && src.getContextType() == org.hl7.fhir.dstu3.model.StructureMap.StructureMapContextType.VARIABLE) + if (src.hasContextType() && src.getContextType() != org.hl7.fhir.dstu3.model.StructureMap.StructureMapContextType.VARIABLE) throw new Error("This conversion is not supported. Consult code maintainers"); // this should never happens - no one knows what the intent was here. if (src.hasElement()) tgt.setElementElement(VersionConvertor_30_50.convertString(src.getElementElement())); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 8590bcab8..e48a5289e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -205,6 +205,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte protected TerminologyCache txCache; protected TimeTracker clock; private boolean tlogging = true; + private ICanonicalResourceLocator locator; public BaseWorkerContext() throws FileNotFoundException, IOException, FHIRException { txCache = new TerminologyCache(lock, null); @@ -498,9 +499,17 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte @Override public CodeSystem fetchCodeSystem(String system) { + CodeSystem cs; synchronized (lock) { - return codeSystems.get(system); + cs = codeSystems.get(system); } + if (cs == null && locator != null) { + locator.findResource(system); + synchronized (lock) { + cs = codeSystems.get(system); + } + } + return cs; } @Override @@ -2002,4 +2011,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } + public ICanonicalResourceLocator getLocator() { + return locator; + } + + public void setLocator(ICanonicalResourceLocator locator) { + this.locator = locator; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index c0a4ff404..5e1fada61 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -170,6 +170,10 @@ public interface IWorkerContext { } } + public interface ICanonicalResourceLocator { + void findResource(String url); // if it can be found, put it in the context + } + public interface IContextResourceLoader { /** * @return List of the resource types that shoud be loaded diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java index 4fbe39b18..b50a97fc4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java @@ -468,8 +468,12 @@ public class XmlParser extends ParserBase { private String convertForDateFormatFromExternal(String fmt, String av) throws FHIRException { if ("v3".equals(fmt)) { - DateTimeType d = DateTimeType.parseV3(av); - return d.asStringValue(); + try { + DateTimeType d = DateTimeType.parseV3(av); + return d.asStringValue(); + } catch (Exception e) { + return av; // not at all clear what to do in this case. + } } else throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATA_FORMAT_, fmt)); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java index 630b14f9b..9d693aae1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java @@ -50,6 +50,26 @@ public class OperationDefinitionRenderer extends TerminologyRenderer { x.para().tx("URL: [base]/"+c.getValue()+"/[id]/$"+opd.getCode()); } + if (opd.hasInputProfile()) { + XhtmlNode p = x.para(); + p.tx("Input parameters Profile: "); + StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, opd.getInputProfile()); + if (sd == null) { + p.pre().tx(opd.getInputProfile()); + } else { + p.ah(sd.getUserString("path")).tx(sd.present()); + } + } + if (opd.hasOutputProfile()) { + XhtmlNode p = x.para(); + p.tx("Output parameters Profile: "); + StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, opd.getOutputProfile()); + if (sd == null) { + p.pre().tx(opd.getOutputProfile()); + } else { + p.ah(sd.getUserString("path")).tx(sd.present()); + } + } x.para().tx("Parameters"); XhtmlNode tbl = x.table( "grid"); XhtmlNode tr = tbl.tr(); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index 15296debb..4a4a53ea3 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -1093,11 +1093,13 @@ public class Utilities { public static boolean isAbsoluteUrl(String ref) { - return ref != null && (ref.startsWith("http:") || ref.startsWith("https:") || ref.startsWith("urn:uuid:") || ref.startsWith("urn:oid:") || - Utilities.startsWithInList(ref, "urn:iso:", "urn:iso-iec:", "urn:iso-cie:", "urn:iso-astm:", "urn:iso-ieee:", "urn:iec:")); // rfc5141 + if (ref != null && ref.contains(":")) { + String scheme = ref.substring(0, ref.indexOf(":")); + return existsInList(scheme, "http", "https", "urn") || isToken(scheme) || Utilities.startsWithInList(ref, "urn:iso:", "urn:iso-iec:", "urn:iso-cie:", "urn:iso-astm:", "urn:iso-ieee:", "urn:iec:"); // rfc5141 + } + return false; } - public static boolean equivalent(String l, String r) { if (Utilities.noString(l) && Utilities.noString(r)) return true; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java index 4e7479f0d..310872d96 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java @@ -814,7 +814,7 @@ public class NpmPackage { // } public String getWebLocation() { - if (npm.has("url")) { + if (npm.has("url") && npm.get("url").isJsonPrimitive()) { return PackageHacker.fixPackageUrl(npm.get("url").getAsString()); } else { return JSONUtil.str(npm, "canonical"); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index e845dc6dc..9d4ffdaec 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -833,7 +833,7 @@ public class BaseValidator { String targetUrl = null; String version = ""; String resourceType = null; - if (ref.startsWith("http") || ref.startsWith("urn")) { + if (ref.startsWith("http:") || ref.startsWith("urn:") || Utilities.isAbsoluteUrl(ref)) { // We've got an absolute reference, no need to calculate if (ref.contains("/_history/")) { targetUrl = ref.substring(0, ref.indexOf("/_history/") - 1); 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 ae3bc199c..3486d1040 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 @@ -9,6 +9,7 @@ import org.hl7.fhir.convertors.txClient.TerminologyClientFactory; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.context.IWorkerContext.ICanonicalResourceLocator; import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; import org.hl7.fhir.r5.context.SimpleWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; @@ -138,6 +139,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst @Getter @Setter private PrintWriter mapLog; @Getter @Setter private boolean debug = false; @Getter @Setter private IValidatorResourceFetcher fetcher; + @Getter @Setter private ICanonicalResourceLocator locator; @Getter @Setter private boolean assumeValidRestReferences; @Getter @Setter private boolean noExtensibleBindingMessages; @Getter @Setter private boolean securityChecks; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java index 716ed4896..b2e9062b9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java @@ -4,6 +4,7 @@ import com.google.gson.JsonObject; import org.hl7.fhir.convertors.txClient.TerminologyClientFactory; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.IWorkerContext.ICanonicalResourceLocator; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.terminologies.TerminologyClient; @@ -26,7 +27,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { +public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, ICanonicalResourceLocator { List mappingsUris = new ArrayList<>(); private FilesystemPackageCacheManager pcm; @@ -63,7 +64,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { url = url.substring(0, url.lastIndexOf("|")); } - if (type.equals("uri") && isMappingUri(url)) { + if (type != null && type.equals("uri") && isMappingUri(url)) { return true; } @@ -128,6 +129,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { pidMap.put(pid+"|"+ver, null); } if (pi != null) { + context.loadFromPackage(pi, null); return pi.hasCanonical(url); } } @@ -249,4 +251,12 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { return true; } + @Override + public void findResource(String url) { + try { + resolveURL(null, null, url, null); + } catch (Exception e) { + } + } + } 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 377ea95ed..bb7e0156e 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 @@ -246,7 +246,9 @@ public class ValidationService { validator.setSecurityChecks(cliContext.isSecurityChecks()); validator.setCrumbTrails(cliContext.isCrumbTrails()); validator.setShowTimes(cliContext.isShowTimes()); - validator.setFetcher(new StandAloneValidatorFetcher(validator.getPcm(), validator.getContext(), validator)); + StandAloneValidatorFetcher fetcher = new StandAloneValidatorFetcher(validator.getPcm(), validator.getContext(), validator); + validator.setFetcher(fetcher); + validator.getContext().setLocator(fetcher); validator.getBundleValidationRules().addAll(cliContext.getBundleValidationRules()); TerminologyCache.setNoCaching(cliContext.isNoInternalCaching()); validator.prepare(); // generate any missing snapshots 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 a20839f05..36e47f7d6 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 @@ -1052,6 +1052,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat res = false; txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); } else { + if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + removeTrackedMessagesForLocation(errors, element, path); + } res = false; } } diff --git a/pom.xml b/pom.xml index 87e633f7e..c5da4e7eb 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.1.0 - 1.1.62 + 1.1.63-SNAPSHOT 5.7.1 1.7.1 3.0.0-M4