Fix to not remove whitespaces in the payload to ES
[aai/search-data-service.git] / src / main / java / org / onap / aai / sa / searchdbabstraction / elasticsearch / dao / ElasticSearchHttpController.java
index 383fcb0..98a254c 100644 (file)
@@ -2,8 +2,8 @@
  * ============LICENSE_START=======================================================
  * org.onap.aai
  * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
- * Copyright © 2017 Amdocs
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * ============LICENSE_END=========================================================
- *
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
  */
 package org.onap.aai.sa.searchdbabstraction.elasticsearch.dao;
 
-import com.att.aft.dme2.internal.google.common.base.Throwables;
+import com.google.common.base.Throwables;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import edu.emory.mathcs.backport.java.util.Arrays;
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.JSONParser;
@@ -51,14 +48,17 @@ import org.onap.aai.sa.searchdbabstraction.entity.SearchOperationResult;
 import org.onap.aai.sa.searchdbabstraction.logging.SearchDbMsgs;
 import org.onap.aai.sa.searchdbabstraction.util.AggregationParsingUtil;
 import org.onap.aai.sa.searchdbabstraction.util.DocumentSchemaUtil;
+import org.onap.aai.sa.searchdbabstraction.util.ElasticSearchPayloadTranslator;
 import org.onap.aai.sa.searchdbabstraction.util.SearchDbConstants;
-import org.openecomp.cl.api.LogFields;
-import org.openecomp.cl.api.LogLine;
-import org.openecomp.cl.api.Logger;
-import org.openecomp.cl.eelf.LoggerFactory;
-import org.openecomp.cl.mdc.MdcContext;
-import org.openecomp.cl.mdc.MdcOverride;
+import org.onap.aai.cl.api.LogFields;
+import org.onap.aai.cl.api.LogLine;
+import org.onap.aai.cl.api.Logger;
+import org.onap.aai.cl.eelf.LoggerFactory;
+import org.onap.aai.cl.mdc.MdcContext;
+import org.onap.aai.cl.mdc.MdcOverride;
 import org.onap.aai.sa.rest.DocumentSchema;
+import org.onap.aai.sa.searchdbabstraction.entity.SuggestHit;
+import org.onap.aai.sa.searchdbabstraction.entity.SuggestHits;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -76,10 +76,13 @@ import java.net.ProtocolException;
 import java.net.URL;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicBoolean;
-import javax.ws.rs.core.Response.Status;
+
+import org.springframework.http.HttpStatus;
+
 
 
 /**
@@ -177,7 +180,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
         //result.setResult("{\"index\": \"" + index + ", \"type\": \"" + DEFAULT_TYPE + "\"}");
       }
 
-    } catch (DocumentStoreOperationException e) {
+    } catch (DocumentStoreOperationException | IOException e) {
 
       result.setFailureCause("Document store operation failure.  Cause: " + e.getMessage());
     }
@@ -185,6 +188,27 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
     return result;
   }
 
+  @Override
+  public OperationResult createDynamicIndex(String index, String dynamicSchema) {
+    OperationResult result = new OperationResult();
+    result.setResultCode(500);
+
+    try {
+      result = createTable(index, dynamicSchema);
+
+      // ElasticSearch will return us a 200 code on success when we
+      // want to report a 201, so translate the result here.
+      result.setResultCode((result.getResultCode() == 200) ? 201 : result.getResultCode());
+      if (isSuccess(result)) {
+        result.setResult("{\"url\": \"" + ApiUtils.buildIndexUri(index) + "\"}");
+      }
+    } catch (DocumentStoreOperationException e) {
+      result.setFailureCause("Document store operation failure.  Cause: " + e.getMessage());
+    }
+
+    return result;
+  }
+
 
   @Override
   public OperationResult deleteIndex(String indexName) throws DocumentStoreOperationException {
@@ -327,6 +351,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
 
     try {
       conn.setRequestMethod("PUT");
+      conn.setRequestProperty("Content-Type", "application/json");
     } catch (ProtocolException e) {
       shutdownConnection(conn);
       throw new DocumentStoreOperationException("Failed to set HTTP request method to PUT.", e);
@@ -342,7 +367,12 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
     sb.append(indexMappings);
     sb.append("}}");
 
-    attachContent(conn, sb.toString());
+    try {
+       attachContent(conn, ElasticSearchPayloadTranslator.translateESPayload(sb.toString()));
+    } catch(IOException e) {
+       logger.error(SearchDbMsgs.INDEX_CREATE_FAILURE, e);
+       throw new DocumentStoreOperationException(e.getMessage(), e);
+    }
 
     logger.debug("\ncreateTable(), Sending 'PUT' request to URL : " + conn.getURL());
     logger.debug("Request content: " + sb.toString());
@@ -362,29 +392,76 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
     return opResult;
   }
 
+  /**
+   * Will send the passed in JSON payload to Elasticsearch using the
+   * provided index name in an attempt to create the index.
+   *
+   * @param indexName - The name of the index to be created
+   * @param settingsAndMappings - The actual JSON object that will define the index
+   * @return - The operation result of writing into Elasticsearch
+   * @throws DocumentStoreOperationException
+   */
+  protected OperationResult createTable(String indexName, String settingsAndMappings) throws DocumentStoreOperationException {
+    OperationResult result = new OperationResult();
+    result.setResultCode(500);
+    result.setResult(INTERNAL_SERVER_ERROR_ELASTIC_SEARCH_OPERATION_FAULT);
+
+    // Grab the current time so we can use it to generate a metrics log.
+    MdcOverride override = getStartTime(new MdcOverride());
+
+    String fullUrl = getFullUrl("/" + indexName + "/", false);
+    HttpURLConnection conn = initializeConnection(fullUrl);
+
+    try {
+      conn.setRequestMethod("PUT");
+      conn.setRequestProperty("Content-Type", "application/json");
+    } catch (ProtocolException e) {
+      shutdownConnection(conn);
+      throw new DocumentStoreOperationException("Failed to set HTTP request method to PUT.", e);
+    }
+
+    try {
+       attachContent(conn, ElasticSearchPayloadTranslator.translateESPayload(settingsAndMappings));
+    } catch(IOException e) {
+       logger.error(SearchDbMsgs.INDEX_CREATE_FAILURE, e);
+       throw new DocumentStoreOperationException(e.getMessage());
+    }
+    handleResponse(conn, result);
+
+    // Generate a metrics log so we can track how long the operation took.
+    metricsLogger.info(SearchDbMsgs.CREATE_INDEX_TIME,
+            new LogFields()
+                    .setField(LogLine.DefinedFields.RESPONSE_CODE, result.getResultCode())
+                    .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResultCode()),
+            override,
+            indexName);
+
+    return result;
+  }
+
   @Override
-  public DocumentOperationResult createDocument(String                  indexName, 
+  public DocumentOperationResult createDocument(String                  indexName,
                                                 DocumentStoreDataEntity document,
                                                 boolean                 allowImplicitIndexCreation)
       throws DocumentStoreOperationException {
-    
+
     if(!allowImplicitIndexCreation) {
-      
+
       // Before we do anything, make sure that the specified index actually exists in the
       // document store - we don't want to rely on ElasticSearch to fail the document
       // create because it could be configured to implicitly create a non-existent index,
       // which can lead to hard-to-debug behaviour with queries down the road.
       OperationResult indexExistsResult = checkIndexExistence(indexName);
       if ((indexExistsResult.getResultCode() < 200) || (indexExistsResult.getResultCode() >= 300)) {
-  
+
         DocumentOperationResult opResult = new DocumentOperationResult();
-        opResult.setResultCode(Status.NOT_FOUND.getStatusCode());
+        opResult.setResultCode(HttpStatus.NOT_FOUND.value());
         opResult.setResult("Document Index '" + indexName + "' does not exist.");
         opResult.setFailureCause("Document Index '" + indexName + "' does not exist.");
         return opResult;
       }
     }
-    
+
     if (document.getId() == null || document.getId().isEmpty()) {
       return createDocumentWithoutId(indexName, document);
     } else {
@@ -399,13 +476,13 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
     DocumentOperationResult opResult = checkDocumentExistence(indexName, document.getId());
 
 
-    if (opResult.getResultCode() != Status.NOT_FOUND.getStatusCode()) {
-      if (opResult.getResultCode() == Status.OK.getStatusCode()) {
+    if (opResult.getResultCode() != HttpStatus.NOT_FOUND.value()) {
+      if (opResult.getResultCode() == HttpStatus.CONFLICT.value()) {
         opResult.setFailureCause("A document with the same id already exists.");
       } else {
         opResult.setFailureCause("Failed to verify a document with the specified id does not already exist.");
       }
-      opResult.setResultCode(Status.CONFLICT.getStatusCode());
+      opResult.setResultCode(HttpStatus.CONFLICT.value());
       return opResult;
     }
 
@@ -493,9 +570,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
 
   private void attachDocument(HttpURLConnection conn, DocumentStoreDataEntity doc)
       throws DocumentStoreOperationException {
-    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+//    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+    conn.setRequestProperty("Content-Type", "application/json");
     conn.setRequestProperty("Connection", "Close");
-
     attachContent(conn, doc.getContentInJson());
   }
 
@@ -549,28 +626,28 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
   }
 
   @Override
-  public DocumentOperationResult updateDocument(String                  indexName, 
+  public DocumentOperationResult updateDocument(String                  indexName,
                                                 DocumentStoreDataEntity document,
                                                 boolean                 allowImplicitIndexCreation)
       throws DocumentStoreOperationException {
-    
+
     if(!allowImplicitIndexCreation) {
-      
+
       // Before we do anything, make sure that the specified index actually exists in the
       // document store - we don't want to rely on ElasticSearch to fail the document
       // create because it could be configured to implicitly create a non-existent index,
       // which can lead to hard-to-debug behaviour with queries down the road.
       OperationResult indexExistsResult = checkIndexExistence(indexName);
       if ((indexExistsResult.getResultCode() < 200) || (indexExistsResult.getResultCode() >= 300)) {
-  
+
         DocumentOperationResult opResult = new DocumentOperationResult();
-        opResult.setResultCode(Status.NOT_FOUND.getStatusCode());
+        opResult.setResultCode(HttpStatus.NOT_FOUND.value());
         opResult.setResult("Document Index '" + indexName + "' does not exist.");
         opResult.setFailureCause("Document Index '" + indexName + "' does not exist.");
         return opResult;
       }
     }
-    
+
     DocumentOperationResult opResult = new DocumentOperationResult();
 
     // Initialize operation result with a failure codes / fault string
@@ -586,6 +663,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
 
     try {
       conn.setRequestMethod("PUT");
+      conn.setRequestProperty("Content-Type", "application/json");
     } catch (ProtocolException e) {
       shutdownConnection(conn);
       throw new DocumentStoreOperationException("Failed to set HTTP request method to PUT.", e);
@@ -759,6 +837,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
 
     try {
       conn.setRequestMethod("POST");
+      conn.setRequestProperty("Content-Type", "application/json");
     } catch (ProtocolException e) {
       shutdownConnection(conn);
       throw new DocumentStoreOperationException("Failed to set HTTP request method to POST.", e);
@@ -785,6 +864,53 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
     return opResult;
   }
 
+
+  public SearchOperationResult suggestionQueryWithPayload(String indexName, String query)
+          throws DocumentStoreOperationException {
+
+    SearchOperationResult opResult = new SearchOperationResult();
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Querying Suggestion index: " + indexName + " with query string: " + query);
+    }
+
+    // Initialize operation result with a failure codes / fault string
+    opResult.setResultCode(500);
+    opResult.setResult(INTERNAL_SERVER_ERROR_ELASTIC_SEARCH_OPERATION_FAULT);
+
+    String fullUrl = getFullUrl("/" + indexName + "/_suggest", false);
+
+    // Grab the current time so we can use it to generate a metrics log.
+    MdcOverride override = getStartTime(new MdcOverride());
+
+    HttpURLConnection conn = initializeConnection(fullUrl);
+
+    try {
+      conn.setRequestMethod("POST");
+      conn.setRequestProperty("Content-Type", "application/json");
+    } catch (ProtocolException e) {
+      shutdownConnection(conn);
+      throw new DocumentStoreOperationException("Failed to set HTTP request method to POST.", e);
+    }
+
+    attachContent(conn, query);
+
+    logger.debug("\nsearch(), Sending 'POST' request to URL : " + conn.getURL());
+    logger.debug("Request body =  Elasticsearch query = " + query);
+
+    handleResponse(conn, opResult);
+    buildSuggestResult(opResult, indexName);
+
+    metricsLogger.info(SearchDbMsgs.QUERY_DOCUMENT_TIME,
+            new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode())
+                    .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()),
+            override, indexName, query);
+
+    shutdownConnection(conn);
+
+    return opResult;
+  }
+
   private void attachContent(HttpURLConnection conn, String content)
       throws DocumentStoreOperationException {
     OutputStream outputStream = null;
@@ -871,8 +997,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
       throw new DocumentStoreOperationException("Failed getting the response body payload.", e);
     }
 
-    if (resultCode == Status.CONFLICT.getStatusCode()) {
-      opResult.setResultCode(Status.PRECONDITION_FAILED.getStatusCode());
+    if (resultCode == HttpStatus.CONFLICT.value()) {
+      opResult.setResultCode(HttpStatus.PRECONDITION_FAILED.value());
     } else {
       opResult.setResultCode(resultCode);
     }
@@ -1631,4 +1757,64 @@ public class ElasticSearchHttpController implements DocumentStoreInterface {
 
   }
 
-}
+  private void buildSuggestResult(SearchOperationResult result, String index)
+          throws DocumentStoreOperationException {
+
+    JSONParser parser = new JSONParser ();
+    JSONObject root;
+    try {
+      root = (JSONObject) parser.parse ( result.getResult () );
+      if (result.getResultCode () >= 200 && result.getResultCode () <= 299) {
+        JSONArray hitArray = (JSONArray) root.get ( "suggest-vnf" );
+        JSONObject hitdata = (JSONObject) hitArray.get ( 0 );
+        JSONArray optionsArray = (JSONArray) hitdata.get ( "options" );
+        SuggestHits suggestHits = new SuggestHits ();
+        suggestHits.setTotalHits ( String.valueOf ( optionsArray.size () ) );
+
+        ArrayList<SuggestHit> suggestHitArray = new ArrayList<SuggestHit> ();
+
+        for (int i = 0; i < optionsArray.size (); i++) {
+          JSONObject hit = (JSONObject) optionsArray.get ( i );
+
+          SuggestHit suggestHit = new SuggestHit ();
+          suggestHit.setScore ( (hit.get ( "score" ) != null) ? hit.get ( "score" ).toString () : "" );
+          suggestHit.setText ( (hit.get ( "text" ) != null) ? hit.get ( "text" ).toString () : "" );
+          Document doc = new Document ();
+          if (hit.get ( "_version" ) != null) {
+            doc.setEtag ( (hit.get ( "_version" ) != null) ? hit.get ( "_version" ).toString () : "" );
+          }
+          doc.setUrl ( buildDocumentResponseUrl ( index,
+                  (hit.get ( "_id" ) != null) ? hit.get ( "_id" ).toString () : "" ) );
+
+          doc.setContent ( (JSONObject) hit.get ( "payload" ) );
+          suggestHit.setDocument ( doc );
+          suggestHitArray.add ( suggestHit );
+        }
+        suggestHits.setHits ( suggestHitArray.toArray ( new SuggestHit[suggestHitArray.size ()] ) );
+        result.setSuggestResult ( suggestHits );
+
+        JSONObject aggregations = (JSONObject) root.get ( "aggregations" );
+        if (aggregations != null) {
+          AggregationResult[] aggResults =
+                  AggregationParsingUtil.parseAggregationResults ( aggregations );
+          AggregationResults aggs = new AggregationResults ();
+          aggs.setAggregations ( aggResults );
+          result.setAggregationResult ( aggs );
+        }
+
+        // success
+      } else {
+        JSONObject error = (JSONObject) root.get ( "error" );
+        if (error != null) {
+          result.setError (
+                  new ErrorResult ( error.get ( "type" ).toString (), error.get ( "reason" ).toString () ) );
+        }
+      }
+    } catch (Exception e) {
+      throw new DocumentStoreOperationException (
+              "Failed to parse Elastic Search response." + result.getResult () );
+    }
+  }
+
+
+  }