From 92d46093b33c6708bf36112e6e3d7bb8e872c57e Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 23 Jan 2024 11:45:18 -0500 Subject: [PATCH 01/28] WIP add base engines to ValidationService --- .../cli/services/ValidationService.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) 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 1b38b541f..ea615bb4a 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 @@ -74,11 +74,27 @@ public class ValidationService { private final SessionCache sessionCache; private String runDate; + private Map baseEngines = new HashMap<>(); + + public void putBaseEngine(String key, CliContext cliContext) throws IOException, URISyntaxException { + //FIXME instantiate engine and put below + String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); + + ValidationEngine baseEngine = buildValidationEngine(cliContext, definitions, new TimeTracker()); + baseEngines.put(key, baseEngine); + } + + public ValidationEngine getBaseEngine(String key) { + return baseEngines.get(key); + } + public ValidationService() { sessionCache = new PassiveExpiringSessionCache(); runDate = new SimpleDateFormat("hh:mm:ss", new Locale("en", "US")).format(new Date()); } + + public ValidationService(SessionCache cache) { this.sessionCache = cache; } @@ -443,7 +459,15 @@ public class ValidationService { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } - ValidationEngine validator = buildValidationEngine(cliContext, definitions, tt); + ValidationEngine validator; + if (cliContext.isCheckIPSCodes()) { + System.out.println("Getting base engine: IPS"); + validator = new ValidationEngine(baseEngines.get("ips")); + } else { + System.out.println("Building new validator engine."); + validator = buildValidationEngine(cliContext, definitions, tt); + } + sessionId = sessionCache.cacheSession(validator); } else { System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id."); From 1ff9bd59151302c1dd3eca498c4c44b91c126021 Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 25 Jan 2024 18:17:50 -0500 Subject: [PATCH 02/28] WIP: selector widget --- .../fhir/validation/cli/model/CliContext.java | 22 +++++++++++++++---- .../cli/services/ValidationService.java | 6 +++-- 2 files changed, 22 insertions(+), 6 deletions(-) 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 445e8a59b..bd08285f3 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 @@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class CliContext { + @JsonProperty("baseEngine") + private String baseEngine = null; @JsonProperty("doNative") private boolean doNative = false; @JsonProperty("hintAboutNonMustSupport") @@ -156,7 +158,17 @@ public class CliContext { @JsonProperty("bestPracticeLevel") private BestPracticeWarningLevel bestPracticeLevel = BestPracticeWarningLevel.Warning; - + + @JsonProperty("baseEngine") + public String getBaseEngine() { + return baseEngine; + } + + @JsonProperty("baseEngine") + public CliContext setBaseEngine(String baseEngine) { + this.baseEngine = baseEngine; + return this; + } @JsonProperty("map") public String getMap() { @@ -745,7 +757,8 @@ public class CliContext { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CliContext that = (CliContext) o; - return doNative == that.doNative && + return Objects.equals(baseEngine, that.baseEngine) && + doNative == that.doNative && hintAboutNonMustSupport == that.hintAboutNonMustSupport && recursive == that.recursive && doDebug == that.doDebug && @@ -798,7 +811,7 @@ public class CliContext { @Override public int hashCode() { - return Objects.hash(doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, + 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); @@ -807,7 +820,8 @@ public class CliContext { @Override public String toString() { return "CliContext{" + - "doNative=" + doNative + + " baseEngine=" + baseEngine + + ", doNative=" + doNative + ", extensions=" + extensions + ", hintAboutNonMustSupport=" + hintAboutNonMustSupport + ", recursive=" + recursive + 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 ea615bb4a..ee1acc5f1 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 @@ -88,6 +88,8 @@ public class ValidationService { return baseEngines.get(key); } + public boolean hasBaseEngineForKey(String key) { return baseEngines.containsKey(key); } + public ValidationService() { sessionCache = new PassiveExpiringSessionCache(); runDate = new SimpleDateFormat("hh:mm:ss", new Locale("en", "US")).format(new Date()); @@ -460,9 +462,9 @@ public class ValidationService { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } ValidationEngine validator; - if (cliContext.isCheckIPSCodes()) { + if (hasBaseEngineForKey(cliContext.getBaseEngine())) { System.out.println("Getting base engine: IPS"); - validator = new ValidationEngine(baseEngines.get("ips")); + validator = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); } else { System.out.println("Building new validator engine."); validator = buildValidationEngine(cliContext, definitions, tt); From b0a68a788e5ab40eb15ee8e2021cd77ea9b23d33 Mon Sep 17 00:00:00 2001 From: "dotasek.dev" Date: Mon, 29 Jan 2024 12:11:45 -0500 Subject: [PATCH 03/28] Fix base engine println --- .../org/hl7/fhir/validation/cli/services/ValidationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ee1acc5f1..cadd5ef26 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 @@ -463,7 +463,7 @@ public class ValidationService { } ValidationEngine validator; if (hasBaseEngineForKey(cliContext.getBaseEngine())) { - System.out.println("Getting base engine: IPS"); + System.out.println("Getting base engine: " + cliContext.getBaseEngine()); validator = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); } else { System.out.println("Building new validator engine."); From 2c61dd150b2133396fef472fbea278b25d867a1a Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 29 Jan 2024 17:54:03 -0500 Subject: [PATCH 04/28] Add tests for base engine use --- .../cli/services/ValidationServiceTest.java | 26 +++- .../tests/ValidationEngineTests.java | 140 ++++++++++++++---- .../4.0.1/all-systems.cache | 125 +++------------- 3 files changed, 158 insertions(+), 133 deletions(-) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java index 945c9b537..ffc52303c 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java @@ -36,6 +36,7 @@ import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.model.FileInfo; import org.hl7.fhir.validation.cli.model.ValidationRequest; +import org.hl7.fhir.validation.cli.model.ValidationResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -52,8 +53,9 @@ class ValidationServiceTest { final String DUMMY_SV = "1.2.3"; + @DisplayName("Test validation session persists in session cache") @Test - void validateSources() throws Exception { + void validationSessionTest() throws Exception { TestingUtilities.injectCorePackageLoader(); SessionCache sessionCache = Mockito.spy(new PassiveExpiringSessionCache()); ValidationService myService = new ValidationService(sessionCache); @@ -80,6 +82,28 @@ class ValidationServiceTest { } } + @DisplayName("Test validation session will inherit a base validation engine") + @Test + void validationSessionBaseEngineTest() throws Exception { + TestingUtilities.injectCorePackageLoader(); + + ValidationService myService = Mockito.spy(new ValidationService()); + + CliContext baseContext = new CliContext().setBaseEngine("myDummyKey").setSv("4.0.1").setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService")); + myService.putBaseEngine("myDummyKey", baseContext); + + String resource = IOUtils.toString(getFileFromResourceAsStream("detected_issues.json"), StandardCharsets.UTF_8); + List filesToValidate = new ArrayList<>(); + filesToValidate.add(new FileInfo().setFileName("test_resource.json").setFileContent(resource).setFileType(Manager.FhirFormat.JSON.getExtension())); + + ValidationRequest request = new ValidationRequest().setCliContext(new CliContext()).setFilesToValidate(filesToValidate); + myService.validateSources(request); + + verify(myService, Mockito.times(1)).getBaseEngine("myDummyKey"); + + + } + private InputStream getFileFromResourceAsStream(String fileName) { // The class loader that loaded the class ClassLoader classLoader = getClass().getClassLoader(); 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 2a5c50930..451e4e3a9 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 @@ -1,5 +1,6 @@ package org.hl7.fhir.validation.tests; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; @@ -17,6 +18,7 @@ import org.hl7.fhir.validation.IgLoader; import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.tests.utilities.TestUtilities; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class ValidationEngineTests { @@ -26,6 +28,88 @@ public class ValidationEngineTests { public static boolean inbuild; + @Test + @DisplayName("A ValidationEngine copied from another validation engine shouldn't interfere with the original during validations") + void validateWithParallelCopiedEngine() throws Exception { + + final String INPUT_1 = "patient-duplicate.json"; + final String INPUT_2 = "patient-lang1.json"; + final String INPUT_3 = "patient-id-bad-1.json"; + + final String[] ISSUE_CODES_1 = { "invalid" }; + final String[] ISSUE_CODES_2 = {"business-rule"}; + final String[] ISSUE_CODES_3 = {"invalid", "invariant"}; + + ValidationEngine originalEngine = TestUtilities.getValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, FhirPublication.R4, "4.0.1"); + + final ValidationEngine[] validationEngines = new ValidationEngine[10]; + validationEngines[0] = originalEngine; + + final OperationOutcome[] outcomes = new OperationOutcome[validationEngines.length]; + + for (int i = 1; i < validationEngines.length; i++) { + validationEngines[i] = new ValidationEngine(originalEngine); + } + + final String[] testInputs = { + INPUT_1, + INPUT_1, + INPUT_2, + INPUT_3, + INPUT_1, + INPUT_2, + INPUT_3, + INPUT_1, + INPUT_2, + INPUT_3 + }; + // Pick 3 validation cases + final String[][] testCodes = { + ISSUE_CODES_1, + ISSUE_CODES_1, + ISSUE_CODES_2, + ISSUE_CODES_3, + ISSUE_CODES_1, + ISSUE_CODES_2, + ISSUE_CODES_3, + ISSUE_CODES_1, + ISSUE_CODES_2, + ISSUE_CODES_3 + }; + + + List threads = new ArrayList<>(); + for (int i = 0; i < validationEngines.length; i++) { + final int index = i; + Thread t = new Thread(() -> { + try { + final String testInput = testInputs[index]; + outcomes[index] = validationEngines[index].validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", testInput), null); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Thread " + index + " failed"); + } + }); + t.start(); + threads.add(t); + } + threads.forEach(t -> { + try { + t.join(); + } catch (InterruptedException e) { + + } + }); + + for (int i = 0; i < outcomes.length; i++) { + assertEquals(testCodes[i].length, outcomes[i].getIssue().size()); + for (int j = 0; j < outcomes[i].getIssue().size(); j++) { + System.out.print(i + " " + j); + assertEquals(testCodes[i][j], outcomes[i].getIssue().get(j).getCode().toCode()); + } + } + } + @Test public void test401Xml() throws Exception { if (!TestUtilities.silent) @@ -43,9 +127,9 @@ public class ValidationEngineTests { System.out.println(" " + iss.getDetails().getText()); } } - Assertions.assertEquals(0, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(1, h); + assertEquals(0, e); + assertEquals(0, w); + assertEquals(1, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); } @@ -60,9 +144,9 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(0, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(1, h); + assertEquals(0, e); + assertEquals(0, w); + assertEquals(1, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -85,9 +169,9 @@ public class ValidationEngineTests { System.out.println(" " + iss.getDetails().getText()); } } - Assertions.assertEquals(0, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(1, h); + assertEquals(0, e); + assertEquals(0, w); + assertEquals(1, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); } @@ -108,9 +192,9 @@ public class ValidationEngineTests { System.out.println(" " + iss.getDetails().getText()); } } - Assertions.assertEquals(0, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(1, h); + assertEquals(0, e); + assertEquals(0, w); + assertEquals(1, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -135,9 +219,9 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(2, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(0, h); + assertEquals(2, e); + assertEquals(0, w); + assertEquals(0, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -163,9 +247,9 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(2, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(0, h); + assertEquals(2, e); + assertEquals(0, w); + assertEquals(0, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -191,9 +275,9 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(1, e); - Assertions.assertEquals(2, w); - Assertions.assertEquals(2, h); + assertEquals(1, e); + assertEquals(2, w); + assertEquals(2, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -216,7 +300,7 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(0, e); + assertEquals(0, e); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -242,9 +326,9 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(1, e); - Assertions.assertEquals(0, w); - Assertions.assertEquals(0, h); + assertEquals(1, e); + assertEquals(0, w); + assertEquals(0, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); @@ -269,9 +353,9 @@ public class ValidationEngineTests { int e = errors(op); int w = warnings(op); int h = hints(op); - Assertions.assertEquals(0, e); - Assertions.assertEquals(6, w); - Assertions.assertEquals(2, h); + assertEquals(0, e); + assertEquals(6, w); + assertEquals(2, h); assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server"); if (!TestUtilities.silent) System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages"); diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache index f7f0e1275..f0971df16 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache @@ -13,7 +13,6 @@ v: { "code" : "application/octet-stream", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -34,7 +33,6 @@ v: { "code" : "de-CH", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -55,7 +53,6 @@ v: { "code" : "application/pdf", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -77,7 +74,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -99,7 +95,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -120,7 +115,6 @@ v: { "code" : "image/*", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -142,7 +136,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -163,7 +156,6 @@ v: { "code" : "en-IN", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -184,7 +176,6 @@ v: { "code" : "image/jpg", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -205,7 +196,6 @@ v: { "code" : "de-CH", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -226,7 +216,6 @@ v: { "code" : "de-CH", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -248,7 +237,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -270,7 +258,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -292,7 +279,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -314,7 +300,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -337,7 +322,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#cm' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodyweight|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -376,7 +360,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -399,7 +382,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#kg/m2' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodyweight|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -440,7 +422,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230901", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -463,7 +444,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#kg' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodylength|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -503,7 +483,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#kg/m2' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodylength|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -542,7 +521,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -566,7 +544,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230901", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -590,7 +567,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230901", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -611,7 +587,6 @@ v: { "code" : "application/pdf", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -635,7 +610,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230901", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -659,7 +633,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230901", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -682,7 +655,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#min' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodyweight|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -722,7 +694,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#min' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodylength|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -762,7 +733,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#min' was not found in the value set 'https://mednet.swiss/fhir/ValueSet/mni-timeOfGestation|0.5.0'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -802,7 +772,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#mmol/L' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodyweight|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -842,7 +811,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#mmol/L' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodylength|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -882,7 +850,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#mmol/L' was not found in the value set 'https://mednet.swiss/fhir/ValueSet/mni-timeOfGestation|0.5.0'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -922,7 +889,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#%' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodyweight|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -962,7 +928,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#%' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodylength|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1002,7 +967,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#%' was not found in the value set 'https://mednet.swiss/fhir/ValueSet/mni-timeOfGestation|0.5.0'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1042,7 +1006,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#wk' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodyweight|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1082,7 +1045,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#wk' was not found in the value set 'http://hl7.org/fhir/ValueSet/ucum-bodylength|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1122,7 +1084,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#kg' was not found in the value set 'https://mednet.swiss/fhir/ValueSet/mni-timeOfGestation|0.5.0'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1161,7 +1122,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1184,7 +1144,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#cm' was not found in the value set 'https://mednet.swiss/fhir/ValueSet/mni-timeOfGestation|0.5.0'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1223,7 +1182,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1246,7 +1204,6 @@ v: { "error" : "The provided code 'http://unitsofmeasure.org#kg/m2' was not found in the value set 'https://mednet.swiss/fhir/ValueSet/mni-timeOfGestation|0.5.0'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1285,7 +1242,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1307,7 +1263,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1328,7 +1283,6 @@ v: { "code" : "json", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1349,7 +1303,6 @@ v: { "code" : "xml", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1370,7 +1323,6 @@ v: { "code" : "application/fhir+json", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1391,7 +1343,6 @@ v: { "code" : "en-US", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1412,7 +1363,6 @@ v: { "code" : "en", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1433,7 +1383,6 @@ v: { "code" : "text/plain", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1457,7 +1406,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230901", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1480,7 +1428,6 @@ v: { "system" : "urn:iso:std:iso:3166", "version" : "2018", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1501,7 +1448,6 @@ v: { "code" : "text/css", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1522,7 +1468,6 @@ v: { "code" : "de-CH", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1547,7 +1492,6 @@ v: { "error" : "The provided code 'http://ihe.net/fhir/ihe.formatcode.fhir/CodeSystem/formatcode#urn:ihe:iti:xds:2017:mimeTypeSufficient ('MimeType sufficient')' was not found in the value set 'http://hl7.org/fhir/ValueSet/formatcodes|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1646,7 +1590,6 @@ v: { "system" : "http://loinc.org", "version" : "2.74", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1667,7 +1610,6 @@ v: { "code" : "en-US", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1746,7 +1688,6 @@ v: { "code" : "en-NZ", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1768,7 +1709,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1790,7 +1730,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1812,7 +1751,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1834,7 +1772,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1856,7 +1793,6 @@ v: { "system" : "urn:iso:std:iso:3166", "version" : "2018", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1880,7 +1816,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#26643006' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1921,7 +1856,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#767525000' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1961,7 +1895,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -1984,7 +1917,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2008,7 +1940,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#109081006' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2048,7 +1979,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2072,7 +2002,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#292954005' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2112,7 +2041,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2136,7 +2064,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#428673006' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2176,7 +2103,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2200,7 +2126,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#7947003' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2241,7 +2166,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#81464008' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2282,7 +2206,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#48546005' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2322,7 +2245,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2346,7 +2268,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#158965000' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2387,7 +2308,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#96309000' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2428,7 +2348,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#714081009' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2469,7 +2388,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#25246002' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2510,7 +2428,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#96067005' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2550,7 +2467,6 @@ v: { "system" : "http://snomed.info/sct", "version" : "http://snomed.info/sct/900000000000207008/version/20230131", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2574,7 +2490,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#34206005' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2615,7 +2530,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#108537001' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2656,7 +2570,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#126212009' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2697,7 +2610,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#292360004' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2738,7 +2650,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#16217701000119102' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2779,7 +2690,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#287903004' was not found in the value set 'http://terminology.hl7.org/ValueSet/snomed-intl-ips|20211027'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -2817,7 +2727,6 @@ v: { "code" : "application/xml", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2838,7 +2747,6 @@ v: { "code" : "text/xml", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2919,7 +2827,6 @@ v: { "system" : "urn:iso:std:iso:3166", "version" : "2018", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -2940,7 +2847,6 @@ v: { "code" : "text/cql.identifier", "system" : "urn:ietf:bcp:13", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3021,7 +2927,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3046,7 +2951,6 @@ v: { "error" : "The provided code 'urn:iso:std:iso:11073:10101#150456 ('MDC_PULS_OXIM_SAT_O2')' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -3102,7 +3006,6 @@ v: { "error" : "The provided code 'http://snomed.info/sct#276885007' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult|4.0.1'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -3141,7 +3044,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3162,7 +3064,6 @@ v: { "code" : "en-AU", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3183,7 +3084,6 @@ v: { "code" : "en", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3204,7 +3104,6 @@ v: { "code" : "en-US", "system" : "urn:ietf:bcp:47", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3227,7 +3126,6 @@ v: { "system" : "urn:iso:std:iso:3166", "version" : "2018", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3249,7 +3147,6 @@ v: { "system" : "http://unitsofmeasure.org", "version" : "2.0.1", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome" } @@ -3271,7 +3168,6 @@ v: { "error" : "The provided code 'urn:ietf:bcp:13#[%payloadFormat%]' was not found in the value set 'http://hl7.org/fhir/ValueSet/mimetypes|4.0.1'; Unknown code '[%payloadFormat%]' in the CodeSystem 'urn:ietf:bcp:13' version ''", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -3312,3 +3208,24 @@ v: { } ------------------------------------------------------------------------------------- +{"code" : { + "code" : "en-AU" +}, "url": "http://hl7.org/fhir/ValueSet/languages", "version": "4.0.1", "langs":"en-AU", "useServer":"true", "useClient":"true", "guessSystem":"true", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "English (Region=Australia)", + "code" : "en-AU", + "system" : "urn:ietf:bcp:47", + "server" : "http://tx-dev.fhir.org/r4", + "unknown-systems" : "", + "issues" : { + "resourceType" : "OperationOutcome" +} + +} +------------------------------------------------------------------------------------- From b1973d6eb2a50e5b89a0421dbacff3437cc91237 Mon Sep 17 00:00:00 2001 From: "dotasek.dev" Date: Tue, 30 Jan 2024 11:53:56 -0500 Subject: [PATCH 05/28] Fix incomplete test --- .../cli/services/ValidationServiceTest.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java index ffc52303c..157f29c84 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java @@ -60,9 +60,7 @@ class ValidationServiceTest { SessionCache sessionCache = Mockito.spy(new PassiveExpiringSessionCache()); ValidationService myService = new ValidationService(sessionCache); - String resource = IOUtils.toString(getFileFromResourceAsStream("detected_issues.json"), StandardCharsets.UTF_8); - List filesToValidate = new ArrayList<>(); - filesToValidate.add(new FileInfo().setFileName("test_resource.json").setFileContent(resource).setFileType(Manager.FhirFormat.JSON.getExtension())); + List filesToValidate = getFilesToValidate(); ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService"))).setFilesToValidate(filesToValidate); // Validation run 1...nothing cached yet @@ -91,17 +89,33 @@ class ValidationServiceTest { CliContext baseContext = new CliContext().setBaseEngine("myDummyKey").setSv("4.0.1").setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService")); myService.putBaseEngine("myDummyKey", baseContext); + verify(myService, Mockito.times(1)).buildValidationEngine(any(), any(), any()); - String resource = IOUtils.toString(getFileFromResourceAsStream("detected_issues.json"), StandardCharsets.UTF_8); + { + final List filesToValidate = getFilesToValidate(); + final ValidationRequest request = new ValidationRequest().setCliContext(new CliContext()).setFilesToValidate(filesToValidate); + myService.validateSources(request); + + verify(myService, Mockito.times(0)).getBaseEngine("myDummyKey"); + verify(myService, Mockito.times(2)).buildValidationEngine(any(), any(), any()); + } + + { + final List filesToValidate = getFilesToValidate(); + final ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setBaseEngine("myDummyKey")).setFilesToValidate(filesToValidate); + myService.validateSources(request); + + verify(myService, Mockito.times(1)).getBaseEngine("myDummyKey"); + verify(myService, Mockito.times(2)).buildValidationEngine(any(), any(), any()); + } + } + + private List getFilesToValidate() throws IOException { List filesToValidate = new ArrayList<>(); + String resource = IOUtils.toString(getFileFromResourceAsStream("detected_issues.json"), StandardCharsets.UTF_8); + filesToValidate.add(new FileInfo().setFileName("test_resource.json").setFileContent(resource).setFileType(Manager.FhirFormat.JSON.getExtension())); - - ValidationRequest request = new ValidationRequest().setCliContext(new CliContext()).setFilesToValidate(filesToValidate); - myService.validateSources(request); - - verify(myService, Mockito.times(1)).getBaseEngine("myDummyKey"); - - + return filesToValidate; } private InputStream getFileFromResourceAsStream(String fileName) { From 464af9de906d5ab1831bcf9873df15e94ade33b3 Mon Sep 17 00:00:00 2001 From: "dotasek.dev" Date: Wed, 31 Jan 2024 09:28:08 -0500 Subject: [PATCH 06/28] Only do version determination when ValidationEngine must be created --- .../cli/services/ValidationService.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 0299f0463..304babb13 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 @@ -102,10 +102,7 @@ public class ValidationService { } public ValidationResponse validateSources(ValidationRequest request) throws Exception { - if (request.getCliContext().getSv() == null) { - String sv = determineVersion(request.getCliContext(), request.sessionId); - request.getCliContext().setSv(sv); - } + String definitions = VersionUtilities.packageForVersion(request.getCliContext().getSv()) + "#" + VersionUtilities.getCurrentVersion(request.getCliContext().getSv()); @@ -486,6 +483,8 @@ public class ValidationService { public String initializeValidator(CliContext cliContext, String definitions, TimeTracker tt, String sessionId) throws Exception { tt.milestone(); + + if (!sessionCache.sessionExists(sessionId)) { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); @@ -496,6 +495,10 @@ public class ValidationService { validator = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); } else { System.out.println("Building new validator engine."); + if (cliContext.getSv() == null) { + String sv = determineVersion(cliContext); + cliContext.setSv(sv); + } validator = buildValidationEngine(cliContext, definitions, tt); } @@ -579,12 +582,7 @@ public class ValidationService { System.out.println(" Package Summary: "+ validationEngine.getContext().loadedPackageSummary()); } - public String determineVersion(CliContext cliContext) throws Exception { - return determineVersion(cliContext, null); - } - - public String determineVersion(CliContext cliContext, String sessionId) throws Exception { if (cliContext.getMode() != EngineMode.VALIDATION && cliContext.getMode() != EngineMode.INSTALL) { return "5.0"; } From 77f8cdb9dfaa37ec270d43d31a1af238a1948bed Mon Sep 17 00:00:00 2001 From: "dotasek.dev" Date: Wed, 31 Jan 2024 15:21:32 -0500 Subject: [PATCH 07/28] Fix lang and locale settings for baseEngine use --- .../fhir/validation/cli/services/ValidationService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 304babb13..9ad95d0d6 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 @@ -492,7 +492,13 @@ public class ValidationService { ValidationEngine validator; if (hasBaseEngineForKey(cliContext.getBaseEngine())) { System.out.println("Getting base engine: " + cliContext.getBaseEngine()); - validator = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); + validator = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); + /* As a service, it wouldn't be efficient to have a base validation engine + * for every language. So we just use the baseEngine and set the language + * manually afterward. + */ + validator.setLanguage(cliContext.getLang()); + validator.setLocale(cliContext.getLocale()); } else { System.out.println("Building new validator engine."); if (cliContext.getSv() == null) { From cb2d6c491e807355f2b7d52c32a8087465b7b1d7 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 31 Jan 2024 16:24:37 -0500 Subject: [PATCH 08/28] Set locale and language for baseEngine use --- .../validation/cli/services/ValidationService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 9ad95d0d6..bc35dde60 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 @@ -489,26 +489,26 @@ public class ValidationService { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } - ValidationEngine validator; + ValidationEngine validationEngine; if (hasBaseEngineForKey(cliContext.getBaseEngine())) { System.out.println("Getting base engine: " + cliContext.getBaseEngine()); - validator = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); + validationEngine = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); /* As a service, it wouldn't be efficient to have a base validation engine * for every language. So we just use the baseEngine and set the language * manually afterward. */ - validator.setLanguage(cliContext.getLang()); - validator.setLocale(cliContext.getLocale()); + validationEngine.setLanguage(cliContext.getLang()); + validationEngine.setLocale(cliContext.getLocale()); } else { System.out.println("Building new validator engine."); if (cliContext.getSv() == null) { String sv = determineVersion(cliContext); cliContext.setSv(sv); } - validator = buildValidationEngine(cliContext, definitions, tt); + validationEngine = buildValidationEngine(cliContext, definitions, tt); } - sessionId = sessionCache.cacheSession(validator); + sessionId = sessionCache.cacheSession(validationEngine); } else { System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id."); } From 2e7694a91ab97634769eba05ab02cd09991d53df Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 28 May 2024 16:21:05 -0400 Subject: [PATCH 09/28] Remove FIXME --- .../hl7/fhir/validation/cli/services/ValidationService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 14f7e44ff..8afc6d114 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 @@ -78,7 +78,7 @@ public class ValidationService { private Map baseEngines = new HashMap<>(); public void putBaseEngine(String key, CliContext cliContext) throws IOException, URISyntaxException { - //FIXME instantiate engine and put below + String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); ValidationEngine baseEngine = buildValidationEngine(cliContext, definitions, new TimeTracker()); @@ -484,8 +484,6 @@ public class ValidationService { public String initializeValidator(CliContext cliContext, String definitions, TimeTracker tt, String sessionId) throws Exception { tt.milestone(); - - if (!sessionCache.sessionExists(sessionId)) { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); From 596d5b55978748a94031f89c4ca4c8a1b4e3a668 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 28 May 2024 17:22:55 -0400 Subject: [PATCH 10/28] Add sanity check for locale changes --- .../org/hl7/fhir/utilities/i18n/I18nBaseTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java index 9a1e02991..f19b0eea9 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/i18n/I18nBaseTest.java @@ -151,4 +151,17 @@ class I18nBaseTest { assertNull(enLocale.getRootKeyFromPlural(rootKey + "_many")); } + @Test + public void testMessagesChangeWhenLocaleDoes() { + I18nTestClass i18nInstance = new I18nTestClass(); + i18nInstance.setLocale(Locale.forLanguageTag("de")); + + String deMessage = i18nInstance.formatMessage(I18nConstants.ERROR_PARSING_JSON_, "test"); + assertEquals("Fehler beim Parsen von JSON: test", deMessage); + + i18nInstance.setLocale(Locale.forLanguageTag("en")); + String enMessage = i18nInstance.formatMessage(I18nConstants.ERROR_PARSING_JSON_, "test"); + assertEquals("Error parsing JSON: test", enMessage); + + } } \ No newline at end of file From 40cccaad5bd3a415a9f85503f90dd6e0c2a59630 Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 30 May 2024 13:36:59 -0400 Subject: [PATCH 11/28] Make baseEngines concurrent, just in case --- .../fhir/validation/cli/services/ValidationService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 8afc6d114..1c18cfcbe 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 @@ -2,7 +2,6 @@ package org.hl7.fhir.validation.cli.services; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -11,6 +10,7 @@ import java.lang.management.MemoryMXBean; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; @@ -75,10 +75,9 @@ public class ValidationService { private final SessionCache sessionCache; private String runDate; - private Map baseEngines = new HashMap<>(); + private final Map baseEngines = new ConcurrentHashMap<>(); public void putBaseEngine(String key, CliContext cliContext) throws IOException, URISyntaxException { - String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); ValidationEngine baseEngine = buildValidationEngine(cliContext, definitions, new TimeTracker()); @@ -89,6 +88,8 @@ public class ValidationService { return baseEngines.get(key); } + public Set getBaseEngineKeys() { return baseEngines.keySet(); } + public boolean hasBaseEngineForKey(String key) { return baseEngines.containsKey(key); } public ValidationService() { From a2cf7897f0c8f18a57d819c9993a41349a660b8b Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 30 May 2024 14:48:03 -0400 Subject: [PATCH 12/28] Log session cache size --- .../fhir/validation/cli/services/ValidationService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 1c18cfcbe..a8565b8fe 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 @@ -491,7 +491,7 @@ public class ValidationService { } ValidationEngine validationEngine; if (hasBaseEngineForKey(cliContext.getBaseEngine())) { - System.out.println("Getting base engine: " + cliContext.getBaseEngine()); + System.out.println("Building new validator engine from base engine: " + cliContext.getBaseEngine()); validationEngine = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); /* As a service, it wouldn't be efficient to have a base validation engine * for every language. So we just use the baseEngine and set the language @@ -500,17 +500,17 @@ public class ValidationService { validationEngine.setLanguage(cliContext.getLang()); validationEngine.setLocale(cliContext.getLocale()); } else { - System.out.println("Building new validator engine."); + System.out.println("Building new validator engine from CliContext"); if (cliContext.getSv() == null) { String sv = determineVersion(cliContext); cliContext.setSv(sv); } validationEngine = buildValidationEngine(cliContext, definitions, tt); } - sessionId = sessionCache.cacheSession(validationEngine); + System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size()); } else { - System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id."); + System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id. Cache size = " + sessionCache.getSessionIds().size()); } return sessionId; } From ff4e237bd57879571cf9600d5453facadf8505f6 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 5 Jun 2024 15:29:00 -0400 Subject: [PATCH 13/28] Catch null --- .../org/hl7/fhir/validation/cli/services/ValidationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a8565b8fe..9d100cd94 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 @@ -490,7 +490,7 @@ public class ValidationService { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } ValidationEngine validationEngine; - if (hasBaseEngineForKey(cliContext.getBaseEngine())) { + if (cliContext.getBaseEngine() != null && hasBaseEngineForKey(cliContext.getBaseEngine())) { System.out.println("Building new validator engine from base engine: " + cliContext.getBaseEngine()); validationEngine = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); /* As a service, it wouldn't be efficient to have a base validation engine From 563d6fe27ae87568c9cdb623e6a4fff9f91cb2c7 Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 17 Jun 2024 11:26:25 -0400 Subject: [PATCH 14/28] Gentle refactor + track session number --- .../cli/services/ValidationService.java | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) 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 49eae3937..cab82c48c 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 @@ -489,24 +489,7 @@ public class ValidationService { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } - ValidationEngine validationEngine; - if (cliContext.getBaseEngine() != null && hasBaseEngineForKey(cliContext.getBaseEngine())) { - System.out.println("Building new validator engine from base engine: " + cliContext.getBaseEngine()); - validationEngine = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); - /* As a service, it wouldn't be efficient to have a base validation engine - * for every language. So we just use the baseEngine and set the language - * manually afterward. - */ - validationEngine.setLanguage(cliContext.getLang()); - validationEngine.setLocale(cliContext.getLocale()); - } else { - System.out.println("Building new validator engine from CliContext"); - if (cliContext.getSv() == null) { - String sv = determineVersion(cliContext); - cliContext.setSv(sv); - } - validationEngine = buildValidationEngine(cliContext, definitions, tt); - } + ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, definitions, tt); sessionId = sessionCache.cacheSession(validationEngine); System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size()); } else { @@ -515,6 +498,28 @@ public class ValidationService { return sessionId; } + private ValidationEngine getValidationEngineFromCliContext(CliContext cliContext, String definitions, TimeTracker tt) throws Exception { + ValidationEngine validationEngine; + if (cliContext.getBaseEngine() != null && hasBaseEngineForKey(cliContext.getBaseEngine())) { + System.out.println("Building new validator engine from base engine: " + cliContext.getBaseEngine()); + validationEngine = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); + /* As a service, it wouldn't be efficient to have a base validation engine + * for every language. So we just use the baseEngine and set the language + * manually afterward. + */ + validationEngine.setLanguage(cliContext.getLang()); + validationEngine.setLocale(cliContext.getLocale()); + } else { + System.out.println("Building new validator engine from CliContext"); + if (cliContext.getSv() == null) { + String sv = determineVersion(cliContext); + cliContext.setSv(sv); + } + validationEngine = buildValidationEngine(cliContext, definitions, tt); + } + return validationEngine; + } + protected ValidationEngine.ValidationEngineBuilder getValidationEngineBuilder() { return new ValidationEngine.ValidationEngineBuilder(); } From 4fa581d2425eedfca8ed9ad2ed65938a152675a3 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 18 Jun 2024 15:24:58 -0400 Subject: [PATCH 15/28] WIP add a decorator to limit cache size --- .../MaxSizeSessionCacheDecorator.java | 83 +++++++++++++++++++ .../services/PassiveExpiringSessionCache.java | 12 +++ .../validation/cli/services/SessionCache.java | 19 ++++- .../cli/services/ValidationService.java | 17 +++- .../MaxSizeSessionCacheDecoratorTest.java | 54 ++++++++++++ 5 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java create mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java new file mode 100644 index 000000000..73d16e9f0 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java @@ -0,0 +1,83 @@ +package org.hl7.fhir.validation.cli.services; + +import org.hl7.fhir.validation.ValidationEngine; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +public class MaxSizeSessionCacheDecorator implements SessionCache { + + public final int maxSize; + public final SessionCache sessionCache; + + private final List sessionIds; + + public MaxSizeSessionCacheDecorator(SessionCache sessionCache, int maxSize) { + this.sessionCache = sessionCache; + this.maxSize = maxSize; + this.sessionIds = new ArrayList<>(sessionCache.getSessionIds()); + if (this.sessionIds.size() > maxSize) { + throw new IllegalArgumentException("Session cache size exceeds the maximum size"); + } + } + + @Override + public String cacheSession(ValidationEngine validationEngine) { + checkSizeAndMaintainMax(null); + String key = sessionCache.cacheSession(validationEngine); + sessionIds.add(key); + return key; + } + + @Override + public String cacheSession(Supplier validationEngineSupplier) { + checkSizeAndMaintainMax(null); + ValidationEngine validationEngine = validationEngineSupplier.get(); + return sessionCache.cacheSession(validationEngine); + } + + private void checkSizeAndMaintainMax(String keyToAdd) { + if (keyToAdd != null || sessionCache.sessionExists(keyToAdd)) { + return; + } + Set sessionIds = sessionCache.getSessionIds(); + //Sync our tracked keys, in case the underlying cache has changed + this.sessionIds.removeIf(key -> !sessionIds.contains(key)); + + if (this.sessionIds.size() >= maxSize) { + final String key = this.sessionIds.remove(0); + sessionCache.removeSession(key); + } + } + + @Override + public String cacheSession(String sessionId, ValidationEngine validationEngine) { + checkSizeAndMaintainMax(sessionId); + cacheSession(sessionId, validationEngine); + return sessionCache.cacheSession( + sessionId, validationEngine); + } + + @Override + public boolean sessionExists(String sessionId) { + return sessionCache.sessionExists(sessionId); + } + + @Override + public ValidationEngine removeSession(String sessionId) { + sessionIds.remove(sessionId); + return sessionCache.removeSession(sessionId); + } + + @Override + public ValidationEngine fetchSessionValidatorEngine(String sessionId) { + return sessionCache.fetchSessionValidatorEngine(sessionId); + } + + @Override + public Set getSessionIds() { + return sessionCache.getSessionIds(); + } +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java index b3a9a18b6..d68a7ae3a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java @@ -3,6 +3,7 @@ package org.hl7.fhir.validation.cli.services; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import org.apache.commons.collections4.map.PassiveExpiringMap; import org.hl7.fhir.validation.ValidationEngine; @@ -44,6 +45,12 @@ public class PassiveExpiringSessionCache implements SessionCache { return generatedId; } + @Override + public String cacheSession(Supplier validationEngineSupplier) { + ValidationEngine engine = validationEngineSupplier.get(); + return this.cacheSession(engine); + } + /** * Stores the initialized {@link ValidationEngine} in the cache with the passed in id as the key. If a null key is * passed in, a new key is generated and returned. @@ -96,6 +103,11 @@ public class PassiveExpiringSessionCache implements SessionCache { return cachedSessions.containsKey(sessionId); } + @Override + public ValidationEngine removeSession(String sessionId) { + return cachedSessions.remove(sessionId); + } + /** * Returns the stored {@link ValidationEngine} associated with the passed in session id, if one such instance exists. * @param sessionId The {@link String} session id. diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java index 11b5afd0d..73be98c49 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java @@ -1,6 +1,7 @@ package org.hl7.fhir.validation.cli.services; import java.util.Set; +import java.util.function.Supplier; import org.hl7.fhir.validation.ValidationEngine; @@ -14,6 +15,14 @@ public interface SessionCache { */ String cacheSession(ValidationEngine validationEngine); + /** + * Uses the passed {@link Supplier} to generate a {@link ValidationEngine} and add it to the cache. Returns the + * session id that will be associated with the generated instance. + * @param validationEngineSupplier {@link Supplier} of {@link ValidationEngine} + * @return The {@link String} id associated with the stored instance. + */ + String cacheSession(Supplier validationEngineSupplier); + /** * Stores the initialized {@link ValidationEngine} in the cache with the passed in id as the key. If a null key is * passed in, a new key is generated and returned. @@ -23,9 +32,6 @@ public interface SessionCache { */ String cacheSession(String sessionId, ValidationEngine validationEngine); - - - /** * Checks if the passed in {@link String} id exists in the set of stored session id. * @param sessionId The {@link String} id to search for. @@ -33,6 +39,13 @@ public interface SessionCache { */ boolean sessionExists(String sessionId); + /** + * Removes the {@link ValidationEngine} associated with the passed in session id. + * @param sessionId The {@link String} session id. + * @return The {@link ValidationEngine} instance that was removed. + */ + ValidationEngine removeSession(String sessionId); + /** * Returns the stored {@link ValidationEngine} associated with the passed in session id, if one such instance exists. * @param sessionId The {@link String} session id. 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 cab82c48c..f85a01122 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 @@ -489,9 +489,20 @@ public class ValidationService { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } - ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, definitions, tt); - sessionId = sessionCache.cacheSession(validationEngine); - System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size()); + + // Send a supplier instead of instantiating. This will permit the sessionCache to manage existing sessions (drop + // or expire old sessions as needed) + sessionId = sessionCache.cacheSession(() -> { + + try { + return buildValidationEngine(cliContext, definitions, tt); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + }); } else { System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id. Cache size = " + sessionCache.getSessionIds().size()); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java new file mode 100644 index 000000000..0f04d4d69 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java @@ -0,0 +1,54 @@ +package org.hl7.fhir.validation.cli.services; + +import org.hl7.fhir.validation.ValidationEngine; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.ArrayList; + + +import static org.mockito.Mockito.mock; + +public class MaxSizeSessionCacheDecoratorTest { + + private List getMockedEngines(int count) { + List engines = new ArrayList<>(); + for (int i = 0; i < count; i++) { + engines.add(mock(ValidationEngine.class)); + } + return engines; + } + + private LinkedHashMap addMockedEngines(SessionCache cache, int count) { + LinkedHashMap engineMap = new LinkedHashMap<>(); + List engines = getMockedEngines(count); + for (ValidationEngine engine : engines) { + String key = cache.cacheSession(engine); + engineMap.put(key, engine); + } + return engineMap; + } + + @Test + public void trivialCase() { + + MaxSizeSessionCacheDecorator maxSizeSessionCacheDecorator = new MaxSizeSessionCacheDecorator(new PassiveExpiringSessionCache(), 4); + + LinkedHashMap initialEngines = addMockedEngines(maxSizeSessionCacheDecorator, 3); + + Assertions.assertEquals(3, maxSizeSessionCacheDecorator.getSessionIds().size()); + + List newEngines = getMockedEngines(2); + + for (ValidationEngine engine : newEngines) { + maxSizeSessionCacheDecorator.cacheSession(engine); + } + + Assertions.assertEquals(4, maxSizeSessionCacheDecorator.getSessionIds().size()); + + Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains() + } + +} From 3a7507c832c68e5bb38704af2cd0648e13158af4 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 18 Jun 2024 16:05:50 -0400 Subject: [PATCH 16/28] Fix test on MaxSize --- .../services/MaxSizeSessionCacheDecoratorTest.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java index 0f04d4d69..646e6bf79 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java @@ -40,15 +40,18 @@ public class MaxSizeSessionCacheDecoratorTest { Assertions.assertEquals(3, maxSizeSessionCacheDecorator.getSessionIds().size()); - List newEngines = getMockedEngines(2); + LinkedHashMap newEngines = addMockedEngines(maxSizeSessionCacheDecorator, 2); - for (ValidationEngine engine : newEngines) { - maxSizeSessionCacheDecorator.cacheSession(engine); - } Assertions.assertEquals(4, maxSizeSessionCacheDecorator.getSessionIds().size()); + Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); + Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); + Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(newEngines, 0))); + Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(newEngines, 1))); - Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains() } + private String getKeyByIndex(LinkedHashMap engineMap, int index) { + return (String) engineMap.keySet().toArray()[index]; + } } From 15e9ba30f0d8518bed23ff82282c01ea1c5de8e1 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 18 Jun 2024 17:30:12 -0400 Subject: [PATCH 17/28] Change to explicitly 'expiring' the oldest session. --- ... ExplicitExpirySessionCacheDecorator.java} | 35 +++++++------ .../MaxSizeSessionCacheDecoratorTest.java | 51 +++++++++++++++---- 2 files changed, 60 insertions(+), 26 deletions(-) rename org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/{MaxSizeSessionCacheDecorator.java => ExplicitExpirySessionCacheDecorator.java} (70%) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java similarity index 70% rename from org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java rename to org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java index 73d16e9f0..9b6697a96 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecorator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java @@ -7,25 +7,31 @@ import java.util.List; import java.util.Set; import java.util.function.Supplier; -public class MaxSizeSessionCacheDecorator implements SessionCache { +public class ExplicitExpirySessionCacheDecorator implements SessionCache { + - public final int maxSize; public final SessionCache sessionCache; private final List sessionIds; - public MaxSizeSessionCacheDecorator(SessionCache sessionCache, int maxSize) { + public ExplicitExpirySessionCacheDecorator(SessionCache sessionCache) { this.sessionCache = sessionCache; - this.maxSize = maxSize; this.sessionIds = new ArrayList<>(sessionCache.getSessionIds()); - if (this.sessionIds.size() > maxSize) { - throw new IllegalArgumentException("Session cache size exceeds the maximum size"); + } + + public boolean expireOldestSession() { + if (sessionIds.isEmpty()) { + return false; } + String oldestSessionId = sessionIds.get(0); + sessionIds.remove(oldestSessionId); + sessionCache.removeSession(oldestSessionId); + return true; } @Override public String cacheSession(ValidationEngine validationEngine) { - checkSizeAndMaintainMax(null); + maintainSessionIds(null); String key = sessionCache.cacheSession(validationEngine); sessionIds.add(key); return key; @@ -33,12 +39,14 @@ public class MaxSizeSessionCacheDecorator implements SessionCache { @Override public String cacheSession(Supplier validationEngineSupplier) { - checkSizeAndMaintainMax(null); + maintainSessionIds(null); ValidationEngine validationEngine = validationEngineSupplier.get(); - return sessionCache.cacheSession(validationEngine); + String key = sessionCache.cacheSession(validationEngine); + sessionIds.add(key); + return key; } - private void checkSizeAndMaintainMax(String keyToAdd) { + private void maintainSessionIds(String keyToAdd) { if (keyToAdd != null || sessionCache.sessionExists(keyToAdd)) { return; } @@ -46,16 +54,11 @@ public class MaxSizeSessionCacheDecorator implements SessionCache { //Sync our tracked keys, in case the underlying cache has changed this.sessionIds.removeIf(key -> !sessionIds.contains(key)); - if (this.sessionIds.size() >= maxSize) { - final String key = this.sessionIds.remove(0); - sessionCache.removeSession(key); - } } @Override public String cacheSession(String sessionId, ValidationEngine validationEngine) { - checkSizeAndMaintainMax(sessionId); - cacheSession(sessionId, validationEngine); + maintainSessionIds(sessionId); return sessionCache.cacheSession( sessionId, validationEngine); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java index 646e6bf79..63db66546 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java @@ -32,23 +32,54 @@ public class MaxSizeSessionCacheDecoratorTest { } @Test - public void trivialCase() { + public void trivialExpiryTest() { - MaxSizeSessionCacheDecorator maxSizeSessionCacheDecorator = new MaxSizeSessionCacheDecorator(new PassiveExpiringSessionCache(), 4); + ExplicitExpirySessionCacheDecorator sessionCache = new ExplicitExpirySessionCacheDecorator(new PassiveExpiringSessionCache()); - LinkedHashMap initialEngines = addMockedEngines(maxSizeSessionCacheDecorator, 3); + LinkedHashMap initialEngines = addMockedEngines(sessionCache, 3); - Assertions.assertEquals(3, maxSizeSessionCacheDecorator.getSessionIds().size()); + Assertions.assertEquals(3, sessionCache.getSessionIds().size()); - LinkedHashMap newEngines = addMockedEngines(maxSizeSessionCacheDecorator, 2); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 0))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); + Assertions.assertTrue(sessionCache.expireOldestSession()); - Assertions.assertEquals(4, maxSizeSessionCacheDecorator.getSessionIds().size()); - Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); - Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); - Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(newEngines, 0))); - Assertions.assertTrue(maxSizeSessionCacheDecorator.getSessionIds().contains(getKeyByIndex(newEngines, 1))); + Assertions.assertEquals(2, sessionCache.getSessionIds().size()); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); + LinkedHashMap newEngines = addMockedEngines(sessionCache, 2); + + Assertions.assertEquals(4, sessionCache.getSessionIds().size()); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 0))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 1))); + + Assertions.assertTrue(sessionCache.expireOldestSession()); + + Assertions.assertEquals(3, sessionCache.getSessionIds().size()); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 0))); + Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 1))); + + Assertions.assertTrue(sessionCache.expireOldestSession()); + Assertions.assertTrue(sessionCache.expireOldestSession()); + Assertions.assertTrue(sessionCache.expireOldestSession()); + Assertions.assertFalse(sessionCache.expireOldestSession()); + } + + @Test + public void producerAddTest() { + ExplicitExpirySessionCacheDecorator maxSizeSessionCacheDecorator = new ExplicitExpirySessionCacheDecorator(new PassiveExpiringSessionCache()); + ValidationEngine producedEngine = mock(ValidationEngine.class); + String sessionId = maxSizeSessionCacheDecorator.cacheSession(() -> { + return producedEngine; + }); + Assertions.assertEquals(1, maxSizeSessionCacheDecorator.getSessionIds().size()); + Assertions.assertSame(producedEngine, maxSizeSessionCacheDecorator.fetchSessionValidatorEngine(sessionId)); } private String getKeyByIndex(LinkedHashMap engineMap, int index) { From 053d29df46dd8c54299fc85536dbf82cc77f2ada Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 25 Jun 2024 13:33:15 -0400 Subject: [PATCH 18/28] Try removing oldest sessions instead of maintaining max size --- ...atorTest.java => ExplicitExpirySessionCacheDecoratorTest.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/{MaxSizeSessionCacheDecoratorTest.java => ExplicitExpirySessionCacheDecoratorTest.java} (100%) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java similarity index 100% rename from org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/MaxSizeSessionCacheDecoratorTest.java rename to org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java From 917ff364a0f66ed16a9d00729979108b931f6b23 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 25 Jun 2024 13:33:59 -0400 Subject: [PATCH 19/28] Try removing oldest sessions instead of maintaining max size 2 --- .../ExplicitExpirySessionCacheDecorator.java | 9 --------- .../cli/services/PassiveExpiringSessionCache.java | 6 ------ .../validation/cli/services/SessionCache.java | 8 -------- .../cli/services/ValidationService.java | 15 +++------------ .../ExplicitExpirySessionCacheDecoratorTest.java | 13 +------------ 5 files changed, 4 insertions(+), 47 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java index 9b6697a96..ff2247f0c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java @@ -37,15 +37,6 @@ public class ExplicitExpirySessionCacheDecorator implements SessionCache { return key; } - @Override - public String cacheSession(Supplier validationEngineSupplier) { - maintainSessionIds(null); - ValidationEngine validationEngine = validationEngineSupplier.get(); - String key = sessionCache.cacheSession(validationEngine); - sessionIds.add(key); - return key; - } - private void maintainSessionIds(String keyToAdd) { if (keyToAdd != null || sessionCache.sessionExists(keyToAdd)) { return; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java index d68a7ae3a..32c7d70da 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java @@ -45,12 +45,6 @@ public class PassiveExpiringSessionCache implements SessionCache { return generatedId; } - @Override - public String cacheSession(Supplier validationEngineSupplier) { - ValidationEngine engine = validationEngineSupplier.get(); - return this.cacheSession(engine); - } - /** * Stores the initialized {@link ValidationEngine} in the cache with the passed in id as the key. If a null key is * passed in, a new key is generated and returned. diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java index 73be98c49..f39b614c9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java @@ -15,14 +15,6 @@ public interface SessionCache { */ String cacheSession(ValidationEngine validationEngine); - /** - * Uses the passed {@link Supplier} to generate a {@link ValidationEngine} and add it to the cache. Returns the - * session id that will be associated with the generated instance. - * @param validationEngineSupplier {@link Supplier} of {@link ValidationEngine} - * @return The {@link String} id associated with the stored instance. - */ - String cacheSession(Supplier validationEngineSupplier); - /** * Stores the initialized {@link ValidationEngine} in the cache with the passed in id as the key. If a null key is * passed in, a new key is generated and returned. 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 f85a01122..6f412397d 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 @@ -490,19 +490,10 @@ public class ValidationService { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } - // Send a supplier instead of instantiating. This will permit the sessionCache to manage existing sessions (drop - // or expire old sessions as needed) - sessionId = sessionCache.cacheSession(() -> { + ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, definitions, tt); + sessionId = sessionCache.cacheSession(validationEngine); + System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size()); - try { - return buildValidationEngine(cliContext, definitions, tt); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - }); } else { System.out.println("Cached session exists for session id " + sessionId + ", returning stored validator session id. Cache size = " + sessionCache.getSessionIds().size()); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java index 63db66546..096bf1741 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java @@ -11,7 +11,7 @@ import java.util.ArrayList; import static org.mockito.Mockito.mock; -public class MaxSizeSessionCacheDecoratorTest { +public class ExplicitExpirySessionCacheDecoratorTest { private List getMockedEngines(int count) { List engines = new ArrayList<>(); @@ -71,17 +71,6 @@ public class MaxSizeSessionCacheDecoratorTest { Assertions.assertFalse(sessionCache.expireOldestSession()); } - @Test - public void producerAddTest() { - ExplicitExpirySessionCacheDecorator maxSizeSessionCacheDecorator = new ExplicitExpirySessionCacheDecorator(new PassiveExpiringSessionCache()); - ValidationEngine producedEngine = mock(ValidationEngine.class); - String sessionId = maxSizeSessionCacheDecorator.cacheSession(() -> { - return producedEngine; - }); - Assertions.assertEquals(1, maxSizeSessionCacheDecorator.getSessionIds().size()); - Assertions.assertSame(producedEngine, maxSizeSessionCacheDecorator.fetchSessionValidatorEngine(sessionId)); - } - private String getKeyByIndex(LinkedHashMap engineMap, int index) { return (String) engineMap.keySet().toArray()[index]; } From 6c3a987a1693f2ef4f1fb6389171da286af1879f Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 3 Jul 2024 14:17:26 -0400 Subject: [PATCH 20/28] Cleanup --- .../src/main/java/org/hl7/fhir/validation/ValidatorCli.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java index 1d9ab26d6..2e8631384 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java @@ -105,7 +105,7 @@ public class ValidatorCli { public static final String JAVA_DISABLED_PROXY_SCHEMES = "jdk.http.auth.proxying.disabledSchemes"; public static final String JAVA_USE_SYSTEM_PROXIES = "java.net.useSystemProxies"; - private static ValidationService validationService = new ValidationService(); + private final static ValidationService validationService = new ValidationService(); protected ValidationService myValidationService; @@ -408,8 +408,4 @@ public class ValidatorCli { return validationEngine; } - protected void validateScan(CliContext cliContext, ValidationEngine validator) throws Exception { - Scanner validationScanner = new Scanner(validator.getContext(), validator.getValidator(null), validator.getIgLoader(), validator.getFhirPathEngine()); - validationScanner.validateScan(cliContext.getOutput(), cliContext.getSources()); - } } \ No newline at end of file From 91f8e27d878d6000c7635c3eaadcb87c9d9d5092 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 3 Jul 2024 17:02:32 -0400 Subject: [PATCH 21/28] Clean up unused cache work --- .../ExplicitExpirySessionCacheDecorator.java | 77 ------------------- .../services/PassiveExpiringSessionCache.java | 5 -- .../validation/cli/services/SessionCache.java | 7 -- ...plicitExpirySessionCacheDecoratorTest.java | 77 ------------------- 4 files changed, 166 deletions(-) delete mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java delete mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java deleted file mode 100644 index ff2247f0c..000000000 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecorator.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.hl7.fhir.validation.cli.services; - -import org.hl7.fhir.validation.ValidationEngine; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - -public class ExplicitExpirySessionCacheDecorator implements SessionCache { - - - public final SessionCache sessionCache; - - private final List sessionIds; - - public ExplicitExpirySessionCacheDecorator(SessionCache sessionCache) { - this.sessionCache = sessionCache; - this.sessionIds = new ArrayList<>(sessionCache.getSessionIds()); - } - - public boolean expireOldestSession() { - if (sessionIds.isEmpty()) { - return false; - } - String oldestSessionId = sessionIds.get(0); - sessionIds.remove(oldestSessionId); - sessionCache.removeSession(oldestSessionId); - return true; - } - - @Override - public String cacheSession(ValidationEngine validationEngine) { - maintainSessionIds(null); - String key = sessionCache.cacheSession(validationEngine); - sessionIds.add(key); - return key; - } - - private void maintainSessionIds(String keyToAdd) { - if (keyToAdd != null || sessionCache.sessionExists(keyToAdd)) { - return; - } - Set sessionIds = sessionCache.getSessionIds(); - //Sync our tracked keys, in case the underlying cache has changed - this.sessionIds.removeIf(key -> !sessionIds.contains(key)); - - } - - @Override - public String cacheSession(String sessionId, ValidationEngine validationEngine) { - maintainSessionIds(sessionId); - return sessionCache.cacheSession( - sessionId, validationEngine); - } - - @Override - public boolean sessionExists(String sessionId) { - return sessionCache.sessionExists(sessionId); - } - - @Override - public ValidationEngine removeSession(String sessionId) { - sessionIds.remove(sessionId); - return sessionCache.removeSession(sessionId); - } - - @Override - public ValidationEngine fetchSessionValidatorEngine(String sessionId) { - return sessionCache.fetchSessionValidatorEngine(sessionId); - } - - @Override - public Set getSessionIds() { - return sessionCache.getSessionIds(); - } -} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java index 32c7d70da..dee8a578d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java @@ -97,11 +97,6 @@ public class PassiveExpiringSessionCache implements SessionCache { return cachedSessions.containsKey(sessionId); } - @Override - public ValidationEngine removeSession(String sessionId) { - return cachedSessions.remove(sessionId); - } - /** * Returns the stored {@link ValidationEngine} associated with the passed in session id, if one such instance exists. * @param sessionId The {@link String} session id. diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java index f39b614c9..2c02bdbe4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java @@ -31,13 +31,6 @@ public interface SessionCache { */ boolean sessionExists(String sessionId); - /** - * Removes the {@link ValidationEngine} associated with the passed in session id. - * @param sessionId The {@link String} session id. - * @return The {@link ValidationEngine} instance that was removed. - */ - ValidationEngine removeSession(String sessionId); - /** * Returns the stored {@link ValidationEngine} associated with the passed in session id, if one such instance exists. * @param sessionId The {@link String} session id. diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java deleted file mode 100644 index 096bf1741..000000000 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ExplicitExpirySessionCacheDecoratorTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.hl7.fhir.validation.cli.services; - -import org.hl7.fhir.validation.ValidationEngine; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.ArrayList; - - -import static org.mockito.Mockito.mock; - -public class ExplicitExpirySessionCacheDecoratorTest { - - private List getMockedEngines(int count) { - List engines = new ArrayList<>(); - for (int i = 0; i < count; i++) { - engines.add(mock(ValidationEngine.class)); - } - return engines; - } - - private LinkedHashMap addMockedEngines(SessionCache cache, int count) { - LinkedHashMap engineMap = new LinkedHashMap<>(); - List engines = getMockedEngines(count); - for (ValidationEngine engine : engines) { - String key = cache.cacheSession(engine); - engineMap.put(key, engine); - } - return engineMap; - } - - @Test - public void trivialExpiryTest() { - - ExplicitExpirySessionCacheDecorator sessionCache = new ExplicitExpirySessionCacheDecorator(new PassiveExpiringSessionCache()); - - LinkedHashMap initialEngines = addMockedEngines(sessionCache, 3); - - Assertions.assertEquals(3, sessionCache.getSessionIds().size()); - - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 0))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); - - Assertions.assertTrue(sessionCache.expireOldestSession()); - - Assertions.assertEquals(2, sessionCache.getSessionIds().size()); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); - - LinkedHashMap newEngines = addMockedEngines(sessionCache, 2); - - Assertions.assertEquals(4, sessionCache.getSessionIds().size()); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 1))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 0))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 1))); - - Assertions.assertTrue(sessionCache.expireOldestSession()); - - Assertions.assertEquals(3, sessionCache.getSessionIds().size()); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(initialEngines, 2))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 0))); - Assertions.assertTrue(sessionCache.getSessionIds().contains(getKeyByIndex(newEngines, 1))); - - Assertions.assertTrue(sessionCache.expireOldestSession()); - Assertions.assertTrue(sessionCache.expireOldestSession()); - Assertions.assertTrue(sessionCache.expireOldestSession()); - Assertions.assertFalse(sessionCache.expireOldestSession()); - } - - private String getKeyByIndex(LinkedHashMap engineMap, int index) { - return (String) engineMap.keySet().toArray()[index]; - } -} From 6c89f0230fd15a142a5f9d34ee5940118b45cd96 Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 4 Jul 2024 11:19:09 -0400 Subject: [PATCH 22/28] WIP add cleanUp method. --- .../cli/services/PassiveExpiringSessionCache.java | 5 +++++ .../org/hl7/fhir/validation/cli/services/SessionCache.java | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java index dee8a578d..e1f0f37fd 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java @@ -118,6 +118,11 @@ public class PassiveExpiringSessionCache implements SessionCache { return cachedSessions.keySet(); } + @Override + public void cleanUp() { + removeExpiredSessions(); + } + /** * Session ids generated internally are UUID {@link String}. * @return A new {@link String} session id. diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java index 2c02bdbe4..a552a10f4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java @@ -43,5 +43,10 @@ public interface SessionCache { * @return {@link Set} of session ids. */ Set getSessionIds(); - + + /** + * Performs any pending maintenance operations needed by the cache. + * */ + public void cleanUp(); + } \ No newline at end of file From 09c8308e0e3ce8cb7600777bcf97708bc71d2d3b Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 4 Jul 2024 12:13:46 -0400 Subject: [PATCH 23/28] Clean up the session cache before adding a new one + fix test runs --- .../cli/services/ValidationService.java | 2 +- ...eTest.java => ValidationServiceTests.java} | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) rename org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/{ValidationServiceTest.java => ValidationServiceTests.java} (95%) 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 6f412397d..44e2544a9 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 @@ -489,7 +489,7 @@ public class ValidationService { if (sessionId != null) { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } - + sessionCache.cleanUp(); ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, definitions, tt); sessionId = sessionCache.cacheSession(validationEngine); System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size()); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java similarity index 95% rename from org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java rename to org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java index e03665c79..6a39e7320 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTest.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java @@ -38,14 +38,13 @@ import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.model.FileInfo; import org.hl7.fhir.validation.cli.model.ValidationRequest; -import org.hl7.fhir.validation.cli.model.ValidationResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; -class ValidationServiceTest { +class ValidationServiceTests { final String DUMMY_SOURCE = "dummySource"; final String DUMMY_SOURCE1 = "dummySource1"; @@ -60,22 +59,25 @@ class ValidationServiceTest { void validationSessionTest() throws Exception { TestingUtilities.injectCorePackageLoader(); SessionCache sessionCache = Mockito.spy(new PassiveExpiringSessionCache()); - ValidationService myService = new ValidationService(sessionCache); + ValidationService myService = Mockito.spy(new ValidationService(sessionCache)); List filesToValidate = getFilesToValidate(); - ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService"))).setFilesToValidate(filesToValidate); + ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService")).setSv("4.0.1")).setFilesToValidate(filesToValidate); // Validation run 1...nothing cached yet myService.validateSources(request); verify(sessionCache, Mockito.times(1)).cacheSession(ArgumentMatchers.any(ValidationEngine.class)); - + verify(sessionCache, Mockito.times(1)).cleanUp(); + verify(myService, Mockito.times(1)).buildValidationEngine(any(), any(), any()); Set sessionIds = sessionCache.getSessionIds(); if (sessionIds.stream().findFirst().isPresent()) { // Verify that after 1 run there is only one entry within the cache Assertions.assertEquals(1, sessionIds.size()); - myService.validateSources(request); - // Verify that the cache has been called on once with the id created in the first run - verify(sessionCache, Mockito.times(1)).fetchSessionValidatorEngine(sessionIds.stream().findFirst().get()); + myService.validateSources(request.setSessionId(sessionIds.stream().findFirst().get())); + // Verify that the cache has been called on twice with the id created in the first run + verify(sessionCache, Mockito.times(2)).fetchSessionValidatorEngine(sessionIds.stream().findFirst().get()); + verify(sessionCache, Mockito.times(1)).cleanUp(); + verify(myService, Mockito.times(1)).buildValidationEngine(any(), any(), any()); } else { // If no sessions exist within the cache after a run, we auto-fail. fail(); @@ -95,7 +97,7 @@ class ValidationServiceTest { { final List filesToValidate = getFilesToValidate(); - final ValidationRequest request = new ValidationRequest().setCliContext(new CliContext()).setFilesToValidate(filesToValidate); + final ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setSv("4.0.1")).setFilesToValidate(filesToValidate); myService.validateSources(request); verify(myService, Mockito.times(0)).getBaseEngine("myDummyKey"); From cdb2967544fcc4e92d580faa07bdc67714533c4b Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 4 Jul 2024 14:45:45 -0400 Subject: [PATCH 24/28] Throw exceptions where definitions or sv is null + fix null sv --- .../org/hl7/fhir/validation/IgLoader.java | 2 +- .../fhir/validation/cli/model/CliContext.java | 2 +- .../services/PassiveExpiringSessionCache.java | 1 - .../validation/cli/services/SessionCache.java | 1 - .../cli/services/ValidationService.java | 29 +++++++++++-------- .../cli/services/ValidationServiceTests.java | 2 +- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/IgLoader.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/IgLoader.java index 925673c4a..e0a1a9edc 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/IgLoader.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/IgLoader.java @@ -316,7 +316,7 @@ public class IgLoader implements IValidationEngineLoader { public void scanForIgVersion(String src, boolean recursive, - VersionSourceInformation versions) throws Exception { + VersionSourceInformation versions) throws IOException { Map source = loadIgSourceForVersion(src, recursive, true, versions); if (source != null) { if (source.containsKey("version.info")) { 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 a5efe4bae..f719f029b 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 @@ -846,7 +846,7 @@ public class CliContext { @Override public String toString() { return "CliContext{" + - " baseEngine=" + baseEngine + + "baseEngine=" + baseEngine + ", doNative=" + doNative + ", extensions=" + extensions + ", hintAboutNonMustSupport=" + hintAboutNonMustSupport + diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java index e1f0f37fd..3293a33e6 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/PassiveExpiringSessionCache.java @@ -3,7 +3,6 @@ package org.hl7.fhir.validation.cli.services; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import org.apache.commons.collections4.map.PassiveExpiringMap; import org.hl7.fhir.validation.ValidationEngine; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java index a552a10f4..40572f3ad 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/SessionCache.java @@ -1,7 +1,6 @@ package org.hl7.fhir.validation.cli.services; import java.util.Set; -import java.util.function.Supplier; import org.hl7.fhir.validation.ValidationEngine; 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 44e2544a9..ea71e5204 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 @@ -78,6 +78,9 @@ public class ValidationService { private final Map baseEngines = new ConcurrentHashMap<>(); public void putBaseEngine(String key, CliContext cliContext) throws IOException, URISyntaxException { + if (cliContext.getSv() == null) { + throw new IllegalArgumentException("Cannot create a base engine without an explicit version"); + } String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); ValidationEngine baseEngine = buildValidationEngine(cliContext, definitions, new TimeTracker()); @@ -105,11 +108,8 @@ public class ValidationService { public ValidationResponse validateSources(ValidationRequest request) throws Exception { - - String definitions = VersionUtilities.packageForVersion(request.getCliContext().getSv()) + "#" + VersionUtilities.getCurrentVersion(request.getCliContext().getSv()); - TimeTracker timeTracker = new TimeTracker(); - String sessionId = initializeValidator(request.getCliContext(), definitions, timeTracker, request.sessionId); + String sessionId = initializeValidator(request.getCliContext(), null, timeTracker, request.sessionId); ValidationEngine validator = sessionCache.fetchSessionValidatorEngine(sessionId); if (request.getCliContext().getProfiles().size() > 0) { @@ -196,7 +196,7 @@ public class ValidationService { return outcome; } - public VersionSourceInformation scanForVersions(CliContext cliContext) throws Exception { + public VersionSourceInformation scanForVersions(CliContext cliContext) throws IOException { VersionSourceInformation versions = new VersionSourceInformation(); IgLoader igLoader = new IgLoader( new FilesystemPackageCacheManager.Builder().build(), @@ -490,7 +490,13 @@ public class ValidationService { System.out.println("No such cached session exists for session id " + sessionId + ", re-instantiating validator."); } sessionCache.cleanUp(); - ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, definitions, tt); + if (cliContext.getSv() == null) { + String sv = determineVersion(cliContext); + cliContext.setSv(sv); + } + final String engineDefinitions = definitions != null ? definitions : VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); + + ValidationEngine validationEngine = getValidationEngineFromCliContext(cliContext, engineDefinitions, tt); sessionId = sessionCache.cacheSession(validationEngine); System.out.println("Cached new session. Cache size = " + sessionCache.getSessionIds().size()); @@ -512,11 +518,10 @@ public class ValidationService { validationEngine.setLanguage(cliContext.getLang()); validationEngine.setLocale(cliContext.getLocale()); } else { - System.out.println("Building new validator engine from CliContext"); - if (cliContext.getSv() == null) { - String sv = determineVersion(cliContext); - cliContext.setSv(sv); + if (definitions == null) { + throw new IllegalArgumentException("Cannot create a validator engine (definitions == null)"); } + System.out.println("Building new validator engine from CliContext"); validationEngine = buildValidationEngine(cliContext, definitions, tt); } return validationEngine; @@ -603,7 +608,7 @@ public class ValidationService { System.out.println(" Package Summary: "+ validationEngine.getContext().loadedPackageSummary()); } - public String determineVersion(CliContext cliContext) throws Exception { + public String determineVersion(CliContext cliContext) throws IOException { if (cliContext.getMode() != EngineMode.VALIDATION && cliContext.getMode() != EngineMode.INSTALL) { return "5.0"; } @@ -622,7 +627,7 @@ public class ValidationService { System.out.println("-> use version " + versions.version()); return versions.version(); } - throw new Exception("-> Multiple versions found. Specify a particular version using the -version parameter"); + throw new IllegalArgumentException("-> Multiple versions found. Specify a particular version using the -version parameter"); } public void generateSpreadsheet(CliContext cliContext, ValidationEngine validator) throws Exception { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java index 6a39e7320..1a090cf84 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/services/ValidationServiceTests.java @@ -63,7 +63,7 @@ class ValidationServiceTests { List filesToValidate = getFilesToValidate(); - ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService")).setSv("4.0.1")).setFilesToValidate(filesToValidate); + ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService"))).setFilesToValidate(filesToValidate); // Validation run 1...nothing cached yet myService.validateSources(request); verify(sessionCache, Mockito.times(1)).cacheSession(ArgumentMatchers.any(ValidationEngine.class)); From 6201b6b478cf8a27823279981f5c28be5f445da9 Mon Sep 17 00:00:00 2001 From: dotasek Date: Thu, 4 Jul 2024 15:10:00 -0400 Subject: [PATCH 25/28] Clean up System.out chatter --- .../org/hl7/fhir/validation/cli/services/ValidationService.java | 2 -- 1 file changed, 2 deletions(-) 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 ea71e5204..2fb45f0e6 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 @@ -509,7 +509,6 @@ public class ValidationService { private ValidationEngine getValidationEngineFromCliContext(CliContext cliContext, String definitions, TimeTracker tt) throws Exception { ValidationEngine validationEngine; if (cliContext.getBaseEngine() != null && hasBaseEngineForKey(cliContext.getBaseEngine())) { - System.out.println("Building new validator engine from base engine: " + cliContext.getBaseEngine()); validationEngine = new ValidationEngine(getBaseEngine(cliContext.getBaseEngine())); /* As a service, it wouldn't be efficient to have a base validation engine * for every language. So we just use the baseEngine and set the language @@ -521,7 +520,6 @@ public class ValidationService { if (definitions == null) { throw new IllegalArgumentException("Cannot create a validator engine (definitions == null)"); } - System.out.println("Building new validator engine from CliContext"); validationEngine = buildValidationEngine(cliContext, definitions, tt); } return validationEngine; From 66e35e1af6057472c7fce92250e11f85aeeeb72f Mon Sep 17 00:00:00 2001 From: oliveregger Date: Wed, 27 Mar 2024 09:43:53 +0100 Subject: [PATCH 26/28] #1583 do not serialize choice group --- .../hl7/fhir/r5/elementmodel/XmlParser.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java index 410c225d6..58a799d6a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java @@ -889,13 +889,15 @@ public class XmlParser extends ParserBase { xml.attribute(child.getProperty().getXmlNamespace(),child.getProperty().getXmlName(), av); } } - if (linkResolver != null) - xml.link(linkResolver.resolveProperty(element.getProperty())); - if (!xml.namespaceDefined(element.getProperty().getXmlNamespace())) { - String abbrev = makeNamespaceAbbrev(element.getProperty(), xml); - xml.namespace(element.getProperty().getXmlNamespace(), abbrev); + if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) { + if (linkResolver != null) + xml.link(linkResolver.resolveProperty(element.getProperty())); + if (!xml.namespaceDefined(element.getProperty().getXmlNamespace())) { + String abbrev = makeNamespaceAbbrev(element.getProperty(), xml); + xml.namespace(element.getProperty().getXmlNamespace(), abbrev); + } + xml.enter(element.getProperty().getXmlNamespace(), elementName); } - xml.enter(element.getProperty().getXmlNamespace(), elementName); if (!root && element.getSpecial() != null) { if (linkResolver != null) @@ -919,9 +921,11 @@ public class XmlParser extends ParserBase { } } } - if (!root && element.getSpecial() != null) - xml.exit(element.getProperty().getXmlNamespace(),element.getType()); - xml.exit(element.getProperty().getXmlNamespace(),elementName); + if (!element.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_ID_CHOICE_GROUP)) { + if (!root && element.getSpecial() != null) + xml.exit(element.getProperty().getXmlNamespace(),element.getType()); + xml.exit(element.getProperty().getXmlNamespace(),elementName); + } } } From ca7a2ba4e4a30093abd3ea4a8be06ff300ec72f6 Mon Sep 17 00:00:00 2001 From: oliveregger Date: Mon, 8 Jul 2024 23:20:32 +0200 Subject: [PATCH 27/28] add testcase for #1583 --- .../java/org/hl7/fhir/r5/test/CDARoundTripTests.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java index 555a21bc5..444bf66a1 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java @@ -1,5 +1,7 @@ package org.hl7.fhir.r5.test; +import static org.junit.Assert.assertEquals; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -26,10 +28,10 @@ public class CDARoundTripTests { @BeforeAll public static void setUp() throws Exception { FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().build(); - context = new SimpleWorkerContext(TestingUtilities.getWorkerContext(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1"))); + context = new SimpleWorkerContext(TestingUtilities.getWorkerContext(pcm.loadPackage("hl7.fhir.r5.core", "5.0.0"))); fp = new FHIRPathEngine(context); - NpmPackage npm = new FilesystemPackageCacheManager.Builder().build().loadPackage("hl7.cda.uv.core", "current"); + NpmPackage npm = new FilesystemPackageCacheManager.Builder().build().loadPackage("hl7.cda.uv.core", "2.0.0-sd"); context.loadFromPackage(npm, null); } @@ -186,6 +188,7 @@ public class CDARoundTripTests { Assertions.assertEquals("LOINC", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.code.codeSystemName"))); Assertions.assertEquals("Episode Note", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.title.xmlText"))); Assertions.assertEquals("Episode Note", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.title.ofType(CDA.ST).xmlText"))); + Assertions.assertEquals("Levin", fp.evaluateToString(null, cdaExample, cdaExample, cdaExample, fp.parse("ClinicalDocument.recordTarget.patientRole.patient.name.item.family.xmlText"))); } @Test @@ -203,6 +206,9 @@ public class CDARoundTripTests { ByteArrayOutputStream baosXml = new ByteArrayOutputStream(); Manager.compose(context, cda, baosXml, FhirFormat.XML, OutputStyle.PRETTY, null); + String result = baosXml.toString(); + // https://github.com/hapifhir/org.hl7.fhir.core/issues/1583 + assertEquals(-1,result.indexOf("")); Element cdaXmlRoundtrip = Manager.parseSingle(context, new ByteArrayInputStream(baosXml.toString().getBytes()), FhirFormat.XML); assertsExample(cdaXmlRoundtrip); } From aa32c57d030f9959e73dfc0a06e01030f2e2af2a Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 9 Jul 2024 12:45:40 -0400 Subject: [PATCH 28/28] Fix test breakages - July 9 2024 (#1681) * Bump version to pass test * Bump test cases --- .../main/java/org/hl7/fhir/utilities/npm/CommonPackages.java | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java index ddddcdafc..eb81e39a0 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java @@ -6,6 +6,6 @@ public class CommonPackages { public static final String VER_XVER = "0.1.0"; public static final String ID_PUBPACK = "hl7.fhir.pubpack"; - public static final String VER_PUBPACK = "0.1.7"; + public static final String VER_PUBPACK = "0.1.8"; } diff --git a/pom.xml b/pom.xml index 1ec333fc1..5e8ef73ca 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 1.26.0 32.0.1-jre 6.4.1 - 1.5.14 + 1.5.15-SNAPSHOT 2.17.0 5.9.2 1.8.2