diff options
8 files changed, 186 insertions, 6 deletions
diff --git a/src/main/java/org/onap/aai/sa/rest/IndexApi.java b/src/main/java/org/onap/aai/sa/rest/IndexApi.java index af934d1..36570dc 100644 --- a/src/main/java/org/onap/aai/sa/rest/IndexApi.java +++ b/src/main/java/org/onap/aai/sa/rest/IndexApi.java @@ -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(); + } } diff --git a/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java b/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java index dc091dd..249f6b1 100644 --- a/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java +++ b/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java @@ -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}") diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java index b0ee35c..2f3350c 100644 --- a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java @@ -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, diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java index 3c19610..ef141ec 100644 --- a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java @@ -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, diff --git a/src/test/java/org/onap/aai/sa/rest/IndexApiTest.java b/src/test/java/org/onap/aai/sa/rest/IndexApiTest.java index 6292cbe..f63ddbd 100644 --- a/src/test/java/org/onap/aai/sa/rest/IndexApiTest.java +++ b/src/test/java/org/onap/aai/sa/rest/IndexApiTest.java @@ -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 diff --git a/src/test/java/org/onap/aai/sa/rest/SearchServiceApiHarness.java b/src/test/java/org/onap/aai/sa/rest/SearchServiceApiHarness.java index 80c058d..b15ccc8 100644 --- a/src/test/java/org/onap/aai/sa/rest/SearchServiceApiHarness.java +++ b/src/test/java/org/onap/aai/sa/rest/SearchServiceApiHarness.java @@ -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}) diff --git a/src/test/java/org/onap/aai/sa/rest/StubEsController.java b/src/test/java/org/onap/aai/sa/rest/StubEsController.java index 326d03a..d5d77ab 100644 --- a/src/test/java/org/onap/aai/sa/rest/StubEsController.java +++ b/src/test/java/org/onap/aai/sa/rest/StubEsController.java @@ -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 index 0000000..1ac1cce --- /dev/null +++ b/src/test/resources/json/dynamicIndex.json @@ -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 |