Skip to content

Commit

Permalink
Fi 1977 slow (#64)
Browse files Browse the repository at this point in the history
* set temp routes, replaced once validator loads + updated spark version

* removing unnecessary headers method

* added back headers method + added 503 response

* return operationOutcome when waiting for validator

* return string + changed issueType to 'incomplete'

* passing style checks

---------

Co-authored-by: rpassas <[email protected]>
  • Loading branch information
rpassas and rpassas authored Aug 1, 2023
1 parent 74ccd94 commit 33b49c9
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 21 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/org/mitre/inferno/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ public class App {

/**
* Starting point for the Validation Service.
* <p>Passing the 'prepare' argument causes the FHIR artifacts needed to be downloaded.</p>
* <p>
* Passing the 'prepare' argument causes the FHIR artifacts needed to be
* downloaded.
* </p>
*
* @param args the application initialization arguments
*/
public static void main(String[] args) {
Expand Down Expand Up @@ -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() {
Expand Down
122 changes: 105 additions & 17 deletions src/main/java/org/mitre/inferno/rest/Endpoints.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
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.post;
import static spark.Spark.put;
import static spark.Spark.unmap;

import com.google.gson.Gson;
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.mitre.inferno.FHIRPathEvaluator;
import org.mitre.inferno.Validator;
import spark.ResponseTransformer;
Expand All @@ -16,44 +27,121 @@ 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) -> "");

}

/**
* Create a wait message while the validator loads.
*
* @throws Exception if operation outcome cannot be parsed
*/
private static String generateWaitMessage() throws Exception {

OperationOutcome.IssueSeverity sev = OperationOutcome.IssueSeverity.ERROR;
OperationOutcomeIssueComponent issue = new OperationOutcomeIssueComponent(
sev,
IssueType.INCOMPLETE);
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);
}

/**
* 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");
res.status(503);
res.body("Validator still loading... please wait.");
return generateWaitMessage();
});
post("*", (req, res) -> {
res.type("application/fhir+json");
res.status(503);
res.body("Validator still loading... please wait.");
return generateWaitMessage();
});
put("*", (req, res) -> {
res.type("application/fhir+json");
res.status(503);
res.body("Validator still loading... please wait.");
return generateWaitMessage();
});
}

/**
* 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) -> "");

if (validator != null) {
ValidatorEndpoint.getInstance(validator);
Expand Down

0 comments on commit 33b49c9

Please sign in to comment.