Adding pass through shema creation 51/36251/1
authorrv871f <richard.vondadelszen@amdocs.com>
Fri, 16 Mar 2018 15:59:32 +0000 (11:59 -0400)
committerrv871f <richard.vondadelszen@amdocs.com>
Fri, 16 Mar 2018 16:02:30 +0000 (12:02 -0400)
Issue-ID: AAI-888
Change-Id: Ifc094758682b39a5f2c5607bccd418de6e36d841
Signed-off-by: rv871f <richard.vondadelszen@amdocs.com>
src/main/java/org/onap/aai/sa/rest/IndexApi.java
src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java
src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java
src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java
src/test/java/org/onap/aai/sa/rest/IndexApiTest.java
src/test/java/org/onap/aai/sa/rest/SearchServiceApiHarness.java
src/test/java/org/onap/aai/sa/rest/StubEsController.java
src/test/resources/json/dynamicIndex.json [new file with mode: 0644]

index af934d1..36570dc 100644 (file)
@@ -46,6 +46,8 @@ import javax.ws.rs.core.Response;
  */
 public class IndexApi {
 
+  private static final String HEADER_VALIDATION_SUCCESS = "SUCCESS";
+  
   protected SearchServiceApi searchService = null;
 
   /**
@@ -188,6 +190,39 @@ public class IndexApi {
     // Finally, return the response.
     return response;
   }
+  
+  /**
+   * This function accepts any JSON and will "blindly" write it to the 
+   * document store.
+   * 
+   * Note, eventually this "dynamic" flow should follow the same JSON-Schema
+   * validation procedure as the normal create index flow.
+   * 
+   * @param dynamicSchema - The JSON string that will be sent to the document store.
+   * @param index - The name of the index to be created.
+   * @param documentStore - The document store specific interface.
+   * @return The result of the document store interface's operation.
+   */
+  public Response processCreateDynamicIndex(String dynamicSchema, HttpServletRequest request,
+      HttpHeaders headers, String index, DocumentStoreInterface documentStore) {
+
+    Response response = null;
+
+    Response validationResponse = validateRequest(request, headers, index, SearchDbMsgs.INDEX_CREATE_FAILURE);
+
+    if (validationResponse.getStatus() != Response.Status.OK.getStatusCode()) {
+      response = validationResponse;
+    } else {
+      OperationResult result = documentStore.createDynamicIndex(index, dynamicSchema);
+
+      int resultCode = (result.getResultCode() == 200) ? 201 : result.getResultCode();
+      String resultString = (result.getFailureCause() == null) ? result.getResult() : result.getFailureCause();
+
+      response = Response.status(resultCode).entity(resultString).build();
+    }
+
+    return response;
+  }
 
 
   /**
@@ -227,7 +262,6 @@ public class IndexApi {
       return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
     }
 
-
     try {
       // Send the request to the document store.
       response = responseFromOperationResult(documentStore.deleteIndex(index));
@@ -237,8 +271,7 @@ public class IndexApi {
           .entity(e.getMessage())
           .build();
     }
-
-
+    
     // Log the result.
     if ((response.getStatus() >= 200) && (response.getStatus() < 300)) {
       logger.info(SearchDbMsgs.DELETED_INDEX, index);
@@ -372,6 +405,26 @@ public class IndexApi {
         .entity(msg)
         .build();
   }
-
-
+  
+  /**
+   * A helper method used for validating/authenticating an incoming request.
+   * 
+   * @param request - The http request that will be validated.
+   * @param headers - The http headers that will be validated.
+   * @param index - The name of the index that the document store request is being made against.
+   * @param failureMsgEnum - The logging message to be used upon validation failure.
+   * @return A success or failure response
+   */
+  private Response validateRequest(HttpServletRequest request, HttpHeaders headers, String index, SearchDbMsgs failureMsgEnum) {
+    try {
+      if (!searchService.validateRequest(headers, request, ApiUtils.Action.POST, ApiUtils.SEARCH_AUTH_POLICY_NAME)) {
+        logger.warn(failureMsgEnum, index, "Authentication failure.");
+        return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
+      }
+    } catch (Exception e) {
+      logger.warn(failureMsgEnum, index, "Unexpected authentication failure - cause: " + e.getMessage());
+      return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
+    }
+    return Response.status(Response.Status.OK).entity(HEADER_VALIDATION_SUCCESS).build();
+  }
 }
index dc091dd..249f6b1 100644 (file)
@@ -86,6 +86,18 @@ public class SearchServiceApi {
     return indexApi.processCreateIndex(requestBody, request, headers, index, documentStore);
   }
 
+  @PUT
+  @Path("/indexes/dynamic/{index}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  public Response processCreateDynamicIndex(String requestBody,
+                                     @Context HttpServletRequest request,
+                                     @Context HttpHeaders headers,
+                                     @PathParam("index") String index) {
+
+    // Forward the request to our index API to create the index.
+    IndexApi indexApi = new IndexApi(this);
+    return indexApi.processCreateDynamicIndex(requestBody, request, headers, index, documentStore);
+  }
 
   @DELETE
   @Path("/indexes/{index}")
index b0ee35c..2f3350c 100644 (file)
@@ -33,6 +33,8 @@ public interface DocumentStoreInterface {
 
   public OperationResult createIndex(String index, DocumentSchema documentSchema);
 
+  public OperationResult createDynamicIndex(String index, String dynamicSchema);
+
   public OperationResult deleteIndex(String indexName) throws DocumentStoreOperationException;
 
   public DocumentOperationResult createDocument(String indexName, 
index 3c19610..ef141ec 100644 (file)
@@ -183,6 +183,26 @@ 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 {
@@ -360,6 +380,47 @@ 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");
+    } catch (ProtocolException e) {
+      shutdownConnection(conn);
+      throw new DocumentStoreOperationException("Failed to set HTTP request method to PUT.", e);
+    }
+    
+    attachContent(conn, settingsAndMappings);
+    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, 
                                                 DocumentStoreDataEntity document,
index 6292cbe..f63ddbd 100644 (file)
@@ -45,7 +45,7 @@ public class IndexApiTest extends JerseyTest {
 
   private final String TOP_URI = "/test/indexes/";
   private final String SIMPLE_DOC_SCHEMA_JSON = "src/test/resources/json/simpleDocument.json";
-
+  private final String DYNAMIC_INDEX_PAYLOAD = "src/test/resources/json/dynamicIndex.json";
 
   @Override
   protected Application configure() {
@@ -166,6 +166,23 @@ public class IndexApiTest extends JerseyTest {
         tokenizedResult[2].equals(EXPECTED_MAPPINGS));
   }
 
+  /**
+   * Tests the dynamic shcema creation flow that send the request
+   * JSON to the data store without any JSON validation against a schema
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void createDynamicIndexTest() throws IOException {
+    String indexName = "super-ultra-dynamic-mega-index";
+    String dynamicUri = TOP_URI + "dynamic/";
+    File indexFile = new File(DYNAMIC_INDEX_PAYLOAD);
+    String indexPayload = TestUtils.readFileToString(indexFile);
+    
+    String result = target(dynamicUri + indexName).request().put(Entity.json(indexPayload), String.class);
+
+    assertEquals(indexPayload, result);
+  }
 
   /**
    * This test validates that a 'create index' request with an improperly
index 80c058d..b15ccc8 100644 (file)
@@ -63,6 +63,18 @@ public class SearchServiceApiHarness extends SearchServiceApi {
     return super.processCreateIndex(requestBody, request, headers, index);
   }
 
+  @PUT
+  @Path("/indexes/dynamic/{index}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Override
+  public Response processCreateDynamicIndex(String requestBody,
+                                     @Context HttpServletRequest request,
+                                     @Context HttpHeaders headers,
+                                     @PathParam("index") String index) {
+
+    return super.processCreateDynamicIndex(requestBody, request, headers, index);
+  }
+
   @DELETE
   @Path("/indexes/{index}")
   @Consumes({MediaType.APPLICATION_JSON})
index 326d03a..d5d77ab 100644 (file)
@@ -73,6 +73,14 @@ public class StubEsController implements DocumentStoreInterface {
     return opResult;
   }
 
+  @Override
+  public OperationResult createDynamicIndex(String index, String dynamicSchema) {
+    OperationResult opResult = new OperationResult();
+    opResult.setResultCode(200);
+    // Directly return the json as this flow should not edit the json in any way
+    opResult.setResult(dynamicSchema);
+    return opResult;
+  }
 
   @Override
   public OperationResult deleteIndex(String indexName) throws DocumentStoreOperationException {
diff --git a/src/test/resources/json/dynamicIndex.json b/src/test/resources/json/dynamicIndex.json
new file mode 100644 (file)
index 0000000..1ac1cce
--- /dev/null
@@ -0,0 +1,15 @@
+{
+       "mappings": {
+               "dynamic_templates": [{
+                               "strings": {
+                                       "match_mapping_type": "string",
+                                       "match": "*",
+                                       "mapping": {
+                                               "type": "string",
+                                               "index": "not_analyzed"
+                                       }
+                               }
+                       }
+               ]
+       }
+}
\ No newline at end of file