From 7fc34e509ab221f21de41046b1728018adf8ea53 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Sep 2024 22:18:01 +0800 Subject: [PATCH 01/21] alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors --- .../validation/ValueSetValidator.java | 4 +- .../org/hl7/fhir/utilities/Utilities.java | 14 +- .../hl7/fhir/utilities/npm/PackageHacker.java | 51 +++---- .../hl7/fhir/validation/ValidationEngine.java | 3 + .../fhir/validation/cli/model/CliContext.java | 22 ++- .../cli/services/ValidationService.java | 1 + .../hl7/fhir/validation/cli/utils/Params.java | 5 +- .../instance/InstanceValidator.java | 132 ++++++++++++------ .../tests/ValidationEngineTests.java | 8 +- 9 files changed, 163 insertions(+), 77 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java index 9315b55ab..dd8b9733a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java @@ -124,11 +124,9 @@ public class ValueSetValidator extends ValueSetProcessBase { } public String getMessage() { return message; - } - + } } - private ValueSet valueset; private Map inner = new HashMap<>(); private ValidationOptions options; 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 c56787430..5c117d455 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 @@ -1491,7 +1491,7 @@ public class Utilities { } public static boolean startsWithInList(String s, Collection list) { - if (s == null) { + if (s == null || list == null) { return false; } for (String l : list) { @@ -2304,4 +2304,16 @@ public class Utilities { } } + public static boolean listValueStartsWith(String s, Set list) { + if (s == null || list == null) { + return false; + } + for (String l : list) { + if (l.startsWith(s)) { + return true; + } + } + return false; + } + } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java index 2e9a45283..225bdd409 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java @@ -31,18 +31,19 @@ public class PackageHacker { public static void main(String[] args) throws FileNotFoundException, IOException { // new PackageHacker().massEdit(new File("/Users/grahamegrieve/web/hl7.org/fhir")); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.core.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.corexml.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.examples.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.expansions.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.search.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.core.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.corexml.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.examples.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.expansions.tgz"); - new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.search.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.core.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.corexml.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.examples.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.expansions.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot1/hl7.fhir.r6.search.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.core.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.corexml.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.examples.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.expansions.tgz"); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/6.0.0-ballot2/hl7.fhir.r6.search.tgz"); -// new PackageHacker().edit(args[0]); +// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/core/v311/package.tgz", "http://hl7.org/fhir/us/core/STU3.1.1"); + new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/core/v700/package.tgz", "http://hl7.org/fhir/us/core/STU7"); } // private void massEdit(File dir) throws IOException { @@ -91,7 +92,7 @@ public class PackageHacker { // } // } - private void edit(String name) throws FileNotFoundException, IOException { + private void edit(String name, String path) throws FileNotFoundException, IOException { File f = ManagedFileAccess.file(name); if (!f.exists()) throw new Error("Unable to find "+f.getAbsolutePath()); @@ -107,15 +108,15 @@ public class PackageHacker { System.out.println("Altering Package "+f.getAbsolutePath()); System.out.println(nice(pck.getNpm())); - if (change(pck.getNpm())) { + if (change(pck.getNpm(), path)) { System.out.println("Revised Package"); System.out.println("======================="); System.out.println(nice(pck.getNpm())); System.out.println("======================="); -// System.out.print("save? y/n: "); -// int r = System.in.read(); -// if (r == 'y') { + System.out.print("save? y/n: "); + int r = System.in.read(); + if (r == 'y') { f.renameTo(ManagedFileAccess.file(Utilities.changeFileExt(name, ".tgz.bak"))); FileOutputStream fso = ManagedFileAccess.outStream(f); try { @@ -123,7 +124,7 @@ public class PackageHacker { } finally { fso.close(); } -// } + } } } @@ -142,13 +143,15 @@ public class PackageHacker { return JsonParser.compose(json, true); } - private boolean change(JsonObject npm) throws FileNotFoundException, IOException { - // fixVersions(npm, ver); - if (npm.has("notForPublication")) { - npm.remove("notForPublication"); - return true; - } - return false; + private boolean change(JsonObject npm, String path) throws FileNotFoundException, IOException { + npm.remove("url"); + npm.add("url", path); + return true; +// if (npm.has("notForPublication")) { +// npm.remove("notForPublication"); +// return true; +// } +// return false; } private void fixVersionInContent(Map content) { 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 a242ca2e9..9bf88787c 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 @@ -226,6 +226,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP @Getter @Setter private boolean allowDoubleQuotesInFHIRPath; @Getter @Setter private boolean checkIPSCodes; @Getter @Setter private BestPracticeWarningLevel bestPracticeLevel; + @Getter @Setter private boolean unknownCodeSystemsCauseErrors; @Getter @Setter private Locale locale; @Getter @Setter private List igs = new ArrayList<>(); @Getter @Setter private List extensionDomains = new ArrayList<>(); @@ -289,6 +290,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP fhirPathEngine = other.fhirPathEngine; igLoader = other.igLoader; jurisdiction = other.jurisdiction; + unknownCodeSystemsCauseErrors = other.unknownCodeSystemsCauseErrors; } /** @@ -906,6 +908,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP if (policyAdvisor != null) { validator.setPolicyAdvisor(policyAdvisor); } + validator.setUnknownCodeSystemsCauseErrors(unknownCodeSystemsCauseErrors); return validator; } 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 f719f029b..79c604c6c 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 @@ -163,6 +163,9 @@ public class CliContext { @JsonProperty("bestPracticeLevel") private BestPracticeWarningLevel bestPracticeLevel = BestPracticeWarningLevel.Warning; + @JsonProperty("unknownCodeSystemsCauseErrors") + private boolean unknownCodeSystemsCauseErrors; + @JsonProperty("baseEngine") public String getBaseEngine() { return baseEngine; @@ -832,6 +835,7 @@ public class CliContext { Objects.equals(watchMode, that.watchMode) && Objects.equals(bestPracticeLevel, that.bestPracticeLevel) && Objects.equals(watchScanDelay, that.watchScanDelay) && + Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) && Objects.equals(watchSettleTime, that.watchSettleTime) ; } @@ -839,8 +843,8 @@ public class CliContext { public int hashCode() { return Objects.hash(baseEngine, doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT, - targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars, watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, - htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes); + targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars, + watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes); } @Override @@ -899,6 +903,7 @@ public class CliContext { ", bestPracticeLevel=" + bestPracticeLevel + ", watchSettleTime=" + watchSettleTime + ", watchScanDelay=" + watchScanDelay + + ", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors + '}'; } @@ -956,4 +961,17 @@ public class CliContext { return this; } + + @JsonProperty("unknownCodeSystemsCauseErrors") + public boolean isUnknownCodeSystemsCauseErrors() { + return unknownCodeSystemsCauseErrors; + } + + + @JsonProperty("unknownCodeSystemsCauseErrors") + public void setUnknownCodeSystemsCauseErrors(boolean unknownCodeSystemsCauseErrors) { + this.unknownCodeSystemsCauseErrors = unknownCodeSystemsCauseErrors; + } + + } \ No newline at end of file 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 fcdea6386..61d4b7d5f 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 @@ -581,6 +581,7 @@ public class ValidationService { } validationEngine.getBundleValidationRules().addAll(cliContext.getBundleValidationRules()); validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction())); + validationEngine.setUnknownCodeSystemsCauseErrors(cliContext.isUnknownCodeSystemsCauseErrors()); TerminologyCache.setNoCaching(cliContext.isNoInternalCaching()); validationEngine.prepare(); // generate any missing snapshots System.out.println(" go (" + timeTracker.milestone() + ")"); 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 23136344d..0983d0072 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 @@ -94,8 +94,7 @@ public class Params { public static final String DISABLE_DEFAULT_RESOURCE_FETCHER = "-disable-default-resource-fetcher"; public static final String CHECK_IPS_CODES = "-check-ips-codes"; public static final String BEST_PRACTICE = "-best-practice"; - - + public static final String UNKNOWN_CODESYSTEMS_CAUSE_ERROR = "-unknown-codesystems-cause-errors"; public static final String RUN_TESTS = "-run-tests"; @@ -320,6 +319,8 @@ public class Params { cliContext.setCrumbTrails(true); } else if (args[i].equals(FOR_PUBLICATION)) { cliContext.setForPublication(true); + } else if (args[i].equals(UNKNOWN_CODESYSTEMS_CAUSE_ERROR)) { + cliContext.setUnknownCodeSystemsCauseErrors(true); } else if (args[i].equals(VERBOSE)) { cliContext.setCrumbTrails(true); } else if (args[i].equals(ALLOW_EXAMPLE_URLS)) { 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 071efc120..dbc8eb164 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 @@ -598,6 +598,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean example ; private IDigitalSignatureServices signatureServices; private ContextUtilities cu; + private boolean unknownCodeSystemsCauseErrors; public InstanceValidator(@Nonnull IWorkerContext theContext, @Nonnull IEvaluationContext hostServices, @Nonnull XVerExtensionManager xverManager) { super(theContext, xverManager, false); @@ -1125,7 +1126,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'"); if (s == null) return true; - ok = processTxIssues(errors, s, element, path, null, "no binding on code", false, null) & ok; + ok = processTxIssues(errors, s, element, path, false, null, null) & ok; if (s.isOk()) { if (s.getMessage() != null && !s.messageIsInIssues()) { @@ -1381,7 +1382,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (cc.hasCoding()) { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, null, cc); - bh.see(processTxIssues(errors, vr, element, path, org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, null, false, null)); + bh.see(processTxIssues(errors, vr, element, path, false, null, null)); timeTracker.tx(t, "vc " + cc.toString()); } } catch (CheckCodeOnServerException e) { @@ -1465,7 +1466,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else { checked.set(true); ValidationResult vr = checkCodeOnServer(stack, valueset, cc); - bh.see(processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(strength), notFoundSeverityNoteForBinding(strength), false, vsRef)); + bh.see(processTxIssues(errors, vr, element, path, false, vsRef, strength)); if (!vr.isOk()) { bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) { @@ -1533,33 +1534,34 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return checkDisp; } - private String notFoundSeverityNoteForBinding(BindingStrength strength) { - if (strength == BindingStrength.REQUIRED) { - return "error because this is a required binding"; - } else { - return null; - } - } +// private String notFoundSeverityNoteForBinding(BindingStrength strength, Set systems) { +// if (strength == BindingStrength.REQUIRED && +// (Utilities.listValueStartsWith("http://hl7.org/fhir", systems) || Utilities.listValueStartsWith("http://terminology.hl7.org", systems))) { +// return "error because this is a required binding to an HL7 code system"; +// } else { +// return null; +// } +// } +// +// /** +// * The terminology server will report an error for an unknown code system or version, or a dependent valueset +// * +// * but we only care for validation if the binding strength is strong enough. +// * @param binding +// * @return +// */ +// private org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundSeverityForBinding(BindingStrength strength, String systems) { +// if (strength == BindingStrength.REQUIRED && +// (Utilities.listValueStartsWith("http://hl7.org/fhir", systems) || Utilities.listValueStartsWith("http://terminology.hl7.org", systems))) { +// return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR; +// } else if (strength == BindingStrength.REQUIRED || strength == BindingStrength.EXTENSIBLE) { +// return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; +// } else { +// return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION; +// } +// } - /** - * The terminology server will report an error for an unknown code system or version, or a dependent valueset - * - * but we only care for validation if the binding strength is strong enough. - * @param binding - * @return - */ - private org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundSeverityForBinding(BindingStrength strength) { - if (strength == BindingStrength.REQUIRED) { - return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR; - } else if (strength == BindingStrength.EXTENSIBLE) { - return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; - } else { - return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION; - } - } - - private boolean processTxIssues(List errors, ValidationResult vr, Element element, String path, - org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundLevel, String notFoundNote, boolean ignoreCantInfer, String vsurl) { + private boolean processTxIssues(List errors, ValidationResult vr, Element element, String path, boolean ignoreCantInfer, String vsurl, BindingStrength bs) { boolean ok = true; if (vr != null) { for (OperationOutcomeIssueComponent iss : vr.getIssues()) { @@ -1567,12 +1569,35 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat && !iss.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "this-code-not-in-vs") && !(ignoreCantInfer || iss.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "cannot-infer"))) { OperationOutcomeIssueComponent i = iss.copy(); - if (i.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found")) { - if (notFoundLevel != null && i.getSeverity().isHigherThan(notFoundLevel) || (vsurl != null && i.getDetails().getText().contains(vsurl))) { - i.setSeverity(notFoundLevel); + if (i.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found")) { + String msg = iss.getDetails().getText(); + boolean isHL7 = msg == null ? false : msg.contains("http://hl7.org/fhir") || msg.contains("http://terminology.hl7.org"); + org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundLevel = null; + String notFoundNote = null; + if (bs == null) { + notFoundLevel = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; + notFoundNote = null; // "binding=null"; + } else if (bs == BindingStrength.REQUIRED && isHL7) { + notFoundLevel = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR; + notFoundNote = "error because this is a required binding to an HL7 code system"; + } else if (bs == BindingStrength.REQUIRED && unknownCodeSystemsCauseErrors) { + notFoundLevel = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR; + notFoundNote = "error because this is a required binding"; + } else if (bs == BindingStrength.REQUIRED) { + notFoundLevel = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; + notFoundNote = null; // "binding=required"; + } else if (bs == BindingStrength.EXTENSIBLE) { + notFoundLevel = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; + notFoundNote = null; // "binding=extensible"; + } else { + notFoundLevel = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; + notFoundNote = null; // "binding="+bs.toCode(); } - if (notFoundNote != null) { - i.getDetails().setText(i.getDetails().getText()+" ("+notFoundNote+")"); + if (notFoundLevel != null && i.getSeverity().isHigherThan(notFoundLevel)) { // && (vsurl != null && i.getDetails().getText().contains(vsurl))) { + i.setSeverity(notFoundLevel); + if (notFoundNote != null) { + i.getDetails().setText(i.getDetails().getText()+" ("+notFoundNote+")"); + } } } if (baseOptions.isDisplayWarningMode() && i.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR && i.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "invalid-display")) { @@ -1592,7 +1617,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat boolean ok = true; if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem(), baseOptions.getFhirVersion())) { ValidationResult vr = checkCodeOnServer(stack, valueset, nextCoding); - ok = processTxIssues(errors, vr, element, path, null, "ex-checkBindings", false, null) && ok; + ok = processTxIssues(errors, vr, element, path, false, null, null) && ok; if (vr.getSeverity() != null && !vr.messageIsInIssues()) { if (vr.getSeverity() == IssueSeverity.INFORMATION) { @@ -1721,7 +1746,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (strength == BindingStrength.REQUIRED) { removeTrackedMessagesForLocation(errors, element, path); } - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(strength), notFoundSeverityNoteForBinding(strength), false, vsRef) && ok; + ok = processTxIssues(errors, vr, element, path, false, vsRef, strength) && ok; timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'"); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) @@ -1766,6 +1791,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return ok; } + private Set getUnknownSystems(ValidationResult vr) { + if (vr == null) { + return null; + } + if (vr.getUnknownSystems() != null && !vr.getUnknownSystems().isEmpty()) { + return vr.getUnknownSystems(); + } + if (vr.getSystem() != null) { + Set set = new HashSet(); + set.add(vr.getSystem()); + return set; + } + return null; + } + private boolean convertCDACodeToCodeableConcept(List errors, String path, Element element, StructureDefinition logical, CodeableConcept cc) { boolean ok = true; cc.setText(element.getNamedChildValue("originalText", false)); @@ -1851,7 +1891,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, valueset, cc); - ok = processTxIssues(errors, vr, element, path, null, "ex-checkMaxValueSet", false, maxVSUrl) && ok; + ok = processTxIssues(errors, vr, element, path, false, maxVSUrl, null) && ok; timeTracker.tx(t, "vc "+cc.toString()); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) @@ -1890,7 +1930,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, valueset, c); - ok = processTxIssues(errors, vr, element, path, null, "ex-checkMaxValueSet-2", false, maxVSUrl) && ok; + ok = processTxIssues(errors, vr, element, path, false, maxVSUrl, null) && ok; timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); if (!vr.isOk()) { @@ -1921,7 +1961,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, valueset, value, baseOptions); - ok = processTxIssues(errors, vr, element, path, null, "ex-checkMaxValueSet-3", false, maxVSUrl) && ok; + ok = processTxIssues(errors, vr, element, path, false, maxVSUrl, null) && ok; timeTracker.tx(t, "vc "+value); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) @@ -2045,7 +2085,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checked.set(true); vr = checkCodeOnServer(stack, valueset, c); } - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(strength), notFoundSeverityNoteForBinding(strength), strength == BindingStrength.EXTENSIBLE, vsRef) && ok; + ok = processTxIssues(errors, vr, element, path, strength == BindingStrength.EXTENSIBLE, vsRef, strength) && ok; timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); if (strength == BindingStrength.REQUIRED) { @@ -3510,7 +3550,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } vr = checkCodeOnServer(stack, vs, value, options); } - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding.getStrength()), notFoundSeverityNoteForBinding(binding.getStrength()), binding.getStrength() != BindingStrength.REQUIRED, binding.getValueSet()) && ok; + ok = processTxIssues(errors, vr, element, path, binding.getStrength() != BindingStrength.REQUIRED, binding.getValueSet(), binding.getStrength()) && ok; timeTracker.tx(t, "vc "+value+""); if (binding.getStrength() == BindingStrength.REQUIRED) { @@ -7791,4 +7831,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat this.fetcher = value; return this; } + + public boolean isUnknownCodeSystemsCauseErrors() { + return unknownCodeSystemsCauseErrors; + } + + public void setUnknownCodeSystemsCauseErrors(boolean unknownCodeSystemsCauseErrors) { + this.unknownCodeSystemsCauseErrors = unknownCodeSystemsCauseErrors; + } + + } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java index bd27a6f41..320f1dd30 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java @@ -247,9 +247,9 @@ public class ValidationEngineTests { Assertions.assertTrue(checkOutcomes("testObs102", op, "Observation.text.div null error/invalid: Wrong namespace on the XHTML ('null', should be 'http://www.w3.org/1999/xhtml')\n"+ "Observation.category null information/business-rule: Reference to experimental CodeSystem http://hl7.org/fhir/observation-category\n"+ - "Observation.code.coding[2].system null information/not-found: A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated\n"+ "Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have a performer\n"+ - "Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have an effective[x] ()")); + "Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have an effective[x] ()\n"+ + "Observation.code.coding[2].system null warning/not-found: A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated")); verifyNoTerminologyRequests(logger); } @@ -265,8 +265,8 @@ public class ValidationEngineTests { System.out.println(" .. load USCore"); OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "observation301.xml"), null); Assertions.assertTrue(checkOutcomes("test301", op, - "Observation.code.coding[3].system null information/not-found: A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated\n"+ - "Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have a performer")); + "Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have a performer\n"+ + "Observation.code.coding[3].system null warning/not-found: A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated")); verifyNoTerminologyRequests(logger); } From 51934de457b17331df7c6453d813f0a83f7b949f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:47:33 +0800 Subject: [PATCH 02/21] Fix bug processing value set includes / excludes that are just value sets (no system value) --- .../fhir/r5/context/BaseWorkerContext.java | 8 ++- .../expansion/ValueSetExpander.java | 53 ++++++++++--------- 2 files changed, 33 insertions(+), 28 deletions(-) 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 e517398be..e33cc0bfa 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 @@ -934,10 +934,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte throw new Error(formatMessage(I18nConstants.NO_VALUE_SET_IN_URL)); } for (ConceptSetComponent inc : vs.getCompose().getInclude()) { - codeSystemsUsed.add(inc.getSystem()); + if (inc.hasSystem()) { + codeSystemsUsed.add(inc.getSystem()); + } } for (ConceptSetComponent inc : vs.getCompose().getExclude()) { - codeSystemsUsed.add(inc.getSystem()); + if (inc.hasSystem()) { + codeSystemsUsed.add(inc.getSystem()); + } } CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java index 28058becb..7e1cfbbf7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java @@ -606,33 +606,35 @@ public class ValueSetExpander extends ValueSetProcessBase { excludeCodes(wc, importValueSetForExclude(wc, imp.getValue(), exp, expParams, false, vs).getExpansion()); } - CodeSystem cs = context.fetchSupplementedCodeSystem(exc.getSystem()); - if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem(), opContext.getOptions().getFhirVersion())) { - ValueSetExpansionOutcome vse = context.expandVS(exc, false, false); - ValueSet valueset = vse.getValueset(); - if (valueset == null) - throw failTSE("Error Expanding ValueSet: "+vse.getError()); - excludeCodes(wc, valueset.getExpansion()); - return; - } - - for (ConceptReferenceComponent c : exc.getConcept()) { - excludeCode(wc, exc.getSystem(), c.getCode()); - } - - if (exc.getFilter().size() > 0) { - if (cs.getContent() == CodeSystemContentMode.FRAGMENT) { - addFragmentWarning(exp, cs); + if (exc.hasSystem()) { + CodeSystem cs = context.fetchSupplementedCodeSystem(exc.getSystem()); + if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem(), opContext.getOptions().getFhirVersion())) { + ValueSetExpansionOutcome vse = context.expandVS(exc, false, false); + ValueSet valueset = vse.getValueset(); + if (valueset == null) + throw failTSE("Error Expanding ValueSet: "+vse.getError()); + excludeCodes(wc, valueset.getExpansion()); + return; } - List filters = new ArrayList<>(); - for (int i = 1; i < exc.getFilter().size(); i++) { - WorkingContext wc1 = new WorkingContext(); - filters.add(wc1); - processFilter(exc, exp, expParams, null, cs, false, exc.getFilter().get(i), wc1, null, true); + + for (ConceptReferenceComponent c : exc.getConcept()) { + excludeCode(wc, exc.getSystem(), c.getCode()); + } + + if (exc.getFilter().size() > 0) { + if (cs.getContent() == CodeSystemContentMode.FRAGMENT) { + addFragmentWarning(exp, cs); + } + List filters = new ArrayList<>(); + for (int i = 1; i < exc.getFilter().size(); i++) { + WorkingContext wc1 = new WorkingContext(); + filters.add(wc1); + processFilter(exc, exp, expParams, null, cs, false, exc.getFilter().get(i), wc1, null, true); + } + ConceptSetFilterComponent fc = exc.getFilter().get(0); + WorkingContext wc1 = dwc; + processFilter(exc, exp, expParams, null, cs, false, fc, wc1, filters, true); } - ConceptSetFilterComponent fc = exc.getFilter().get(0); - WorkingContext wc1 = dwc; - processFilter(exc, exp, expParams, null, cs, false, fc, wc1, filters, true); } } @@ -728,7 +730,6 @@ public class ValueSetExpander extends ValueSetProcessBase { expParams = makeDefaultExpansion(); altCodeParams.seeParameters(expParams); altCodeParams.seeValueSet(source); - source.checkNoModifiers("ValueSet", "expanding"); focus = source.copy(); focus.setIdBase(null); From 810f4b8e3186a392f7b78a29738c767e3c4125c0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:48:01 +0800 Subject: [PATCH 03/21] fix value set rendering creating wrong references --- .../fhir/r5/renderers/ValueSetRenderer.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index eb303d742..4a78a67c8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -754,16 +754,7 @@ public class ValueSetRenderer extends TerminologyRenderer { if (ref == null) { ref = (String) cs.getWebPath(); } - if (ref == null && cs.hasUserData("webroot")) { - ref = (String) cs.getUserData("webroot"); - } - if (ref == null) { - return "?ngen-14?.html"; - } - if (!ref.contains(".html")) { - ref = ref + ".html"; - } - return ref.replace("\\", "/"); + return ref == null ? null : ref.replace("\\", "/"); } private void scanForDesignations(ValueSetExpansionContainsComponent c, List langs, Map designations) { @@ -922,14 +913,18 @@ public class ValueSetRenderer extends TerminologyRenderer { td.addText(code); } else { String href = context.fixReference(getCsRef(e)); - if (href.contains("#")) - href = href + "-"+Utilities.nmtokenize(code); - else - href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code); - if (isAbstract) - td.ah(context.prefixLocalHref(href)).setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).i().addText(code); - else - td.ah(context.prefixLocalHref(href)).addText(code); + if (href == null) { + td.code().tx(code); + } else { + if (href.contains("#")) + href = href + "-"+Utilities.nmtokenize(code); + else + href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code); + if (isAbstract) + td.ah(context.prefixLocalHref(href)).setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).i().addText(code); + else + td.ah(context.prefixLocalHref(href)).addText(code); + } } } @@ -1247,11 +1242,15 @@ public class ValueSetRenderer extends TerminologyRenderer { wli.tx(f.getProperty()+" "+describe(f.getOp())+" "); if (e != null && codeExistsInValueSet(e, f.getValue())) { String href = getContext().fixReference(getCsRef(e)); - if (href.contains("#")) - href = href + "-"+Utilities.nmtokenize(f.getValue()); - else - href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue()); - wli.ah(context.prefixLocalHref(href)).addText(f.getValue()); + if (href == null) { + wli.code().tx(f.getValue()); + } else { + if (href.contains("#")) + href = href + "-"+Utilities.nmtokenize(f.getValue()); + else + href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue()); + wli.ah(context.prefixLocalHref(href)).addText(f.getValue()); + } } else if (inc.hasSystem()) { wli.addText(f.getValue()); ValidationResult vr = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions(), inc.getSystem(), inc.getVersion(), f.getValue(), null); From cfdd1f93803ecffdb7c4f7aab619bc927f1db3a3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:48:46 +0800 Subject: [PATCH 04/21] Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository --- .../r5/renderers/utils/RenderingContext.java | 3 + .../org/hl7/fhir/r5/utils/sql/Runner.java | 65 ++++++--- .../hl7/fhir/r5/utils/sql/StorageJson.java | 17 +-- .../org/hl7/fhir/r5/utils/sql/Validator.java | 131 ++++++++++++------ .../org/hl7/fhir/r5/sql/SQLOnFhirTests.java | 29 ++-- 5 files changed, 157 insertions(+), 88 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java index 572523a1e..687d7c672 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java @@ -554,6 +554,9 @@ public class RenderingContext extends RenderingI18nContext { } public String fixReference(String ref) { + if (ref == null) { + return null; + } if (!Utilities.isAbsoluteUrl(ref)) { return (localPrefix == null ? "" : localPrefix)+ref; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java index 432fff492..aa5d8b4e1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java @@ -103,7 +103,7 @@ public class Runner implements IEvaluationContext { for (JsonObject w : vd.getJsonObjects("where")) { String expr = w.asString("path"); ExpressionNode node = fpe.parse(expr); - boolean pass = fpe.evaluateToBoolean(null, b, b, b, node); + boolean pass = fpe.evaluateToBoolean(vd, b, b, b, node); if (!pass) { ok = false; break; @@ -114,7 +114,7 @@ public class Runner implements IEvaluationContext { rows.add(new ArrayList()); for (JsonObject select : vd.getJsonObjects("select")) { - executeSelect(select, b, rows); + executeSelect(vd, select, b, rows); } for (List row : rows) { storage.addRow(store, row); @@ -124,14 +124,14 @@ public class Runner implements IEvaluationContext { storage.finish(store); } - private void executeSelect(JsonObject select, Base b, List> rows) { + private void executeSelect(JsonObject vd, JsonObject select, Base b, List> rows) { List focus = new ArrayList<>(); if (select.has("forEach")) { - focus.addAll(executeForEach(select, b)); + focus.addAll(executeForEach(vd, select, b)); } else if (select.has("forEachOrNull")) { - focus.addAll(executeForEachOrNull(select, b)); + focus.addAll(executeForEachOrNull(vd, select, b)); if (focus.isEmpty()) { List columns = (List) select.getUserData("columns"); for (List row : rows) { @@ -159,20 +159,20 @@ public class Runner implements IEvaluationContext { List> rowsToAdd = cloneRows(tempRows); for (JsonObject column : select.getJsonObjects("column")) { - executeColumn(column, f, rowsToAdd); + executeColumn(vd, column, f, rowsToAdd); } for (JsonObject sub : select.getJsonObjects("select")) { - executeSelect(sub, f, rowsToAdd); + executeSelect(vd, sub, f, rowsToAdd); } - executeUnionAll(select.getJsonObjects("unionAll"), f, rowsToAdd); + executeUnionAll(vd, select.getJsonObjects("unionAll"), f, rowsToAdd); rows.addAll(rowsToAdd); } } - private void executeUnionAll(List unionList, Base b, List> rows) { + private void executeUnionAll(JsonObject vd, List unionList, Base b, List> rows) { if (unionList.isEmpty()) { return; } @@ -183,7 +183,7 @@ public class Runner implements IEvaluationContext { for (JsonObject union : unionList) { List> tempRows = new ArrayList<>(); tempRows.addAll(sourceRows); - executeSelect(union, b, tempRows); + executeSelect(vd, union, b, tempRows); rows.addAll(tempRows); } } @@ -204,25 +204,25 @@ public class Runner implements IEvaluationContext { return list; } - private List executeForEach(JsonObject focus, Base b) { + private List executeForEach(JsonObject vd, JsonObject focus, Base b) { ExpressionNode n = (ExpressionNode) focus.getUserData("forEach"); List result = new ArrayList<>(); - result.addAll(fpe.evaluate(b, n)); + result.addAll(fpe.evaluate(vd, b, n)); return result; } - private List executeForEachOrNull(JsonObject focus, Base b) { + private List executeForEachOrNull(JsonObject vd, JsonObject focus, Base b) { ExpressionNode n = (ExpressionNode) focus.getUserData("forEachOrNull"); List result = new ArrayList<>(); - result.addAll(fpe.evaluate(b, n)); + result.addAll(fpe.evaluate(vd, b, n)); return result; } - private void executeColumn(JsonObject column, Base b, List> rows) { + private void executeColumn(JsonObject vd, JsonObject column, Base b, List> rows) { ExpressionNode n = (ExpressionNode) column.getUserData("path"); List bl2 = new ArrayList<>(); if (b != null) { - bl2.addAll(fpe.evaluate(b, n)); + bl2.addAll(fpe.evaluate(vd, b, n)); } Column col = (Column) column.getUserData("column"); if (col == null) { @@ -344,14 +344,43 @@ public class Runner implements IEvaluationContext { @Override public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { - throw new Error("Not implemented yet: resolveConstant"); + List list = new ArrayList(); + if (explicitConstant) { + JsonObject vd = (JsonObject) appContext; + JsonObject constant = findConstant(vd, name); + if (constant != null) { + Base b = (Base) constant.getUserData("value"); + if (b != null) { + list.add(b); + } + } + } + return list; } @Override public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { - throw new Error("Not implemented yet: resolveConstantType"); + if (explicitConstant) { + JsonObject vd = (JsonObject) appContext; + JsonObject constant = findConstant(vd, name.substring(1)); + if (constant != null) { + Base b = (Base) constant.getUserData("value"); + if (b != null) { + return new TypeDetails(CollectionStatus.SINGLETON, b.fhirType()); + } + } + } + return null; } + private JsonObject findConstant(JsonObject vd, String name) { + for (JsonObject o : vd.getJsonObjects("constant")) { + if (name.equals(o.asString("name"))) { + return o; + } + } + return null; + } @Override public boolean log(String argument, List focus) { throw new Error("Not implemented yet: log"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java index d0e3aaeed..d76404c03 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java @@ -2,6 +2,7 @@ package org.hl7.fhir.r5.utils.sql; import java.util.List; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.utilities.json.model.JsonArray; import org.hl7.fhir.utilities.json.model.JsonBoolean; @@ -33,16 +34,16 @@ public class StorageJson implements Storage { JsonObject row = new JsonObject(); rows.add(row); for (Cell cell : cells) { - if (cell.getValues().size() == 0) { - row.add(cell.getColumn().getName(), new JsonNull()); - } else if (cell.getValues().size() == 1) { - row.add(cell.getColumn().getName(), makeJsonNode(cell.getValues().get(0))); - } else { + if (cell.getColumn().isColl() || cell.getValues().size() > 1) { JsonArray arr = new JsonArray(); - row.add(cell.getColumn().getName(), arr); + row.add(cell.getColumn().getName(), arr); for (Value value : cell.getValues()) { arr.add(makeJsonNode(value)); - } + } + } else if (cell.getValues().size() == 0) { + row.add(cell.getColumn().getName(), new JsonNull()); + } else { + row.add(cell.getColumn().getName(), makeJsonNode(cell.getValues().get(0))); } } } @@ -87,7 +88,7 @@ public class StorageJson implements Storage { @Override public String getKeyForSourceResource(Base res) { - return res.getIdBase(); + return res.fhirType()+"/"+res.getIdBase(); } @Override diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java index 669fa32de..00c862b5d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java @@ -11,6 +11,27 @@ import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.fhirpath.ExpressionNode; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; import org.hl7.fhir.r5.fhirpath.TypeDetails; +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.Base64BinaryType; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.DecimalType; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.InstantType; +import org.hl7.fhir.r5.model.Integer64Type; +import org.hl7.fhir.r5.model.IntegerType; +import org.hl7.fhir.r5.model.OidType; +import org.hl7.fhir.r5.model.PositiveIntType; +import org.hl7.fhir.r5.model.PrimitiveType; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.TimeType; +import org.hl7.fhir.r5.model.UnsignedIntType; +import org.hl7.fhir.r5.model.UriType; +import org.hl7.fhir.r5.model.UrlType; +import org.hl7.fhir.r5.model.UuidType; import org.hl7.fhir.r5.fhirpath.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IssueMessage; import org.hl7.fhir.utilities.Utilities; @@ -99,7 +120,7 @@ public class Validator { i = 0; if (checkAllObjects(path, viewDefinition, "where")) { for (JsonObject where : viewDefinition.getJsonObjects("where")) { - checkWhere(path+".where["+i+"]", where); + checkWhere(viewDefinition, path+".where["+i+"]", where); i++; } } @@ -108,7 +129,7 @@ public class Validator { i = 0; if (checkAllObjects(path, viewDefinition, "select")) { for (JsonObject select : viewDefinition.getJsonObjects("select")) { - columns.addAll(checkSelect(path+".select["+i+"]", select, t)); + columns.addAll(checkSelect(viewDefinition, path+".select["+i+"]", select, t)); i++; } if (i == 0) { @@ -119,15 +140,15 @@ public class Validator { } } - private List checkSelect(String path, JsonObject select, TypeDetails t) { + private List checkSelect(JsonObject vd, String path, JsonObject select, TypeDetails t) { List columns = new ArrayList<>(); select.setUserData("columns", columns); checkProperties(select, path, "column", "select", "forEach", "forEachOrNull", "unionAll"); if (select.has("forEach")) { - t = checkForEach(path, select, select.get("forEach"), t); + t = checkForEach(vd, path, select, select.get("forEach"), t); } else if (select.has("forEachOrNull")) { - t = checkForEachOrNull(path, select, select.get("forEachOrNull"), t); + t = checkForEachOrNull(vd, path, select, select.get("forEachOrNull"), t); } if (t != null) { @@ -142,7 +163,7 @@ public class Validator { if (!(e instanceof JsonObject)) { error(path+".column["+i+"]", a, "column["+i+"] is a "+e.type().toName()+" not an object", IssueType.INVALID); } else { - columns.add(checkColumn(path+".column["+i+"]", (JsonObject) e, t)); + columns.add(checkColumn(vd, path+".column["+i+"]", (JsonObject) e, t)); } } } @@ -158,14 +179,14 @@ public class Validator { if (!(e instanceof JsonObject)) { error(path+".select["+i+"]", e, "select["+i+"] is not an object", IssueType.INVALID); } else { - columns.addAll(checkSelect(path+".select["+i+"]", (JsonObject) e, t)); + columns.addAll(checkSelect(vd, path+".select["+i+"]", (JsonObject) e, t)); } } } } if (select.has("unionAll")) { - columns.addAll(checkUnion(path, select, select.get("unionAll"), t)); + columns.addAll(checkUnion(vd, path, select, select.get("unionAll"), t)); } if (columns.isEmpty()) { error(path, select, "The select has no columns or selects", IssueType.REQUIRED); @@ -191,7 +212,7 @@ public class Validator { } } - private List checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) { + private List checkUnion(JsonObject vd, String path, JsonObject focus, JsonElement expression, TypeDetails t) { JsonElement a = focus.get("unionAll"); if (!(a instanceof JsonArray)) { error(path+".unionAll", a, "union is not an array", IssueType.INVALID); @@ -203,7 +224,7 @@ public class Validator { if (!(e instanceof JsonObject)) { error(path+".unionAll["+i+"]", e, "unionAll["+i+"] is not an object", IssueType.INVALID); } else { - unionColumns.add(checkSelect(path+".unionAll["+i+"]", (JsonObject) e, t)); + unionColumns.add(checkSelect(vd, path+".unionAll["+i+"]", (JsonObject) e, t)); } i++; } @@ -242,7 +263,7 @@ public class Validator { } } - private Column checkColumn(String path, JsonObject column, TypeDetails t) { + private Column checkColumn(JsonObject vd, String path, JsonObject column, TypeDetails t) { checkProperties(column, path, "path", "name", "description", "collection", "type", "tag"); if (!column.has("path")) { @@ -260,7 +281,7 @@ public class Validator { try { node = fpe.parse(expr); column.setUserData("path", node); - td = fpe.checkOnTypes(null, resourceName, t, node, warnings); + td = fpe.checkOnTypes(vd, resourceName, t, node, warnings); } catch (Exception e) { error(path, expression, e.getMessage(), IssueType.INVALID); } @@ -296,25 +317,31 @@ public class Validator { // ok, name is sorted! if (columnName != null) { column.setUserData("name", columnName); - boolean isColl = (td.getCollectionStatus() != CollectionStatus.SINGLETON); + boolean isColl = false; if (column.has("collection")) { JsonElement collectionJ = column.get("collection"); if (!(collectionJ instanceof JsonBoolean)) { error(path+".collection", collectionJ, "collection is not a boolean", IssueType.INVALID); } else { boolean collection = collectionJ.asJsonBoolean().asBoolean(); - if (!collection && isColl) { - isColl = false; - warning(path, column, "collection is false, but the path statement(s) might return multiple values for the column '"+columnName+"' some inputs"); + if (collection) { + isColl = true; } } } if (isColl) { + if (td.getCollectionStatus() == CollectionStatus.SINGLETON) { + hint(path, column, "collection is true, but the path statement(s) can only return single values for the column '"+columnName+"'"); + } + } else { if (arrays == null) { warning(path, expression, "The column '"+columnName+"' appears to be a collection based on it's path. Collections are not supported in all execution contexts"); } else if (!arrays) { warning(path, expression, "The column '"+columnName+"' appears to be a collection based on it's path, but this is not allowed in the current execution context"); } + if (td.getCollectionStatus() != CollectionStatus.SINGLETON) { + warning(path, column, "collection is not true, but the path statement(s) might return multiple values for the column '"+columnName+"' for some inputs"); + } } Set types = new HashSet<>(); if (node.isNullSet()) { @@ -330,7 +357,7 @@ public class Validator { if (typeJ instanceof JsonString) { String type = typeJ.asString(); if (!td.hasType(type)) { - error(path+".type", typeJ, "The path expression does not return a value of the type '"+type, IssueType.VALUE); + error(path+".type", typeJ, "The path expression does not return a value of the type '"+type+"' - found "+td.describe(), IssueType.VALUE); } else { types.clear(); types.add(simpleType(type)); @@ -377,6 +404,8 @@ public class Validator { case "integer": return ColumnKind.Integer; case "decimal": return ColumnKind.Decimal; case "string": return ColumnKind.String; + case "id": return ColumnKind.String; + case "code": return ColumnKind.String; case "base64Binary": return ColumnKind.Binary; case "time": return ColumnKind.Time; default: return ColumnKind.Complex; @@ -384,7 +413,7 @@ public class Validator { } private boolean isSimpleType(String type) { - return Utilities.existsInList(type, "dateTime", "boolean", "integer", "decimal", "string", "base64Binary"); + return Utilities.existsInList(type, "dateTime", "boolean", "integer", "decimal", "string", "base64Binary", "id", "code", "date", "time"); } private String simpleType(String type) { @@ -413,7 +442,7 @@ public class Validator { return type; } - private TypeDetails checkForEach(String path, JsonObject focus, JsonElement expression, TypeDetails t) { + private TypeDetails checkForEach(JsonObject vd, String path, JsonObject focus, JsonElement expression, TypeDetails t) { if (!(expression instanceof JsonString)) { error(path+".forEach", expression, "forEach is not a string", IssueType.INVALID); return null; @@ -425,7 +454,7 @@ public class Validator { try { ExpressionNode n = fpe.parse(expr); focus.setUserData("forEach", n); - td = fpe.checkOnTypes(null, resourceName, t, n, warnings); + td = fpe.checkOnTypes(vd, resourceName, t, n, warnings); } catch (Exception e) { error(path, expression, e.getMessage(), IssueType.INVALID); } @@ -438,7 +467,7 @@ public class Validator { } } - private TypeDetails checkForEachOrNull(String path, JsonObject focus, JsonElement expression, TypeDetails t) { + private TypeDetails checkForEachOrNull(JsonObject vd, String path, JsonObject focus, JsonElement expression, TypeDetails t) { if (!(expression instanceof JsonString)) { error(path+".forEachOrNull", expression, "forEachOrNull is not a string", IssueType.INVALID); return null; @@ -450,7 +479,7 @@ public class Validator { try { ExpressionNode n = fpe.parse(expr); focus.setUserData("forEachOrNull", n); - td = fpe.checkOnTypes(null, resourceName, t, n, warnings); + td = fpe.checkOnTypes(vd, resourceName, t, n, warnings); } catch (Exception e) { error(path, expression, e.getMessage(), IssueType.INVALID); } @@ -477,69 +506,79 @@ public class Validator { } } if (constant.has("valueBase64Binary")) { - checkIsString(path, constant, "valueBase64Binary"); + checkIsString(path, constant, "valueBase64Binary", new Base64BinaryType()); } else if (constant.has("valueBoolean")) { - checkIsBoolean(path, constant, "valueBoolean"); + checkIsBoolean(path, constant, "valueBoolean", new BooleanType()); } else if (constant.has("valueCanonical")) { - checkIsString(path, constant, "valueCanonical"); + checkIsString(path, constant, "valueCanonical", new CanonicalType()); } else if (constant.has("valueCode")) { - checkIsString(path, constant, "valueCode"); + checkIsString(path, constant, "valueCode", new CodeType()); } else if (constant.has("valueDate")) { - checkIsString(path, constant, "valueDate"); + checkIsString(path, constant, "valueDate", new DateType()); } else if (constant.has("valueDateTime")) { - checkIsString(path, constant, "valueDateTime"); + checkIsString(path, constant, "valueDateTime", new DateTimeType()); } else if (constant.has("valueDecimal")) { - checkIsNumber(path, constant, "valueDecimal"); + checkIsNumber(path, constant, "valueDecimal", new DecimalType()); } else if (constant.has("valueId")) { - checkIsString(path, constant, "valueId"); + checkIsString(path, constant, "valueId", new IdType()); } else if (constant.has("valueInstant")) { - checkIsString(path, constant, "valueInstant"); + checkIsString(path, constant, "valueInstant", new InstantType()); } else if (constant.has("valueInteger")) { - checkIsNumber(path, constant, "valueInteger"); + checkIsNumber(path, constant, "valueInteger", new IntegerType()); } else if (constant.has("valueInteger64")) { - checkIsNumber(path, constant, "valueInteger64"); + checkIsNumber(path, constant, "valueInteger64", new Integer64Type()); } else if (constant.has("valueOid")) { - checkIsString(path, constant, "valueOid"); + checkIsString(path, constant, "valueOid", new OidType()); } else if (constant.has("valueString")) { - checkIsString(path, constant, "valueString"); + checkIsString(path, constant, "valueString", new StringType()); } else if (constant.has("valuePositiveInt")) { - checkIsNumber(path, constant, "valuePositiveInt"); + checkIsNumber(path, constant, "valuePositiveInt", new PositiveIntType()); } else if (constant.has("valueTime")) { - checkIsString(path, constant, "valueTime"); + checkIsString(path, constant, "valueTime", new TimeType()); } else if (constant.has("valueUnsignedInt")) { - checkIsNumber(path, constant, "valueUnsignedInt"); + checkIsNumber(path, constant, "valueUnsignedInt", new UnsignedIntType()); } else if (constant.has("valueUri")) { - checkIsString(path, constant, "valueUri"); + checkIsString(path, constant, "valueUri", new UriType()); } else if (constant.has("valueUrl")) { - checkIsString(path, constant, "valueUrl"); + checkIsString(path, constant, "valueUrl", new UrlType()); } else if (constant.has("valueUuid")) { - checkIsString(path, constant, "valueUuid"); + checkIsString(path, constant, "valueUuid", new UuidType()); } else { error(path, constant, "No value found", IssueType.REQUIRED); } } - private void checkIsString(String path, JsonObject constant, String name) { + private void checkIsString(String path, JsonObject constant, String name, PrimitiveType value) { JsonElement j = constant.get(name); if (!(j instanceof JsonString)) { error(path+"."+name, j, name+" must be a string", IssueType.INVALID); + } else { + value.setValueAsString(j.asString()); + constant.setUserData("value", value); } } - private void checkIsBoolean(String path, JsonObject constant, String name) { + private void checkIsBoolean(String path, JsonObject constant, String name, PrimitiveType value) { JsonElement j = constant.get(name); if (!(j instanceof JsonBoolean)) { error(path+"."+name, j, name+" must be a boolean", IssueType.INVALID); + } else { + value.setValueAsString(j.asString()); + constant.setUserData("value", value); } } - private void checkIsNumber(String path, JsonObject constant, String name) { + private void checkIsNumber(String path, JsonObject constant, String name, PrimitiveType value) { JsonElement j = constant.get(name); if (!(j instanceof JsonNumber)) { error(path+"."+name, j, name+" must be a number", IssueType.INVALID); + } else { + value.setValueAsString(j.asString()); + constant.setUserData("value", value); } } - private void checkWhere(String path, JsonObject where) { + + private void checkWhere(JsonObject vd, String path, JsonObject where) { checkProperties(where, path, "path", "description"); String expr = where.asString("path"); @@ -553,7 +592,7 @@ public class Validator { try { ExpressionNode n = fpe.parse(expr); where.setUserData("path", n); - td = fpe.checkOnTypes(null, resourceName, types, n, warnings); + td = fpe.checkOnTypes(vd, resourceName, types, n, warnings); } catch (Exception e) { error(path, where.get("path"), e.getMessage(), IssueType.INVALID); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java index 9dba811a8..d650980fb 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java @@ -83,23 +83,21 @@ public class SQLOnFhirTests { this.resources = resources; this.testCase = testCase; } - } public static Stream data() throws ParserConfigurationException, SAXException, IOException { List objects = new ArrayList<>(); - File dir = ManagedFileAccess.file("/Users/grahamegrieve/work/sql-on-fhir-v2/tests/content"); - for (File f : dir.listFiles()) { - if (f.getName().endsWith(".json")) { - JsonObject json = JsonParser.parseObject(f); - String name1 = f.getName().replace(".json", ""); - List resources = json.getJsonObjects("resources"); - int i = 0; - for (JsonObject test : json.getJsonObjects("tests")) { - String name2 = test.asString("title"); - objects.add(Arguments.of(name1+":"+name2, new TestDetails(name1+":"+name2, "$.tests["+i+"]", resources, test))); - i++; - } + JsonArray testFiles = (JsonArray) JsonParser.parse(TestingUtilities.loadTestResourceStream("sql-on-fhir", "manifest.json")); + + for (String s : testFiles.asStrings()) { + JsonObject json = JsonParser.parseObject(TestingUtilities.loadTestResourceStream("sql-on-fhir", s)); + String name1 = s.replace(".json", ""); + List resources = json.getJsonObjects("resources"); + int i = 0; + for (JsonObject test : json.getJsonObjects("tests")) { + String name2 = test.asString("title"); + objects.add(Arguments.of(name1+":"+name2, new TestDetails(name1+":"+name2, "$.tests["+i+"]", resources, test))); + i++; } } return objects.stream(); @@ -110,7 +108,6 @@ public class SQLOnFhirTests { @SuppressWarnings("deprecation") @ParameterizedTest(name = "{index}: file {0}") @MethodSource("data") - @Disabled public void test(String name, TestDetails test) throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException, UcumException { this.details = test; Runner runner = new Runner(); @@ -137,8 +134,8 @@ public class SQLOnFhirTests { rows.add("rows", results); JsonObject exp = new JsonObject(); exp.add("rows", test.testCase.getJsonArray("expect")); - sortResults(exp); - sortResults(rows); +// sortResults(exp); +// sortResults(rows); String expS = JsonParser.compose(exp, true); String rowS = JsonParser.compose(rows, true); String c = CompareUtilities.checkJsonSrcIsSame(name, expS, rowS, null); From ef69d12111c68f069c5df4bc8cf3082f4a7560f7 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:49:07 +0800 Subject: [PATCH 05/21] Fix expression for con-3 properly (fix validation problem on some condition resources) --- .../fhir/validation/instance/utils/FHIRPathExpressionFixer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java index c4437511c..55376a293 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java @@ -61,7 +61,7 @@ public class FHIRPathExpressionFixer { } // con-3 in R4 if (expr.equals("clinicalStatus.exists() or verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() or category.select($this='problem-list-item').empty()")) { - return "clinicalStatus.exists() or verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() or category.coding.exists(system='http://terminology.hl7.org/CodeSystem/condition-category' and code ='problem-list-item').empty()"; + return "(verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() and category.coding.exists(system='http://terminology.hl7.org/CodeSystem/condition-category' and code ='problem-list-item').empty()) implies (clinicalStatus.exists())"; } // R5 ballot From d219a20de89a6846b5b4b747ae10da14794fb27c Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:49:28 +0800 Subject: [PATCH 06/21] FHIRPath: Allow _ in constant names (per FHIRPath spec) --- .../src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java index b0d101cdc..080600c69 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java @@ -187,7 +187,7 @@ public class FHIRLexer { cursor++; } else while (cursor < source.length() && ((source.charAt(cursor) >= 'A' && source.charAt(cursor) <= 'Z') || (source.charAt(cursor) >= 'a' && source.charAt(cursor) <= 'z') || - (source.charAt(cursor) >= '0' && source.charAt(cursor) <= '9') || source.charAt(cursor) == ':' || source.charAt(cursor) == '-')) + (source.charAt(cursor) >= '0' && source.charAt(cursor) <= '9') || source.charAt(cursor) == ':' || source.charAt(cursor) == '-' || source.charAt(cursor) == '_')) cursor++; current = source.substring(currentStart, cursor); } else if (ch == '/') { From 07a2b7d2e70728c654b6dbba35db02f62a433d01 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:51:38 +0800 Subject: [PATCH 07/21] Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types --- .../hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index 5d1efdcec..7a63e0c59 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -773,6 +773,25 @@ public class FHIRPathEngine { return execute(new ExecutionContext(null, base != null && base.isResource() ? base : null, base != null && base.isResource() ? base : null, base, base), list, ExpressionNode, true); } + + /** + * evaluate a path and return the matching elements + * + * @param base - the object against which the path is being evaluated + * @param ExpressionNode - the parsed ExpressionNode statement to use + * @return + * @throws FHIRException + * @ + */ + public List evaluate(Object appContext, Base base, ExpressionNode ExpressionNode) throws FHIRException { + List list = new ArrayList(); + if (base != null) { + list.add(base); + } + log = new StringBuilder(); + return execute(new ExecutionContext(appContext, base != null && base.isResource() ? base : null, base != null && base.isResource() ? base : null, base, base), list, ExpressionNode, true); + } + /** * evaluate a path and return the matching elements * @@ -3741,6 +3760,8 @@ public class FHIRPathEngine { } if ((focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime); + } else if ((focus.hasType("time"))) { + return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time, TypeDetails.FP_Time); } else if (focus.hasType("decimal") || focus.hasType("integer")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } else { @@ -6351,7 +6372,7 @@ public class FHIRPathEngine { } result.addTypes(worker.getResourceNames()); } else { - pt = new ProfiledType(t.getCode()); + pt = new ProfiledType(t.getWorkingCode()); } if (pt != null) { if (t.hasProfile()) { From 4692962820aa2acc6b7575a4ed078fc0da17b94e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 17:56:07 +0800 Subject: [PATCH 08/21] release notes --- RELEASE_NOTES.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7b06c6ab5..f9e202bce 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,7 +1,18 @@ ## Validator Changes -* no changes +* Fix expression for con-3 properly (fix validation problem on some condition resources) +* Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types +* FHIRPath: Allow _ in constant names (per FHIRPath spec) +* Fix value set rendering creating wrong references +* Fix bug processing value set includes / excludes that are just value sets (no system value) +* Alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors +* Improve message for when elements are out of order in profile differentials + ## Other code changes -* no changes \ No newline at end of file +* fix problem where profile rendering had spurious 'slices for' nodes everywhere +* Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository +* Fix problem generating value set spreadsheets +* fix concurrent modification error processing language translations +* Check for null fetcher processing ConceptMaps (#1728) From 52668c1c7897cd579f30d17aac3c4b8bf0cdac33 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 9 Sep 2024 21:23:42 +0800 Subject: [PATCH 09/21] fix sql-on-fhir tests --- .../main/java/org/hl7/fhir/utilities/Utilities.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 5c117d455..4042ee2f1 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 @@ -1819,9 +1819,14 @@ public class Utilities { private static Object applyDatePrecision(String v, int precision) { switch (precision) { - case 4: return v.substring(0, 4); - case 6: return v.substring(0, 7); - case 8: return v.substring(0, 10); + case 4: + return v.substring(0, 4); + case 6: + case 7: + return v.substring(0, 7); + case 8: + case 10: + return v.substring(0, 10); case 14: return v.substring(0, 17); case 17: return v; } From ffe0ab6414121c56bc8acecce7497f6af4a738ff Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 10 Sep 2024 00:27:08 +0800 Subject: [PATCH 10/21] 2024 09 gg vs sql (#1738) * Fix bug processing value set includes / excludes that are just value sets (no system value) * fix value set rendering creating wrong references * Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository * Fix expression for con-3 properly (fix validation problem on some condition resources) * FHIRPath: Allow _ in constant names (per FHIRPath spec) * Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types * release notes * fix sql-on-fhir tests --------- Co-authored-by: Grahame Grieve --- RELEASE_NOTES.md | 15 +- .../fhir/r5/context/BaseWorkerContext.java | 8 +- .../org/hl7/fhir/r5/fhirpath/FHIRLexer.java | 2 +- .../hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 23 ++- .../fhir/r5/renderers/ValueSetRenderer.java | 45 +++--- .../r5/renderers/utils/RenderingContext.java | 3 + .../expansion/ValueSetExpander.java | 53 +++---- .../org/hl7/fhir/r5/utils/sql/Runner.java | 65 ++++++--- .../hl7/fhir/r5/utils/sql/StorageJson.java | 17 +-- .../org/hl7/fhir/r5/utils/sql/Validator.java | 131 ++++++++++++------ .../org/hl7/fhir/r5/sql/SQLOnFhirTests.java | 29 ++-- .../org/hl7/fhir/utilities/Utilities.java | 11 +- .../utils/FHIRPathExpressionFixer.java | 2 +- 13 files changed, 257 insertions(+), 147 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7b06c6ab5..f9e202bce 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,7 +1,18 @@ ## Validator Changes -* no changes +* Fix expression for con-3 properly (fix validation problem on some condition resources) +* Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types +* FHIRPath: Allow _ in constant names (per FHIRPath spec) +* Fix value set rendering creating wrong references +* Fix bug processing value set includes / excludes that are just value sets (no system value) +* Alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors +* Improve message for when elements are out of order in profile differentials + ## Other code changes -* no changes \ No newline at end of file +* fix problem where profile rendering had spurious 'slices for' nodes everywhere +* Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository +* Fix problem generating value set spreadsheets +* fix concurrent modification error processing language translations +* Check for null fetcher processing ConceptMaps (#1728) 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 e517398be..e33cc0bfa 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 @@ -934,10 +934,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte throw new Error(formatMessage(I18nConstants.NO_VALUE_SET_IN_URL)); } for (ConceptSetComponent inc : vs.getCompose().getInclude()) { - codeSystemsUsed.add(inc.getSystem()); + if (inc.hasSystem()) { + codeSystemsUsed.add(inc.getSystem()); + } } for (ConceptSetComponent inc : vs.getCompose().getExclude()) { - codeSystemsUsed.add(inc.getSystem()); + if (inc.hasSystem()) { + codeSystemsUsed.add(inc.getSystem()); + } } CacheToken cacheToken = txCache.generateExpandToken(vs, hierarchical); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java index b0d101cdc..080600c69 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRLexer.java @@ -187,7 +187,7 @@ public class FHIRLexer { cursor++; } else while (cursor < source.length() && ((source.charAt(cursor) >= 'A' && source.charAt(cursor) <= 'Z') || (source.charAt(cursor) >= 'a' && source.charAt(cursor) <= 'z') || - (source.charAt(cursor) >= '0' && source.charAt(cursor) <= '9') || source.charAt(cursor) == ':' || source.charAt(cursor) == '-')) + (source.charAt(cursor) >= '0' && source.charAt(cursor) <= '9') || source.charAt(cursor) == ':' || source.charAt(cursor) == '-' || source.charAt(cursor) == '_')) cursor++; current = source.substring(currentStart, cursor); } else if (ch == '/') { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index 5d1efdcec..7a63e0c59 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -773,6 +773,25 @@ public class FHIRPathEngine { return execute(new ExecutionContext(null, base != null && base.isResource() ? base : null, base != null && base.isResource() ? base : null, base, base), list, ExpressionNode, true); } + + /** + * evaluate a path and return the matching elements + * + * @param base - the object against which the path is being evaluated + * @param ExpressionNode - the parsed ExpressionNode statement to use + * @return + * @throws FHIRException + * @ + */ + public List evaluate(Object appContext, Base base, ExpressionNode ExpressionNode) throws FHIRException { + List list = new ArrayList(); + if (base != null) { + list.add(base); + } + log = new StringBuilder(); + return execute(new ExecutionContext(appContext, base != null && base.isResource() ? base : null, base != null && base.isResource() ? base : null, base, base), list, ExpressionNode, true); + } + /** * evaluate a path and return the matching elements * @@ -3741,6 +3760,8 @@ public class FHIRPathEngine { } if ((focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime); + } else if ((focus.hasType("time"))) { + return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time, TypeDetails.FP_Time); } else if (focus.hasType("decimal") || focus.hasType("integer")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } else { @@ -6351,7 +6372,7 @@ public class FHIRPathEngine { } result.addTypes(worker.getResourceNames()); } else { - pt = new ProfiledType(t.getCode()); + pt = new ProfiledType(t.getWorkingCode()); } if (pt != null) { if (t.hasProfile()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index eb303d742..4a78a67c8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -754,16 +754,7 @@ public class ValueSetRenderer extends TerminologyRenderer { if (ref == null) { ref = (String) cs.getWebPath(); } - if (ref == null && cs.hasUserData("webroot")) { - ref = (String) cs.getUserData("webroot"); - } - if (ref == null) { - return "?ngen-14?.html"; - } - if (!ref.contains(".html")) { - ref = ref + ".html"; - } - return ref.replace("\\", "/"); + return ref == null ? null : ref.replace("\\", "/"); } private void scanForDesignations(ValueSetExpansionContainsComponent c, List langs, Map designations) { @@ -922,14 +913,18 @@ public class ValueSetRenderer extends TerminologyRenderer { td.addText(code); } else { String href = context.fixReference(getCsRef(e)); - if (href.contains("#")) - href = href + "-"+Utilities.nmtokenize(code); - else - href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code); - if (isAbstract) - td.ah(context.prefixLocalHref(href)).setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).i().addText(code); - else - td.ah(context.prefixLocalHref(href)).addText(code); + if (href == null) { + td.code().tx(code); + } else { + if (href.contains("#")) + href = href + "-"+Utilities.nmtokenize(code); + else + href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code); + if (isAbstract) + td.ah(context.prefixLocalHref(href)).setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).i().addText(code); + else + td.ah(context.prefixLocalHref(href)).addText(code); + } } } @@ -1247,11 +1242,15 @@ public class ValueSetRenderer extends TerminologyRenderer { wli.tx(f.getProperty()+" "+describe(f.getOp())+" "); if (e != null && codeExistsInValueSet(e, f.getValue())) { String href = getContext().fixReference(getCsRef(e)); - if (href.contains("#")) - href = href + "-"+Utilities.nmtokenize(f.getValue()); - else - href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue()); - wli.ah(context.prefixLocalHref(href)).addText(f.getValue()); + if (href == null) { + wli.code().tx(f.getValue()); + } else { + if (href.contains("#")) + href = href + "-"+Utilities.nmtokenize(f.getValue()); + else + href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue()); + wli.ah(context.prefixLocalHref(href)).addText(f.getValue()); + } } else if (inc.hasSystem()) { wli.addText(f.getValue()); ValidationResult vr = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions(), inc.getSystem(), inc.getVersion(), f.getValue(), null); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java index 572523a1e..687d7c672 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java @@ -554,6 +554,9 @@ public class RenderingContext extends RenderingI18nContext { } public String fixReference(String ref) { + if (ref == null) { + return null; + } if (!Utilities.isAbsoluteUrl(ref)) { return (localPrefix == null ? "" : localPrefix)+ref; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java index 28058becb..7e1cfbbf7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java @@ -606,33 +606,35 @@ public class ValueSetExpander extends ValueSetProcessBase { excludeCodes(wc, importValueSetForExclude(wc, imp.getValue(), exp, expParams, false, vs).getExpansion()); } - CodeSystem cs = context.fetchSupplementedCodeSystem(exc.getSystem()); - if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem(), opContext.getOptions().getFhirVersion())) { - ValueSetExpansionOutcome vse = context.expandVS(exc, false, false); - ValueSet valueset = vse.getValueset(); - if (valueset == null) - throw failTSE("Error Expanding ValueSet: "+vse.getError()); - excludeCodes(wc, valueset.getExpansion()); - return; - } - - for (ConceptReferenceComponent c : exc.getConcept()) { - excludeCode(wc, exc.getSystem(), c.getCode()); - } - - if (exc.getFilter().size() > 0) { - if (cs.getContent() == CodeSystemContentMode.FRAGMENT) { - addFragmentWarning(exp, cs); + if (exc.hasSystem()) { + CodeSystem cs = context.fetchSupplementedCodeSystem(exc.getSystem()); + if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem(), opContext.getOptions().getFhirVersion())) { + ValueSetExpansionOutcome vse = context.expandVS(exc, false, false); + ValueSet valueset = vse.getValueset(); + if (valueset == null) + throw failTSE("Error Expanding ValueSet: "+vse.getError()); + excludeCodes(wc, valueset.getExpansion()); + return; } - List filters = new ArrayList<>(); - for (int i = 1; i < exc.getFilter().size(); i++) { - WorkingContext wc1 = new WorkingContext(); - filters.add(wc1); - processFilter(exc, exp, expParams, null, cs, false, exc.getFilter().get(i), wc1, null, true); + + for (ConceptReferenceComponent c : exc.getConcept()) { + excludeCode(wc, exc.getSystem(), c.getCode()); + } + + if (exc.getFilter().size() > 0) { + if (cs.getContent() == CodeSystemContentMode.FRAGMENT) { + addFragmentWarning(exp, cs); + } + List filters = new ArrayList<>(); + for (int i = 1; i < exc.getFilter().size(); i++) { + WorkingContext wc1 = new WorkingContext(); + filters.add(wc1); + processFilter(exc, exp, expParams, null, cs, false, exc.getFilter().get(i), wc1, null, true); + } + ConceptSetFilterComponent fc = exc.getFilter().get(0); + WorkingContext wc1 = dwc; + processFilter(exc, exp, expParams, null, cs, false, fc, wc1, filters, true); } - ConceptSetFilterComponent fc = exc.getFilter().get(0); - WorkingContext wc1 = dwc; - processFilter(exc, exp, expParams, null, cs, false, fc, wc1, filters, true); } } @@ -728,7 +730,6 @@ public class ValueSetExpander extends ValueSetProcessBase { expParams = makeDefaultExpansion(); altCodeParams.seeParameters(expParams); altCodeParams.seeValueSet(source); - source.checkNoModifiers("ValueSet", "expanding"); focus = source.copy(); focus.setIdBase(null); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java index 432fff492..aa5d8b4e1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java @@ -103,7 +103,7 @@ public class Runner implements IEvaluationContext { for (JsonObject w : vd.getJsonObjects("where")) { String expr = w.asString("path"); ExpressionNode node = fpe.parse(expr); - boolean pass = fpe.evaluateToBoolean(null, b, b, b, node); + boolean pass = fpe.evaluateToBoolean(vd, b, b, b, node); if (!pass) { ok = false; break; @@ -114,7 +114,7 @@ public class Runner implements IEvaluationContext { rows.add(new ArrayList()); for (JsonObject select : vd.getJsonObjects("select")) { - executeSelect(select, b, rows); + executeSelect(vd, select, b, rows); } for (List row : rows) { storage.addRow(store, row); @@ -124,14 +124,14 @@ public class Runner implements IEvaluationContext { storage.finish(store); } - private void executeSelect(JsonObject select, Base b, List> rows) { + private void executeSelect(JsonObject vd, JsonObject select, Base b, List> rows) { List focus = new ArrayList<>(); if (select.has("forEach")) { - focus.addAll(executeForEach(select, b)); + focus.addAll(executeForEach(vd, select, b)); } else if (select.has("forEachOrNull")) { - focus.addAll(executeForEachOrNull(select, b)); + focus.addAll(executeForEachOrNull(vd, select, b)); if (focus.isEmpty()) { List columns = (List) select.getUserData("columns"); for (List row : rows) { @@ -159,20 +159,20 @@ public class Runner implements IEvaluationContext { List> rowsToAdd = cloneRows(tempRows); for (JsonObject column : select.getJsonObjects("column")) { - executeColumn(column, f, rowsToAdd); + executeColumn(vd, column, f, rowsToAdd); } for (JsonObject sub : select.getJsonObjects("select")) { - executeSelect(sub, f, rowsToAdd); + executeSelect(vd, sub, f, rowsToAdd); } - executeUnionAll(select.getJsonObjects("unionAll"), f, rowsToAdd); + executeUnionAll(vd, select.getJsonObjects("unionAll"), f, rowsToAdd); rows.addAll(rowsToAdd); } } - private void executeUnionAll(List unionList, Base b, List> rows) { + private void executeUnionAll(JsonObject vd, List unionList, Base b, List> rows) { if (unionList.isEmpty()) { return; } @@ -183,7 +183,7 @@ public class Runner implements IEvaluationContext { for (JsonObject union : unionList) { List> tempRows = new ArrayList<>(); tempRows.addAll(sourceRows); - executeSelect(union, b, tempRows); + executeSelect(vd, union, b, tempRows); rows.addAll(tempRows); } } @@ -204,25 +204,25 @@ public class Runner implements IEvaluationContext { return list; } - private List executeForEach(JsonObject focus, Base b) { + private List executeForEach(JsonObject vd, JsonObject focus, Base b) { ExpressionNode n = (ExpressionNode) focus.getUserData("forEach"); List result = new ArrayList<>(); - result.addAll(fpe.evaluate(b, n)); + result.addAll(fpe.evaluate(vd, b, n)); return result; } - private List executeForEachOrNull(JsonObject focus, Base b) { + private List executeForEachOrNull(JsonObject vd, JsonObject focus, Base b) { ExpressionNode n = (ExpressionNode) focus.getUserData("forEachOrNull"); List result = new ArrayList<>(); - result.addAll(fpe.evaluate(b, n)); + result.addAll(fpe.evaluate(vd, b, n)); return result; } - private void executeColumn(JsonObject column, Base b, List> rows) { + private void executeColumn(JsonObject vd, JsonObject column, Base b, List> rows) { ExpressionNode n = (ExpressionNode) column.getUserData("path"); List bl2 = new ArrayList<>(); if (b != null) { - bl2.addAll(fpe.evaluate(b, n)); + bl2.addAll(fpe.evaluate(vd, b, n)); } Column col = (Column) column.getUserData("column"); if (col == null) { @@ -344,14 +344,43 @@ public class Runner implements IEvaluationContext { @Override public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { - throw new Error("Not implemented yet: resolveConstant"); + List list = new ArrayList(); + if (explicitConstant) { + JsonObject vd = (JsonObject) appContext; + JsonObject constant = findConstant(vd, name); + if (constant != null) { + Base b = (Base) constant.getUserData("value"); + if (b != null) { + list.add(b); + } + } + } + return list; } @Override public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { - throw new Error("Not implemented yet: resolveConstantType"); + if (explicitConstant) { + JsonObject vd = (JsonObject) appContext; + JsonObject constant = findConstant(vd, name.substring(1)); + if (constant != null) { + Base b = (Base) constant.getUserData("value"); + if (b != null) { + return new TypeDetails(CollectionStatus.SINGLETON, b.fhirType()); + } + } + } + return null; } + private JsonObject findConstant(JsonObject vd, String name) { + for (JsonObject o : vd.getJsonObjects("constant")) { + if (name.equals(o.asString("name"))) { + return o; + } + } + return null; + } @Override public boolean log(String argument, List focus) { throw new Error("Not implemented yet: log"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java index d0e3aaeed..d76404c03 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/StorageJson.java @@ -2,6 +2,7 @@ package org.hl7.fhir.r5.utils.sql; import java.util.List; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.utilities.json.model.JsonArray; import org.hl7.fhir.utilities.json.model.JsonBoolean; @@ -33,16 +34,16 @@ public class StorageJson implements Storage { JsonObject row = new JsonObject(); rows.add(row); for (Cell cell : cells) { - if (cell.getValues().size() == 0) { - row.add(cell.getColumn().getName(), new JsonNull()); - } else if (cell.getValues().size() == 1) { - row.add(cell.getColumn().getName(), makeJsonNode(cell.getValues().get(0))); - } else { + if (cell.getColumn().isColl() || cell.getValues().size() > 1) { JsonArray arr = new JsonArray(); - row.add(cell.getColumn().getName(), arr); + row.add(cell.getColumn().getName(), arr); for (Value value : cell.getValues()) { arr.add(makeJsonNode(value)); - } + } + } else if (cell.getValues().size() == 0) { + row.add(cell.getColumn().getName(), new JsonNull()); + } else { + row.add(cell.getColumn().getName(), makeJsonNode(cell.getValues().get(0))); } } } @@ -87,7 +88,7 @@ public class StorageJson implements Storage { @Override public String getKeyForSourceResource(Base res) { - return res.getIdBase(); + return res.fhirType()+"/"+res.getIdBase(); } @Override diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java index 669fa32de..00c862b5d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java @@ -11,6 +11,27 @@ import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.fhirpath.ExpressionNode; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; import org.hl7.fhir.r5.fhirpath.TypeDetails; +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.Base64BinaryType; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.DecimalType; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.InstantType; +import org.hl7.fhir.r5.model.Integer64Type; +import org.hl7.fhir.r5.model.IntegerType; +import org.hl7.fhir.r5.model.OidType; +import org.hl7.fhir.r5.model.PositiveIntType; +import org.hl7.fhir.r5.model.PrimitiveType; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.TimeType; +import org.hl7.fhir.r5.model.UnsignedIntType; +import org.hl7.fhir.r5.model.UriType; +import org.hl7.fhir.r5.model.UrlType; +import org.hl7.fhir.r5.model.UuidType; import org.hl7.fhir.r5.fhirpath.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IssueMessage; import org.hl7.fhir.utilities.Utilities; @@ -99,7 +120,7 @@ public class Validator { i = 0; if (checkAllObjects(path, viewDefinition, "where")) { for (JsonObject where : viewDefinition.getJsonObjects("where")) { - checkWhere(path+".where["+i+"]", where); + checkWhere(viewDefinition, path+".where["+i+"]", where); i++; } } @@ -108,7 +129,7 @@ public class Validator { i = 0; if (checkAllObjects(path, viewDefinition, "select")) { for (JsonObject select : viewDefinition.getJsonObjects("select")) { - columns.addAll(checkSelect(path+".select["+i+"]", select, t)); + columns.addAll(checkSelect(viewDefinition, path+".select["+i+"]", select, t)); i++; } if (i == 0) { @@ -119,15 +140,15 @@ public class Validator { } } - private List checkSelect(String path, JsonObject select, TypeDetails t) { + private List checkSelect(JsonObject vd, String path, JsonObject select, TypeDetails t) { List columns = new ArrayList<>(); select.setUserData("columns", columns); checkProperties(select, path, "column", "select", "forEach", "forEachOrNull", "unionAll"); if (select.has("forEach")) { - t = checkForEach(path, select, select.get("forEach"), t); + t = checkForEach(vd, path, select, select.get("forEach"), t); } else if (select.has("forEachOrNull")) { - t = checkForEachOrNull(path, select, select.get("forEachOrNull"), t); + t = checkForEachOrNull(vd, path, select, select.get("forEachOrNull"), t); } if (t != null) { @@ -142,7 +163,7 @@ public class Validator { if (!(e instanceof JsonObject)) { error(path+".column["+i+"]", a, "column["+i+"] is a "+e.type().toName()+" not an object", IssueType.INVALID); } else { - columns.add(checkColumn(path+".column["+i+"]", (JsonObject) e, t)); + columns.add(checkColumn(vd, path+".column["+i+"]", (JsonObject) e, t)); } } } @@ -158,14 +179,14 @@ public class Validator { if (!(e instanceof JsonObject)) { error(path+".select["+i+"]", e, "select["+i+"] is not an object", IssueType.INVALID); } else { - columns.addAll(checkSelect(path+".select["+i+"]", (JsonObject) e, t)); + columns.addAll(checkSelect(vd, path+".select["+i+"]", (JsonObject) e, t)); } } } } if (select.has("unionAll")) { - columns.addAll(checkUnion(path, select, select.get("unionAll"), t)); + columns.addAll(checkUnion(vd, path, select, select.get("unionAll"), t)); } if (columns.isEmpty()) { error(path, select, "The select has no columns or selects", IssueType.REQUIRED); @@ -191,7 +212,7 @@ public class Validator { } } - private List checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) { + private List checkUnion(JsonObject vd, String path, JsonObject focus, JsonElement expression, TypeDetails t) { JsonElement a = focus.get("unionAll"); if (!(a instanceof JsonArray)) { error(path+".unionAll", a, "union is not an array", IssueType.INVALID); @@ -203,7 +224,7 @@ public class Validator { if (!(e instanceof JsonObject)) { error(path+".unionAll["+i+"]", e, "unionAll["+i+"] is not an object", IssueType.INVALID); } else { - unionColumns.add(checkSelect(path+".unionAll["+i+"]", (JsonObject) e, t)); + unionColumns.add(checkSelect(vd, path+".unionAll["+i+"]", (JsonObject) e, t)); } i++; } @@ -242,7 +263,7 @@ public class Validator { } } - private Column checkColumn(String path, JsonObject column, TypeDetails t) { + private Column checkColumn(JsonObject vd, String path, JsonObject column, TypeDetails t) { checkProperties(column, path, "path", "name", "description", "collection", "type", "tag"); if (!column.has("path")) { @@ -260,7 +281,7 @@ public class Validator { try { node = fpe.parse(expr); column.setUserData("path", node); - td = fpe.checkOnTypes(null, resourceName, t, node, warnings); + td = fpe.checkOnTypes(vd, resourceName, t, node, warnings); } catch (Exception e) { error(path, expression, e.getMessage(), IssueType.INVALID); } @@ -296,25 +317,31 @@ public class Validator { // ok, name is sorted! if (columnName != null) { column.setUserData("name", columnName); - boolean isColl = (td.getCollectionStatus() != CollectionStatus.SINGLETON); + boolean isColl = false; if (column.has("collection")) { JsonElement collectionJ = column.get("collection"); if (!(collectionJ instanceof JsonBoolean)) { error(path+".collection", collectionJ, "collection is not a boolean", IssueType.INVALID); } else { boolean collection = collectionJ.asJsonBoolean().asBoolean(); - if (!collection && isColl) { - isColl = false; - warning(path, column, "collection is false, but the path statement(s) might return multiple values for the column '"+columnName+"' some inputs"); + if (collection) { + isColl = true; } } } if (isColl) { + if (td.getCollectionStatus() == CollectionStatus.SINGLETON) { + hint(path, column, "collection is true, but the path statement(s) can only return single values for the column '"+columnName+"'"); + } + } else { if (arrays == null) { warning(path, expression, "The column '"+columnName+"' appears to be a collection based on it's path. Collections are not supported in all execution contexts"); } else if (!arrays) { warning(path, expression, "The column '"+columnName+"' appears to be a collection based on it's path, but this is not allowed in the current execution context"); } + if (td.getCollectionStatus() != CollectionStatus.SINGLETON) { + warning(path, column, "collection is not true, but the path statement(s) might return multiple values for the column '"+columnName+"' for some inputs"); + } } Set types = new HashSet<>(); if (node.isNullSet()) { @@ -330,7 +357,7 @@ public class Validator { if (typeJ instanceof JsonString) { String type = typeJ.asString(); if (!td.hasType(type)) { - error(path+".type", typeJ, "The path expression does not return a value of the type '"+type, IssueType.VALUE); + error(path+".type", typeJ, "The path expression does not return a value of the type '"+type+"' - found "+td.describe(), IssueType.VALUE); } else { types.clear(); types.add(simpleType(type)); @@ -377,6 +404,8 @@ public class Validator { case "integer": return ColumnKind.Integer; case "decimal": return ColumnKind.Decimal; case "string": return ColumnKind.String; + case "id": return ColumnKind.String; + case "code": return ColumnKind.String; case "base64Binary": return ColumnKind.Binary; case "time": return ColumnKind.Time; default: return ColumnKind.Complex; @@ -384,7 +413,7 @@ public class Validator { } private boolean isSimpleType(String type) { - return Utilities.existsInList(type, "dateTime", "boolean", "integer", "decimal", "string", "base64Binary"); + return Utilities.existsInList(type, "dateTime", "boolean", "integer", "decimal", "string", "base64Binary", "id", "code", "date", "time"); } private String simpleType(String type) { @@ -413,7 +442,7 @@ public class Validator { return type; } - private TypeDetails checkForEach(String path, JsonObject focus, JsonElement expression, TypeDetails t) { + private TypeDetails checkForEach(JsonObject vd, String path, JsonObject focus, JsonElement expression, TypeDetails t) { if (!(expression instanceof JsonString)) { error(path+".forEach", expression, "forEach is not a string", IssueType.INVALID); return null; @@ -425,7 +454,7 @@ public class Validator { try { ExpressionNode n = fpe.parse(expr); focus.setUserData("forEach", n); - td = fpe.checkOnTypes(null, resourceName, t, n, warnings); + td = fpe.checkOnTypes(vd, resourceName, t, n, warnings); } catch (Exception e) { error(path, expression, e.getMessage(), IssueType.INVALID); } @@ -438,7 +467,7 @@ public class Validator { } } - private TypeDetails checkForEachOrNull(String path, JsonObject focus, JsonElement expression, TypeDetails t) { + private TypeDetails checkForEachOrNull(JsonObject vd, String path, JsonObject focus, JsonElement expression, TypeDetails t) { if (!(expression instanceof JsonString)) { error(path+".forEachOrNull", expression, "forEachOrNull is not a string", IssueType.INVALID); return null; @@ -450,7 +479,7 @@ public class Validator { try { ExpressionNode n = fpe.parse(expr); focus.setUserData("forEachOrNull", n); - td = fpe.checkOnTypes(null, resourceName, t, n, warnings); + td = fpe.checkOnTypes(vd, resourceName, t, n, warnings); } catch (Exception e) { error(path, expression, e.getMessage(), IssueType.INVALID); } @@ -477,69 +506,79 @@ public class Validator { } } if (constant.has("valueBase64Binary")) { - checkIsString(path, constant, "valueBase64Binary"); + checkIsString(path, constant, "valueBase64Binary", new Base64BinaryType()); } else if (constant.has("valueBoolean")) { - checkIsBoolean(path, constant, "valueBoolean"); + checkIsBoolean(path, constant, "valueBoolean", new BooleanType()); } else if (constant.has("valueCanonical")) { - checkIsString(path, constant, "valueCanonical"); + checkIsString(path, constant, "valueCanonical", new CanonicalType()); } else if (constant.has("valueCode")) { - checkIsString(path, constant, "valueCode"); + checkIsString(path, constant, "valueCode", new CodeType()); } else if (constant.has("valueDate")) { - checkIsString(path, constant, "valueDate"); + checkIsString(path, constant, "valueDate", new DateType()); } else if (constant.has("valueDateTime")) { - checkIsString(path, constant, "valueDateTime"); + checkIsString(path, constant, "valueDateTime", new DateTimeType()); } else if (constant.has("valueDecimal")) { - checkIsNumber(path, constant, "valueDecimal"); + checkIsNumber(path, constant, "valueDecimal", new DecimalType()); } else if (constant.has("valueId")) { - checkIsString(path, constant, "valueId"); + checkIsString(path, constant, "valueId", new IdType()); } else if (constant.has("valueInstant")) { - checkIsString(path, constant, "valueInstant"); + checkIsString(path, constant, "valueInstant", new InstantType()); } else if (constant.has("valueInteger")) { - checkIsNumber(path, constant, "valueInteger"); + checkIsNumber(path, constant, "valueInteger", new IntegerType()); } else if (constant.has("valueInteger64")) { - checkIsNumber(path, constant, "valueInteger64"); + checkIsNumber(path, constant, "valueInteger64", new Integer64Type()); } else if (constant.has("valueOid")) { - checkIsString(path, constant, "valueOid"); + checkIsString(path, constant, "valueOid", new OidType()); } else if (constant.has("valueString")) { - checkIsString(path, constant, "valueString"); + checkIsString(path, constant, "valueString", new StringType()); } else if (constant.has("valuePositiveInt")) { - checkIsNumber(path, constant, "valuePositiveInt"); + checkIsNumber(path, constant, "valuePositiveInt", new PositiveIntType()); } else if (constant.has("valueTime")) { - checkIsString(path, constant, "valueTime"); + checkIsString(path, constant, "valueTime", new TimeType()); } else if (constant.has("valueUnsignedInt")) { - checkIsNumber(path, constant, "valueUnsignedInt"); + checkIsNumber(path, constant, "valueUnsignedInt", new UnsignedIntType()); } else if (constant.has("valueUri")) { - checkIsString(path, constant, "valueUri"); + checkIsString(path, constant, "valueUri", new UriType()); } else if (constant.has("valueUrl")) { - checkIsString(path, constant, "valueUrl"); + checkIsString(path, constant, "valueUrl", new UrlType()); } else if (constant.has("valueUuid")) { - checkIsString(path, constant, "valueUuid"); + checkIsString(path, constant, "valueUuid", new UuidType()); } else { error(path, constant, "No value found", IssueType.REQUIRED); } } - private void checkIsString(String path, JsonObject constant, String name) { + private void checkIsString(String path, JsonObject constant, String name, PrimitiveType value) { JsonElement j = constant.get(name); if (!(j instanceof JsonString)) { error(path+"."+name, j, name+" must be a string", IssueType.INVALID); + } else { + value.setValueAsString(j.asString()); + constant.setUserData("value", value); } } - private void checkIsBoolean(String path, JsonObject constant, String name) { + private void checkIsBoolean(String path, JsonObject constant, String name, PrimitiveType value) { JsonElement j = constant.get(name); if (!(j instanceof JsonBoolean)) { error(path+"."+name, j, name+" must be a boolean", IssueType.INVALID); + } else { + value.setValueAsString(j.asString()); + constant.setUserData("value", value); } } - private void checkIsNumber(String path, JsonObject constant, String name) { + private void checkIsNumber(String path, JsonObject constant, String name, PrimitiveType value) { JsonElement j = constant.get(name); if (!(j instanceof JsonNumber)) { error(path+"."+name, j, name+" must be a number", IssueType.INVALID); + } else { + value.setValueAsString(j.asString()); + constant.setUserData("value", value); } } - private void checkWhere(String path, JsonObject where) { + + private void checkWhere(JsonObject vd, String path, JsonObject where) { checkProperties(where, path, "path", "description"); String expr = where.asString("path"); @@ -553,7 +592,7 @@ public class Validator { try { ExpressionNode n = fpe.parse(expr); where.setUserData("path", n); - td = fpe.checkOnTypes(null, resourceName, types, n, warnings); + td = fpe.checkOnTypes(vd, resourceName, types, n, warnings); } catch (Exception e) { error(path, where.get("path"), e.getMessage(), IssueType.INVALID); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java index 9dba811a8..d650980fb 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/sql/SQLOnFhirTests.java @@ -83,23 +83,21 @@ public class SQLOnFhirTests { this.resources = resources; this.testCase = testCase; } - } public static Stream data() throws ParserConfigurationException, SAXException, IOException { List objects = new ArrayList<>(); - File dir = ManagedFileAccess.file("/Users/grahamegrieve/work/sql-on-fhir-v2/tests/content"); - for (File f : dir.listFiles()) { - if (f.getName().endsWith(".json")) { - JsonObject json = JsonParser.parseObject(f); - String name1 = f.getName().replace(".json", ""); - List resources = json.getJsonObjects("resources"); - int i = 0; - for (JsonObject test : json.getJsonObjects("tests")) { - String name2 = test.asString("title"); - objects.add(Arguments.of(name1+":"+name2, new TestDetails(name1+":"+name2, "$.tests["+i+"]", resources, test))); - i++; - } + JsonArray testFiles = (JsonArray) JsonParser.parse(TestingUtilities.loadTestResourceStream("sql-on-fhir", "manifest.json")); + + for (String s : testFiles.asStrings()) { + JsonObject json = JsonParser.parseObject(TestingUtilities.loadTestResourceStream("sql-on-fhir", s)); + String name1 = s.replace(".json", ""); + List resources = json.getJsonObjects("resources"); + int i = 0; + for (JsonObject test : json.getJsonObjects("tests")) { + String name2 = test.asString("title"); + objects.add(Arguments.of(name1+":"+name2, new TestDetails(name1+":"+name2, "$.tests["+i+"]", resources, test))); + i++; } } return objects.stream(); @@ -110,7 +108,6 @@ public class SQLOnFhirTests { @SuppressWarnings("deprecation") @ParameterizedTest(name = "{index}: file {0}") @MethodSource("data") - @Disabled public void test(String name, TestDetails test) throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException, UcumException { this.details = test; Runner runner = new Runner(); @@ -137,8 +134,8 @@ public class SQLOnFhirTests { rows.add("rows", results); JsonObject exp = new JsonObject(); exp.add("rows", test.testCase.getJsonArray("expect")); - sortResults(exp); - sortResults(rows); +// sortResults(exp); +// sortResults(rows); String expS = JsonParser.compose(exp, true); String rowS = JsonParser.compose(rows, true); String c = CompareUtilities.checkJsonSrcIsSame(name, expS, rowS, null); 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 5c117d455..4042ee2f1 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 @@ -1819,9 +1819,14 @@ public class Utilities { private static Object applyDatePrecision(String v, int precision) { switch (precision) { - case 4: return v.substring(0, 4); - case 6: return v.substring(0, 7); - case 8: return v.substring(0, 10); + case 4: + return v.substring(0, 4); + case 6: + case 7: + return v.substring(0, 7); + case 8: + case 10: + return v.substring(0, 10); case 14: return v.substring(0, 17); case 17: return v; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java index c4437511c..55376a293 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java @@ -61,7 +61,7 @@ public class FHIRPathExpressionFixer { } // con-3 in R4 if (expr.equals("clinicalStatus.exists() or verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() or category.select($this='problem-list-item').empty()")) { - return "clinicalStatus.exists() or verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() or category.coding.exists(system='http://terminology.hl7.org/CodeSystem/condition-category' and code ='problem-list-item').empty()"; + return "(verificationStatus.coding.where(system='http://terminology.hl7.org/CodeSystem/condition-ver-status' and code = 'entered-in-error').exists() and category.coding.exists(system='http://terminology.hl7.org/CodeSystem/condition-category' and code ='problem-list-item').empty()) implies (clinicalStatus.exists())"; } // R5 ballot From 4cbf1860d7b89f5990a55d769a996b579c05c01c Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 9 Sep 2024 13:06:57 -0400 Subject: [PATCH 11/21] Bump fhir-test-cases to release version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be5e3727d..ca79ada7c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 1.26.0 32.0.1-jre 6.4.1 - 1.5.21-SNAPSHOT + 1.5.21 2.17.0 5.9.2 1.8.2 From 813f8200a2b7f15b4724b14511fac3f4f6d21b76 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Mon, 9 Sep 2024 18:23:37 +0000 Subject: [PATCH 12/21] Release: v6.3.24 ## Validator Changes * Fix expression for con-3 properly (fix validation problem on some condition resources) * Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types * FHIRPath: Allow _ in constant names (per FHIRPath spec) * Fix value set rendering creating wrong references * Fix bug processing value set includes / excludes that are just value sets (no system value) * Alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors * Improve message for when elements are out of order in profile differentials ## Other code changes * fix problem where profile rendering had spurious 'slices for' nodes everywhere * Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository * Fix problem generating value set spreadsheets * fix concurrent modification error processing language translations * Check for null fetcher processing ConceptMaps (#1728) ***NO_CI*** --- org.hl7.fhir.convertors/pom.xml | 2 +- org.hl7.fhir.dstu2/pom.xml | 2 +- org.hl7.fhir.dstu2016may/pom.xml | 2 +- org.hl7.fhir.dstu3/pom.xml | 2 +- org.hl7.fhir.r4/pom.xml | 2 +- org.hl7.fhir.r4b/pom.xml | 2 +- org.hl7.fhir.r5/pom.xml | 2 +- org.hl7.fhir.report/pom.xml | 2 +- org.hl7.fhir.utilities/pom.xml | 2 +- org.hl7.fhir.validation.cli/pom.xml | 2 +- org.hl7.fhir.validation/pom.xml | 2 +- pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index a0a3869df..50b3e6401 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 8055af71e..a6d0a504b 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index da50ebb03..786d46709 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index 0a6da6acf..b1ed5bb99 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 9fa2bbaa8..f69788507 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.r4b/pom.xml b/org.hl7.fhir.r4b/pom.xml index 25f7e8516..c4656a156 100644 --- a/org.hl7.fhir.r4b/pom.xml +++ b/org.hl7.fhir.r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index 9c76d955d..1ff84dbc5 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml index 7889e540c..c0c0a6af2 100644 --- a/org.hl7.fhir.report/pom.xml +++ b/org.hl7.fhir.report/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 76ea1bc06..2471e5442 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index f0907cd29..65839379b 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index 9bf7272b5..d20acce66 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 ../pom.xml diff --git a/pom.xml b/pom.xml index ca79ada7c..5cbe8d761 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ HAPI FHIR --> org.hl7.fhir.core - 6.3.24-SNAPSHOT + 6.3.24 pom From 2c8122a42eeec607133f26b1bef14c4611ea13e4 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Mon, 9 Sep 2024 19:09:24 +0000 Subject: [PATCH 13/21] Updating version to: 6.3.25-SNAPSHOT and incrementing test cases dependency. --- RELEASE_NOTES.md | 15 ++------------- org.hl7.fhir.convertors/pom.xml | 2 +- org.hl7.fhir.dstu2/pom.xml | 2 +- org.hl7.fhir.dstu2016may/pom.xml | 2 +- org.hl7.fhir.dstu3/pom.xml | 2 +- org.hl7.fhir.r4/pom.xml | 2 +- org.hl7.fhir.r4b/pom.xml | 2 +- org.hl7.fhir.r5/pom.xml | 2 +- org.hl7.fhir.report/pom.xml | 2 +- org.hl7.fhir.utilities/pom.xml | 2 +- org.hl7.fhir.validation.cli/pom.xml | 2 +- org.hl7.fhir.validation/pom.xml | 2 +- pom.xml | 2 +- 13 files changed, 14 insertions(+), 25 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f9e202bce..7b06c6ab5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,18 +1,7 @@ ## Validator Changes -* Fix expression for con-3 properly (fix validation problem on some condition resources) -* Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types -* FHIRPath: Allow _ in constant names (per FHIRPath spec) -* Fix value set rendering creating wrong references -* Fix bug processing value set includes / excludes that are just value sets (no system value) -* Alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors -* Improve message for when elements are out of order in profile differentials - +* no changes ## Other code changes -* fix problem where profile rendering had spurious 'slices for' nodes everywhere -* Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository -* Fix problem generating value set spreadsheets -* fix concurrent modification error processing language translations -* Check for null fetcher processing ConceptMaps (#1728) +* no changes \ No newline at end of file diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index 50b3e6401..370eb607f 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index a6d0a504b..317170b7b 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index 786d46709..c804e1719 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index b1ed5bb99..0e79f835a 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index f69788507..032363f4c 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4b/pom.xml b/org.hl7.fhir.r4b/pom.xml index c4656a156..147230f24 100644 --- a/org.hl7.fhir.r4b/pom.xml +++ b/org.hl7.fhir.r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index 1ff84dbc5..7e6fb6a09 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml index c0c0a6af2..990d2b2a9 100644 --- a/org.hl7.fhir.report/pom.xml +++ b/org.hl7.fhir.report/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 2471e5442..b2817b460 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 65839379b..e0b001a4a 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index d20acce66..1dcd143bb 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 5cbe8d761..4f8248ecc 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ HAPI FHIR --> org.hl7.fhir.core - 6.3.24 + 6.3.25-SNAPSHOT pom From 44464de8077de575b1530b14aa770fd050fb88c2 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 10 Sep 2024 09:38:02 -0400 Subject: [PATCH 14/21] Remove cqlframework dependencies --- org.hl7.fhir.validation/pom.xml | 31 ------------------- .../instance/InstanceValidator.java | 2 +- .../validation/special/TxTesterSorters.java | 4 +-- 3 files changed, 3 insertions(+), 34 deletions(-) diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index 1dcd143bb..cc68b5cb2 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -119,37 +119,6 @@ true - - - info.cqframework - cql - ${info_cqframework_version} - - - info.cqframework - model - ${info_cqframework_version} - - - info.cqframework - elm - ${info_cqframework_version} - - - info.cqframework - cql-to-elm - ${info_cqframework_version} - - - info.cqframework - quick - ${info_cqframework_version} - - - info.cqframework - qdm - ${info_cqframework_version} - com.squareup.okhttp3 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 dbc8eb164..302bc448d 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 @@ -46,7 +46,7 @@ import javax.annotation.Nonnull; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.fhir.ucum.Decimal; -import org.hl7.elm.r1.Code; + import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java index 35d069e8a..a85c4db8b 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java @@ -8,10 +8,10 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import org.hl7.fhir.ParametersParameter; + import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; -import org.hl7.fhir.r5.model.Base; + import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.OperationOutcome; import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent; From bc0303535a02697c9cc942c6fcf83cfebbdf5924 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 11 Sep 2024 12:05:17 -0400 Subject: [PATCH 15/21] Fix for cache init on existing directories (#1743) * Add tests+fix for cache init on existing directories * Clear the cache if it is the wrong version * Link to FHIR spec docs for .index.json --- .../npm/FilesystemPackageCacheManager.java | 22 ++-- .../hl7/fhir/utilities/npm/NpmPackage.java | 12 +- .../npm/FilesystemPackageManagerTests.java | 118 +++++++++++++++++- 3 files changed, 140 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java index df192cc19..ec0495874 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java @@ -210,26 +210,30 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple Utilities.createDirectory(cacheFolder.getAbsolutePath()); createIniFile(); } else { - if (!isCacheFolderValid()) { + if (!iniFileExists()) { + createIniFile(); + } + if (!isIniFileCurrentVersion()) { clearCache(); createIniFile(); - } else { - deleteOldTempDirectories(); } + deleteOldTempDirectories(); } return null; }); } - private boolean isCacheFolderValid() throws IOException { + private boolean iniFileExists() throws IOException { String iniPath = getPackagesIniPath(); File iniFile = ManagedFileAccess.file(iniPath); - if (!(iniFile.exists())) { - return false; - } + return iniFile.exists(); + } + + private boolean isIniFileCurrentVersion() throws IOException { + String iniPath = getPackagesIniPath(); IniFile ini = new IniFile(iniPath); - String v = ini.getStringProperty("cache", "version"); - return CACHE_VERSION.equals(v); + String version = ini.getStringProperty("cache", "version"); + return CACHE_VERSION.equals(version); } private void deleteOldTempDirectories() throws IOException { 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 5ab8a9113..f599f6758 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 @@ -648,7 +648,17 @@ public class NpmPackage { } - + /** + * Create a package .index.json file for a package folder. + *

+ * See the FHIR specification for details on .index.json + * format and usage. + * + * @param desc + * @param folder + * @throws FileNotFoundException + * @throws IOException + */ public void indexFolder(String desc, NpmPackageFolder folder) throws FileNotFoundException, IOException { List remove = new ArrayList<>(); NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder(); diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/npm/FilesystemPackageManagerTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/npm/FilesystemPackageManagerTests.java index 96754c2b8..eba7b198a 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/npm/FilesystemPackageManagerTests.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/npm/FilesystemPackageManagerTests.java @@ -1,22 +1,25 @@ package org.hl7.fhir.utilities.npm; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import javax.annotation.Nonnull; +import org.hl7.fhir.utilities.IniFile; import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.EnabledOnOs; @@ -100,7 +103,6 @@ public class FilesystemPackageManagerTests { @DisabledOnOs(OS.WINDOWS) public void testSystemCacheDirectory() throws IOException { File folder = new FilesystemPackageCacheManager.Builder().withSystemCacheFolder().getCacheFolder(); - assertEquals( "/var/lib/.fhir/packages", folder.getAbsolutePath()); } @@ -124,6 +126,118 @@ public class FilesystemPackageManagerTests { return params.stream(); } + private void createDummyTemp(File cacheDirectory, String lowerCase) throws IOException { + createDummyPackage(cacheDirectory, lowerCase); + } + + private void createDummyPackage(File cacheDirectory, String packageName, String packageVersion) throws IOException { + String directoryName = packageName + "#" + packageVersion; + createDummyPackage(cacheDirectory, directoryName); + } + + private static void createDummyPackage(File cacheDirectory, String directoryName) throws IOException { + File packageDirectory = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), directoryName); + packageDirectory.mkdirs(); + + File dummyContentFile = ManagedFileAccess.file(packageDirectory.getAbsolutePath(), "dummy.txt"); + FileWriter wr = new FileWriter(dummyContentFile); + wr.write("Ain't nobody here but us chickens"); + wr.flush(); + wr.close(); + } + + private void assertThatDummyTempExists(File cacheDirectory, String dummyTempPackage) throws IOException { + File dummyTempDirectory = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), dummyTempPackage); + assertThat(dummyTempDirectory).exists(); + + File dummyContentFile = ManagedFileAccess.file(dummyTempDirectory.getAbsolutePath(), "dummy.txt"); + assertThat(dummyContentFile).exists(); + } + + @Test + public void testCreatesIniIfDoesntExistAndCacheStaysIntact() throws IOException { + File cacheDirectory = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest")); + File cacheIni = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "packages.ini"); + + createDummyPackage(cacheDirectory, "example.fhir.uv.myig", "1.2.3"); + + String dummyTempPackage = UUID.randomUUID().toString().toLowerCase(); + createDummyTemp(cacheDirectory, dummyTempPackage); + assertThatDummyTempExists(cacheDirectory, dummyTempPackage); + + assertThat(cacheIni).doesNotExist(); + FilesystemPackageCacheManager filesystemPackageCacheManager = new FilesystemPackageCacheManager.Builder().withCacheFolder(cacheDirectory.getAbsolutePath()).build(); + assertInitializedTestCacheIsValid(cacheDirectory, true); + } + + + + @Test + public void testClearsCacheIfVersionIsWrong() throws IOException { + File cacheDirectory = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest")); + File cacheIni = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "packages.ini"); + + createDummyPackage(cacheDirectory, "example.fhir.uv.myig", "1.2.3"); + String dummyTempPackage = UUID.randomUUID().toString().toLowerCase(); + createDummyTemp(cacheDirectory, dummyTempPackage); + assertThatDummyTempExists(cacheDirectory, dummyTempPackage); + + + IniFile ini = new IniFile(cacheIni.getAbsolutePath()); + ini.setStringProperty("cache", "version", "2", null); + ini.save(); + + assertThat(cacheIni).exists(); + FilesystemPackageCacheManager filesystemPackageCacheManager = new FilesystemPackageCacheManager.Builder().withCacheFolder(cacheDirectory.getAbsolutePath()).build(); + assertInitializedTestCacheIsValid(cacheDirectory, false); + } + + @Test + public void testCacheStaysIntactIfVersionIsTheSame() throws IOException { + File cacheDirectory = ManagedFileAccess.fromPath(Files.createTempDirectory("fpcm-multithreadingTest")); + File cacheIni = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "packages.ini"); + + createDummyPackage(cacheDirectory, "example.fhir.uv.myig", "1.2.3"); + String dummyTempPackage = UUID.randomUUID().toString().toLowerCase(); + createDummyTemp(cacheDirectory, dummyTempPackage); + assertThatDummyTempExists(cacheDirectory, dummyTempPackage); + + + IniFile ini = new IniFile(cacheIni.getAbsolutePath()); + ini.setStringProperty("cache", "version", "3", null); + ini.save(); + + assertThat(cacheIni).exists(); + FilesystemPackageCacheManager filesystemPackageCacheManager = new FilesystemPackageCacheManager.Builder().withCacheFolder(cacheDirectory.getAbsolutePath()).build(); + assertInitializedTestCacheIsValid(cacheDirectory, true); + } + + private void assertInitializedTestCacheIsValid(File cacheDirectory, boolean dummyPackageShouldExist) throws IOException { + assertThat(cacheDirectory).exists(); + File iniFile = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "packages.ini"); + assertThat(ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "packages.ini")).exists(); + IniFile ini = new IniFile(iniFile.getAbsolutePath()); + String version = ini.getStringProperty("cache", "version"); + assertThat(version).isEqualTo("3"); + + File[] files = cacheDirectory.listFiles(); + if (dummyPackageShouldExist) { + // Check that only packages.ini and our dummy package are in the cache. Our previous temp should be deleted. + assertThat(files).hasSize(2); // packages.ini and example.fhir.uv.myig#1.2.3 (directory) + + File dummyPackage = ManagedFileAccess.file(cacheDirectory.getAbsolutePath(), "example.fhir.uv.myig#1.2.3"); + assertThat(dummyPackage).exists(); + + File dummyContentFile = ManagedFileAccess.file(dummyPackage.getAbsolutePath(), "dummy.txt"); + assertThat(dummyContentFile).exists(); + } else { + // Check that only packages.ini is in the cache. + assertThat(files).hasSize(1); + } + + + } + @MethodSource("packageCacheMultiThreadTestParams") @ParameterizedTest public void packageCacheMultiThreadTest(final int threadTotal, final int packageCacheManagerTotal) throws IOException { From 3524fd78c7e306f470a4ce1c25d545590d0d1040 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 11 Sep 2024 12:06:49 -0400 Subject: [PATCH 16/21] Update RELEASE_NOTES.md ***NO_CI*** --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7b06c6ab5..f965d2ad5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,4 +4,4 @@ ## Other code changes -* no changes \ No newline at end of file +* Fix logic for cache clearing on package cache initialization From f09bc48f7d3c3e165c39ad66b1747ce3bb8d8b11 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Wed, 11 Sep 2024 17:40:08 +0000 Subject: [PATCH 17/21] Release: v6.3.25 ## Validator Changes * no changes ## Other code changes * Fix logic for cache clearing on package cache initialization ***NO_CI*** --- org.hl7.fhir.convertors/pom.xml | 2 +- org.hl7.fhir.dstu2/pom.xml | 2 +- org.hl7.fhir.dstu2016may/pom.xml | 2 +- org.hl7.fhir.dstu3/pom.xml | 2 +- org.hl7.fhir.r4/pom.xml | 2 +- org.hl7.fhir.r4b/pom.xml | 2 +- org.hl7.fhir.r5/pom.xml | 2 +- org.hl7.fhir.report/pom.xml | 2 +- org.hl7.fhir.utilities/pom.xml | 2 +- org.hl7.fhir.validation.cli/pom.xml | 2 +- org.hl7.fhir.validation/pom.xml | 2 +- pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index 370eb607f..7b91b779c 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 317170b7b..3933f0255 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index c804e1719..aff212b64 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index 0e79f835a..67d2867d9 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 032363f4c..2451791b8 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.r4b/pom.xml b/org.hl7.fhir.r4b/pom.xml index 147230f24..5606df2e1 100644 --- a/org.hl7.fhir.r4b/pom.xml +++ b/org.hl7.fhir.r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index 7e6fb6a09..74651cb14 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml index 990d2b2a9..36f4c16a5 100644 --- a/org.hl7.fhir.report/pom.xml +++ b/org.hl7.fhir.report/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index b2817b460..b43b89875 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index e0b001a4a..873bb1d94 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index 1dcd143bb..b59bfc076 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 ../pom.xml diff --git a/pom.xml b/pom.xml index 4f8248ecc..abf77b819 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ HAPI FHIR --> org.hl7.fhir.core - 6.3.25-SNAPSHOT + 6.3.25 pom From 4af3deff26023c8dd7242b136145b3c0754795be Mon Sep 17 00:00:00 2001 From: markiantorno Date: Wed, 11 Sep 2024 18:28:22 +0000 Subject: [PATCH 18/21] Updating version to: 6.3.26-SNAPSHOT and incrementing test cases dependency. --- RELEASE_NOTES.md | 2 +- org.hl7.fhir.convertors/pom.xml | 2 +- org.hl7.fhir.dstu2/pom.xml | 2 +- org.hl7.fhir.dstu2016may/pom.xml | 2 +- org.hl7.fhir.dstu3/pom.xml | 2 +- org.hl7.fhir.r4/pom.xml | 2 +- org.hl7.fhir.r4b/pom.xml | 2 +- org.hl7.fhir.r5/pom.xml | 2 +- org.hl7.fhir.report/pom.xml | 2 +- org.hl7.fhir.utilities/pom.xml | 2 +- org.hl7.fhir.validation.cli/pom.xml | 2 +- org.hl7.fhir.validation/pom.xml | 2 +- pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f965d2ad5..7b06c6ab5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,4 +4,4 @@ ## Other code changes -* Fix logic for cache clearing on package cache initialization +* no changes \ No newline at end of file diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index 7b91b779c..497a67ca5 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 3933f0255..1ef5e9c18 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index aff212b64..10836492e 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index 67d2867d9..9b9f1ede2 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 2451791b8..bc3634f35 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4b/pom.xml b/org.hl7.fhir.r4b/pom.xml index 5606df2e1..df709cb6a 100644 --- a/org.hl7.fhir.r4b/pom.xml +++ b/org.hl7.fhir.r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index 74651cb14..8bb7cfbb9 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml index 36f4c16a5..f0adc1c86 100644 --- a/org.hl7.fhir.report/pom.xml +++ b/org.hl7.fhir.report/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index b43b89875..e4d08a59f 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 873bb1d94..54e5ebfa8 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index b59bfc076..d2b328684 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index abf77b819..23d34f2b9 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ HAPI FHIR --> org.hl7.fhir.core - 6.3.25 + 6.3.26-SNAPSHOT pom From 337aaf80cfa83946d5fe5266a8c78dcfd3e20eff Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 12 Sep 2024 11:54:40 +0800 Subject: [PATCH 19/21] Process relative URLs properly in base when generating snapshots --- .../profile/ProfilePathProcessor.java | 24 ++++++++--------- .../conformance/profile/ProfileUtilities.java | 27 ++++++++++--------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java index 6a939f95a..a26d81569 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java @@ -289,7 +289,7 @@ public class ProfilePathProcessor { start++; } else { // we're just going to accept the differential slicing at face value - ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy()); + ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); @@ -667,14 +667,14 @@ public class ProfilePathProcessor { // some of what's in currentBase overrides template template = profileUtilities.fillOutFromBase(template, currentBase); - ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), template); + ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), template, true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); res = outcome; profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); if (diffMatches.get(0).hasSliceName()) { template = currentBase.copy(); - template = profileUtilities.updateURLs(getUrl(), getWebUrl(), template); + template = profileUtilities.updateURLs(getUrl(), getWebUrl(), template, true); template.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), template.getPath(), getRedirector(), getContextPathSource())); checkToSeeIfSlicingExists(diffMatches.get(0), template); @@ -866,13 +866,13 @@ public class ProfilePathProcessor { private void processSimplePathWithEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) { - ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy()); + ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); profileUtilities.updateConstraintSources(outcome, getSourceStructureDefinition().getUrl()); profileUtilities.checkExtensions(outcome); profileUtilities.updateFromObligationProfiles(outcome); - profileUtilities.updateURLs(url, webUrl, outcome); + profileUtilities.updateURLs(url, webUrl, outcome, true); profileUtilities.markDerived(outcome); if (cursors.resultPathBase == null) cursors.resultPathBase = outcome.getPath(); @@ -1033,7 +1033,7 @@ public class ProfilePathProcessor { if (!currentBase.isChoice() && !profileUtilities.ruleMatches(dSlice.getRules(), bSlice.getRules())) throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.SLICING_RULES_ON_DIFFERENTIAL__DO_NOT_MATCH_THOSE_ON_BASE___RULE___, profileUtilities.summarizeSlicing(dSlice), profileUtilities.summarizeSlicing(bSlice), path, cursors.contextName)); } - ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy()); + ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); if (diffMatches.get(0).hasSlicing() || !diffMatches.get(0).hasSliceName()) { @@ -1095,7 +1095,7 @@ public class ProfilePathProcessor { // We need to copy children of the backbone element before we start messing around with slices int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); for (int i = cursors.baseCursor + 1; i <= newBaseLimit; i++) { - outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(i).copy()); + outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(i).copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); debugCheck(outcome); getResult().getElement().add(outcome); @@ -1106,7 +1106,7 @@ public class ProfilePathProcessor { List baseMatches = profileUtilities.getSiblings(cursors.base.getElement(), currentBase); for (ElementDefinition baseItem : baseMatches) { cursors.baseCursor = cursors.base.getElement().indexOf(baseItem); - outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), baseItem.copy()); + outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), baseItem.copy(), true); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); outcome.setSlicing(null); @@ -1139,7 +1139,7 @@ public class ProfilePathProcessor { cursors.baseCursor++; // just copy any children on the base while (cursors.baseCursor < cursors.base.getElement().size() && cursors.base.getElement().get(cursors.baseCursor).getPath().startsWith(path) && !cursors.base.getElement().get(cursors.baseCursor).getPath().equals(path)) { - outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(cursors.baseCursor).copy()); + outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(cursors.baseCursor).copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); if (!outcome.getPath().startsWith(cursors.resultPathBase)) throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); @@ -1166,7 +1166,7 @@ public class ProfilePathProcessor { for (ElementDefinition baseItem : baseMatches) if (baseItem.getSliceName().equals(diffItem.getSliceName())) throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.NAMED_ITEMS_ARE_OUT_OF_ORDER_IN_THE_SLICE)); - outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy()); + outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); // outcome = updateURLs(url, diffItem.copy()); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); @@ -1409,7 +1409,7 @@ public class ProfilePathProcessor { private void processPathWithSlicedBaseAndEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List diffMatches, ProfilePathProcessorState cursors, String path, MappingAssistant mapHelper) { if (profileUtilities.hasInnerDiffMatches(getDifferential(), path, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), true)) { // so we just copy it in - ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy()); + ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); profileUtilities.markDerived(outcome); @@ -1457,7 +1457,7 @@ public class ProfilePathProcessor { // the differential doesn't say anything about this item // copy across the currentbase, and all of its children and siblings while (cursors.baseCursor < cursors.base.getElement().size() && cursors.base.getElement().get(cursors.baseCursor).getPath().startsWith(path)) { - ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(cursors.baseCursor).copy()); + ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(cursors.baseCursor).copy(), true); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); if (!outcome.getPath().startsWith(cursors.resultPathBase)) throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH_IN_PROFILE___VS_, getProfileName(), outcome.getPath(), cursors.resultPathBase)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 79d668b36..7a75e0358 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -739,7 +739,7 @@ public class ProfileUtilities { if (existing != null) { updateFromDefinition(existing, e, profileName, false, url, base, derived, "StructureDefinition.differential.element["+i+"]", mappingDetails); } else { - ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); + ElementDefinition outcome = updateURLs(url, webUrl, e.copy(), true); e.setUserData(UD_GENERATED_IN_SNAPSHOT, outcome); derived.getSnapshot().addElement(outcome); if (walksInto(diff.getElement(), e)) { @@ -1042,7 +1042,7 @@ public class ProfileUtilities { // don't do this. should already be in snapshot ... addInheritedElementsForSpecialization(snapshot, focus, sd.getBaseDefinition(), path, url, weburl); for (ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().contains(".")) { - ElementDefinition outcome = updateURLs(url, weburl, ed.copy()); + ElementDefinition outcome = updateURLs(url, weburl, ed.copy(), true); outcome.setPath(outcome.getPath().replace(sd.getTypeName(), path)); snapshot.getElement().add(outcome); } else { @@ -1548,7 +1548,6 @@ public class ProfileUtilities { protected void removeStatusExtensions(ElementDefinition outcome) { outcome.removeExtension(ToolingExtensions.EXT_FMM_LEVEL); outcome.removeExtension(ToolingExtensions.EXT_FMM_SUPPORT); - outcome.removeExtension(ToolingExtensions.EXT_FMM_DERIVED); outcome.removeExtension(ToolingExtensions.EXT_STANDARDS_STATUS); outcome.removeExtension(ToolingExtensions.EXT_NORMATIVE_VERSION); outcome.removeExtension(ToolingExtensions.EXT_WORKGROUP); @@ -1911,7 +1910,7 @@ public class ProfileUtilities { * @param element - the Element to update * @return - the updated Element */ - public ElementDefinition updateURLs(String url, String webUrl, ElementDefinition element) { + public ElementDefinition updateURLs(String url, String webUrl, ElementDefinition element, boolean processRelatives) { if (element != null) { ElementDefinition defn = element; if (defn.hasBinding() && defn.getBinding().hasValueSet() && defn.getBinding().getValueSet().startsWith("#")) @@ -1929,24 +1928,24 @@ public class ProfileUtilities { if (webUrl != null) { // also, must touch up the markdown if (element.hasDefinition()) { - element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false)); + element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, processRelatives)); } if (element.hasComment()) { - element.setComment(processRelativeUrls(element.getComment(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false)); + element.setComment(processRelativeUrls(element.getComment(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, processRelatives)); } if (element.hasRequirements()) { - element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false)); + element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, processRelatives)); } if (element.hasMeaningWhenMissing()) { - element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false)); + element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, processRelatives)); } if (element.hasBinding() && element.getBinding().hasDescription()) { - element.getBinding().setDescription(processRelativeUrls(element.getBinding().getDescription(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false)); + element.getBinding().setDescription(processRelativeUrls(element.getBinding().getDescription(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, processRelatives)); } for (Extension ext : element.getExtension()) { if (ext.hasValueMarkdownType()) { MarkdownType md = ext.getValueMarkdownType(); - md.setValue(processRelativeUrls(md.getValue(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, false)); + md.setValue(processRelativeUrls(md.getValue(), webUrl, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, processRelatives)); } } } @@ -2371,7 +2370,6 @@ public class ProfileUtilities { if (elist.size() == 2) { dest.getExtension().remove(elist.get(1)); } - updateExtensionsFromDefinition(dest, source); for (ElementDefinition ed : obligationProfileElements) { @@ -2423,6 +2421,9 @@ public class ProfileUtilities { if (e.hasDefinition()) { base.setDefinition(processRelativeUrls(e.getDefinition(), webroot, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, true)); } + if (e.getBinding().hasDescription()) { + base.getBinding().setDescription(processRelativeUrls(e.getBinding().getDescription(), webroot, context.getSpecUrl(), context.getResourceNames(), masterSourceFileNames, localFileNames, true)); + } base.setShort(e.getShort()); if (e.hasCommentElement()) base.setCommentElement(e.getCommentElement()); @@ -2466,9 +2467,9 @@ public class ProfileUtilities { if (derived.hasDefinitionElement()) { if (derived.getDefinition().startsWith("...")) base.setDefinition(Utilities.appendDerivedTextToBase(base.getDefinition(), derived.getDefinition())); - else if (!Base.compareDeep(derived.getDefinitionElement(), base.getDefinitionElement(), false)) + else if (!Base.compareDeep(derived.getDefinitionElement(), base.getDefinitionElement(), false)) { base.setDefinitionElement(derived.getDefinitionElement().copy()); - else if (trimDifferential) + } else if (trimDifferential) derived.setDefinitionElement(null); else if (derived.hasDefinitionElement()) derived.getDefinitionElement().setUserData(UD_DERIVATION_EQUALS, true); From 8056dced3efb3a44d66178aa52351dac367dd389 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 12 Sep 2024 11:55:24 +0800 Subject: [PATCH 20/21] Allow JSON named extensions to be structure types other than logical --- .../main/java/org/hl7/fhir/r5/context/ContextUtilities.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java index 02890a037..12bc847a2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java @@ -366,7 +366,11 @@ public class ContextUtilities implements ProfileKnowledgeProvider { public StructureDefinition fetchByJsonName(String key) { for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) { ElementDefinition ed = sd.getSnapshot().getElementFirstRep(); - if (sd.getKind() == StructureDefinitionKind.LOGICAL && ed != null && ed.hasExtension(ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED) && + if (/*sd.getKind() == StructureDefinitionKind.LOGICAL && */ + // this is turned off because it's valid to use a FHIR type directly in + // an extension of this kind, and that can't be a logical model. Any profile on + // a type is acceptable as long as it has the json name on it + ed != null && ed.hasExtension(ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED) && key.equals(ToolingExtensions.readStringExtension(ed, ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED))) { return sd; } From 07f1981a8c49d1843e1391977bddd33ff0b7e6ae Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 12 Sep 2024 11:56:04 +0800 Subject: [PATCH 21/21] Fix for NPE processing packages --- .../fhir/r5/renderers/StructureDefinitionRenderer.java | 2 +- .../main/java/org/hl7/fhir/utilities/npm/NpmPackage.java | 8 +++++--- pom.xml | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java index bcb245cc6..5e7b63d20 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java @@ -3431,7 +3431,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { sdMapCache.put(url, sdCache); String webroot = sd.getUserString("webroot"); for (ElementDefinition e : sd.getSnapshot().getElement()) { - context.getProfileUtilities().updateURLs(sd.getUrl(), webroot, e); + context.getProfileUtilities().updateURLs(sd.getUrl(), webroot, e, false); sdCache.put(e.getId(), e); } } 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 5ab8a9113..6daa184af 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 @@ -237,9 +237,11 @@ public class NpmPackage { public List listFiles() { List res = new ArrayList<>(); if (folder != null) { - for (File f : folder.listFiles()) { - if (!f.isDirectory() && !Utilities.existsInList(f.getName(), "package.json", ".index.json", ".index.db", ".oids.json", ".oids.db")) { - res.add(f.getName()); + if (folder.exists()) { + for (File f : folder.listFiles()) { + if (!f.isDirectory() && !Utilities.existsInList(f.getName(), "package.json", ".index.json", ".index.db", ".oids.json", ".oids.db")) { + res.add(f.getName()); + } } } } else { diff --git a/pom.xml b/pom.xml index 4f8248ecc..e842abb9d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 1.26.0 32.0.1-jre 6.4.1 - 1.5.21 + 1.5.22-SNAPSHOT 2.17.0 5.9.2 1.8.2