From cd55208eed4c783edadfc10c3bd157486ead5f26 Mon Sep 17 00:00:00 2001 From: rpassas Date: Tue, 25 Jul 2023 16:30:56 -0400 Subject: [PATCH 1/6] set temp routes, replaced once validator loads + updated spark version --- build.gradle.kts | 2 +- src/main/java/org/mitre/inferno/App.java | 10 ++- .../org/mitre/inferno/rest/Endpoints.java | 87 +++++++++++++++---- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index acfcf56..9a46de0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { implementation("org.slf4j", "slf4j-log4j12", "1.7.30") // Web Server - implementation("com.sparkjava", "spark-core", "2.9.1") + implementation("com.sparkjava", "spark-core", "2.9.4") // Testing stuff testImplementation("org.junit.jupiter", "junit-jupiter", "5.9.3") diff --git a/src/main/java/org/mitre/inferno/App.java b/src/main/java/org/mitre/inferno/App.java index eb13a73..46cc968 100644 --- a/src/main/java/org/mitre/inferno/App.java +++ b/src/main/java/org/mitre/inferno/App.java @@ -10,7 +10,11 @@ public class App { /** * Starting point for the Validation Service. - *

Passing the 'prepare' argument causes the FHIR artifacts needed to be downloaded.

+ *

+ * Passing the 'prepare' argument causes the FHIR artifacts needed to be + * downloaded. + *

+ * * @param args the application initialization arguments */ public static void main(String[] args) { @@ -60,11 +64,11 @@ private static void startApp() { Logger logger = LoggerFactory.getLogger(App.class); logger.info("Starting Server..."); SparkUtils.createServerWithRequestLog(logger); + Endpoints.setupLoadingRoutes(getPortNumber()); Endpoints.getInstance( initializeValidator(), initializePathEvaluator(), - getPortNumber() - ); + getPortNumber()); } private static int getPortNumber() { diff --git a/src/main/java/org/mitre/inferno/rest/Endpoints.java b/src/main/java/org/mitre/inferno/rest/Endpoints.java index 726382e..03ef174 100644 --- a/src/main/java/org/mitre/inferno/rest/Endpoints.java +++ b/src/main/java/org/mitre/inferno/rest/Endpoints.java @@ -3,11 +3,16 @@ import static spark.Spark.before; import static spark.Spark.options; import static spark.Spark.port; +import static spark.Spark.get; +import static spark.Spark.post; +import static spark.Spark.put; +import static spark.Spark.unmap; import com.google.gson.Gson; import org.mitre.inferno.FHIRPathEvaluator; import org.mitre.inferno.Validator; import spark.ResponseTransformer; +import com.google.gson.JsonObject; public class Endpoints { public static final ResponseTransformer TO_JSON = new Gson()::toJson; @@ -16,44 +21,92 @@ public class Endpoints { private final Validator validator; private final FHIRPathEvaluator pathEvaluator; - private Endpoints(Validator validator, FHIRPathEvaluator evaluator, int port) { + private Endpoints(Validator validator, FHIRPathEvaluator evaluator) { this.validator = validator; this.pathEvaluator = evaluator; - port(port); createRoutes(); } + /** + * This adds permissive CORS headers to all requests. + */ + private static void setHeaders() { + + before((req, res) -> { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS"); + res.header("Access-Control-Allow-Headers", "*"); + }); + + // This responds to OPTIONS requests, used by browsers to "preflight" check CORS + // requests, + // with a 200 OK response with no content and the CORS headers above + options("*", (req, res) -> ""); + + } + + /** + * Set up temporary routes while the validator still loads. + * + * @param port the port to listen for requests on + */ + public static void setupLoadingRoutes(int port) { + port(port); + setHeaders(); + + JsonObject warning = new JsonObject(); + warning.addProperty("Warning", "Validator still loading... please wait."); + + get("*", (req, res) -> { + res.type("application/fhir+json"); + return warning; + }); + post("*", (req, res) -> { + res.type("application/fhir+json"); + return warning; + }); + put("*", (req, res) -> { + res.type("application/fhir+json"); + return warning; + }); + } + + /** + * Remove temporary routes set. This is meant to be called once the validator + * has been loaded. + */ + public static void teardownLoadingRoutes() { + unmap("*", "get"); + unmap("*", "post"); + unmap("*", "put"); + } + /** * Get the existing Endpoints or create one if it does not already exist. * - * @param validator the Validator that should be used at the /validator endpoint. + * @param validator the Validator that should be used at the /validator + * endpoint. * Passing null will skip setting up the /validator endpoint. - * @param evaluator the FHIRPathEvaluator that should be used at the /fhirpath endpoint. + * @param evaluator the FHIRPathEvaluator that should be used at the /fhirpath + * endpoint. * Passing null will skip setting up the /fhirpath endpoint. - * @param port the port to listen for requests on + * @param port the port to listen for requests on * @return the singleton Endpoints */ public static Endpoints getInstance(Validator validator, FHIRPathEvaluator evaluator, int port) { + teardownLoadingRoutes(); if (endpoints == null) { - endpoints = new Endpoints(validator, evaluator, port); + endpoints = new Endpoints(validator, evaluator); } return endpoints; } /** - * Creates the API routes for receiving and processing HTTP requests from clients. + * Creates the API routes for receiving and processing HTTP requests from + * clients. */ private void createRoutes() { - // This adds permissive CORS headers to all requests - before((req, res) -> { - res.header("Access-Control-Allow-Origin", "*"); - res.header("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS"); - res.header("Access-Control-Allow-Headers", "*"); - }); - - // This responds to OPTIONS requests, used by browsers to "preflight" check CORS requests, - // with a 200 OK response with no content and the CORS headers above - options("*", (req, res) -> ""); + setHeaders(); if (validator != null) { ValidatorEndpoint.getInstance(validator); From 5311ed420eb4d791082a88718d85f85b633c2788 Mon Sep 17 00:00:00 2001 From: rpassas Date: Tue, 25 Jul 2023 16:48:25 -0400 Subject: [PATCH 2/6] removing unnecessary headers method --- .../org/mitre/inferno/rest/Endpoints.java | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/mitre/inferno/rest/Endpoints.java b/src/main/java/org/mitre/inferno/rest/Endpoints.java index 03ef174..eacc1d4 100644 --- a/src/main/java/org/mitre/inferno/rest/Endpoints.java +++ b/src/main/java/org/mitre/inferno/rest/Endpoints.java @@ -28,10 +28,12 @@ private Endpoints(Validator validator, FHIRPathEvaluator evaluator) { } /** - * This adds permissive CORS headers to all requests. + * Set up temporary routes while the validator still loads. + * + * @param port the port to listen for requests on */ - private static void setHeaders() { - + public static void setupLoadingRoutes(int port) { + port(port); before((req, res) -> { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS"); @@ -43,17 +45,6 @@ private static void setHeaders() { // with a 200 OK response with no content and the CORS headers above options("*", (req, res) -> ""); - } - - /** - * Set up temporary routes while the validator still loads. - * - * @param port the port to listen for requests on - */ - public static void setupLoadingRoutes(int port) { - port(port); - setHeaders(); - JsonObject warning = new JsonObject(); warning.addProperty("Warning", "Validator still loading... please wait."); @@ -106,7 +97,6 @@ public static Endpoints getInstance(Validator validator, FHIRPathEvaluator evalu * clients. */ private void createRoutes() { - setHeaders(); if (validator != null) { ValidatorEndpoint.getInstance(validator); From 2f989d2ed1918bb6c09fcf0341a64ec482ff65f3 Mon Sep 17 00:00:00 2001 From: rpassas Date: Thu, 27 Jul 2023 16:24:38 -0400 Subject: [PATCH 3/6] added back headers method + added 503 response --- .../org/mitre/inferno/rest/Endpoints.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/mitre/inferno/rest/Endpoints.java b/src/main/java/org/mitre/inferno/rest/Endpoints.java index eacc1d4..b3fd774 100644 --- a/src/main/java/org/mitre/inferno/rest/Endpoints.java +++ b/src/main/java/org/mitre/inferno/rest/Endpoints.java @@ -12,7 +12,6 @@ import org.mitre.inferno.FHIRPathEvaluator; import org.mitre.inferno.Validator; import spark.ResponseTransformer; -import com.google.gson.JsonObject; public class Endpoints { public static final ResponseTransformer TO_JSON = new Gson()::toJson; @@ -28,12 +27,10 @@ private Endpoints(Validator validator, FHIRPathEvaluator evaluator) { } /** - * Set up temporary routes while the validator still loads. - * - * @param port the port to listen for requests on + * This adds permissive CORS headers to all requests. */ - public static void setupLoadingRoutes(int port) { - port(port); + private static void setHeaders() { + before((req, res) -> { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS"); @@ -45,20 +42,34 @@ public static void setupLoadingRoutes(int port) { // with a 200 OK response with no content and the CORS headers above options("*", (req, res) -> ""); - JsonObject warning = new JsonObject(); - warning.addProperty("Warning", "Validator still loading... please wait."); + } + + /** + * Set up temporary routes while the validator still loads. + * + * @param port the port on which to listen for requests + */ + public static void setupLoadingRoutes(int port) { + port(port); + setHeaders(); get("*", (req, res) -> { res.type("application/fhir+json"); - return warning; + res.status(503); + res.body("Validator still loading... please wait."); + return res; }); post("*", (req, res) -> { res.type("application/fhir+json"); - return warning; + res.status(503); + res.body("Validator still loading... please wait."); + return res; }); put("*", (req, res) -> { res.type("application/fhir+json"); - return warning; + res.status(503); + res.body("Validator still loading... please wait."); + return res; }); } From 575bf7401e73647f6bfe0327fd2e0d47df7d6f30 Mon Sep 17 00:00:00 2001 From: rpassas Date: Fri, 28 Jul 2023 14:44:08 -0400 Subject: [PATCH 4/6] return operationOutcome when waiting for validator --- .../org/mitre/inferno/rest/Endpoints.java | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mitre/inferno/rest/Endpoints.java b/src/main/java/org/mitre/inferno/rest/Endpoints.java index b3fd774..aad7ea1 100644 --- a/src/main/java/org/mitre/inferno/rest/Endpoints.java +++ b/src/main/java/org/mitre/inferno/rest/Endpoints.java @@ -8,11 +8,21 @@ import static spark.Spark.put; import static spark.Spark.unmap; +import java.io.IOException; + import com.google.gson.Gson; import org.mitre.inferno.FHIRPathEvaluator; import org.mitre.inferno.Validator; import spark.ResponseTransformer; +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.OperationOutcome; +import org.hl7.fhir.r5.model.OperationOutcome.IssueType; +import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.IntegerType; +import org.hl7.fhir.r5.model.CodeType; + public class Endpoints { public static final ResponseTransformer TO_JSON = new Gson()::toJson; private static Endpoints endpoints = null; @@ -44,6 +54,34 @@ private static void setHeaders() { } + /** + * Create a wait message while the validator loads. + * + * @throws Exception + */ + private static OperationOutcome generateWaitMessage() throws Exception { + + OperationOutcome.IssueSeverity sev = OperationOutcome.IssueSeverity.ERROR; + OperationOutcomeIssueComponent issue = new OperationOutcomeIssueComponent( + sev, + IssueType.INFORMATIONAL); + String message = "Validator still loading... please wait."; + issue.setDiagnostics(message); + issue.setDetails(new CodeableConcept().setText(message)); + issue.addExtension( + "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line", + new IntegerType(1)); + issue.addExtension( + "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col", + new IntegerType(1)); + issue.addExtension( + "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source", + new CodeType("ValidationService")); + OperationOutcome oo = new OperationOutcome(issue); + // return new JsonParser().composeString(oo); + return oo; + } + /** * Set up temporary routes while the validator still loads. * @@ -57,19 +95,19 @@ public static void setupLoadingRoutes(int port) { res.type("application/fhir+json"); res.status(503); res.body("Validator still loading... please wait."); - return res; + return generateWaitMessage(); }); post("*", (req, res) -> { res.type("application/fhir+json"); res.status(503); res.body("Validator still loading... please wait."); - return res; + return generateWaitMessage(); }); put("*", (req, res) -> { res.type("application/fhir+json"); res.status(503); res.body("Validator still loading... please wait."); - return res; + return generateWaitMessage(); }); } From 5978cbad3f97da8506f606e0bd3c753f176ceaad Mon Sep 17 00:00:00 2001 From: rpassas Date: Fri, 28 Jul 2023 17:02:00 -0400 Subject: [PATCH 5/6] return string + changed issueType to 'incomplete' --- src/main/java/org/mitre/inferno/rest/Endpoints.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/mitre/inferno/rest/Endpoints.java b/src/main/java/org/mitre/inferno/rest/Endpoints.java index aad7ea1..2cd0f47 100644 --- a/src/main/java/org/mitre/inferno/rest/Endpoints.java +++ b/src/main/java/org/mitre/inferno/rest/Endpoints.java @@ -59,12 +59,12 @@ private static void setHeaders() { * * @throws Exception */ - private static OperationOutcome generateWaitMessage() throws Exception { + private static String generateWaitMessage() throws Exception { OperationOutcome.IssueSeverity sev = OperationOutcome.IssueSeverity.ERROR; OperationOutcomeIssueComponent issue = new OperationOutcomeIssueComponent( sev, - IssueType.INFORMATIONAL); + IssueType.INCOMPLETE); String message = "Validator still loading... please wait."; issue.setDiagnostics(message); issue.setDetails(new CodeableConcept().setText(message)); @@ -78,8 +78,7 @@ private static OperationOutcome generateWaitMessage() throws Exception { "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source", new CodeType("ValidationService")); OperationOutcome oo = new OperationOutcome(issue); - // return new JsonParser().composeString(oo); - return oo; + return new JsonParser().composeString(oo); } /** From 50883a078b610424512ce12ca55a3c35de48990e Mon Sep 17 00:00:00 2001 From: rpassas Date: Mon, 31 Jul 2023 14:44:59 -0400 Subject: [PATCH 6/6] passing style checks --- .../org/mitre/inferno/rest/Endpoints.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/mitre/inferno/rest/Endpoints.java b/src/main/java/org/mitre/inferno/rest/Endpoints.java index 2cd0f47..830afc6 100644 --- a/src/main/java/org/mitre/inferno/rest/Endpoints.java +++ b/src/main/java/org/mitre/inferno/rest/Endpoints.java @@ -1,27 +1,24 @@ package org.mitre.inferno.rest; import static spark.Spark.before; +import static spark.Spark.get; import static spark.Spark.options; import static spark.Spark.port; -import static spark.Spark.get; import static spark.Spark.post; import static spark.Spark.put; import static spark.Spark.unmap; -import java.io.IOException; - import com.google.gson.Gson; -import org.mitre.inferno.FHIRPathEvaluator; -import org.mitre.inferno.Validator; -import spark.ResponseTransformer; - import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.OperationOutcome; import org.hl7.fhir.r5.model.OperationOutcome.IssueType; import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent; -import org.hl7.fhir.r5.model.CodeableConcept; -import org.hl7.fhir.r5.model.IntegerType; -import org.hl7.fhir.r5.model.CodeType; +import org.mitre.inferno.FHIRPathEvaluator; +import org.mitre.inferno.Validator; +import spark.ResponseTransformer; public class Endpoints { public static final ResponseTransformer TO_JSON = new Gson()::toJson; @@ -57,7 +54,7 @@ private static void setHeaders() { /** * Create a wait message while the validator loads. * - * @throws Exception + * @throws Exception if operation outcome cannot be parsed */ private static String generateWaitMessage() throws Exception {