From 45606fff84e0118d98594429c163d607dfad8e61 Mon Sep 17 00:00:00 2001
From: Patrick LaRocque <plarocque@mitre.org>
Date: Fri, 13 May 2022 10:41:38 -0400
Subject: [PATCH 1/6] Add the davinci-associated-resource extension to the card
 containing a reference to the resource that the card is in response to.

---
 .../src/main/java/org/cdshooks/Card.java      |  9 ++++++
 .../main/java/org/cdshooks/CardExtension.java | 32 +++++++++++++++++++
 .../java/org/hl7/davinci/r4/Utilities.java    |  7 ++++
 .../cdshooks/services/crd/CdsService.java     |  2 +-
 .../services/crd/r4/OrderSelectService.java   |  9 ++++++
 .../endpoint/components/CardBuilder.java      | 22 +++++++++----
 6 files changed, 74 insertions(+), 7 deletions(-)
 create mode 100644 resources/src/main/java/org/cdshooks/CardExtension.java

diff --git a/resources/src/main/java/org/cdshooks/Card.java b/resources/src/main/java/org/cdshooks/Card.java
index a4b125703..2801dbf66 100755
--- a/resources/src/main/java/org/cdshooks/Card.java
+++ b/resources/src/main/java/org/cdshooks/Card.java
@@ -16,6 +16,7 @@ public class Card {
   private List<Suggestion> suggestions = null;
   private SelectionBehaviorEnum selectionBehavior = null;
   private List<Link> links = null;
+  private CardExtension extension = null;
 
   public Card() { this.uuid = UUID.randomUUID().toString(); }
 
@@ -101,6 +102,14 @@ public void setLinks(List<Link> links) {
     this.links = links;
   }
 
+  public CardExtension getExtension() {
+    return this.extension;
+  }
+
+  public void setExtension(CardExtension extension) {
+    this.extension = extension;
+  }
+
   public enum IndicatorEnum {
     INFO("info"),
 
diff --git a/resources/src/main/java/org/cdshooks/CardExtension.java b/resources/src/main/java/org/cdshooks/CardExtension.java
new file mode 100644
index 000000000..604bfbcc0
--- /dev/null
+++ b/resources/src/main/java/org/cdshooks/CardExtension.java
@@ -0,0 +1,32 @@
+package org.cdshooks;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CardExtension {
+  
+  @JsonProperty("davinci-associated-resource")
+  private List<String> associatedResources;
+
+  public CardExtension() {
+    associatedResources = new ArrayList<>();
+  }
+
+  public CardExtension(List<String> configurationOptions) {
+    this.associatedResources = configurationOptions;
+  }
+
+  public List<String> getAssociatedResources() {
+    return associatedResources;
+  }
+
+  public void setAssociatedResources(List<String> associatedResources) {
+    this.associatedResources = associatedResources;
+  }
+
+  public void addAssociatedResource(String associatedResource) {
+    this.associatedResources.add(associatedResource);
+  }
+}
diff --git a/resources/src/main/java/org/hl7/davinci/r4/Utilities.java b/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
index 222269bff..f51304f17 100644
--- a/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
+++ b/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
@@ -189,6 +189,13 @@ public static IBaseResource parseFhirData(String resourceString) {
     return parser.parseResource(resourceString);
   }
 
+  public static String getIdFromIBaseResource(IBaseResource baseResource) {
+    if (baseResource == null) {
+      return "";
+    }
+    return baseResource.getIdElement().getValue();
+  }
+
   public static FhirResourceInfo getFhirResourceInfo(IBaseResource baseResource) {
     String resourceType = baseResource.fhirType(); // grab the FHIR resource type out of the resource
     resourceType = resourceType.toLowerCase();
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index e7d7bdaf5..59225068a 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -268,7 +268,7 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
 
         // apply the DrugInteractions
         if (results.getDrugInteraction().getApplies()) {
-          response.addCard(CardBuilder.drugInteractionCard(results.getDrugInteraction()));
+          response.addCard(CardBuilder.drugInteractionCard(results.getDrugInteraction(), results.getRequest()));
         }
       }
     }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java
index 83b6a00ec..178679788 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java
@@ -20,7 +20,9 @@
 import org.hl7.davinci.r4.crdhook.CrdPrefetch;
 import org.hl7.davinci.r4.crdhook.orderselect.CrdPrefetchTemplateElements;
 import org.hl7.davinci.r4.crdhook.orderselect.OrderSelectRequest;
+import org.hl7.fhir.instance.model.api.IBaseResource;
 import org.hl7.fhir.r4.model.Coding;
+import org.json.simple.JSONObject;
 import org.opencds.cqf.cql.engine.execution.Context;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -69,6 +71,13 @@ protected CqlResultsForCard executeCqlAndGetRelevantResults(Context context, Str
 
     CoverageRequirements coverageRequirements = new CoverageRequirements();
     coverageRequirements.setApplies(false);
+
+    if (evaluateStatement("RESULT_requestId", context) != null) {
+      results.setRequest((IBaseResource) evaluateStatement("RESULT_requestId", context));
+      coverageRequirements.setRequestId(JSONObject.escape(fhirComponents.getFhirContext().newJsonParser()
+          .encodeResourceToString(results.getRequest())));
+    }
+
     results.setCoverageRequirements(coverageRequirements);
 
     AlternativeTherapy alternativeTherapy = new AlternativeTherapy();
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java b/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
index 618198fca..561966a5b 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
@@ -85,7 +85,8 @@ public CqlResultsForCard setRequest(IBaseResource request) {
    * @return card with appropriate information
    */
   public static Card transform(CardTypes cardType, CqlResultsForCard cqlResults, Boolean addLink) {
-    Card card = baseCard(cardType);
+    String requestId = Utilities.getIdFromIBaseResource(cqlResults.getRequest());
+    Card card = baseCard(cardType, requestId);
 
     if (addLink) {
       Link link = new Link();
@@ -152,7 +153,7 @@ public static Card transform(CardTypes cardType, CqlResultsForCard cqlResults, L
    * @return valid card
    */
   public static Card summaryCard(CardTypes cardType, String summary) {
-    Card card = baseCard(cardType);
+    Card card = baseCard(cardType, "");
     card.setSummary(summary);
     return card;
   }
@@ -160,7 +161,8 @@ public static Card summaryCard(CardTypes cardType, String summary) {
   public static Card alternativeTherapyCard(AlternativeTherapy alternativeTherapy, IBaseResource resource,
                                             FhirComponentsT fhirComponents) {
     logger.info("Build Alternative Therapy Card: " + alternativeTherapy.toString());
-    Card card = baseCard(CardTypes.THERAPY_ALTERNATIVES_OPT);
+    String requestId = Utilities.getIdFromIBaseResource(resource);
+    Card card = baseCard(CardTypes.THERAPY_ALTERNATIVES_OPT, requestId);
 
     card.setSummary("Alternative Therapy Suggested");
     card.setDetail(alternativeTherapy.getDisplay() + " (" + alternativeTherapy.getCode() + ") should be used instead.");
@@ -203,9 +205,10 @@ public static Card alternativeTherapyCard(AlternativeTherapy alternativeTherapy,
     return card;
   }
 
-  public static Card drugInteractionCard(DrugInteraction drugInteraction) {
+  public static Card drugInteractionCard(DrugInteraction drugInteraction, IBaseResource resource) {
     logger.info("Build Drug Interaction Card: " + drugInteraction.getSummary());
-    Card card = baseCard(CardTypes.CONTRAINDICATION);
+    String requestId = Utilities.getIdFromIBaseResource(resource);
+    Card card = baseCard(CardTypes.CONTRAINDICATION, requestId);
     card.setSummary(drugInteraction.getSummary());
     card.setDetail(drugInteraction.getDetail());
     card.setIndicator(Card.IndicatorEnum.WARNING);
@@ -383,10 +386,17 @@ public static void errorCardIfNonePresent(CardTypes cardType, CdsResponse respon
     }
   }
 
-  private static Card baseCard(CardTypes cardType) {
+  private static Card baseCard(CardTypes cardType, String requestId) {
     Card card = new Card();
     card.setIndicator(Card.IndicatorEnum.INFO);
     card.setSource(createSource(cardType));
+
+    if (!requestId.isEmpty()) {
+      CardExtension cardExtension = new CardExtension();
+      cardExtension.addAssociatedResource(requestId);
+      card.setExtension(cardExtension);
+    }
+    
     return card;
   }
 }

From 49e67d858082b9a2f4ae451ac703a75af56522c0 Mon Sep 17 00:00:00 2001
From: Patrick LaRocque <41444457+plarocque4@users.noreply.github.com>
Date: Wed, 25 May 2022 15:37:19 -0400
Subject: [PATCH 2/6] Add the davinci-associated-resource extension to the card
 containing a reference to the resource that the card is in response to.
 (#273)

---
 .../src/main/java/org/cdshooks/Card.java      |  9 ++++++
 .../main/java/org/cdshooks/CardExtension.java | 32 +++++++++++++++++++
 .../java/org/hl7/davinci/r4/Utilities.java    |  7 ++++
 .../cdshooks/services/crd/CdsService.java     |  2 +-
 .../services/crd/r4/OrderSelectService.java   |  9 ++++++
 .../endpoint/components/CardBuilder.java      | 22 +++++++++----
 6 files changed, 74 insertions(+), 7 deletions(-)
 create mode 100644 resources/src/main/java/org/cdshooks/CardExtension.java

diff --git a/resources/src/main/java/org/cdshooks/Card.java b/resources/src/main/java/org/cdshooks/Card.java
index a4b125703..2801dbf66 100755
--- a/resources/src/main/java/org/cdshooks/Card.java
+++ b/resources/src/main/java/org/cdshooks/Card.java
@@ -16,6 +16,7 @@ public class Card {
   private List<Suggestion> suggestions = null;
   private SelectionBehaviorEnum selectionBehavior = null;
   private List<Link> links = null;
+  private CardExtension extension = null;
 
   public Card() { this.uuid = UUID.randomUUID().toString(); }
 
@@ -101,6 +102,14 @@ public void setLinks(List<Link> links) {
     this.links = links;
   }
 
+  public CardExtension getExtension() {
+    return this.extension;
+  }
+
+  public void setExtension(CardExtension extension) {
+    this.extension = extension;
+  }
+
   public enum IndicatorEnum {
     INFO("info"),
 
diff --git a/resources/src/main/java/org/cdshooks/CardExtension.java b/resources/src/main/java/org/cdshooks/CardExtension.java
new file mode 100644
index 000000000..604bfbcc0
--- /dev/null
+++ b/resources/src/main/java/org/cdshooks/CardExtension.java
@@ -0,0 +1,32 @@
+package org.cdshooks;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CardExtension {
+  
+  @JsonProperty("davinci-associated-resource")
+  private List<String> associatedResources;
+
+  public CardExtension() {
+    associatedResources = new ArrayList<>();
+  }
+
+  public CardExtension(List<String> configurationOptions) {
+    this.associatedResources = configurationOptions;
+  }
+
+  public List<String> getAssociatedResources() {
+    return associatedResources;
+  }
+
+  public void setAssociatedResources(List<String> associatedResources) {
+    this.associatedResources = associatedResources;
+  }
+
+  public void addAssociatedResource(String associatedResource) {
+    this.associatedResources.add(associatedResource);
+  }
+}
diff --git a/resources/src/main/java/org/hl7/davinci/r4/Utilities.java b/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
index 222269bff..f51304f17 100644
--- a/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
+++ b/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
@@ -189,6 +189,13 @@ public static IBaseResource parseFhirData(String resourceString) {
     return parser.parseResource(resourceString);
   }
 
+  public static String getIdFromIBaseResource(IBaseResource baseResource) {
+    if (baseResource == null) {
+      return "";
+    }
+    return baseResource.getIdElement().getValue();
+  }
+
   public static FhirResourceInfo getFhirResourceInfo(IBaseResource baseResource) {
     String resourceType = baseResource.fhirType(); // grab the FHIR resource type out of the resource
     resourceType = resourceType.toLowerCase();
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index e7d7bdaf5..59225068a 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -268,7 +268,7 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
 
         // apply the DrugInteractions
         if (results.getDrugInteraction().getApplies()) {
-          response.addCard(CardBuilder.drugInteractionCard(results.getDrugInteraction()));
+          response.addCard(CardBuilder.drugInteractionCard(results.getDrugInteraction(), results.getRequest()));
         }
       }
     }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java
index 83b6a00ec..178679788 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectService.java
@@ -20,7 +20,9 @@
 import org.hl7.davinci.r4.crdhook.CrdPrefetch;
 import org.hl7.davinci.r4.crdhook.orderselect.CrdPrefetchTemplateElements;
 import org.hl7.davinci.r4.crdhook.orderselect.OrderSelectRequest;
+import org.hl7.fhir.instance.model.api.IBaseResource;
 import org.hl7.fhir.r4.model.Coding;
+import org.json.simple.JSONObject;
 import org.opencds.cqf.cql.engine.execution.Context;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -69,6 +71,13 @@ protected CqlResultsForCard executeCqlAndGetRelevantResults(Context context, Str
 
     CoverageRequirements coverageRequirements = new CoverageRequirements();
     coverageRequirements.setApplies(false);
+
+    if (evaluateStatement("RESULT_requestId", context) != null) {
+      results.setRequest((IBaseResource) evaluateStatement("RESULT_requestId", context));
+      coverageRequirements.setRequestId(JSONObject.escape(fhirComponents.getFhirContext().newJsonParser()
+          .encodeResourceToString(results.getRequest())));
+    }
+
     results.setCoverageRequirements(coverageRequirements);
 
     AlternativeTherapy alternativeTherapy = new AlternativeTherapy();
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java b/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
index 618198fca..561966a5b 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
@@ -85,7 +85,8 @@ public CqlResultsForCard setRequest(IBaseResource request) {
    * @return card with appropriate information
    */
   public static Card transform(CardTypes cardType, CqlResultsForCard cqlResults, Boolean addLink) {
-    Card card = baseCard(cardType);
+    String requestId = Utilities.getIdFromIBaseResource(cqlResults.getRequest());
+    Card card = baseCard(cardType, requestId);
 
     if (addLink) {
       Link link = new Link();
@@ -152,7 +153,7 @@ public static Card transform(CardTypes cardType, CqlResultsForCard cqlResults, L
    * @return valid card
    */
   public static Card summaryCard(CardTypes cardType, String summary) {
-    Card card = baseCard(cardType);
+    Card card = baseCard(cardType, "");
     card.setSummary(summary);
     return card;
   }
@@ -160,7 +161,8 @@ public static Card summaryCard(CardTypes cardType, String summary) {
   public static Card alternativeTherapyCard(AlternativeTherapy alternativeTherapy, IBaseResource resource,
                                             FhirComponentsT fhirComponents) {
     logger.info("Build Alternative Therapy Card: " + alternativeTherapy.toString());
-    Card card = baseCard(CardTypes.THERAPY_ALTERNATIVES_OPT);
+    String requestId = Utilities.getIdFromIBaseResource(resource);
+    Card card = baseCard(CardTypes.THERAPY_ALTERNATIVES_OPT, requestId);
 
     card.setSummary("Alternative Therapy Suggested");
     card.setDetail(alternativeTherapy.getDisplay() + " (" + alternativeTherapy.getCode() + ") should be used instead.");
@@ -203,9 +205,10 @@ public static Card alternativeTherapyCard(AlternativeTherapy alternativeTherapy,
     return card;
   }
 
-  public static Card drugInteractionCard(DrugInteraction drugInteraction) {
+  public static Card drugInteractionCard(DrugInteraction drugInteraction, IBaseResource resource) {
     logger.info("Build Drug Interaction Card: " + drugInteraction.getSummary());
-    Card card = baseCard(CardTypes.CONTRAINDICATION);
+    String requestId = Utilities.getIdFromIBaseResource(resource);
+    Card card = baseCard(CardTypes.CONTRAINDICATION, requestId);
     card.setSummary(drugInteraction.getSummary());
     card.setDetail(drugInteraction.getDetail());
     card.setIndicator(Card.IndicatorEnum.WARNING);
@@ -383,10 +386,17 @@ public static void errorCardIfNonePresent(CardTypes cardType, CdsResponse respon
     }
   }
 
-  private static Card baseCard(CardTypes cardType) {
+  private static Card baseCard(CardTypes cardType, String requestId) {
     Card card = new Card();
     card.setIndicator(Card.IndicatorEnum.INFO);
     card.setSource(createSource(cardType));
+
+    if (!requestId.isEmpty()) {
+      CardExtension cardExtension = new CardExtension();
+      cardExtension.addAssociatedResource(requestId);
+      card.setExtension(cardExtension);
+    }
+    
     return card;
   }
 }

From 4717fefdc5cff0054b9b97fc1a2b4c1b4c196e9c Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Thu, 16 Jun 2022 16:03:38 -0400
Subject: [PATCH 3/6] move next question endpoint to FHIR controller

---
 .vscode/launch.json                           | 17 +++++-
 .../endpoint/controllers/FhirController.java  | 21 ++++++++
 .../QuestionnaireNextQuestionOperation.java}  | 54 +++++++------------
 server/src/main/resources/application.yml     |  4 +-
 4 files changed, 56 insertions(+), 40 deletions(-)
 rename server/src/main/java/org/hl7/davinci/endpoint/{controllers/QuestionnaireController.java => fhir/r4/QuestionnaireNextQuestionOperation.java} (90%)

diff --git a/.vscode/launch.json b/.vscode/launch.json
index 049e8a441..fbc4b14be 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -6,10 +6,23 @@
     "configurations": [
         {
             "type": "java",
-            "name": "Debug CRD (Local + Docker)", // gradle bootRun -Pdebug,
+            "name": "Launch Application",
+            "request": "launch",
+            "mainClass": "org.hl7.davinci.endpoint.Application",
+            "projectName": "server"
+        },
+        {
+            "type": "java",
+            "name": "Launch Java Program",
+            "request": "launch",
+            "mainClass": ""
+        },
+        {
+            "type": "java",
+            "name": "Debug CRD (Local + Docker)",
             "request": "attach",
             "hostName": "localhost",
-            "port": 8091,
+            "port": 8091
         }
     ]
 } 
\ No newline at end of file
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java
index dfb5925fb..d78976f07 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java
@@ -5,6 +5,7 @@
 import org.hl7.davinci.endpoint.Utils;
 import org.hl7.davinci.endpoint.database.FhirResource;
 import org.hl7.davinci.endpoint.database.FhirResourceRepository;
+import org.hl7.davinci.endpoint.fhir.r4.QuestionnaireNextQuestionOperation;
 import org.hl7.davinci.endpoint.fhir.r4.QuestionnairePackageOperation;
 import org.hl7.davinci.endpoint.files.FileResource;
 import org.hl7.davinci.endpoint.files.FileStore;
@@ -260,4 +261,24 @@ public ResponseEntity<String> questionnaireForOrderOperation(HttpServletRequest
     return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
           .body(resource);
   }
+
+
+  @PostMapping(value = "/fhir/{fhirVersion}/Questionnaire/$next-question", consumes = { MediaType.APPLICATION_JSON_VALUE, "application/fhir+json" })
+  public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, HttpEntity<String> entity, @PathVariable String fhirVersion) {
+    fhirVersion = fhirVersion.toUpperCase();
+    
+    logger.info("POST /fhir/" + fhirVersion + "/Questionnaire/$next-question");
+
+    if (fhirVersion.equalsIgnoreCase("R4")) {
+      QuestionnaireNextQuestionOperation operation = new QuestionnaireNextQuestionOperation(fileStore);
+      return operation.getNextQuestionOperation(entity.getBody(), request);
+    } else {
+      logger.warning("unsupported FHIR version: " + fhirVersion + ", not storing");
+      HttpStatus status = HttpStatus.BAD_REQUEST;
+      MediaType contentType = MediaType.TEXT_PLAIN;
+
+      return ResponseEntity.status(status).contentType(contentType)
+          .body("Bad Request");
+    }
+  }
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
similarity index 90%
rename from server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
rename to server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
index 43c4d6de3..6473198a2 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
@@ -1,5 +1,6 @@
-package org.hl7.davinci.endpoint.controllers;
+package org.hl7.davinci.endpoint.fhir.r4;
 
+import org.hl7.davinci.endpoint.files.FileStore;
 import org.hl7.davinci.endpoint.Application;
 import org.hl7.davinci.endpoint.files.FileResource;
 import org.hl7.davinci.endpoint.files.FileStore;
@@ -10,6 +11,7 @@
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.*;
 
 import ca.uhn.fhir.context.FhirContext;
@@ -42,25 +44,20 @@
 import org.hl7.fhir.r4.model.Reference;
 import org.hl7.davinci.endpoint.files.QuestionnaireEmbeddedCQLProcessor;
 
-// --- ORDER OF RESPONSE-REQUEST OPERATIONS
-// (REQUEST) External user sends the initial QuestionnaireResponse JSON that contains which questionnaire it would like to trigger as n element the "contained" field.
-// (RESPONSE) QuestionnaireController adds the first question with its answerResponse options (with its linkId and text) to the JSON in QuestionnaireResponse.contained.item[] and sends it back.
-// (REQUEST) External user adds their answer to the question to the JSON in QuestionnaireResponse.item[] and sends it back.
-// (RESPONSE) QuestionnaireController takes that response and adds the next indicated question to the JSON in QuestionnaireResponse.contained.item[] and sends it back.
-// Repeat intil QuestionnaireController reaches a leaf-node, then it sets the status to "completed" from "in-progress"
-// Ultimately, The QuestionnaireController responses add ONLY to the QuestionnaireResponse.contained.item[]. The external requester adds answers to QuestionnaireResponse.item[] and includes the associated linkid and text.
-
-@CrossOrigin
-@RestController
-@RequestMapping("/Questionnaire")
-public class QuestionnaireController {
 
-    @Autowired
-    private FileStore fileStore;
+public class QuestionnaireNextQuestionOperation {
+    
+    FileStore fileStore;
 
     private QuestionnaireEmbeddedCQLProcessor questionnaireEmbeddedCQLProcessor;
 
-    public QuestionnaireController() {
+     // Logger.
+     private static Logger logger = Logger.getLogger(Application.class.getName());
+     // Trees that track the current and next questions. Is key-value mappng of: Map<Questionnaire ID -> AdaptiveQuestionnaireTree>
+     private static final Map<String, AdaptiveQuestionnaireTree> questionnaireTrees = new HashMap<String, AdaptiveQuestionnaireTree>();
+
+    public QuestionnaireNextQuestionOperation(FileStore fileStore) {
+        this.fileStore = fileStore;
         this.questionnaireEmbeddedCQLProcessor = new QuestionnaireEmbeddedCQLProcessor();
     }
 
@@ -267,29 +264,14 @@ private boolean isLeafNode() {
         }
     }
 
-    // Logger.
-    private static Logger logger = Logger.getLogger(Application.class.getName());
-    // Trees that track the current and next questions. Is key-value mappng of: Map<Questionnaire ID -> AdaptiveQuestionnaireTree>
-    private static final Map<String, AdaptiveQuestionnaireTree> questionnaireTrees = new HashMap<String, AdaptiveQuestionnaireTree>();
-
-    /**
-     * Retrieves the next question based on the request.
-     * @param request
-     * @param entity
-     * @return
-     */
-    @PostMapping(value = "/$next-question", consumes = { MediaType.APPLICATION_JSON_VALUE, "application/fhir+json" })
-    public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, HttpEntity<String> entity) {
-        return getNextQuestionOperation(entity.getBody(), request);
-    }
-
-    /**
+    
+     /**
      * Returns the next question based on the request.
      * @param body
      * @param request
      * @return
      */
-    private ResponseEntity<String> getNextQuestionOperation(String body, HttpServletRequest request) {
+    public ResponseEntity<String> getNextQuestionOperation(String body, HttpServletRequest request) {
         logger.info("POST /Questionnaire/$next-question fhir+");
 
         FhirContext ctx = new FhirComponents().getFhirContext();
@@ -348,7 +330,7 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 // Pull the resulting next question that the recieved responses and answers point to from the tree without including its children.
                 List<QuestionnaireItemComponent> nextQuestionSetResults = currentTree.getNextQuestionsForAnswers(allResponses, inputQuestionnaireResponse);
                 // Add the next set of questions to the response.
-                QuestionnaireController.addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResults);
+                QuestionnaireNextQuestionOperation.addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResults);
                 // Check that there no duplicates in the set of questions.
                 if ((new HashSet(((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().stream().map(item -> item.getLinkId()).collect(Collectors.toList()))).size() != ((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().size()){
                     throw new RuntimeException("Attempted to send a set of questions with duplicates. Question IDs are: " + (((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().stream().map(item -> item.getLinkId()).collect(Collectors.toList())));
@@ -414,4 +396,4 @@ private static void addQuestionSetToQuestionnaireResponse(QuestionnaireResponse
         Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
         questionSet.forEach(questionItem -> containedQuestionnaire.addItem(questionItem));
     }
-}
\ No newline at end of file
+}
diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml
index e9c775811..920e77621 100644
--- a/server/src/main/resources/application.yml
+++ b/server/src/main/resources/application.yml
@@ -68,8 +68,8 @@ gitHubConfig:
   useZipForReload: true
 
 localDb:
-  path: CDS-Library/CRD-DTR/
-  examplesPath: CDS-Library/Examples/
+  path: server/CDS-Library/CRD-DTR/
+  examplesPath: server/CDS-Library/Examples/
 
 valueSetCachePath: ValueSetCache/
 

From 31ccbd161d0877f4c4eb8a373f02f8ca73cf0da5 Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Fri, 17 Jun 2022 15:12:42 -0400
Subject: [PATCH 4/6] cleanup

---
 .vscode/launch.json                           |  6 ---
 .../QuestionnaireNextQuestionOperation.java   | 51 ++++++++++---------
 server/src/main/resources/application.yml     |  4 +-
 3 files changed, 28 insertions(+), 33 deletions(-)

diff --git a/.vscode/launch.json b/.vscode/launch.json
index fbc4b14be..0b0fa68e4 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -11,12 +11,6 @@
             "mainClass": "org.hl7.davinci.endpoint.Application",
             "projectName": "server"
         },
-        {
-            "type": "java",
-            "name": "Launch Java Program",
-            "request": "launch",
-            "mainClass": ""
-        },
         {
             "type": "java",
             "name": "Debug CRD (Local + Docker)",
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
index 6473198a2..8d1002a89 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
@@ -1,25 +1,5 @@
 package org.hl7.davinci.endpoint.fhir.r4;
 
-import org.hl7.davinci.endpoint.files.FileStore;
-import org.hl7.davinci.endpoint.Application;
-import org.hl7.davinci.endpoint.files.FileResource;
-import org.hl7.davinci.endpoint.files.FileStore;
-import org.hl7.fhir.instance.model.api.IDomainResource;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
-import org.springframework.web.bind.annotation.*;
-
-import ca.uhn.fhir.context.FhirContext;
-import org.hl7.davinci.r4.FhirComponents;
-
-import ca.uhn.fhir.parser.DataFormatException;
-import ca.uhn.fhir.parser.IParser;
-
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -32,18 +12,39 @@
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.hl7.davinci.endpoint.Application;
+import org.hl7.davinci.endpoint.files.FileResource;
+import org.hl7.davinci.endpoint.files.FileStore;
+import org.hl7.davinci.endpoint.files.QuestionnaireEmbeddedCQLProcessor;
+import org.hl7.davinci.r4.FhirComponents;
+import org.hl7.fhir.instance.model.api.IDomainResource;
 import org.hl7.fhir.r4.model.Questionnaire;
-import org.hl7.fhir.r4.model.QuestionnaireResponse;
-import org.hl7.fhir.r4.model.Resource;
-import org.hl7.fhir.r4.model.ResourceType;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
+import org.hl7.fhir.r4.model.QuestionnaireResponse;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
 import org.hl7.fhir.r4.model.Reference;
-import org.hl7.davinci.endpoint.files.QuestionnaireEmbeddedCQLProcessor;
+import org.hl7.fhir.r4.model.Resource;
+import org.hl7.fhir.r4.model.ResourceType;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+
 
+// --- ORDER OF RESPONSE-REQUEST OPERATIONS
+// (REQUEST) External user sends the initial QuestionnaireResponse JSON that contains which questionnaire it would like to trigger as n element the "contained" field.
+// (RESPONSE) QuestionnaireController adds the first question with its answerResponse options (with its linkId and text) to the JSON in QuestionnaireResponse.contained.item[] and sends it back.
+// (REQUEST) External user adds their answer to the question to the JSON in QuestionnaireResponse.item[] and sends it back.
+// (RESPONSE) QuestionnaireController takes that response and adds the next indicated question to the JSON in QuestionnaireResponse.contained.item[] and sends it back.
+// Repeat intil QuestionnaireController reaches a leaf-node, then it sets the status to "completed" from "in-progress"
+// Ultimately, The QuestionnaireController responses add ONLY to the QuestionnaireResponse.contained.item[]. The external requester adds answers to QuestionnaireResponse.item[] and includes the associated linkid and text.
 
 public class QuestionnaireNextQuestionOperation {
     
@@ -52,7 +53,7 @@ public class QuestionnaireNextQuestionOperation {
     private QuestionnaireEmbeddedCQLProcessor questionnaireEmbeddedCQLProcessor;
 
      // Logger.
-     private static Logger logger = Logger.getLogger(Application.class.getName());
+     private static Logger logger = Logger.getLogger(QuestionnaireNextQuestionOperation.class.getName());
      // Trees that track the current and next questions. Is key-value mappng of: Map<Questionnaire ID -> AdaptiveQuestionnaireTree>
      private static final Map<String, AdaptiveQuestionnaireTree> questionnaireTrees = new HashMap<String, AdaptiveQuestionnaireTree>();
 
diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml
index 920e77621..e9c775811 100644
--- a/server/src/main/resources/application.yml
+++ b/server/src/main/resources/application.yml
@@ -68,8 +68,8 @@ gitHubConfig:
   useZipForReload: true
 
 localDb:
-  path: server/CDS-Library/CRD-DTR/
-  examplesPath: server/CDS-Library/Examples/
+  path: CDS-Library/CRD-DTR/
+  examplesPath: CDS-Library/Examples/
 
 valueSetCachePath: ValueSetCache/
 

From 2aab4c7ad03b5f4fe3469511cd3051ff9497e273 Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Thu, 23 Jun 2022 10:46:28 -0400
Subject: [PATCH 5/6] address CR comments

---
 .../org/hl7/davinci/endpoint/controllers/FhirController.java   | 2 +-
 .../endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java   | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java
index d78976f07..90fe75460 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/FhirController.java
@@ -271,7 +271,7 @@ public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, H
 
     if (fhirVersion.equalsIgnoreCase("R4")) {
       QuestionnaireNextQuestionOperation operation = new QuestionnaireNextQuestionOperation(fileStore);
-      return operation.getNextQuestionOperation(entity.getBody(), request);
+      return operation.execute(entity.getBody(), request);
     } else {
       logger.warning("unsupported FHIR version: " + fhirVersion + ", not storing");
       HttpStatus status = HttpStatus.BAD_REQUEST;
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
index 8d1002a89..0dc932d8a 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/QuestionnaireNextQuestionOperation.java
@@ -12,7 +12,6 @@
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.hl7.davinci.endpoint.Application;
 import org.hl7.davinci.endpoint.files.FileResource;
 import org.hl7.davinci.endpoint.files.FileStore;
 import org.hl7.davinci.endpoint.files.QuestionnaireEmbeddedCQLProcessor;
@@ -272,7 +271,7 @@ private boolean isLeafNode() {
      * @param request
      * @return
      */
-    public ResponseEntity<String> getNextQuestionOperation(String body, HttpServletRequest request) {
+    public ResponseEntity<String> execute(String body, HttpServletRequest request) {
         logger.info("POST /Questionnaire/$next-question fhir+");
 
         FhirContext ctx = new FhirComponents().getFhirContext();

From b64c1f6d26fccf4995daaa84354b29b91d2e258e Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Thu, 23 Jun 2022 14:23:15 -0400
Subject: [PATCH 6/6] add to metadata

---
 .../java/org/hl7/davinci/endpoint/fhir/r4/Metadata.java     | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/Metadata.java b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/Metadata.java
index 3043354f3..429b7bc7c 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/Metadata.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/fhir/r4/Metadata.java
@@ -17,6 +17,7 @@
 import org.hl7.fhir.r4.model.CapabilityStatement.TypeRestfulInteraction;
 import org.hl7.fhir.r4.model.Enumerations.FHIRVersion;
 import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
+import org.hl7.fhir.r5.model.CapabilityStatement2.CapabilityStatement2ImplementationComponent;
 
 
 /**
@@ -91,6 +92,11 @@ private CapabilityStatement buildCapabilityStatement(String baseUrl) {
     questionnairePackageOperation.setDefinition("http://hl7.org/fhir/us/davinci-dtr/OperationDefinition/Questionnaire-package");
     questionnairePackageOperation.setDocumentation("Retrieve the Questionnaire(s), Libraries, and Valuesets for a given order and coverage. This operation is to support HL7 DaVinci DTR.");
     questionnaire.addOperation(questionnairePackageOperation);
+    CapabilityStatementRestResourceOperationComponent questionnaireNextQuestionOperation = new CapabilityStatementRestResourceOperationComponent();
+    questionnaireNextQuestionOperation.setName("next-question");
+    questionnaireNextQuestionOperation.setDefinition("http://hl7.org/fhir/uv/sdc/OperationDefinition/Questionnaire-next-question");
+    questionnaireNextQuestionOperation.setDocumentation("Retrieve the next question(s) for a given Questionnaire and the answer(s) to the current question(s).");
+    questionnaire.addOperation(questionnaireNextQuestionOperation);
     rest.addResource(questionnaire);
 
     // QuestionnaireResponse Resource
