add endpoint to query suggestion via ES
[aai/search-data-service.git] / src / main / java / org / onap / aai / sa / rest / DocumentApi.java
index 0ec29a2..63109ef 100644 (file)
@@ -31,6 +31,7 @@ import org.onap.aai.sa.searchdbabstraction.entity.DocumentOperationResult;
 import org.onap.aai.sa.searchdbabstraction.entity.SearchOperationResult;
 import org.onap.aai.sa.searchdbabstraction.logging.SearchDbMsgs;
 import org.onap.aai.sa.searchdbabstraction.searchapi.SearchStatement;
+import org.onap.aai.sa.searchdbabstraction.searchapi.SuggestionStatement;
 import org.onap.aai.cl.api.LogFields;
 import org.onap.aai.cl.api.LogLine;
 import org.onap.aai.cl.api.Logger;
@@ -47,20 +48,19 @@ public class DocumentApi {
   private static final String REQUEST_HEADER_RESOURCE_VERSION = "If-Match";
   private static final String RESPONSE_HEADER_RESOURCE_VERSION = "ETag";
   private static final String REQUEST_HEADER_ALLOW_IMPLICIT_INDEX_CREATION = "X-CreateIndex";
-  
+
   protected SearchServiceApi searchService = null;
 
   private Logger logger = LoggerFactory.getInstance().getLogger(DocumentApi.class.getName());
-  private Logger auditLogger = LoggerFactory.getInstance()
-      .getAuditLogger(DocumentApi.class.getName());
+  private Logger auditLogger =
+      LoggerFactory.getInstance().getAuditLogger(DocumentApi.class.getName());
 
   public DocumentApi(SearchServiceApi searchService) {
     this.searchService = searchService;
   }
 
   public Response processPost(String content, HttpServletRequest request, HttpHeaders headers,
-                              HttpServletResponse httpResponse, String index,
-                              DocumentStoreInterface documentStore) {
+      HttpServletResponse httpResponse, String index, DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
@@ -77,8 +77,7 @@ public class DocumentApi {
         isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
             ApiUtils.SEARCH_AUTH_POLICY_NAME);
       } catch (Exception e) {
-        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
-            "DocumentApi.processPost",
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processPost",
             e.getMessage());
         return handleError(request, content, Status.FORBIDDEN);
       }
@@ -90,7 +89,8 @@ public class DocumentApi {
       DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
       document.setContent(content);
 
-      DocumentOperationResult result = documentStore.createDocument(index, document, implicitlyCreateIndex(headers));
+      DocumentOperationResult result =
+          documentStore.createDocument(index, document, implicitlyCreateIndex(headers));
       String output = null;
       if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
         output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument());
@@ -117,8 +117,8 @@ public class DocumentApi {
   }
 
   public Response processPut(String content, HttpServletRequest request, HttpHeaders headers,
-                             HttpServletResponse httpResponse, String index,
-                             String id, DocumentStoreInterface documentStore) {
+      HttpServletResponse httpResponse, String index, String id,
+      DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
@@ -135,8 +135,7 @@ public class DocumentApi {
         isValid = searchService.validateRequest(headers, request, ApiUtils.Action.PUT,
             ApiUtils.SEARCH_AUTH_POLICY_NAME);
       } catch (Exception e) {
-        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
-            "DocumentApi.processPut",
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processPut",
             e.getMessage());
         return handleError(request, content, Status.FORBIDDEN);
       }
@@ -145,8 +144,8 @@ public class DocumentApi {
         return handleError(request, content, Status.FORBIDDEN);
       }
 
-      String resourceVersion = headers.getRequestHeaders()
-          .getFirst(REQUEST_HEADER_RESOURCE_VERSION);
+      String resourceVersion =
+          headers.getRequestHeaders().getFirst(REQUEST_HEADER_RESOURCE_VERSION);
 
       DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
       document.setId(id);
@@ -185,8 +184,8 @@ public class DocumentApi {
   }
 
   public Response processDelete(String content, HttpServletRequest request, HttpHeaders headers,
-                                HttpServletResponse httpResponse, String index, String id,
-                                DocumentStoreInterface documentStore) {
+      HttpServletResponse httpResponse, String index, String id,
+      DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
@@ -199,8 +198,7 @@ public class DocumentApi {
         isValid = searchService.validateRequest(headers, request, ApiUtils.Action.DELETE,
             ApiUtils.SEARCH_AUTH_POLICY_NAME);
       } catch (Exception e) {
-        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
-            "DocumentApi.processDelete",
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processDelete",
             e.getMessage());
         return handleError(request, content, Status.FORBIDDEN);
       }
@@ -209,8 +207,8 @@ public class DocumentApi {
         return handleError(request, content, Status.FORBIDDEN);
       }
 
-      String resourceVersion = headers.getRequestHeaders()
-          .getFirst(REQUEST_HEADER_RESOURCE_VERSION);
+      String resourceVersion =
+          headers.getRequestHeaders().getFirst(REQUEST_HEADER_RESOURCE_VERSION);
       if (resourceVersion == null || resourceVersion.isEmpty()) {
         return handleError(request, "Request header 'If-Match' missing",
             javax.ws.rs.core.Response.Status.BAD_REQUEST);
@@ -251,8 +249,8 @@ public class DocumentApi {
   }
 
   public Response processGet(String content, HttpServletRequest request, HttpHeaders headers,
-                             HttpServletResponse httpResponse, String index, String id,
-                             DocumentStoreInterface documentStore) {
+      HttpServletResponse httpResponse, String index, String id,
+      DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
@@ -265,8 +263,7 @@ public class DocumentApi {
         isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET,
             ApiUtils.SEARCH_AUTH_POLICY_NAME);
       } catch (Exception e) {
-        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
-            "DocumentApi.processGet",
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processGet",
             e.getMessage());
         return handleError(request, content, Status.FORBIDDEN);
       }
@@ -275,8 +272,8 @@ public class DocumentApi {
         return handleError(request, content, Status.FORBIDDEN);
       }
 
-      String resourceVersion = headers.getRequestHeaders()
-          .getFirst(REQUEST_HEADER_RESOURCE_VERSION);
+      String resourceVersion =
+          headers.getRequestHeaders().getFirst(REQUEST_HEADER_RESOURCE_VERSION);
 
       DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
       document.setId(id);
@@ -308,8 +305,7 @@ public class DocumentApi {
   }
 
   public Response processSearchWithGet(String content, HttpServletRequest request,
-                                       HttpHeaders headers, String index,
-                                       String queryText, DocumentStoreInterface documentStore) {
+      HttpHeaders headers, String index, String queryText, DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
@@ -323,8 +319,7 @@ public class DocumentApi {
         isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET,
             ApiUtils.SEARCH_AUTH_POLICY_NAME);
       } catch (Exception e) {
-        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
-            "processSearchWithGet",
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processSearchWithGet",
             e.getMessage());
         return handleError(request, content, Status.FORBIDDEN);
       }
@@ -336,8 +331,8 @@ public class DocumentApi {
       SearchOperationResult result = documentStore.search(index, queryText);
       String output = null;
       if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
-        output = mapper.writerWithDefaultPrettyPrinter()
-            .writeValueAsString(result.getSearchResult());
+        output =
+            mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSearchResult());
       } else {
         output = result.getError() != null
             ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
@@ -356,14 +351,13 @@ public class DocumentApi {
   }
 
   public Response queryWithGetWithPayload(String content, HttpServletRequest request,
-                                          HttpHeaders headers, String index,
-                                          DocumentStoreInterface documentStore) {
+      HttpHeaders headers, String index, DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
 
-    logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "GET", (request != null)
-        ? request.getRequestURL().toString() : "");
+    logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "GET",
+        (request != null) ? request.getRequestURL().toString() : "");
     if (logger.isDebugEnabled()) {
       logger.debug("Request Body: " + content);
     }
@@ -371,14 +365,13 @@ public class DocumentApi {
   }
 
   public Response processSearchWithPost(String content, HttpServletRequest request,
-                                        HttpHeaders headers, String index,
-                                        DocumentStoreInterface documentStore) {
+      HttpHeaders headers, String index, DocumentStoreInterface documentStore) {
 
     // Initialize the MDC Context for logging purposes.
     ApiUtils.initMdcContext(request, headers);
 
-    logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST", (request != null)
-        ? request.getRequestURL().toString() : "");
+    logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST",
+        (request != null) ? request.getRequestURL().toString() : "");
     if (logger.isDebugEnabled()) {
       logger.debug("Request Body: " + content);
     }
@@ -386,18 +379,33 @@ public class DocumentApi {
     return processQuery(index, content, request, headers, documentStore);
   }
 
+  public Response processSuggestQueryWithPost(String content, HttpServletRequest request,
+      HttpHeaders headers, String index, DocumentStoreInterface documentStore) {
+
+    // Initialize the MDC Context for logging purposes.
+    ApiUtils.initMdcContext(request, headers);
+
+    logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST",
+        (request != null) ? request.getRequestURL().toString() : "");
+    if (logger.isDebugEnabled()) {
+      logger.debug("Request Body: " + content);
+    }
+
+    return processSuggestQuery(index, content, request, headers, documentStore);
+  }
+
   /**
-   * Common handler for query requests. This is called by both the GET with
-   * payload and POST with payload variants of the query endpoint.
+   * Common handler for query requests. This is called by both the GET with payload and POST with
+   * payload variants of the query endpoint.
    *
-   * @param index   - The index to be queried against.
+   * @param index - The index to be queried against.
    * @param content - The payload containing the query structure.
    * @param request - The HTTP request.
    * @param headers - The HTTP headers.
    * @return - A standard HTTP response.
    */
   private Response processQuery(String index, String content, HttpServletRequest request,
-                                HttpHeaders headers, DocumentStoreInterface documentStore) {
+      HttpHeaders headers, DocumentStoreInterface documentStore) {
 
     try {
       ObjectMapper mapper = new ObjectMapper();
@@ -415,9 +423,7 @@ public class DocumentApi {
             ApiUtils.SEARCH_AUTH_POLICY_NAME);
 
       } catch (Exception e) {
-        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
-            "processQuery",
-            e.getMessage());
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processQuery", e.getMessage());
         return handleError(request, content, Status.FORBIDDEN);
       }
 
@@ -438,8 +444,8 @@ public class DocumentApi {
 
       // Now, submit the search statement, translated into
       // ElasticSearch syntax, to the document store DAO.
-      SearchOperationResult result = documentStore.searchWithPayload(index,
-          searchStatement.toElasticSearch());
+      SearchOperationResult result =
+          documentStore.searchWithPayload(index, searchStatement.toElasticSearch());
       String output = null;
       if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
         output = prepareOutput(mapper, result);
@@ -461,37 +467,124 @@ public class DocumentApi {
     }
   }
 
-  
   /**
-   * Checks the supplied HTTP headers to see if we should allow the underlying document 
-   * store to implicitly create the index referenced in a document PUT or POST if it
-   * does not already exist in the data store.
+   * Common handler for query requests. This is called by both the GET with payload and POST with
+   * payload variants of the query endpoint.
+   *
+   * @param index - The index to be queried against.
+   * @param content - The payload containing the query structure.
+   * @param request - The HTTP request.
+   * @param headers - The HTTP headers.
+   * @return - A standard HTTP response.
+   */
+  private Response processSuggestQuery(String index, String content, HttpServletRequest request,
+      HttpHeaders headers, DocumentStoreInterface documentStore) {
+
+    try {
+      ObjectMapper mapper = new ObjectMapper();
+      mapper.setSerializationInclusion(Include.NON_EMPTY);
+
+      // Make sure that we were supplied a payload before proceeding.
+      if (content == null) {
+        return handleError(request, content, Status.BAD_REQUEST);
+      }
+
+      // Validate that the request has the appropriate authorization.
+      boolean isValid;
+      try {
+        isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
+            ApiUtils.SEARCH_AUTH_POLICY_NAME);
+
+      } catch (Exception e) {
+        logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processQuery", e.getMessage());
+        return handleError(request, content, Status.FORBIDDEN);
+      }
+
+      if (!isValid) {
+        return handleError(request, content, Status.FORBIDDEN);
+      }
+
+      SuggestionStatement suggestionStatement;
+
+      try {
+        // Marshall the supplied request payload into a search statement
+        // object.
+        suggestionStatement = mapper.readValue(content, SuggestionStatement.class);
+
+      } catch (Exception e) {
+        return handleError(request, e.getMessage(), Status.BAD_REQUEST);
+      }
+
+      // Now, submit the search statement, translated into
+      // ElasticSearch syntax, to the document store DAO.
+      SearchOperationResult result =
+          documentStore.suggestionQueryWithPayload(index, suggestionStatement.toElasticSearch());
+      String output = null;
+      if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
+        output = prepareSuggestOutput(mapper, result);
+      } else {
+        output = result.getError() != null
+            ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
+            : result.getFailureCause();
+      }
+      Response response = Response.status(result.getResultCode()).entity(output).build();
+
+      // Clear the MDC context so that no other transaction inadvertently
+      // uses our transaction id.
+      ApiUtils.clearMdcContext();
+
+      return response;
+
+    } catch (Exception e) {
+      return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
+    }
+  }
+
+  /**
+   * Checks the supplied HTTP headers to see if we should allow the underlying document store to
+   * implicitly create the index referenced in a document PUT or POST if it does not already exist
+   * in the data store.
    * 
    * @param headers - The HTTP headers to examine.
    * 
-   * @return - true if the headers indicate that missing indices should be implicitly created,
-   *           false otherwise.
+   * @return - true if the headers indicate that missing indices should be implicitly created, false
+   *         otherwise.
    */
   private boolean implicitlyCreateIndex(HttpHeaders headers) {
-    
+
     boolean createIndexIfNotPresent = false;
-    String implicitIndexCreationHeader = 
+    String implicitIndexCreationHeader =
         headers.getRequestHeaders().getFirst(REQUEST_HEADER_ALLOW_IMPLICIT_INDEX_CREATION);
-    
-    if( (implicitIndexCreationHeader != null) && (implicitIndexCreationHeader.equals("true")) ) {
+
+    if ((implicitIndexCreationHeader != null) && (implicitIndexCreationHeader.equals("true"))) {
       createIndexIfNotPresent = true;
     }
-    
+
     return createIndexIfNotPresent;
   }
-  
-  
+
   private String prepareOutput(ObjectMapper mapper, SearchOperationResult result)
       throws JsonProcessingException {
     StringBuffer output = new StringBuffer();
     output.append("{\r\n\"searchResult\":");
-    output.append(mapper.writerWithDefaultPrettyPrinter()
-        .writeValueAsString(result.getSearchResult()));
+    output.append(
+        mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSearchResult()));
+    AggregationResults aggs = result.getAggregationResult();
+    if (aggs != null) {
+      output.append(",\r\n\"aggregationResult\":");
+      output.append(mapper.setSerializationInclusion(Include.NON_NULL)
+          .writerWithDefaultPrettyPrinter().writeValueAsString(aggs));
+    }
+    output.append("\r\n}");
+    return output.toString();
+  }
+
+  private String prepareSuggestOutput(ObjectMapper mapper, SearchOperationResult result)
+      throws JsonProcessingException {
+    StringBuffer output = new StringBuffer();
+    output.append("{\r\n\"searchResult\":");
+    output.append(
+        mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSuggestResult()));
     AggregationResults aggs = result.getAggregationResult();
     if (aggs != null) {
       output.append(",\r\n\"aggregationResult\":");
@@ -514,8 +607,7 @@ public class DocumentApi {
         (request != null) ? request.getRemoteHost() : "", Integer.toString(status.getStatusCode()));
 
     auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST,
-        new LogFields()
-            .setField(LogLine.DefinedFields.RESPONSE_CODE, status.getStatusCode())
+        new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, status.getStatusCode())
             .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, status.getReasonPhrase()),
         (request != null) ? request.getMethod() : "",
         (request != null) ? request.getRequestURL().toString() : "",